Merge QQ3A.200605.002 into master

Bug: 158095402
Merged-In: I26f7303c27fd5d14a8668137d32bb554ab4b47e7
Change-Id: I655d550990b2e32a11682ee26145465b310d159b
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 481589c..99cf388 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -16,6 +16,7 @@
 
 <manifest
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
     package="com.android.car.dialer">
 
     <uses-sdk android:minSdkVersion="24" android:targetSdkVersion='24'/>
@@ -38,7 +39,8 @@
         android:name=".DialerApplication"
         android:directBootAware="true"
         android:label="@string/phone_app_name"
-        android:icon="@drawable/ic_app_icon">
+        android:icon="@drawable/ic_app_icon"
+        android:supportsRtl="true">
 
         <activity android:name=".ui.TelecomActivity"
                   android:launchMode="singleTask"
@@ -109,12 +111,6 @@
             android:permission="android.permission.BIND_JOB_SERVICE">
         </service>
 
-        <!-- BroadcastReceiver for receiving Intents from Notification mechanism. -->
-        <receiver
-            android:directBootAware="true"
-            android:exported="false"
-            android:name="com.android.car.dialer.notification.NotificationReceiver"/>
-
         <receiver
             android:directBootAware="true"
             android:name="com.android.car.dialer.notification.MissedCallReceiver">
@@ -130,5 +126,14 @@
                 <action android:name="android.bluetooth.device.action.BOND_STATE_CHANGED"/>
             </intent-filter>
         </receiver>
+
+        <!-- Workaround for b/113294940, remove when converting to Android.bp -->
+        <provider
+            android:name="androidx.lifecycle.ProcessLifecycleOwnerInitializer"
+            tools:replace="android:authorities"
+            android:authorities="${applicationId}"
+            android:exported="false"
+            android:multiprocess="true" />
+
     </application>
 </manifest>
diff --git a/res/color/contact_details_icon_tint.xml b/res/color/contact_details_icon_tint.xml
new file mode 100644
index 0000000..3d694c3
--- /dev/null
+++ b/res/color/contact_details_icon_tint.xml
@@ -0,0 +1,26 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!--
+  Copyright (C) 2019 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="@color/primary_icon_color"
+          android:state_enabled="true"
+          android:state_activated="false"/>
+    <item android:color="#FFFFFF"
+          android:state_enabled="true"
+          android:state_activated="true"/>
+    <item android:color="#80FFFFFF"
+          android:state_enabled="false"/>
+</selector>
diff --git a/res/color/icon_accent_activatable.xml b/res/color/icon_accent_activatable.xml
index cf4b9e7..c652f07 100644
--- a/res/color/icon_accent_activatable.xml
+++ b/res/color/icon_accent_activatable.xml
@@ -16,6 +16,6 @@
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:color="@color/primary_icon_color"
           android:state_activated="false"/>
-    <item android:color="?android:colorAccent"
+    <item android:color="@color/audio_output_accent"
           android:state_activated="true"/>
 </selector>
diff --git a/res/color/text_accent_activatable.xml b/res/color/text_accent_activatable.xml
deleted file mode 100644
index 4f1dc0f..0000000
--- a/res/color/text_accent_activatable.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version='1.0' encoding='UTF-8'?>
-<!-- Copyright (C) 2019 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:color="@color/primary_text_color"
-          android:state_activated="false"/>
-    <item android:color="?android:colorAccent"
-          android:state_activated="true"/>
-</selector>
diff --git a/res/drawable/ic_arrow_right.xml b/res/drawable/ic_arrow_right.xml
index 051b6a0..877c510 100644
--- a/res/drawable/ic_arrow_right.xml
+++ b/res/drawable/ic_arrow_right.xml
@@ -18,7 +18,8 @@
         android:viewportHeight="48"
         android:width="@dimen/primary_icon_size"
         android:height="@dimen/primary_icon_size"
-        android:tint="@color/icon_tint_state_list">
+        android:tint="@color/icon_tint_state_list"
+        android:autoMirrored="true">
 
     <path android:pathData="M0-.25h48v48H0z"/>
     <path
diff --git a/res/drawable/ic_favorite_activatable.xml b/res/drawable/ic_favorite_activatable.xml
index 7285513..d40add4 100644
--- a/res/drawable/ic_favorite_activatable.xml
+++ b/res/drawable/ic_favorite_activatable.xml
@@ -15,7 +15,11 @@
 -->
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:drawable="@drawable/ic_favorite_empty"
-          android:state_activated="false"/>
+          android:state_activated="false" android:state_enabled="true"/>
     <item android:drawable="@drawable/ic_favorite"
-          android:state_activated="true"/>
+          android:state_activated="true" android:state_enabled="true"/>
+    <item android:drawable="@drawable/ic_favorite"
+          android:state_activated="true" android:state_enabled="false"/>
+    <item android:drawable="@drawable/ic_imported_favorite"
+          android:state_activated="false" android:state_enabled="false"/>
 </selector>
diff --git a/res/drawable/ic_imported_favorite.xml b/res/drawable/ic_imported_favorite.xml
new file mode 100644
index 0000000..c6858bb
--- /dev/null
+++ b/res/drawable/ic_imported_favorite.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2020 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="@dimen/primary_icon_size"
+        android:height="@dimen/primary_icon_size"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path android:pathData="M17 3H7c-1.1 0-1.99.9-1.99 2L5 21l7-3 7 3V5c0-1.1-.9-2-2-2z"
+          android:fillColor="#ff000000"/>
+</vector>
diff --git a/res/layout-port/dialpad_fragment.xml b/res/layout-port/dialpad_fragment.xml
index fffbf70..fd046c1 100644
--- a/res/layout-port/dialpad_fragment.xml
+++ b/res/layout-port/dialpad_fragment.xml
@@ -16,16 +16,22 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent">
 
+    <androidx.constraintlayout.widget.Guideline
+        android:id="@+id/type_down_guideline"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        app:layout_constraintGuide_percent="@dimen/config_type_down_guideline_percent"/>
+
     <fragment
         android:id="@+id/dialpad_fragment"
         android:name="com.android.car.dialer.ui.dialpad.KeypadFragment"
         android:layout_height="wrap_content"
         android:layout_width="wrap_content"
-        android:layout_marginTop="@dimen/keypad_margin"
-        app:layout_constraintTop_toBottomOf="@+id/title"
-        app:layout_constraintBottom_toTopOf="@+id/call_button"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintEnd_toEndOf="parent"/>
+        android:layout_marginStart="@dimen/dialpad_info_margin_start"
+        app:layout_constraintTop_toBottomOf="@id/type_down_guideline"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toStartOf="parent"/>
 
     <TextView
         android:id="@+id/title"
@@ -37,9 +43,8 @@
         android:autoSizeMinTextSize="@dimen/dialpad_info_title_text_size_min"
         android:autoSizeMaxTextSize="@dimen/dialpad_info_title_text_size_max"
         app:layout_constraintVertical_chainStyle="packed"
-        app:layout_constraintTop_toTopOf="parent"
-        app:layout_constraintBottom_toTopOf="@id/dialpad_fragment"
-        app:layout_constraintStart_toStartOf="@id/dialpad_fragment"
+        app:layout_constraintTop_toTopOf="@id/dialpad_fragment"
+        app:layout_constraintStart_toEndOf="@id/dialpad_fragment"
         app:layout_constraintEnd_toStartOf="@+id/delete_button"
         app:layout_goneMarginEnd="@dimen/dialpad_info_edge_padding_size"/>
 
@@ -48,19 +53,10 @@
         style="@style/DialpadSecondaryButton"
         android:src="@drawable/ic_backspace"
         android:layout_marginStart="@dimen/dialpad_info_title_padding_size"
-        android:layout_marginEnd="@dimen/dialpad_info_edge_padding_size"
+        android:layout_marginEnd="@dimen/dialpad_info_margin_end"
         app:layout_constraintTop_toTopOf="@id/title"
         app:layout_constraintBottom_toBottomOf="@id/title"
         app:layout_constraintStart_toEndOf="@id/title"
-        app:layout_constraintEnd_toEndOf="@id/dialpad_fragment"/>
-
-    <include
-        layout="@layout/dialpad_user_profile"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        app:layout_constraintTop_toBottomOf="@id/title"
-        app:layout_constraintBottom_toTopOf="@id/dialpad_fragment"
-        app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintEnd_toEndOf="parent"/>
 
     <ImageView
@@ -70,7 +66,32 @@
         android:layout_height="@dimen/call_button_height"
         android:src="@drawable/icon_call_button"
         android:layout_marginBottom="@dimen/call_button_bottom_margin"
-        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintBottom_toBottomOf="@id/dialpad_fragment"
+        app:layout_constraintStart_toEndOf="@id/dialpad_fragment"
+        app:layout_constraintEnd_toEndOf="parent"/>
+
+    <View
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/dialpad_line_divider_height"
+        android:background="@color/divider_color"
+        android:layout_marginStart="@dimen/horizontal_divider_inset"
+        android:layout_marginEnd="@dimen/horizontal_divider_inset"
+        app:layout_constraintTop_toBottomOf="@id/type_down_guideline"/>
+
+    <include
+        layout="@layout/dialpad_user_profile"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        app:layout_constraintTop_toBottomOf="@id/title"
+        app:layout_constraintStart_toEndOf="@id/dialpad_fragment"
+        app:layout_constraintEnd_toEndOf="parent"/>
+
+    <include
+        layout="@layout/list_fragment"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toTopOf="@id/type_down_guideline"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintEnd_toEndOf="parent"/>
 
diff --git a/res/layout-w1280dp/type_down_list_item.xml b/res/layout-w1280dp/type_down_list_item.xml
new file mode 100644
index 0000000..fec8a53
--- /dev/null
+++ b/res/layout-w1280dp/type_down_list_item.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<androidx.constraintlayout.widget.ConstraintLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/contact_result"
+    android:foreground="?android:attr/selectableItemBackground"
+    android:layout_width="match_parent"
+    android:layout_height="@dimen/type_down_list_item_height">
+
+    <ImageView
+        android:id="@+id/contact_picture"
+        android:layout_width="@dimen/avatar_icon_size"
+        android:layout_height="@dimen/avatar_icon_size"
+        android:layout_marginStart="@dimen/contact_result_avatar_margin"
+        android:scaleType="centerCrop"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toStartOf="parent"/>
+
+    <TextView
+        android:id="@+id/contact_name"
+        android:layout_marginStart="@dimen/contact_result_name_margin"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:singleLine="true"
+        android:textAppearance="@style/TextAppearance.ContactResultTitle"
+        android:duplicateParentState="true"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toTopOf="@+id/phone_number"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"/>
+
+    <TextView
+        android:id="@+id/phone_number"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:singleLine="true"
+        app:layout_constraintTop_toBottomOf="@id/contact_name"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toStartOf="@id/contact_name"/>
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/res/layout/add_favorite_number_list_item.xml b/res/layout/add_favorite_number_list_item.xml
deleted file mode 100644
index ffe5445..0000000
--- a/res/layout/add_favorite_number_list_item.xml
+++ /dev/null
@@ -1,60 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2019 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<androidx.constraintlayout.widget.ConstraintLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    android:layout_width="match_parent"
-    android:layout_height="@dimen/add_favorite_number_list_height"
-    android:background="?android:attr/selectableItemBackground"
-    android:paddingStart="@dimen/add_favorite_number_list_padding"
-    android:paddingEnd="@dimen/add_favorite_number_list_padding">
-
-    <TextView
-        android:id="@+id/phone_number"
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:textAppearance="@style/TextAppearance.AddFavoriteNumberTitle"
-        android:duplicateParentState="true"
-        app:layout_constraintVertical_chainStyle="packed"
-        app:layout_constraintTop_toTopOf="parent"
-        app:layout_constraintBottom_toTopOf="@+id/phone_number_description"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintEnd_toStartOf="@+id/phone_number_checkbox"/>
-
-    <TextView
-        android:id="@+id/phone_number_description"
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:textAppearance="@style/TextAppearance.AddFavoriteNumberSubtitle"
-        android:duplicateParentState="true"
-        app:layout_constraintTop_toBottomOf="@id/phone_number"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintEnd_toStartOf="@id/phone_number_checkbox"/>
-
-    <ImageView
-        android:id="@+id/phone_number_checkbox"
-        android:src="@drawable/ic_favorite_activatable"
-        android:layout_width="@dimen/primary_icon_size"
-        android:layout_height="@dimen/primary_icon_size"
-        android:tint="@color/primary_icon_selector"
-        android:duplicateParentState="true"
-        app:layout_constraintTop_toTopOf="parent"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintEnd_toEndOf="parent"/>
-
-</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/res/layout/audio_route_list_item.xml b/res/layout/audio_route_list_item.xml
deleted file mode 100644
index c1510ef..0000000
--- a/res/layout/audio_route_list_item.xml
+++ /dev/null
@@ -1,49 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-  http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
--->
-<androidx.constraintlayout.widget.ConstraintLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    android:layout_width="match_parent"
-    android:layout_height="@dimen/audio_route_height"
-    android:background="?android:attr/selectableItemBackground"
-    android:elevation="@dimen/dialer_card_elevation">
-    <androidx.constraintlayout.widget.Guideline
-        android:id="@+id/text_start"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:orientation="vertical"
-        app:layout_constraintGuide_begin="@dimen/audio_route_constraint_guide_begin"/>
-    <ImageView
-        android:id="@+id/icon"
-        android:layout_width="@dimen/audio_route_icon_size"
-        android:layout_height="@dimen/audio_route_icon_size"
-        android:scaleType="fitCenter"
-        android:layout_marginStart="@dimen/audio_route_content_padding"
-        android:tint="@color/icon_accent_activatable"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toTopOf="parent"/>
-    <TextView
-        android:id="@+id/body"
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:textAppearance="?android:attr/textAppearanceLarge"
-        android:textColor="@color/text_accent_activatable"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintStart_toStartOf="@+id/text_start"
-        app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintTop_toTopOf="parent"/>
-</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/res/layout/audio_route_switch_dialog.xml b/res/layout/audio_route_switch_dialog.xml
deleted file mode 100644
index 7a5013c..0000000
--- a/res/layout/audio_route_switch_dialog.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<androidx.recyclerview.widget.RecyclerView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/list"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:clipChildren="false"/>
diff --git a/res/layout/contact_result.xml b/res/layout/contact_result.xml
index f6188f7..1e85899 100644
--- a/res/layout/contact_result.xml
+++ b/res/layout/contact_result.xml
@@ -39,8 +39,9 @@
         android:layout_height="wrap_content"
         android:singleLine="true"
         android:textAppearance="@style/TextAppearance.ContactResultTitle"
+        android:duplicateParentState="true"
         app:layout_constraintTop_toTopOf="parent"
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintEnd_toEndOf="parent"/>
-</androidx.constraintlayout.widget.ConstraintLayout>
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/res/layout/dialpad_fragment.xml b/res/layout/dialpad_fragment.xml
index 6627e70..a265518 100644
--- a/res/layout/dialpad_fragment.xml
+++ b/res/layout/dialpad_fragment.xml
@@ -39,9 +39,8 @@
     <FrameLayout
         android:layout_height="0dp"
         android:layout_width="0dp"
-        app:layout_constraintDimensionRatio="W, 1:1"
-        app:layout_constraintTop_toTopOf="@id/dialpad_fragment"
-        app:layout_constraintBottom_toBottomOf="@id/dialpad_fragment"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintStart_toEndOf="@id/divider"
         app:layout_constraintEnd_toEndOf="parent">
         <include layout="@layout/dialpad_info"/>
diff --git a/res/layout/dialpad_info.xml b/res/layout/dialpad_info.xml
index f182721..4219ba9 100644
--- a/res/layout/dialpad_info.xml
+++ b/res/layout/dialpad_info.xml
@@ -38,17 +38,27 @@
         android:autoSizeMaxTextSize="@dimen/dialpad_info_title_text_size_max"
         app:layout_constraintTop_toTopOf="parent"
         app:layout_constraintBottom_toBottomOf="@id/dialpad_info_guideline"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintEnd_toStartOf="@+id/delete_button"/>
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintRight_toLeftOf="@+id/delete_button"/>
 
     <ImageButton
         android:id="@+id/delete_button"
         style="@style/DialpadSecondaryButton"
         android:src="@drawable/ic_backspace"
-        android:layout_marginStart="@dimen/dialpad_info_title_padding_size"
+        android:layout_marginLeft="@dimen/dialpad_info_title_padding_size"
+        android:layout_marginRight="@dimen/dialpad_info_margin_end"
         app:layout_constraintTop_toTopOf="@id/title"
         app:layout_constraintBottom_toBottomOf="@id/title"
-        app:layout_constraintStart_toEndOf="@id/title"
+        app:layout_constraintLeft_toRightOf="@id/title"
+        app:layout_constraintRight_toRightOf="parent"/>
+
+    <ImageView
+        android:id="@+id/call_button"
+        style="@style/DialpadPrimaryButton"
+        android:src="@drawable/icon_call_button"
+        android:layout_marginBottom="@dimen/dialpad_info_edge_padding_size"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintEnd_toEndOf="parent"/>
 
     <include
@@ -57,15 +67,17 @@
         android:layout_height="0dp"
         android:layout_marginTop="@dimen/dialpad_user_profile_padding"
         app:layout_constraintTop_toBottomOf="@id/title"
-        app:layout_constraintBottom_toTopOf="@+id/call_button"
+        app:layout_constraintBottom_toTopOf="@id/call_button"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintEnd_toEndOf="parent"/>
 
-    <ImageView
-        android:id="@+id/call_button"
-        style="@style/DialpadPrimaryButton"
-        android:src="@drawable/icon_call_button"
-        app:layout_constraintBottom_toBottomOf="parent"
+    <include
+        layout="@layout/list_fragment"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_marginBottom="@dimen/dialpad_user_profile_padding"
+        app:layout_constraintTop_toBottomOf="@id/title"
+        app:layout_constraintBottom_toTopOf="@id/call_button"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintEnd_toEndOf="parent"/>
 
diff --git a/res/layout/favorite_contact_list_item.xml b/res/layout/favorite_contact_list_item.xml
index 82002fe..032a153 100644
--- a/res/layout/favorite_contact_list_item.xml
+++ b/res/layout/favorite_contact_list_item.xml
@@ -13,13 +13,12 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<LinearLayout
+<androidx.constraintlayout.widget.ConstraintLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
     android:id="@+id/text_container"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:orientation="vertical"
-    android:gravity="center_horizontal"
     android:background="?android:attr/selectableItemBackground">
 
     <ImageView
@@ -27,19 +26,32 @@
         android:layout_width="@dimen/large_avatar_icon_size"
         android:layout_height="@dimen/large_avatar_icon_size"
         android:layout_marginBottom="@dimen/favorites_avatar_margin_bottom"
-        android:scaleType="centerCrop"/>
+        android:scaleType="centerCrop"
+        app:layout_constraintVertical_chainStyle="packed"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toTopOf="@+id/title"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"/>
 
     <TextView
         android:id="@+id/title"
         android:textAppearance="?android:attr/textAppearanceLarge"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:singleLine="true"/>
+        android:singleLine="true"
+        app:layout_constraintTop_toBottomOf="@id/icon"
+        app:layout_constraintBottom_toTopOf="@+id/text"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"/>
 
     <TextView
         android:id="@+id/text"
         android:textAppearance="?android:attr/textAppearanceSmall"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:singleLine="true"/>
-</LinearLayout>
+        android:singleLine="true"
+        app:layout_constraintTop_toBottomOf="@id/title"
+        app:layout_constraintBottom_toTopOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"/>
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/res/layout/keypad.xml b/res/layout/keypad.xml
index a63b5ae..7d8d47c 100644
--- a/res/layout/keypad.xml
+++ b/res/layout/keypad.xml
@@ -30,8 +30,8 @@
         app:layout_constraintVertical_chainStyle="packed"
         app:layout_constraintTop_toTopOf="parent"
         app:layout_constraintBottom_toTopOf="@+id/four"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintEnd_toStartOf="@+id/two"/>
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintRight_toLeftOf="@+id/two"/>
 
     <com.android.car.dialer.ui.dialpad.KeypadButton
         android:id="@+id/two"
@@ -41,8 +41,8 @@
         app:layout_constraintVertical_chainStyle="packed"
         app:layout_constraintTop_toTopOf="parent"
         app:layout_constraintBottom_toTopOf="@+id/five"
-        app:layout_constraintStart_toEndOf="@id/one"
-        app:layout_constraintEnd_toStartOf="@+id/three"/>
+        app:layout_constraintLeft_toRightOf="@id/one"
+        app:layout_constraintRight_toLeftOf="@+id/three"/>
 
     <com.android.car.dialer.ui.dialpad.KeypadButton
         android:id="@+id/three"
@@ -52,8 +52,8 @@
         app:layout_constraintVertical_chainStyle="packed"
         app:layout_constraintTop_toTopOf="parent"
         app:layout_constraintBottom_toTopOf="@+id/six"
-        app:layout_constraintStart_toEndOf="@id/two"
-        app:layout_constraintEnd_toEndOf="parent"/>
+        app:layout_constraintLeft_toRightOf="@id/two"
+        app:layout_constraintRight_toRightOf="parent"/>
 
     <!-- Row 2 -->
     <com.android.car.dialer.ui.dialpad.KeypadButton
@@ -64,8 +64,8 @@
         app:layout_constraintHorizontal_chainStyle="packed"
         app:layout_constraintTop_toBottomOf="@id/one"
         app:layout_constraintBottom_toTopOf="@+id/seven"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintEnd_toStartOf="@+id/five"/>
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintRight_toLeftOf="@+id/five"/>
 
     <com.android.car.dialer.ui.dialpad.KeypadButton
         android:id="@+id/five"
@@ -74,8 +74,8 @@
         style="@style/KeypadButtonStyle"
         app:layout_constraintTop_toBottomOf="@id/two"
         app:layout_constraintBottom_toTopOf="@+id/eight"
-        app:layout_constraintStart_toEndOf="@id/four"
-        app:layout_constraintEnd_toStartOf="@+id/six"/>
+        app:layout_constraintLeft_toRightOf="@id/four"
+        app:layout_constraintRight_toLeftOf="@+id/six"/>
 
     <com.android.car.dialer.ui.dialpad.KeypadButton
         android:id="@+id/six"
@@ -84,8 +84,8 @@
         style="@style/KeypadButtonStyle"
         app:layout_constraintTop_toBottomOf="@id/three"
         app:layout_constraintBottom_toTopOf="@+id/nine"
-        app:layout_constraintStart_toEndOf="@id/five"
-        app:layout_constraintEnd_toEndOf="parent"/>
+        app:layout_constraintLeft_toRightOf="@id/five"
+        app:layout_constraintRight_toRightOf="parent"/>
 
 
     <!-- Row 3 -->
@@ -97,8 +97,8 @@
         app:layout_constraintHorizontal_chainStyle="packed"
         app:layout_constraintTop_toBottomOf="@id/four"
         app:layout_constraintBottom_toTopOf="@+id/star"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintEnd_toStartOf="@+id/eight"/>
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintRight_toLeftOf="@+id/eight"/>
 
     <com.android.car.dialer.ui.dialpad.KeypadButton
         android:id="@+id/eight"
@@ -107,8 +107,8 @@
         style="@style/KeypadButtonStyle"
         app:layout_constraintTop_toBottomOf="@id/five"
         app:layout_constraintBottom_toTopOf="@+id/zero"
-        app:layout_constraintStart_toEndOf="@id/seven"
-        app:layout_constraintEnd_toStartOf="@+id/nine"/>
+        app:layout_constraintLeft_toRightOf="@id/seven"
+        app:layout_constraintRight_toLeftOf="@+id/nine"/>
 
     <com.android.car.dialer.ui.dialpad.KeypadButton
         android:id="@+id/nine"
@@ -117,8 +117,8 @@
         style="@style/KeypadButtonStyle"
         app:layout_constraintTop_toBottomOf="@id/six"
         app:layout_constraintBottom_toTopOf="@+id/pound"
-        app:layout_constraintStart_toEndOf="@id/eight"
-        app:layout_constraintEnd_toEndOf="parent"/>
+        app:layout_constraintLeft_toRightOf="@id/eight"
+        app:layout_constraintRight_toRightOf="parent"/>
 
     <!-- Row 4 -->
     <com.android.car.dialer.ui.dialpad.KeypadButton
@@ -129,8 +129,8 @@
         app:layout_constraintHorizontal_chainStyle="packed"
         app:layout_constraintTop_toBottomOf="@id/seven"
         app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintEnd_toStartOf="@+id/zero"/>
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintRight_toLeftOf="@+id/zero"/>
 
     <com.android.car.dialer.ui.dialpad.KeypadButton
         android:id="@+id/zero"
@@ -139,8 +139,8 @@
         style="@style/KeypadButtonStyle"
         app:layout_constraintTop_toBottomOf="@id/eight"
         app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintStart_toEndOf="@id/star"
-        app:layout_constraintEnd_toStartOf="@+id/pound"/>
+        app:layout_constraintLeft_toRightOf="@id/star"
+        app:layout_constraintRight_toLeftOf="@+id/pound"/>
 
     <com.android.car.dialer.ui.dialpad.KeypadButton
         android:id="@+id/pound"
@@ -149,8 +149,8 @@
         style="@style/KeypadButtonStyle"
         app:layout_constraintTop_toBottomOf="@id/nine"
         app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintStart_toEndOf="@id/zero"
-        app:layout_constraintEnd_toEndOf="parent"/>
+        app:layout_constraintLeft_toRightOf="@id/zero"
+        app:layout_constraintRight_toRightOf="parent"/>
 
     <include layout="@layout/keypad_dividers"/>
 
diff --git a/res/layout/phone_number_list_item.xml b/res/layout/phone_number_list_item.xml
deleted file mode 100644
index 04e1b61..0000000
--- a/res/layout/phone_number_list_item.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<com.android.car.dialer.widget.CheckableRelativeLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:paddingStart="?android:listPreferredItemPaddingStart"
-    android:paddingEnd="?android:listPreferredItemPaddingEnd">
-
-    <RadioButton
-        android:id="@+id/phone_number_checkbox"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_centerVertical="true"
-        android:layout_marginEnd="@dimen/phone_number_radio_list_padding"/>
-    <TextView
-        android:id="@+id/phone_number"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_toEndOf="@id/phone_number_checkbox"
-        android:textAppearance="?android:attr/textAppearanceMedium"/>
-    <TextView
-        android:id="@+id/phone_number_description"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_below="@id/phone_number"
-        android:layout_toEndOf="@id/phone_number_checkbox"
-        android:textAppearance="?android:attr/textAppearanceSmall"/>
-
-</com.android.car.dialer.widget.CheckableRelativeLayout>
diff --git a/res/layout/type_down_list_item.xml b/res/layout/type_down_list_item.xml
new file mode 100644
index 0000000..a0476f6
--- /dev/null
+++ b/res/layout/type_down_list_item.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<androidx.constraintlayout.widget.ConstraintLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/contact_result"
+    android:foreground="?android:attr/selectableItemBackground"
+    android:layout_width="match_parent"
+    android:layout_height="@dimen/type_down_list_item_height">
+
+    <TextView
+        android:id="@+id/contact_name"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:singleLine="true"
+        android:textAppearance="@style/TextAppearance.ContactResultTitle"
+        android:duplicateParentState="true"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toTopOf="@+id/phone_number"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"/>
+
+    <TextView
+        android:id="@+id/phone_number"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:singleLine="true"
+        app:layout_constraintTop_toBottomOf="@id/contact_name"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toStartOf="@id/contact_name"/>
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/res/values-h610dp/dimens.xml b/res/values-h610dp/dimens.xml
index 23a9548..7e70374 100644
--- a/res/values-h610dp/dimens.xml
+++ b/res/values-h610dp/dimens.xml
@@ -24,6 +24,8 @@
     <!-- Control bar bottom padding -->
     <dimen name="in_call_controller_bar_margin_bottom">@*android:dimen/car_padding_2</dimen>
 
+    <dimen name="type_down_list_item_height">@dimen/list_item_height</dimen>
+
     <dimen name="fab_outline_size">104dp</dimen>
     <dimen name="fab_ripple_radius">52dp</dimen>
 
diff --git a/res/values-port/dimens.xml b/res/values-port/dimens.xml
index cdb70cf..5318d56 100644
--- a/res/values-port/dimens.xml
+++ b/res/values-port/dimens.xml
@@ -22,4 +22,5 @@
     <dimen name="keypad_margin">@*android:dimen/car_padding_4</dimen>
 
     <dimen name="dialpad_info_title_text_size_max">36sp</dimen>
+    <dimen name="dialpad_info_margin_start">154dp</dimen>
 </resources>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index f1aa7b0..49d2e52 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -18,13 +18,11 @@
     <color name="phone_call">@*android:color/car_green_700</color>
     <color name="phone_end_call">@*android:color/car_red_500a</color>
     <color name="onhold_call_background">@*android:color/car_grey_868</color>
+    <color name="audio_output_accent">@*android:color/car_accent</color>
 
     <!-- Dialpad page -->
     <color name="call_button_outline">@*android:color/car_green_500</color>
 
-    <!--Contact details-->
-    <color name="contact_details_icon_tint">@color/primary_icon_color</color>
-
     <color name="car_key2">@color/car_key2_dark</color>
     <color name="car_key2_light">@*android:color/car_grey_400</color>
     <color name="car_key2_dark">@*android:color/car_grey_700</color>
diff --git a/res/values/configs.xml b/res/values/configs.xml
index 03bb96e..03e7a89 100644
--- a/res/values/configs.xml
+++ b/res/values/configs.xml
@@ -24,8 +24,6 @@
     <item name="config_background_image_alpha" format="float" type="dimen">1.0</item>
     <item name="config_background_image_error_alpha" format="float" type="dimen">1.0</item>
 
-    <bool name="config_show_detailed_user_profile_on_dialpad">false</bool>
-
     <!-- A config determines whether to show divider and callog_action_button for callog items
     that are not stored as contacts. "true" will show divider and button as disable state and
     "false" will make divider and button invisible for non contacts. -->
@@ -48,4 +46,12 @@
 
     <!-- Config gate the full screen incall ui -->
     <bool name="config_show_fullscreen_incall_ui">true</bool>
+
+    <!-- A config determines whether to show type down list on Dialpad -->
+    <bool name="config_show_type_down_list_on_dialpad">true</bool>
+    <!-- A config determines whether to show user profile for input number only when type down is
+    not showing. This config works only when config_show_type_down_list_on_dialpad is false. -->
+    <bool name="config_show_detailed_user_profile_on_dialpad">false</bool>
+    <!-- A percent determines the size of the area for type down list on Dialpad in portrait mode. -->
+    <item name="config_type_down_guideline_percent" format="float" type="dimen">0.3</item>
 </resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 010fca4..16d2ce1 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -17,13 +17,10 @@
     <!-- Dialer -->
     <dimen name="dialer_card_elevation">2dp</dimen>
 
-    <!-- Audio route dimentions -->
-    <dimen name="audio_route_height">@dimen/control_bar_height</dimen>
-    <dimen name="audio_route_content_padding">@dimen/list_item_padding</dimen>
-    <dimen name="audio_route_icon_size">@dimen/primary_icon_size</dimen>
+    <!-- TODO: UNUSED! REMOVE THIS. b/149125789-->
     <dimen name="audio_route_constraint_guide_begin">@dimen/list_item_guideline</dimen>
 
-    <!-- Call list dimentions -->
+    <!-- Call list dimensions -->
     <dimen name="call_history_item_height">@dimen/list_item_height</dimen>
     <dimen name="call_history_item_padding">@dimen/list_item_padding</dimen>
     <dimen name="call_history_guideline_begin">@dimen/list_item_guideline_begin</dimen>
@@ -88,6 +85,9 @@
     <dimen name="dialpad_info_title_text_size_min">24sp</dimen>
     <dimen name="dialpad_contact_avatar_size">64dp</dimen>
     <dimen name="dialpad_contact_label_margin">12dp</dimen>
+    <dimen name="dialpad_info_margin_end">112dp</dimen>
+    <dimen name="dialpad_info_margin_start">154dp</dimen>
+    <dimen name="type_down_list_item_height">@dimen/control_bar_height</dimen>
 
     <!-- Keypad dimensions -->
     <dimen name="keypad_minimum_size">@dimen/touch_target_size</dimen>
@@ -99,10 +99,6 @@
     <dimen name="favorite_card_space_vertical">@*android:dimen/car_padding_2</dimen>
     <dimen name="favorites_avatar_margin_bottom">@*android:dimen/car_padding_3</dimen>
 
-    <!-- Add faovirte flow dimensions -->
-    <dimen name="add_favorite_number_list_height">@dimen/list_item_height</dimen>
-    <dimen name="add_favorite_number_list_padding">@dimen/list_item_padding</dimen>
-
     <dimen name="call_fab_elevation">8dp</dimen>
     <dimen name="bksp_button_width">@dimen/touch_target_size</dimen>
 
@@ -134,6 +130,7 @@
     <dimen name="list_item_padding">@*android:dimen/car_keyline_1</dimen>
     <dimen name="list_divider_inset">@*android:dimen/car_keyline_1</dimen>
     <dimen name="list_divider_height">@*android:dimen/car_list_divider_height</dimen>
+    <dimen name="horizontal_divider_inset">@*android:dimen/car_padding_2</dimen>
     <dimen name="vertical_divider_inset">@*android:dimen/car_padding_2</dimen>
     <dimen name="vertical_divider_width">2dp</dimen>
     <dimen name="primary_icon_size">@*android:dimen/car_primary_icon_size</dimen>
@@ -145,7 +142,6 @@
     <dimen name="touch_target_width">156dp</dimen>
     <dimen name="subheader_list_height">76dp</dimen>
     <dimen name="control_bar_height">96dp</dimen>
-    <dimen name="phone_number_radio_list_padding">@*android:dimen/car_padding_2</dimen>
 
     <!-- Loading status view dimensions -->
     <dimen name="loading_info_icon_size">56dp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 0418d13..a890cd1 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -141,14 +141,27 @@
     <!-- Description for the default phone number of the contact [CHAR LIMIT=30] -->
     <string name="primary_number_description">
         <xliff:g id="label" example="Mobile">%1$s</xliff:g>
-        " - Default"
+        ", default"
     </string>
 
-    <!-- Description for the favorite phone number in add to favorite flow [CHAR LIMIT=30] -->
+    <!-- Description for the phone favorite number in the add-to-favorite flow [CHAR LIMIT=30] -->
     <string name="favorite_number_description">
+        "Favorite - "
         <xliff:g id="label" example="Mobile">%1$s</xliff:g>
-        " - Favorite"
     </string>
+    <!-- Description for the local favorite number in the add-to-favorite flow and the contact details page [CHAR LIMIT=30] -->
+    <string name="local_favorite_number_description">
+        "Local favorite - "
+        <xliff:g id="label" example="Mobile">%1$s</xliff:g>
+    </string>
+
+    <!-- Header for phone favorite numbers [CHAR LIMIT=30] -->
+    <string name="phone_favorites">Favorites</string>
+    <!-- Header for local favorite numbers [CHAR LIMIT=30] -->
+    <string name="local_favorites">Local favorites</string>
+
+    <!-- Message in the add-to-favorite flow that no phone number available for this contact [CHAR_LIMIT=50] -->
+    <string name="no_phone_numbers">No phone numbers</string>
 
     <!-- Heads Up Notification -->
     <!-- Name of incoming call notification channel in app info [CHAR LIMIT=50] -->
@@ -192,4 +205,8 @@
     <string name="given_name_first_title">First name</string>
     <!-- Title of family name [CHAR LIMIT=40]-->
     <string name="family_name_first_title">Last name</string>
+
+    <!-- Audio route selection dialog. Placeholder resources for overriding -->
+    <string name="audio_route_dialog_title">Output call audio to:</string>
+    <string name="audio_route_dialog_subtitle"></string>
 </resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index af01627..a7c24a3 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -106,7 +106,9 @@
     </style>
 
     <!-- Contact results -->
-    <style name="TextAppearance.ContactResultTitle" parent="TextAppearance.Body1"/>
+    <style name="TextAppearance.ContactResultTitle" parent="TextAppearance.Body1">
+        <item name="android:textColor">@color/primary_text_selector</item>
+    </style>
 
     <!-- Add to favorite flow dialog -->
     <style name="TextAppearance.AddFavoriteNumberTitle" parent="TextAppearance.Body1">
diff --git a/res/values/themes.xml b/res/values/themes.xml
index bae1bc3..11152f4 100644
--- a/res/values/themes.xml
+++ b/res/values/themes.xml
@@ -18,6 +18,7 @@
     <style name="Theme.Dialer" parent="Theme.CarUi">
         <item name="android:listDivider">@drawable/list_divider</item>
         <item name="android:buttonStyle">@style/Widget.Dialer.Button</item>
+        <item name="android:textDirection">locale</item>
     </style>
 
     <style name="Theme.Dialer.Telecom">
diff --git a/src/com/android/car/dialer/Constants.java b/src/com/android/car/dialer/Constants.java
index fc86af8..1011f28 100644
--- a/src/com/android/car/dialer/Constants.java
+++ b/src/com/android/car/dialer/Constants.java
@@ -34,6 +34,8 @@
                 "com.android.car.dialer.EXTRA_ACTION_READ_MISSED";
         /** Intent extra flag to show incoming call. */
         public static final String EXTRA_SHOW_INCOMING_CALL = "show_incoming_call";
+
+        public static final String EXTRA_SHOW_DIALPAD = "show_dialpad";
     }
 
     /** Constants used by {@link androidx.core.app.JobIntentService}s. */
@@ -41,6 +43,6 @@
         public static final int NOTIFICATION_SERVICE = 2019;
     }
 
-    /**See {@link android.content.res.Resources#getIdentifier(String, String, String)}*/
+    /** See {@link android.content.res.Resources#getIdentifier(String, String, String)} */
     public static int INVALID_RES_ID = 0;
 }
diff --git a/src/com/android/car/dialer/notification/InCallNotificationController.java b/src/com/android/car/dialer/notification/InCallNotificationController.java
index e68b35b..624f104 100644
--- a/src/com/android/car/dialer/notification/InCallNotificationController.java
+++ b/src/com/android/car/dialer/notification/InCallNotificationController.java
@@ -171,13 +171,13 @@
 
     private PendingIntent getFullscreenIntent(Call call) {
         Intent intent = getIntent(NotificationService.ACTION_SHOW_FULLSCREEN_UI, call);
-        return PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+        return PendingIntent.getService(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
     }
 
     private Notification.Action getAction(Call call, @StringRes int actionText,
             String intentAction) {
         CharSequence text = mContext.getString(actionText);
-        PendingIntent intent = PendingIntent.getBroadcast(
+        PendingIntent intent = PendingIntent.getService(
                 mContext,
                 0,
                 getIntent(intentAction, call),
@@ -186,7 +186,7 @@
     }
 
     private Intent getIntent(String action, Call call) {
-        Intent intent = new Intent(action, null, mContext, NotificationReceiver.class);
+        Intent intent = new Intent(action, null, mContext, NotificationService.class);
         intent.putExtra(NotificationService.EXTRA_CALL_ID, call.getDetails().getTelecomCallId());
         return intent;
     }
diff --git a/src/com/android/car/dialer/notification/MissedCallNotificationController.java b/src/com/android/car/dialer/notification/MissedCallNotificationController.java
index 5b0fd86..3de1e60 100644
--- a/src/com/android/car/dialer/notification/MissedCallNotificationController.java
+++ b/src/com/android/car/dialer/notification/MissedCallNotificationController.java
@@ -195,8 +195,8 @@
 
     private PendingIntent getDeleteIntent() {
         Intent intent = new Intent(NotificationService.ACTION_READ_ALL_MISSED, null, mContext,
-                NotificationReceiver.class);
-        PendingIntent pendingIntent = PendingIntent.getBroadcast(
+                NotificationService.class);
+        PendingIntent pendingIntent = PendingIntent.getService(
                 mContext,
                 0,
                 intent,
@@ -207,7 +207,7 @@
     private Notification.Action getAction(String phoneNumberString, @StringRes int actionText,
             String intentAction) {
         CharSequence text = mContext.getString(actionText);
-        PendingIntent intent = PendingIntent.getBroadcast(
+        PendingIntent intent = PendingIntent.getService(
                 mContext,
                 0,
                 getIntent(intentAction, phoneNumberString),
@@ -216,7 +216,7 @@
     }
 
     private Intent getIntent(String action, String phoneNumberString) {
-        Intent intent = new Intent(action, null, mContext, NotificationReceiver.class);
+        Intent intent = new Intent(action, null, mContext, NotificationService.class);
         intent.putExtra(NotificationService.EXTRA_CALL_ID, phoneNumberString);
         return intent;
     }
diff --git a/src/com/android/car/dialer/notification/NotificationReceiver.java b/src/com/android/car/dialer/notification/NotificationReceiver.java
deleted file mode 100644
index 8c7d8d7..0000000
--- a/src/com/android/car/dialer/notification/NotificationReceiver.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.car.dialer.notification;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-
-/** This class is responsible for offloading notification broadcasts to the notification service. */
-public class NotificationReceiver extends BroadcastReceiver {
-
-    @Override
-    public void onReceive(Context context, Intent intent) {
-        NotificationService.enqueueWork(context, intent);
-    }
-}
diff --git a/src/com/android/car/dialer/notification/NotificationService.java b/src/com/android/car/dialer/notification/NotificationService.java
index f7b8a01..54abe15 100644
--- a/src/com/android/car/dialer/notification/NotificationService.java
+++ b/src/com/android/car/dialer/notification/NotificationService.java
@@ -16,13 +16,13 @@
 
 package com.android.car.dialer.notification;
 
+import android.app.Service;
 import android.content.Context;
 import android.content.Intent;
+import android.os.IBinder;
 import android.telecom.Call;
 import android.text.TextUtils;
 
-import androidx.core.app.JobIntentService;
-
 import com.android.car.dialer.Constants;
 import com.android.car.dialer.telecom.UiCallManager;
 import com.android.car.dialer.ui.activecall.InCallActivity;
@@ -31,11 +31,11 @@
 import java.util.List;
 
 /**
- * A {@link JobIntentService} that is used to handle actions from notifications to:
+ * A {@link Service} that is used to handle actions from notifications to:
  * <ul><li>answer or inject an incoming call.
  * <li>call back or message to a missed call.
  */
-public class NotificationService extends JobIntentService {
+public class NotificationService extends Service {
     static final String ACTION_ANSWER_CALL = "CD.ACTION_ANSWER_CALL";
     static final String ACTION_DECLINE_CALL = "CD.ACTION_DECLINE_CALL";
     static final String ACTION_SHOW_FULLSCREEN_UI = "CD.ACTION_SHOW_FULLSCREEN_UI";
@@ -44,21 +44,20 @@
     static final String ACTION_READ_ALL_MISSED = "CD.ACTION_READ_ALL_MISSED";
     static final String EXTRA_CALL_ID = "CD.EXTRA_CALL_ID";
 
-    /** Create an intent to handle reading all missed call action and schedule for executing. */
-    public static void readAllMissedCall(Context context) {
-        Intent readAllMissedCallIntent = new Intent(context, NotificationReceiver.class);
-        readAllMissedCallIntent.setAction(ACTION_READ_ALL_MISSED);
-        enqueueWork(context, readAllMissedCallIntent);
+    @Override
+    public IBinder onBind(Intent intent) {
+        return null;
     }
 
-    /** Enqueue the intent. */
-    static void enqueueWork(Context context, Intent intent) {
-        enqueueWork(
-                context, NotificationService.class, Constants.JobIds.NOTIFICATION_SERVICE, intent);
+    /** Create an intent to handle reading all missed call action and schedule for executing. */
+    public static void readAllMissedCall(Context context) {
+        Intent readAllMissedCallIntent = new Intent(context, NotificationService.class);
+        readAllMissedCallIntent.setAction(ACTION_READ_ALL_MISSED);
+        context.startService(readAllMissedCallIntent);
     }
 
     @Override
-    protected void onHandleWork(Intent intent) {
+    public int onStartCommand(Intent intent, int flags, int startId) {
         String action = intent.getAction();
         String callId = intent.getStringExtra(EXTRA_CALL_ID);
         switch (action) {
@@ -92,6 +91,8 @@
             default:
                 break;
         }
+
+        return START_NOT_STICKY;
     }
 
     private void answerCall(String callId) {
diff --git a/src/com/android/car/dialer/telecom/InCallRouter.java b/src/com/android/car/dialer/telecom/InCallRouter.java
index 5e0376d..4ba91e4 100644
--- a/src/com/android/car/dialer/telecom/InCallRouter.java
+++ b/src/com/android/car/dialer/telecom/InCallRouter.java
@@ -22,6 +22,7 @@
 
 import androidx.annotation.MainThread;
 
+import com.android.car.dialer.Constants;
 import com.android.car.dialer.R;
 import com.android.car.dialer.log.L;
 import com.android.car.dialer.notification.InCallNotificationController;
@@ -83,8 +84,10 @@
         int state = call.getState();
         if (state == Call.STATE_RINGING) {
             routeToNotification(call);
-        } else {
-            routeToInCallPage(call);
+        } else if (state != Call.STATE_DISCONNECTED) {
+            // Don't launch the in call page if state is disconnected.
+            // Otherwise, the InCallActivity finishes right after onCreate() and flashes.
+            routeToInCallPage(false);
         }
     }
 
@@ -134,7 +137,11 @@
             @Override
             public void onStateChanged(Call call, int state) {
                 L.d(TAG, "Ringing call state changed to %d", state);
-                routeToInCallPage(call);
+                if (call.getState() != Call.STATE_DISCONNECTED) {
+                    // Don't launch the in call page if state is disconnected. Otherwise, the
+                    // InCallActivity finishes right after onCreate() and flashes.
+                    routeToInCallPage(false);
+                }
                 mInCallNotificationController.cancelInCallNotification(call);
                 call.unregisterCallback(this);
             }
@@ -144,16 +151,14 @@
     /**
      * Launches {@link InCallActivity} and presents the on going call in the in call page.
      */
-    private void routeToInCallPage(Call call) {
+    void routeToInCallPage(boolean showDialpad) {
         // It has been configured not to show the fullscreen incall ui.
         if (!mShowFullscreenIncallUi) {
             return;
         }
-        // Don't launch the in call page if state is disconnected. Otherwise, the InCallActivity
-        // finishes right after onCreate() and flashes.
-        if (call.getState() != Call.STATE_DISCONNECTED) {
-            Intent launchIntent = new Intent(mContext, InCallActivity.class);
-            mContext.startActivity(launchIntent);
-        }
+
+        Intent launchIntent = new Intent(mContext, InCallActivity.class);
+        launchIntent.putExtra(Constants.Intents.EXTRA_SHOW_INCOMING_CALL, showDialpad);
+        mContext.startActivity(launchIntent);
     }
 }
diff --git a/src/com/android/car/dialer/telecom/InCallServiceImpl.java b/src/com/android/car/dialer/telecom/InCallServiceImpl.java
index a5faaf6..d60c9d1 100644
--- a/src/com/android/car/dialer/telecom/InCallServiceImpl.java
+++ b/src/com/android/car/dialer/telecom/InCallServiceImpl.java
@@ -37,7 +37,8 @@
     /** An action which indicates a bind is from local component. */
     public static final String ACTION_LOCAL_BIND = "local_bind";
 
-    private CopyOnWriteArrayList<Callback> mCallbacks = new CopyOnWriteArrayList<>();
+    private CopyOnWriteArrayList<CallAudioStateCallback> mCallAudioStateCallbacks =
+            new CopyOnWriteArrayList<>();
 
     private InCallRouter mInCallRouter;
 
@@ -59,6 +60,14 @@
         boolean onTelecomCallRemoved(Call telecomCall);
     }
 
+    /** Listens to call audio state changes. Callbacks will be called on the main thread. */
+    public interface CallAudioStateCallback {
+        /**
+         * Called when the call audio state has changed.
+         */
+        void onCallAudioStateChanged(CallAudioState callAudioState);
+    }
+
     @Override
     public void onCreate() {
         super.onCreate();
@@ -76,21 +85,12 @@
     @Override
     public void onCallAdded(Call telecomCall) {
         L.d(TAG, "onCallAdded: %s", telecomCall);
-
-        for (Callback callback : mCallbacks) {
-            callback.onTelecomCallAdded(telecomCall);
-        }
-
         mInCallRouter.onCallAdded(telecomCall);
     }
 
     @Override
     public void onCallRemoved(Call telecomCall) {
         L.d(TAG, "onCallRemoved: %s", telecomCall);
-        for (Callback callback : mCallbacks) {
-            callback.onTelecomCallRemoved(telecomCall);
-        }
-
         mInCallRouter.onCallRemoved(telecomCall);
     }
 
@@ -113,17 +113,23 @@
 
     @Override
     public void onCallAudioStateChanged(CallAudioState audioState) {
-        for (Callback callback : mCallbacks) {
+        for (CallAudioStateCallback callback : mCallAudioStateCallbacks) {
             callback.onCallAudioStateChanged(audioState);
         }
     }
 
-    public void registerCallback(Callback callback) {
-        mCallbacks.add(callback);
+    @Override
+    public void onBringToForeground(boolean showDialpad) {
+        L.d(TAG, "onBringToForeground: %s", showDialpad);
+        mInCallRouter.routeToInCallPage(showDialpad);
     }
 
-    public void unregisterCallback(Callback callback) {
-        mCallbacks.remove(callback);
+    public void addCallAudioStateChangedCallback(CallAudioStateCallback callback) {
+        mCallAudioStateCallbacks.add(callback);
+    }
+
+    public void removeCallAudioStateChangedCallback(CallAudioStateCallback callback) {
+        mCallAudioStateCallbacks.remove(callback);
     }
 
     public void addActiveCallListChangedCallback(ActiveCallListChangedCallback callback) {
@@ -134,15 +140,6 @@
         mInCallRouter.unregisterActiveCallHandler(callback);
     }
 
-    @Deprecated
-    interface Callback {
-        void onTelecomCallAdded(Call telecomCall);
-
-        void onTelecomCallRemoved(Call telecomCall);
-
-        void onCallAudioStateChanged(CallAudioState audioState);
-    }
-
     /**
      * Local binder only available for Car Dialer package.
      */
diff --git a/src/com/android/car/dialer/telecom/UiCallManager.java b/src/com/android/car/dialer/telecom/UiCallManager.java
index 956121c..6822c2d 100644
--- a/src/com/android/car/dialer/telecom/UiCallManager.java
+++ b/src/com/android/car/dialer/telecom/UiCallManager.java
@@ -42,11 +42,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.ITelephony;
 
-import com.google.i18n.phonenumbers.PhoneNumberUtil;
-import com.google.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberType;
-import com.google.i18n.phonenumbers.PhoneNumberUtil.ValidationResult;
-import com.google.i18n.phonenumbers.Phonenumber;
-
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -307,24 +302,13 @@
     }
 
     /**
-     * Runs basic validation check of a phone number, to verify it is the correct length
-     * in an internationalized way. Further validation on whether the number actually exists
-     * is left for the phone carrier.
+     * Runs basic validation check of a phone number, to verify it is not empty.
      */
     private boolean isValidNumber(String number) {
-        Phonenumber.PhoneNumber phoneNumber = TelecomUtils.createI18nPhoneNumber(mContext,
-                number);
-        if (phoneNumber != null) {
-            for (PhoneNumberType type : PhoneNumberType.values()) {
-                ValidationResult result =
-                        PhoneNumberUtil.getInstance().isPossibleNumberForTypeWithReason(phoneNumber,
-                                type);
-                if (result != ValidationResult.TOO_SHORT && result != ValidationResult.TOO_LONG) {
-                    return true;
-                }
-            }
+        if (TextUtils.isEmpty(number)) {
+            return false;
         }
-        return false;
+        return true;
     }
 
     public void callVoicemail() {
diff --git a/src/com/android/car/dialer/ui/TelecomActivity.java b/src/com/android/car/dialer/ui/TelecomActivity.java
index 121aeb2..5092450 100644
--- a/src/com/android/car/dialer/ui/TelecomActivity.java
+++ b/src/com/android/car/dialer/ui/TelecomActivity.java
@@ -17,6 +17,7 @@
 package com.android.car.dialer.ui;
 
 import android.app.SearchManager;
+import android.bluetooth.BluetoothDevice;
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.graphics.drawable.Drawable;
@@ -30,7 +31,6 @@
 import androidx.annotation.Nullable;
 import androidx.fragment.app.Fragment;
 import androidx.fragment.app.FragmentActivity;
-import androidx.fragment.app.FragmentManager;
 import androidx.lifecycle.LiveData;
 import androidx.lifecycle.MutableLiveData;
 import androidx.lifecycle.ViewModelProviders;
@@ -69,8 +69,7 @@
  * <p>Based on call and connectivity status, it will choose the right page to display.
  */
 public class TelecomActivity extends FragmentActivity implements
-        DialerBaseFragment.DialerFragmentParent, FragmentManager.OnBackStackChangedListener,
-        Toolbar.OnHeightChangedListener {
+        DialerBaseFragment.DialerFragmentParent {
     private static final String TAG = "CD.TelecomActivity";
     private LiveData<String> mBluetoothErrorMsgLiveData;
     private LiveData<Integer> mDialerAppStateLiveData;
@@ -78,6 +77,7 @@
     // View objects for this activity.
     private TelecomPageTab.Factory mTabFactory;
     private Toolbar mCarUiToolbar;
+    private BluetoothDevice mBluetoothDevice;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -87,7 +87,6 @@
         setContentView(R.layout.telecom_activity);
 
         mCarUiToolbar = findViewById(R.id.car_ui_toolbar);
-        mCarUiToolbar.registerToolbarHeightChangeListener(this);
 
         setupTabLayout();
 
@@ -99,6 +98,7 @@
                 dialerAppState -> updateCurrentFragment(dialerAppState));
         MutableLiveData<Integer> toolbarTitleMode = viewModel.getToolbarTitleMode();
         toolbarTitleMode.setValue(Themes.getAttrInteger(this, R.attr.toolbarTitleMode));
+        viewModel.getRefreshTabsLiveData().observe(this, this::refreshTabs);
 
         InCallViewModel inCallViewModel = ViewModelProviders.of(this).get(InCallViewModel.class);
         mOngoingCallListLiveData = inCallViewModel.getOngoingCallList();
@@ -108,25 +108,11 @@
         handleIntent();
     }
 
-    @Override
-    public void onStart() {
-        getSupportFragmentManager().addOnBackStackChangedListener(this);
-        onBackStackChanged();
-        super.onStart();
-        L.d(TAG, "onStart");
-    }
-
-    @Override
-    public void onStop() {
-        super.onStop();
-        L.d(TAG, "onStop");
-        getSupportFragmentManager().removeOnBackStackChangedListener(this);
-    }
-
-    @Override
-    protected void onDestroy() {
-        super.onDestroy();
-        mCarUiToolbar.unregisterToolbarHeightChangeListener(this);
+    private void refreshTabs(boolean refreshTabs) {
+        L.v(TAG, "hfp connected device list Changes.");
+        if (refreshTabs) {
+            setupTabLayout();
+        }
     }
 
     @Override
@@ -269,6 +255,7 @@
     private void setupTabLayout() {
         boolean wasContentFragmentRestored = false;
         mTabFactory = new TelecomPageTab.Factory(this, getSupportFragmentManager());
+        mCarUiToolbar.clearAllTabs();
         for (int i = 0; i < mTabFactory.getTabCount(); i++) {
             TelecomPageTab tab = mTabFactory.createTab(getBaseContext(), i);
             mCarUiToolbar.addTab(tab);
@@ -356,25 +343,6 @@
     }
 
     @Override
-    public void onBackStackChanged() {
-        L.d(TAG, "onBackStackChanged");
-        Fragment topFragment = getSupportFragmentManager().findFragmentById(
-                R.id.content_fragment_container);
-        if (topFragment instanceof DialerBaseFragment) {
-            ((DialerBaseFragment) topFragment).setupToolbar(mCarUiToolbar);
-        }
-    }
-
-    @Override
-    public void onHeightChanged(int height) {
-        Fragment topFragment = getSupportFragmentManager().findFragmentById(
-                R.id.content_fragment_container);
-        if (topFragment instanceof DialerBaseFragment) {
-            ((DialerBaseFragment) topFragment).setToolbarHeight(height);
-        }
-    }
-
-    @Override
     public boolean onNavigateUp() {
         if (isBackNavigationAvailable()) {
             onBackPressed();
diff --git a/src/com/android/car/dialer/ui/TelecomActivityViewModel.java b/src/com/android/car/dialer/ui/TelecomActivityViewModel.java
index e28c4ce..cd16402 100644
--- a/src/com/android/car/dialer/ui/TelecomActivityViewModel.java
+++ b/src/com/android/car/dialer/ui/TelecomActivityViewModel.java
@@ -27,11 +27,13 @@
 import androidx.lifecycle.LiveData;
 import androidx.lifecycle.MediatorLiveData;
 import androidx.lifecycle.MutableLiveData;
+import androidx.lifecycle.Transformations;
 
 import com.android.car.dialer.R;
 import com.android.car.dialer.livedata.BluetoothHfpStateLiveData;
 import com.android.car.dialer.livedata.BluetoothPairListLiveData;
 import com.android.car.dialer.livedata.BluetoothStateLiveData;
+import com.android.car.dialer.livedata.HfpDeviceListLiveData;
 import com.android.car.dialer.log.L;
 import com.android.car.dialer.telecom.UiBluetoothMonitor;
 
@@ -52,10 +54,13 @@
     private final Context mApplicationContext;
     private final LiveData<String> mErrorStringLiveData;
     private final MutableLiveData<Integer> mDialerAppStateLiveData;
+    private final LiveData<Boolean> mRefreshTabsLiveData;
 
     private final ToolbarTitleLiveData mToolbarTitleLiveData;
     private final MutableLiveData<Integer> mToolbarTitleMode;
 
+    private BluetoothDevice mBluetoothDevice;
+
     /**
      * App state indicates if bluetooth is connected or it should just show the content fragments.
      */
@@ -90,6 +95,22 @@
         }
 
         mDialerAppStateLiveData = new DialerAppStateLiveData(mErrorStringLiveData);
+
+        HfpDeviceListLiveData hfpDeviceListLiveData = new HfpDeviceListLiveData(getApplication());
+        mRefreshTabsLiveData = Transformations.map(hfpDeviceListLiveData, (hfpDeviceList) -> {
+            if (hfpDeviceList != null && !hfpDeviceList.isEmpty()) {
+                if (!hfpDeviceList.contains(mBluetoothDevice)) {
+                    mBluetoothDevice = hfpDeviceList.get(0);
+                    return true;
+                }
+            } else {
+                if (mBluetoothDevice != null) {
+                    mBluetoothDevice = null;
+                    return true;
+                }
+            }
+            return false;
+        });
     }
 
     /**
@@ -108,6 +129,9 @@
         return mToolbarTitleMode;
     }
 
+    /**
+     * Returns the state of Dialer App.
+     */
     public MutableLiveData<Integer> getDialerAppState() {
         return mDialerAppStateLiveData;
     }
@@ -120,6 +144,13 @@
         return mErrorStringLiveData;
     }
 
+    /**
+     * Returns the live data which monitors whether to refresh Dialer.
+     */
+    public LiveData<Boolean> getRefreshTabsLiveData() {
+        return mRefreshTabsLiveData;
+    }
+
     private static class DialerAppStateLiveData extends MediatorLiveData<Integer> {
         private final LiveData<String> mErrorStringLiveData;
 
@@ -203,16 +234,16 @@
                     isBluetoothEnabled,
                     hasPairedDevices,
                     isHfpConnected);
-            if (!isBluetoothEnabled) {
-                setValue(mContext.getString(R.string.bluetooth_disabled));
-            } else if (!hasPairedDevices) {
-                setValue(mContext.getString(R.string.bluetooth_unpaired));
-            } else if (!isHfpConnected) {
-                setValue(mContext.getString(R.string.no_hfp));
-            } else {
+            if (isHfpConnected) {
                 if (!NO_BT_ERROR.equals(getValue())) {
                     setValue(NO_BT_ERROR);
                 }
+            } else if (!isBluetoothEnabled) {
+                setValue(mContext.getString(R.string.bluetooth_disabled));
+            } else if (!hasPairedDevices) {
+                setValue(mContext.getString(R.string.bluetooth_unpaired));
+            } else {
+                setValue(mContext.getString(R.string.no_hfp));
             }
         }
 
diff --git a/src/com/android/car/dialer/ui/activecall/InCallActivity.java b/src/com/android/car/dialer/ui/activecall/InCallActivity.java
index 015dc08..af438a1 100644
--- a/src/com/android/car/dialer/ui/activecall/InCallActivity.java
+++ b/src/com/android/car/dialer/ui/activecall/InCallActivity.java
@@ -41,6 +41,7 @@
     private Fragment mOngoingCallFragment;
     private Fragment mIncomingCallFragment;
 
+    private InCallViewModel mInCallViewModel;
     private MutableLiveData<Boolean> mShowIncomingCall;
     private LiveData<Call> mIncomingCallLiveData;
 
@@ -56,12 +57,12 @@
         mIncomingCallFragment = getSupportFragmentManager().findFragmentById(
                 R.id.incoming_call_fragment);
 
-        mShowIncomingCall = new MutableLiveData();
-        InCallViewModel inCallViewModel = ViewModelProviders.of(this).get(InCallViewModel.class);
+        mShowIncomingCall = new MutableLiveData<>();
+        mInCallViewModel = ViewModelProviders.of(this).get(InCallViewModel.class);
         mIncomingCallLiveData = LiveDataFunctions.iff(mShowIncomingCall,
-                inCallViewModel.getIncomingCall());
-        LiveDataFunctions.pair(inCallViewModel.getOngoingCallList(), mIncomingCallLiveData).observe(
-                this, this::updateVisibility);
+                mInCallViewModel.getIncomingCall());
+        LiveDataFunctions.pair(mInCallViewModel.getOngoingCallList(),
+                mIncomingCallLiveData).observe(this, this::updateVisibility);
 
         handleIntent();
     }
@@ -97,9 +98,11 @@
     private void handleIntent() {
         Intent intent = getIntent();
 
-        if (intent != null && getIntent().getBooleanExtra(
-                Constants.Intents.EXTRA_SHOW_INCOMING_CALL, false)) {
-            mShowIncomingCall.setValue(true);
+        if (intent != null) {
+            mShowIncomingCall.setValue(
+                    getIntent().getBooleanExtra(Constants.Intents.EXTRA_SHOW_INCOMING_CALL, false));
+            mInCallViewModel.getDialpadOpenState().setValue(
+                    getIntent().getBooleanExtra(Constants.Intents.EXTRA_SHOW_DIALPAD, false));
         } else {
             mShowIncomingCall.setValue(false);
         }
diff --git a/src/com/android/car/dialer/ui/activecall/InCallViewModel.java b/src/com/android/car/dialer/ui/activecall/InCallViewModel.java
index 328695b..39210fa 100644
--- a/src/com/android/car/dialer/ui/activecall/InCallViewModel.java
+++ b/src/com/android/car/dialer/ui/activecall/InCallViewModel.java
@@ -23,6 +23,7 @@
 import android.content.ServiceConnection;
 import android.os.IBinder;
 import android.telecom.Call;
+import android.telecom.CallAudioState;
 
 import androidx.annotation.NonNull;
 import androidx.core.util.Pair;
@@ -53,7 +54,7 @@
  * in call page should use a different ViewModel.
  */
 public class InCallViewModel extends AndroidViewModel implements
-        InCallServiceImpl.ActiveCallListChangedCallback {
+        InCallServiceImpl.ActiveCallListChangedCallback, InCallServiceImpl.CallAudioStateCallback {
     private static final String TAG = "CD.InCallViewModel";
 
     private final MutableLiveData<List<Call>> mCallListLiveData;
@@ -68,6 +69,7 @@
     private final LiveData<Call> mSecondaryCallLiveData;
     private final LiveData<CallDetail> mSecondaryCallDetailLiveData;
     private final LiveData<Integer> mAudioRouteLiveData;
+    private MutableLiveData<CallAudioState> mCallAudioStateLiveData;
     private final MutableLiveData<Boolean> mDialpadIsOpen;
     private final ShowOnholdCallLiveData mShowOnholdCall;
     private LiveData<Long> mCallConnectTimeLiveData;
@@ -86,6 +88,7 @@
             }
             updateCallList();
             mInCallService.addActiveCallListChangedCallback(InCallViewModel.this);
+            mInCallService.addCallAudioStateChangedCallback(InCallViewModel.this);
         }
 
         @Override
@@ -114,6 +117,7 @@
 
         mIncomingCallLiveData = new MutableLiveData<>();
         mOngoingCallListLiveData = new MutableLiveData<>();
+        mCallAudioStateLiveData = new MutableLiveData<>();
         mCallComparator = new CallComparator();
         mCallListLiveData = new MutableLiveData<List<Call>>() {
             @Override
@@ -231,6 +235,13 @@
         return mAudioRouteLiveData;
     }
 
+    /**
+     * Returns current call audio state.
+     */
+    public MutableLiveData<CallAudioState> getCallAudioState() {
+        return mCallAudioStateLiveData;
+    }
+
     /** Return the {@link MutableLiveData} for dialpad open state. */
     public MutableLiveData<Boolean> getDialpadOpenState() {
         return mDialpadIsOpen;
@@ -257,6 +268,12 @@
         return false;
     }
 
+    @Override
+    public void onCallAudioStateChanged(CallAudioState callAudioState) {
+        L.i(TAG, "onCallAudioStateChanged %s %s", callAudioState, this);
+        mCallAudioStateLiveData.setValue(callAudioState);
+    }
+
     private void updateCallList() {
         List<Call> callList = new ArrayList<>();
         callList.addAll(mInCallService.getCalls());
@@ -271,6 +288,7 @@
                 call.unregisterCallback(mCallStateChangedCallback);
             }
             mInCallService.removeActiveCallListChangedCallback(this);
+            mInCallService.removeCallAudioStateChangedCallback(this);
         }
         mInCallService = null;
     }
diff --git a/src/com/android/car/dialer/ui/activecall/OnGoingCallControllerBarFragment.java b/src/com/android/car/dialer/ui/activecall/OnGoingCallControllerBarFragment.java
index 8a822db..cda13a4 100644
--- a/src/com/android/car/dialer/ui/activecall/OnGoingCallControllerBarFragment.java
+++ b/src/com/android/car/dialer/ui/activecall/OnGoingCallControllerBarFragment.java
@@ -17,11 +17,13 @@
 package com.android.car.dialer.ui.activecall;
 
 import android.app.AlertDialog;
-import android.content.Context;
+import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.telecom.Call;
 import android.telecom.CallAudioState;
 import android.telecom.CallAudioState.CallAudioRoute;
+import android.text.SpannableString;
+import android.text.style.ForegroundColorSpan;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -36,16 +38,19 @@
 import androidx.lifecycle.LiveData;
 import androidx.lifecycle.MutableLiveData;
 import androidx.lifecycle.ViewModelProviders;
-import androidx.recyclerview.widget.LinearLayoutManager;
-import androidx.recyclerview.widget.RecyclerView;
 
 import com.android.car.apps.common.util.ViewUtils;
 import com.android.car.dialer.R;
 import com.android.car.dialer.log.L;
 import com.android.car.dialer.telecom.UiCallManager;
+import com.android.car.ui.AlertDialogBuilder;
+import com.android.car.ui.recyclerview.CarUiContentListItem;
+import com.android.car.ui.recyclerview.CarUiListItem;
+import com.android.car.ui.recyclerview.CarUiListItemAdapter;
 
 import com.google.common.collect.ImmutableMap;
 
+import java.util.ArrayList;
 import java.util.List;
 
 /** A Fragment of the bar which controls on going call. */
@@ -73,7 +78,9 @@
                     .build();
 
     private AlertDialog mAudioRouteSelectionDialog;
-    private AudioRouteListAdapter mAudioRouteAdapter;
+    private List<CarUiListItem> mAudioRouteListItems;
+    private List<Integer> mAvailableRoutes;
+    private CarUiListItemAdapter mAudioRouteAdapter;
     private View mMuteButton;
     private View mAudioRouteView;
     private ImageView mAudioRouteButton;
@@ -83,24 +90,51 @@
     private MutableLiveData<Boolean> mDialpadState;
     private LiveData<List<Call>> mCallListLiveData;
     private int mPrimaryCallState;
+    private int mActiveRoute;
+    private MutableLiveData<CallAudioState> mCallAudioState;
 
     @Override
     public void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        View dialogView = LayoutInflater.from(getContext()).inflate(
-                R.layout.audio_route_switch_dialog, null, false);
-        RecyclerView list = dialogView.findViewById(R.id.list);
-        list.setLayoutManager(new LinearLayoutManager(getContext()));
+        mAvailableRoutes = UiCallManager.get().getSupportedAudioRoute();
+        mActiveRoute = UiCallManager.get().getAudioRoute();
 
-        mAudioRouteSelectionDialog = new AlertDialog.Builder(getContext())
-                .setView(dialogView)
-                .create();
+        if (mAvailableRoutes.contains(CallAudioState.ROUTE_EARPIECE)
+                && mAvailableRoutes.contains(CallAudioState.ROUTE_WIRED_HEADSET)) {
+            // Keep either ROUTE_EARPIECE or ROUTE_WIRED_HEADSET, but not both of them.
+            mAvailableRoutes.remove(CallAudioState.ROUTE_WIRED_HEADSET);
+        }
 
-        List<Integer> availableRoutes = UiCallManager.get().getSupportedAudioRoute();
-        int activeRoute = UiCallManager.get().getAudioRoute();
-        mAudioRouteAdapter = new AudioRouteListAdapter(getContext(), availableRoutes, activeRoute);
-        list.setAdapter(mAudioRouteAdapter);
+        mAudioRouteListItems = new ArrayList<>();
+        mAudioRouteAdapter = new CarUiListItemAdapter(mAudioRouteListItems);
+
+        for (Integer audioRoute : mAvailableRoutes) {
+            CarUiContentListItem item = new CarUiContentListItem(CarUiContentListItem.Action.NONE);
+            AudioRouteInfo routeInfo = getAudioRouteInfo(audioRoute);
+            Drawable drawable = getResources().getDrawable(routeInfo.mIcon, null);
+            drawable.setTintList(
+                    getResources().getColorStateList(R.color.icon_accent_activatable, null));
+            item.setIcon(drawable);
+            item.setOnItemClickedListener((i) -> {
+                onSetAudioRoute(audioRoute);
+            });
+            String routeTitle = getString(routeInfo.mLabel);
+            item.setTitle(mActiveRoute == audioRoute ? withAccentColor(routeTitle) : routeTitle);
+            item.setActivated(mActiveRoute == audioRoute);
+            mAudioRouteListItems.add(item);
+        }
+
+        AlertDialogBuilder audioRouteSelectionDialogBuilder = new AlertDialogBuilder(getContext())
+                .setAdapter(mAudioRouteAdapter)
+                .setTitle(getString(R.string.audio_route_dialog_title));
+
+        String subtitle = getString(R.string.audio_route_dialog_subtitle);
+        if (!subtitle.isEmpty()) {
+            audioRouteSelectionDialogBuilder.setSubtitle(subtitle);
+        }
+
+        mAudioRouteSelectionDialog = audioRouteSelectionDialogBuilder.create();
 
         InCallViewModel inCallViewModel = ViewModelProviders.of(getActivity()).get(
                 InCallViewModel.class);
@@ -110,6 +144,7 @@
         inCallViewModel.getAudioRoute().observe(this, this::updateViewBasedOnAudioRoute);
 
         mDialpadState = inCallViewModel.getDialpadOpenState();
+        mCallAudioState = inCallViewModel.getCallAudioState();
 
         mCallListLiveData = inCallViewModel.getAllCallList();
         mCallListLiveData.observe(this, v -> updatePauseButtonEnabledState());
@@ -133,6 +168,8 @@
             }
         });
 
+        mCallAudioState.observe(this, state -> mMuteButton.setActivated(state.isMuted()));
+
         View dialPadButton = fragmentView.findViewById(R.id.toggle_dialpad_button);
         dialPadButton.setOnClickListener(v -> mDialpadState.setValue(!mDialpadState.getValue()));
         mDialpadState.observe(this, activated -> dialPadButton.setActivated(activated));
@@ -147,7 +184,6 @@
         if (audioRoutes.size() > 1) {
             mAudioRouteView.setOnClickListener((v) -> {
                 mAudioRouteView.setActivated(true);
-                mAudioRouteAdapter.setActiveAudioRoute(UiCallManager.get().getAudioRoute());
                 mAudioRouteSelectionDialog.show();
             });
         }
@@ -179,6 +215,26 @@
         }
     }
 
+    private void updateAudioRouteListItems() {
+        for (int i = 0; i < mAvailableRoutes.size(); i++) {
+            int audioRoute = mAvailableRoutes.get(i);
+            CarUiContentListItem item = (CarUiContentListItem) mAudioRouteListItems.get(i);
+            boolean isActiveRoute = audioRoute == mActiveRoute;
+            String routeTitle = item.getTitle().toString();
+            item.setActivated(isActiveRoute);
+            item.setTitle(isActiveRoute ? withAccentColor(routeTitle) : routeTitle);
+        }
+        mAudioRouteAdapter.notifyDataSetChanged();
+    }
+
+    private CharSequence withAccentColor(String routeTitle) {
+        ForegroundColorSpan activeRouteSpan = new ForegroundColorSpan(
+                getResources().getColor(R.color.audio_output_accent, null));
+        SpannableString spannableTitle = new SpannableString(routeTitle);
+        spannableTitle.setSpan(activeRouteSpan, 0, routeTitle.length(), 0);
+        return spannableTitle;
+    }
+
     /** Set the call state and change the view for the pause button accordingly */
     private void setCallState(int callState) {
         L.d(TAG, "Call State: %s", callState);
@@ -224,6 +280,8 @@
 
     private void onSetAudioRoute(@CallAudioRoute int audioRoute) {
         UiCallManager.get().setAudioRoute(audioRoute);
+        mActiveRoute = audioRoute;
+        updateAudioRouteListItems();
         mAudioRouteSelectionDialog.dismiss();
     }
 
@@ -233,6 +291,8 @@
         }
 
         L.i(TAG, "Audio Route State: " + audioRoute);
+        mActiveRoute = audioRoute;
+        updateAudioRouteListItems();
         AudioRouteInfo audioRouteInfo = getAudioRouteInfo(audioRoute);
         if (mAudioRouteButton != null) {
             mAudioRouteButton.setImageResource(audioRouteInfo.mIconActivatable);
@@ -274,64 +334,4 @@
             mIconActivatable = iconActivatable;
         }
     }
-
-    private class AudioRouteListAdapter extends
-            RecyclerView.Adapter<AudioRouteItemViewHolder> {
-        private List<Integer> mSupportedRoutes;
-        private Context mContext;
-        private int mActiveAudioRoute;
-
-        AudioRouteListAdapter(Context context,
-                List<Integer> supportedRoutes,
-                int activeAudioRoute) {
-            mSupportedRoutes = supportedRoutes;
-            mActiveAudioRoute = activeAudioRoute;
-            mContext = context;
-            if (mSupportedRoutes.contains(CallAudioState.ROUTE_EARPIECE)
-                    && mSupportedRoutes.contains(CallAudioState.ROUTE_WIRED_HEADSET)) {
-                // Keep either ROUTE_EARPIECE or ROUTE_WIRED_HEADSET, but not both of them.
-                mSupportedRoutes.remove(CallAudioState.ROUTE_WIRED_HEADSET);
-            }
-        }
-
-        public void setActiveAudioRoute(int route) {
-            if (mActiveAudioRoute != route) {
-                mActiveAudioRoute = route;
-                notifyDataSetChanged();
-            }
-        }
-
-        @Override
-        public AudioRouteItemViewHolder onCreateViewHolder(ViewGroup container, int position) {
-            View listItemView = LayoutInflater.from(mContext).inflate(
-                    R.layout.audio_route_list_item, container, false);
-            return new AudioRouteItemViewHolder(listItemView);
-        }
-
-        @Override
-        public void onBindViewHolder(AudioRouteItemViewHolder viewHolder, int position) {
-            int audioRoute = mSupportedRoutes.get(position);
-            AudioRouteInfo routeInfo = getAudioRouteInfo(audioRoute);
-            viewHolder.mBody.setText(routeInfo.mLabel);
-            viewHolder.mIcon.setImageResource(routeInfo.mIcon);
-            viewHolder.itemView.setActivated(audioRoute == mActiveAudioRoute);
-            viewHolder.itemView.setOnClickListener((v) -> onSetAudioRoute(audioRoute));
-        }
-
-        @Override
-        public int getItemCount() {
-            return mSupportedRoutes.size();
-        }
-    }
-
-    private static class AudioRouteItemViewHolder extends RecyclerView.ViewHolder {
-        public final ImageView mIcon;
-        public final TextView mBody;
-
-        public AudioRouteItemViewHolder(View itemView) {
-            super(itemView);
-            mIcon = itemView.findViewById(R.id.icon);
-            mBody = itemView.findViewById(R.id.body);
-        }
-    }
 }
diff --git a/src/com/android/car/dialer/ui/calllog/CallHistoryFragment.java b/src/com/android/car/dialer/ui/calllog/CallHistoryFragment.java
index 2658885..6595a06 100644
--- a/src/com/android/car/dialer/ui/calllog/CallHistoryFragment.java
+++ b/src/com/android/car/dialer/ui/calllog/CallHistoryFragment.java
@@ -42,6 +42,7 @@
 
     @Override
     public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
         // Don't recreate the adapter if we already have one, so that the list items
         // will display immediately upon the view being recreated. If they're not displayed
         // immediately, we won't remember our scroll position.
diff --git a/src/com/android/car/dialer/ui/calllog/CallLogViewHolder.java b/src/com/android/car/dialer/ui/calllog/CallLogViewHolder.java
index bef477b..9ffde69 100644
--- a/src/com/android/car/dialer/ui/calllog/CallLogViewHolder.java
+++ b/src/com/android/car/dialer/ui/calllog/CallLogViewHolder.java
@@ -31,7 +31,6 @@
 import com.android.car.dialer.ui.view.ContactAvatarOutputlineProvider;
 import com.android.car.dialer.widget.CallTypeIconsView;
 import com.android.car.telephony.common.Contact;
-import com.android.car.telephony.common.InMemoryPhoneBook;
 import com.android.car.telephony.common.PhoneCallLog;
 import com.android.car.telephony.common.TelecomUtils;
 
@@ -70,12 +69,13 @@
      * Binds the view holder with relevant data.
      */
     public void bind(UiCallLog uiCallLog) {
+        Contact contact = uiCallLog.getContact();
+
         TelecomUtils.setContactBitmapAsync(
                 mAvatarView.getContext(),
                 mAvatarView,
-                uiCallLog.getAvatarUri(),
-                uiCallLog.getInitials(),
-                uiCallLog.getTitle());
+                contact,
+                uiCallLog.getNumber());
 
         mTitleView.setText(uiCallLog.getTitle());
         for (PhoneCallLog.Record record : uiCallLog.getCallRecords()) {
@@ -99,7 +99,7 @@
         ViewUtils.setOnClickListener(mPlaceCallView,
                 view -> UiCallManager.get().placeCall(uiCallLog.getNumber()));
 
-        setUpActionButton(uiCallLog);
+        setUpActionButton(contact);
     }
 
     /**
@@ -110,12 +110,11 @@
         ViewUtils.setOnClickListener(mPlaceCallView, null);
     }
 
-    private void setUpActionButton(UiCallLog uiCallLog) {
+    private void setUpActionButton(Contact contact) {
         if (mActionButton == null) {
             return;
         }
 
-        Contact contact = InMemoryPhoneBook.get().lookupContactEntry(uiCallLog.getNumber());
         boolean forceShowActionButton = itemView.getResources().getBoolean(
                 R.bool.config_show_calllog_action_button_for_non_contact);
 
diff --git a/src/com/android/car/dialer/ui/common/ContactResultsLiveData.java b/src/com/android/car/dialer/ui/common/ContactResultsLiveData.java
new file mode 100644
index 0000000..4f82d7f
--- /dev/null
+++ b/src/com/android/car/dialer/ui/common/ContactResultsLiveData.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car.dialer.ui.common;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.ContactsContract;
+import android.text.TextUtils;
+
+import androidx.annotation.Nullable;
+import androidx.lifecycle.LiveData;
+import androidx.lifecycle.MediatorLiveData;
+
+import com.android.car.dialer.livedata.SharedPreferencesLiveData;
+import com.android.car.dialer.ui.common.entity.ContactSortingInfo;
+import com.android.car.telephony.common.Contact;
+import com.android.car.telephony.common.InMemoryPhoneBook;
+import com.android.car.telephony.common.ObservableAsyncQuery;
+import com.android.car.telephony.common.QueryParam;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Represents a list of {@link Contact} based on the search query
+ */
+public class ContactResultsLiveData extends
+        MediatorLiveData<List<ContactResultsLiveData.ContactResultListItem>> {
+    private static final String[] CONTACT_DETAILS_PROJECTION = {
+            ContactsContract.CommonDataKinds.Phone._ID,
+            ContactsContract.CommonDataKinds.Phone.LOOKUP_KEY,
+            ContactsContract.CommonDataKinds.Phone.NUMBER,
+    };
+    private final Context mContext;
+    private final SearchQueryParamProvider mSearchQueryParamProvider;
+    private final ObservableAsyncQuery mObservableAsyncQuery;
+    private final LiveData<String> mSearchQueryLiveData;
+    private final LiveData<List<Contact>> mContactListLiveData;
+    private final SharedPreferencesLiveData mSortOrderPreferenceLiveData;
+    private boolean mShowOnlyOneEntry;
+
+    /**
+     * @param searchQueryLiveData represents a list of strings that are used to query the data
+     * @param sortOrderPreferenceLiveData has the information on how to order the acquired contacts.
+     * @param showOnlyOneEntry determines whether to show only entry per contact.
+     */
+    public ContactResultsLiveData(Context context,
+            LiveData<String> searchQueryLiveData,
+            SharedPreferencesLiveData sortOrderPreferenceLiveData,
+            boolean showOnlyOneEntry) {
+        mContext = context;
+        mShowOnlyOneEntry = showOnlyOneEntry;
+        mSearchQueryParamProvider = new SearchQueryParamProvider(searchQueryLiveData);
+        mObservableAsyncQuery = new ObservableAsyncQuery(mSearchQueryParamProvider,
+                context.getContentResolver(), this::onQueryFinished);
+
+        mContactListLiveData = InMemoryPhoneBook.get().getContactsLiveData();
+        addSource(mContactListLiveData, this::onContactsChange);
+        mSearchQueryLiveData = searchQueryLiveData;
+        addSource(mSearchQueryLiveData, this::onSearchQueryChanged);
+
+        mSortOrderPreferenceLiveData = sortOrderPreferenceLiveData;
+        addSource(mSortOrderPreferenceLiveData, this::onSortOrderChanged);
+    }
+
+    /**
+     * This constructor only allows one entry per contact.
+     *
+     * @param searchQueryLiveData represents a list of strings that are used to query the data
+     * @param sortOrderPreferenceLiveData has the information on how to order the acquired contacts.
+     */
+    public ContactResultsLiveData(Context context,
+            LiveData<String> searchQueryLiveData,
+            SharedPreferencesLiveData sortOrderPreferenceLiveData) {
+        this(context, searchQueryLiveData, sortOrderPreferenceLiveData, true);
+    }
+
+    private void onContactsChange(List<Contact> contactList) {
+        if (contactList == null || contactList.isEmpty()) {
+            mObservableAsyncQuery.stopQuery();
+            setValue(Collections.emptyList());
+        } else {
+            onSearchQueryChanged(mSearchQueryLiveData.getValue());
+        }
+    }
+
+    private void onSearchQueryChanged(String searchQuery) {
+        if (TextUtils.isEmpty(searchQuery)) {
+            mObservableAsyncQuery.stopQuery();
+            setValue(Collections.emptyList());
+        } else {
+            mObservableAsyncQuery.startQuery();
+        }
+    }
+
+    private void onSortOrderChanged(SharedPreferences unusedSharedPreferences) {
+        setValue(getValue());
+    }
+
+    private void onQueryFinished(@Nullable Cursor cursor) {
+        if (cursor == null) {
+            setValue(Collections.emptyList());
+            return;
+        }
+
+        List<ContactResultListItem> contactResults = new ArrayList<>();
+        while (cursor.moveToNext()) {
+            int lookupKeyColIdx = cursor.getColumnIndex(
+                    ContactsContract.CommonDataKinds.Phone.LOOKUP_KEY);
+            int numberIdx = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
+            String lookupKey = cursor.getString(lookupKeyColIdx);
+            String number = cursor.getString(numberIdx);
+            List<Contact> lookupResults = InMemoryPhoneBook.get().lookupContactByKey(lookupKey);
+            for (Contact contact : lookupResults) {
+                contactResults.add(new ContactResultListItem(contact, number));
+            }
+        }
+
+        if (mShowOnlyOneEntry) {
+            Set<Contact> set = new HashSet<>();
+            contactResults = contactResults.stream()
+                    .filter(o -> set.add(o.getContact()))
+                    .collect(Collectors.toList());
+        }
+
+        setValue(contactResults);
+        cursor.close();
+    }
+
+    /**
+     * Sort and replace null list with empty list.
+     */
+    @Override
+    public void setValue(List<ContactResultListItem> contactResults) {
+        if (contactResults != null && !contactResults.isEmpty()) {
+            Collections.sort(contactResults, (o1, o2) -> ContactSortingInfo.getSortingInfo(
+                    mContext, mSortOrderPreferenceLiveData).first.compare(o1.mContact,
+                    o2.mContact));
+        }
+        super.setValue(contactResults == null ? Collections.EMPTY_LIST : contactResults);
+    }
+
+    private static class SearchQueryParamProvider implements QueryParam.Provider {
+        private final LiveData<String> mSearchQueryLiveData;
+
+        private SearchQueryParamProvider(LiveData<String> searchQueryLiveData) {
+            mSearchQueryLiveData = searchQueryLiveData;
+        }
+
+        @Nullable
+        @Override
+        public QueryParam getQueryParam() {
+            Uri lookupUri = Uri.withAppendedPath(
+                    ContactsContract.CommonDataKinds.Phone.CONTENT_FILTER_URI,
+                    Uri.encode(mSearchQueryLiveData.getValue()));
+            return new QueryParam(lookupUri, CONTACT_DETAILS_PROJECTION, null,
+                    /* selectionArgs= */null, /* orderBy= */null);
+        }
+    }
+
+    /**
+     * Represent a contact search result.
+     */
+    public static class ContactResultListItem {
+        private final Contact mContact;
+        private final String mNumber;
+
+        public ContactResultListItem(Contact contact, String number) {
+            mContact = contact;
+            mNumber = number;
+        }
+
+        /**
+         * Returns the contact.
+         */
+        public Contact getContact() {
+            return mContact;
+        }
+
+        /**
+         * Returns the number. It is a string read from column
+         * {@link ContactsContract.CommonDataKinds.Phone#NUMBER}.
+         */
+        public String getNumber() {
+            return mNumber;
+        }
+    }
+}
diff --git a/src/com/android/car/dialer/ui/common/DialerBaseFragment.java b/src/com/android/car/dialer/ui/common/DialerBaseFragment.java
index 9e7aa7a..04afb88 100644
--- a/src/com/android/car/dialer/ui/common/DialerBaseFragment.java
+++ b/src/com/android/car/dialer/ui/common/DialerBaseFragment.java
@@ -18,7 +18,9 @@
 
 import android.app.Activity;
 import android.os.Bundle;
+import android.view.View;
 
+import androidx.annotation.CallSuper;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.fragment.app.Fragment;
@@ -55,6 +57,17 @@
         mToolbarHeight = new MutableLiveData<>();
     }
 
+    @CallSuper
+    @Override
+    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+        Toolbar toolbar = getActivity().findViewById(R.id.car_ui_toolbar);
+        // Null check for unit tests to pass
+        if (toolbar != null) {
+            setupToolbar(toolbar);
+        }
+    }
+
     @Override
     public void onStart() {
         super.onStart();
@@ -64,7 +77,7 @@
     /**
      * Customizes the tool bar. Can be overridden in subclasses.
      */
-    public void setupToolbar(@NonNull Toolbar toolbar) {
+    protected void setupToolbar(@NonNull Toolbar toolbar) {
         TelecomActivityViewModel viewModel = ViewModelProviders.of(getActivity()).get(
                 TelecomActivityViewModel.class);
         LiveData<String> toolbarTitleLiveData = viewModel.getToolbarTitle();
@@ -79,7 +92,7 @@
 
         setShowToolbarBackground(true);
 
-        setToolbarHeight(toolbar.getHeight());
+        setToolbarHeight(toolbar);
     }
 
     /**
@@ -106,7 +119,13 @@
     /**
      * Sets the toolbar height.
      */
-    public final void setToolbarHeight(int toolbarHeight) {
+    protected final void setToolbarHeight(Toolbar toolbar) {
+        int toolbarFirstRowHeight = getResources().getDimensionPixelSize(
+                R.dimen.car_ui_toolbar_first_row_height);
+        int toolbarHeight = toolbar.isTabsInSecondRow() && getToolbarState() == Toolbar.State.HOME
+                ? toolbarFirstRowHeight + getResources().getDimensionPixelSize(
+                R.dimen.car_ui_toolbar_second_row_height)
+                : toolbarFirstRowHeight;
         mToolbarHeight.setValue(toolbarHeight);
     }
 
diff --git a/src/com/android/car/dialer/ui/common/DialerUtils.java b/src/com/android/car/dialer/ui/common/DialerUtils.java
index 3bcca38..5826e42 100644
--- a/src/com/android/car/dialer/ui/common/DialerUtils.java
+++ b/src/com/android/car/dialer/ui/common/DialerUtils.java
@@ -17,6 +17,10 @@
 package com.android.car.dialer.ui.common;
 
 import android.content.Context;
+import android.content.res.Resources;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
 import com.android.car.dialer.R;
 import com.android.car.dialer.log.L;
@@ -24,6 +28,8 @@
 import com.android.car.telephony.common.PhoneNumber;
 import com.android.car.telephony.common.TelecomUtils;
 import com.android.car.ui.AlertDialogBuilder;
+import com.android.car.ui.recyclerview.CarUiRadioButtonListItem;
+import com.android.car.ui.recyclerview.CarUiRadioButtonListItemAdapter;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -41,8 +47,9 @@
     public interface PhoneNumberSelectionCallback {
         /**
          * Called when a phone number is chosen.
+         *
          * @param phoneNumber The phone number
-         * @param always Whether the user pressed "aways" or "just once"
+         * @param always      Whether the user pressed "aways" or "just once"
          */
         void onPhoneNumberSelected(PhoneNumber phoneNumber, boolean always);
     }
@@ -54,16 +61,28 @@
     public static void showPhoneNumberSelector(Context context,
             List<PhoneNumber> numbers,
             PhoneNumberSelectionCallback callback) {
+
         final List<PhoneNumber> selectedPhoneNumber = new ArrayList<>();
+        List<CarUiRadioButtonListItem> items = new ArrayList<>();
+        CarUiRadioButtonListItemAdapter adapter = new CarUiRadioButtonListItemAdapter(items);
+
+        for (PhoneNumber number : numbers) {
+            CharSequence readableLabel = number.getReadableLabel(context.getResources());
+            CarUiRadioButtonListItem item = new CarUiRadioButtonListItem();
+            item.setTitle(number.isPrimary()
+                    ? context.getString(R.string.primary_number_description, readableLabel)
+                    : readableLabel);
+            item.setBody(number.getNumber());
+            item.setOnCheckedChangeListener((i, isChecked) -> {
+                selectedPhoneNumber.clear();
+                selectedPhoneNumber.add(number);
+            });
+            items.add(item);
+        }
+
         new AlertDialogBuilder(context)
                 .setTitle(R.string.select_number_dialog_title)
-                .setSingleChoiceItems(
-                        new PhoneNumberListAdapter(context, numbers),
-                        -1,
-                        ((dialog, which) -> {
-                            selectedPhoneNumber.clear();
-                            selectedPhoneNumber.add(numbers.get(which));
-                        }))
+                .setSingleChoiceItems(adapter, null)
                 .setNeutralButton(R.string.select_number_dialog_just_once_button,
                         (dialog, which) -> {
                             if (!selectedPhoneNumber.isEmpty()) {
@@ -106,4 +125,23 @@
             L.w(TAG, "contact %s doesn't have any phone number", contact.getDisplayName());
         }
     }
+
+    /**
+     * Returns true if the contact has postal address and show postal address config is true.
+     */
+    private static boolean hasPostalAddress(Resources resources, @NonNull Contact contact) {
+        boolean showPostalAddress = resources.getBoolean(
+                R.bool.config_show_postal_address);
+        return showPostalAddress && (!contact.getPostalAddresses().isEmpty());
+    }
+
+    /**
+     * Returns true if the contact has either phone number or postal address to show.
+     */
+    public static boolean hasContactDetail(Resources resources, @Nullable Contact contact) {
+        boolean hasContactDetail = contact != null
+                && (!contact.getNumbers().isEmpty() || DialerUtils.hasPostalAddress(
+                resources, contact));
+        return hasContactDetail;
+    }
 }
diff --git a/src/com/android/car/dialer/ui/common/FavoritePhoneNumberListAdapter.java b/src/com/android/car/dialer/ui/common/FavoritePhoneNumberListAdapter.java
deleted file mode 100644
index 4f93fd0..0000000
--- a/src/com/android/car/dialer/ui/common/FavoritePhoneNumberListAdapter.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.car.dialer.ui.common;
-
-import android.content.Context;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-import android.widget.TextView;
-
-import com.android.car.dialer.R;
-import com.android.car.telephony.common.Contact;
-import com.android.car.telephony.common.PhoneNumber;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * {@link BaseAdapter} that presents the {@link PhoneNumber} and its type as two line list
- * item with stars to indicate favorite state or user selection to add to favorite. Currently
- * favorite phone number is set to disabled so user can not take any action for an existing favorite
- * phone number.
- */
-public class FavoritePhoneNumberListAdapter extends BaseAdapter {
-    private final Context mContext;
-    private final FavoritePhoneNumberPresenter mFavoritePhoneNumberPresenter;
-    private final List<PhoneNumber> mPhoneNumbers;
-    private Contact mContact;
-
-    /**
-     * A presenter that presents the favorite state for phone number and provides the click
-     * listener.
-     */
-    public interface FavoritePhoneNumberPresenter {
-        /**
-         * Provides the click listener for the given phone number and its present view.
-         */
-        void onItemClicked(PhoneNumber phoneNumber, View itemView);
-    }
-
-    public FavoritePhoneNumberListAdapter(Context context,
-            FavoritePhoneNumberPresenter favoritePhoneNumberPresenter) {
-        mContext = context;
-        mFavoritePhoneNumberPresenter = favoritePhoneNumberPresenter;
-        mPhoneNumbers = new ArrayList<>();
-    }
-
-    /**
-     * Sets the phone numbers to display
-     */
-    public void setPhoneNumbers(Contact contact, List<PhoneNumber> phoneNumbers) {
-        mPhoneNumbers.clear();
-        mContact = contact;
-        mPhoneNumbers.addAll(phoneNumbers);
-
-        notifyDataSetChanged();
-    }
-
-    public Contact getContact() {
-        return mContact;
-    }
-
-    @Override
-    public int getCount() {
-        return mPhoneNumbers.size();
-    }
-
-    @Override
-    public PhoneNumber getItem(int position) {
-        return mPhoneNumbers.get(position);
-    }
-
-    @Override
-    public long getItemId(int position) {
-        return position;
-    }
-
-    @Override
-    public View getView(int position, View convertView, ViewGroup parent) {
-        View itemView;
-        if (convertView == null) {
-            itemView = LayoutInflater.from(mContext).inflate(
-                    R.layout.add_favorite_number_list_item, parent, false);
-        } else {
-            itemView = convertView;
-        }
-        PhoneNumber phoneNumber = getItem(position);
-        bind(phoneNumber, itemView);
-        return itemView;
-    }
-
-    void bind(PhoneNumber phoneNumber, View itemView) {
-        TextView phoneNumberView = itemView.findViewById(R.id.phone_number);
-        TextView phoneNumberDescriptionView = itemView.findViewById(R.id.phone_number_description);
-        phoneNumberView.setText(phoneNumber.getRawNumber());
-        CharSequence readableLabel = phoneNumber.getReadableLabel(itemView.getResources());
-
-        if (phoneNumber.isFavorite()) {
-            phoneNumberDescriptionView.setText(
-                    itemView.getResources().getString(R.string.favorite_number_description,
-                            readableLabel));
-            itemView.setActivated(true);
-            itemView.setEnabled(false);
-        } else {
-            phoneNumberDescriptionView.setText(readableLabel);
-            itemView.setActivated(false);
-            itemView.setEnabled(true);
-            itemView.setOnClickListener(
-                    view -> mFavoritePhoneNumberPresenter.onItemClicked(phoneNumber, itemView));
-        }
-    }
-}
diff --git a/src/com/android/car/dialer/ui/common/PhoneNumberListAdapter.java b/src/com/android/car/dialer/ui/common/PhoneNumberListAdapter.java
deleted file mode 100644
index 923fb51..0000000
--- a/src/com/android/car/dialer/ui/common/PhoneNumberListAdapter.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.car.dialer.ui.common;
-
-import android.content.Context;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.car.dialer.R;
-import com.android.car.telephony.common.PhoneNumber;
-
-import java.util.List;
-
-/**
- * {@link ArrayAdapter} that simply presents the {@link PhoneNumber} and its type as two line list
- * item.
- */
-public class PhoneNumberListAdapter extends ArrayAdapter<PhoneNumber> {
-    private final Context mContext;
-
-    public PhoneNumberListAdapter(Context context, List<PhoneNumber> phoneNumbers) {
-        super(context, R.layout.phone_number_list_item, R.id.phone_number, phoneNumbers);
-        mContext = context;
-    }
-
-    @Override
-    public View getView(int position, @Nullable View convertView,
-            @NonNull ViewGroup parent) {
-        View view = super.getView(position, convertView, parent);
-        PhoneNumber phoneNumber = getItem(position);
-        if (phoneNumber == null) {
-            return view;
-        }
-        TextView phoneNumberView = view.findViewById(R.id.phone_number);
-        phoneNumberView.setText(phoneNumber.getRawNumber());
-        TextView phoneNumberDescriptionView = view.findViewById(R.id.phone_number_description);
-        CharSequence readableLabel = phoneNumber.getReadableLabel(mContext.getResources());
-        if (phoneNumber.isPrimary()) {
-            phoneNumberDescriptionView.setText(
-                    mContext.getString(R.string.primary_number_description, readableLabel));
-        } else {
-            phoneNumberDescriptionView.setText(readableLabel);
-        }
-        return view;
-    }
-}
diff --git a/src/com/android/car/dialer/ui/common/UiCallLogLiveData.java b/src/com/android/car/dialer/ui/common/UiCallLogLiveData.java
index 5b6d06a..1c48f1a 100644
--- a/src/com/android/car/dialer/ui/common/UiCallLogLiveData.java
+++ b/src/com/android/car/dialer/ui/common/UiCallLogLiveData.java
@@ -16,7 +16,12 @@
 
 package com.android.car.dialer.ui.common;
 
+import android.content.ContentResolver;
 import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.PhoneLookup;
 import android.text.TextUtils;
 import android.text.format.DateUtils;
 
@@ -43,16 +48,21 @@
 import java.util.Calendar;
 import java.util.Collections;
 import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
 
 /**
- * Represents a list of {@link UiCallLog}s and label {@link String}s for UI representation.
- * This live data gets data source from both call log and contact list. It also refresh
- * itself on the relative time in the body text.
+ * Represents a list of {@link UiCallLog}s and label {@link String}s for UI representation. This
+ * live data gets data source from both call log and contact list. It also refresh itself on the
+ * relative time in the body text.
  */
 public class UiCallLogLiveData extends MediatorLiveData<List<Object>> {
     private static final String TAG = "CD.UiCallLogLiveData";
 
     private static final String TYPE_AND_RELATIVE_TIME_JOINER = ", ";
+    private final ExecutorService mExecutorService;
+    private Future<?> mRunnableFuture;
     private Context mContext;
 
     public UiCallLogLiveData(Context context,
@@ -60,6 +70,8 @@
             CallHistoryLiveData callHistoryLiveData,
             LiveData<List<Contact>> contactListLiveData) {
         mContext = context;
+        mExecutorService = Executors.newSingleThreadExecutor();
+
         addSource(callHistoryLiveData, this::onCallHistoryChanged);
         addSource(contactListLiveData,
                 (contacts) -> onCallHistoryChanged(callHistoryLiveData.getValue()));
@@ -71,7 +83,14 @@
         if (callLogs == null && getValue() == null) {
             return;
         }
-        setValue(convert(callLogs));
+
+        if (mRunnableFuture != null) {
+            mRunnableFuture.cancel(true);
+        }
+        Runnable runnable = () -> {
+            postValue(convert(callLogs));
+        };
+        mRunnableFuture = mExecutorService.submit(runnable);
     }
 
     private void updateRelativeTime() {
@@ -133,14 +152,51 @@
             String relativeTime = getRelativeTime(phoneCallLog.getLastCallEndTimestamp());
             if (TelecomUtils.isVoicemailNumber(mContext, number)) {
                 String title = mContext.getString(R.string.voicemail);
-                UiCallLog uiCallLog = new UiCallLog(title, relativeTime, number, null, null,
+                UiCallLog uiCallLog = new UiCallLog(title, relativeTime, number, null,
                         phoneCallLog.getAllCallRecords());
                 uiCallLogs.add(uiCallLog);
                 continue;
             }
 
-            Contact contact = inMemoryPhoneBook.lookupContactEntry(number);
             String title;
+            CharSequence typeLabel = "";
+            Contact contact = null;
+
+            // If InMemoryPhoneBook hasn't finished loading, there is still a chance that this
+            // number can be found there later. So query will not be proceeded now.
+            // TODO: will move to utils later.
+            if (inMemoryPhoneBook.isLoaded()) {
+                contact = inMemoryPhoneBook.lookupContactEntry(number);
+                if (contact == null && !TextUtils.isEmpty(number)) {
+                    ContentResolver cr = mContext.getContentResolver();
+                    try (Cursor cursor = cr.query(
+                            Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI,
+                                    Uri.encode(number)),
+                            new String[]{
+                                    PhoneLookup.LOOKUP_KEY,
+                                    PhoneLookup.TYPE,
+                                    PhoneLookup.LABEL,
+                            },
+                            null, null, null)) {
+
+                        if (cursor != null && cursor.moveToFirst()) {
+                            int lookupKeyColIdx = cursor.getColumnIndex(PhoneLookup.LOOKUP_KEY);
+                            int typeColumn = cursor.getColumnIndex(PhoneLookup.TYPE);
+                            int labelColumn = cursor.getColumnIndex(PhoneLookup.LABEL);
+
+                            List<Contact> lookupResults =
+                                    InMemoryPhoneBook.get().lookupContactByKey(
+                                            cursor.getString(lookupKeyColIdx));
+                            contact = lookupResults.size() > 0 ? lookupResults.get(0) : null;
+                            int type = cursor.getInt(typeColumn);
+                            String label = cursor.getString(labelColumn);
+                            typeLabel = ContactsContract.CommonDataKinds.Phone.getTypeLabel(
+                                    mContext.getResources(), type, label);
+                        }
+                    }
+                }
+            }
+
             if (contact != null && contact.getDisplayName() != null) {
                 title = contact.getDisplayName();
             } else if (!TextUtils.isEmpty(number)) {
@@ -153,10 +209,11 @@
 
             UiCallLog uiCallLog = new UiCallLog(
                     title,
-                    getSecondaryText(getType(phoneNumber), relativeTime),
+                    getSecondaryText(
+                            TextUtils.isEmpty(typeLabel) ? getType(phoneNumber) : typeLabel,
+                            relativeTime),
                     number,
-                    contact != null ? contact.getInitials() : null,
-                    contact != null ? contact.getAvatarUri() : null,
+                    contact,
                     phoneCallLog.getAllCallRecords());
 
             uiCallLogs.add(uiCallLog);
diff --git a/src/com/android/car/dialer/ui/common/entity/ActionButton.java b/src/com/android/car/dialer/ui/common/entity/ActionButton.java
new file mode 100644
index 0000000..46dafe9
--- /dev/null
+++ b/src/com/android/car/dialer/ui/common/entity/ActionButton.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car.dialer.ui.common.entity;
+
+/**Objects stand for buttons.*/
+public final class ActionButton {
+}
diff --git a/src/com/android/car/dialer/ui/common/entity/Header.java b/src/com/android/car/dialer/ui/common/entity/Header.java
new file mode 100644
index 0000000..c184dfe
--- /dev/null
+++ b/src/com/android/car/dialer/ui/common/entity/Header.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car.dialer.ui.common.entity;
+
+/**
+ * Objects stand for headers.
+ */
+public final class Header {
+    private final String mHeader;
+
+    public Header(String header) {
+        mHeader = header;
+    }
+
+    /**
+     * Getter to return the header string.
+     */
+    public String getHeader() {
+        return mHeader;
+    }
+}
diff --git a/src/com/android/car/dialer/ui/common/entity/UiCallLog.java b/src/com/android/car/dialer/ui/common/entity/UiCallLog.java
index 9de9730..35ae239 100644
--- a/src/com/android/car/dialer/ui/common/entity/UiCallLog.java
+++ b/src/com/android/car/dialer/ui/common/entity/UiCallLog.java
@@ -16,11 +16,10 @@
 
 package com.android.car.dialer.ui.common.entity;
 
-import android.net.Uri;
-
 import androidx.annotation.Nullable;
 
 import com.android.car.dialer.livedata.CallHistoryLiveData;
+import com.android.car.telephony.common.Contact;
 import com.android.car.telephony.common.PhoneCallLog;
 
 import java.util.ArrayList;
@@ -32,29 +31,26 @@
  */
 public class UiCallLog {
     private final String mTitle;
+    private String mText;
     private final String mNumber;
     @Nullable
-    private final String mInitials;
-    private final Uri mAvatarUri;
+    private final Contact mContact;
     private final List<PhoneCallLog.Record> mCallRecords;
-    private String mText;
 
-    public UiCallLog(String title, String text, String number, @Nullable String initials,
-            Uri avatarUri, List<PhoneCallLog.Record> callRecords) {
+    public UiCallLog(String title, String text, String number, @Nullable Contact contact,
+            List<PhoneCallLog.Record> callRecords) {
         mTitle = title;
         mText = text;
         mNumber = number;
-        mInitials = initials;
-        mAvatarUri = avatarUri;
+        mContact = contact;
         mCallRecords = new ArrayList<>(callRecords);
     }
 
     /**
-     * Returns the initials of a call log item. Returns null if this item is not a contact.
+     * Updates the body text.
      */
-    @Nullable
-    public String getInitials() {
-        return mInitials;
+    public void setText(String text) {
+        mText = text;
     }
 
     /**
@@ -72,23 +68,18 @@
     }
 
     /**
-     * Updates the body text.
-     */
-    public void setText(String text) {
-        mText = text;
-    }
-
-    /**
      * Returns the number of this call log.
      */
     public String getNumber() {
         return mNumber;
     }
 
-    /** Returns the avatar of the contact associated with the number. */
+    /**
+     * Returns the related contact of a call log item. Returns null if this item is not a contact.
+     */
     @Nullable
-    public Uri getAvatarUri() {
-        return mAvatarUri;
+    public Contact getContact() {
+        return mContact;
     }
 
     /**
diff --git a/src/com/android/car/dialer/ui/contact/ContactDetailsFragment.java b/src/com/android/car/dialer/ui/contact/ContactDetailsFragment.java
index a301393..7f33f46 100644
--- a/src/com/android/car/dialer/ui/contact/ContactDetailsFragment.java
+++ b/src/com/android/car/dialer/ui/contact/ContactDetailsFragment.java
@@ -94,6 +94,7 @@
 
     @Override
     public void onViewCreated(View view, Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
         ContactDetailsAdapter contactDetailsAdapter = new ContactDetailsAdapter(getContext(),
                 mContact, this);
         getRecyclerView().setAdapter(contactDetailsAdapter);
@@ -111,6 +112,10 @@
     private void onContactChanged(Contact contact) {
         getArguments().clear();
         Toolbar toolbar = getActivity().findViewById(R.id.car_ui_toolbar);
+        // Null check to have unit tests to pass.
+        if (toolbar == null) {
+            return;
+        }
         toolbar.setTitle(null);
         toolbar.setLogo(null);
         if (mShowActionBarView) {
@@ -148,7 +153,7 @@
     }
 
     @Override
-    public void setupToolbar(@NonNull Toolbar toolbar) {
+    protected void setupToolbar(@NonNull Toolbar toolbar) {
         toolbar.setState(getToolbarState());
         toolbar.setMenuItems(null);
 
@@ -156,6 +161,8 @@
         if (!mShowActionBarView) {
             setShowToolbarBackground(false);
         }
+
+        setToolbarHeight(toolbar);
     }
 
     @Override
diff --git a/src/com/android/car/dialer/ui/contact/ContactDetailsViewHolder.java b/src/com/android/car/dialer/ui/contact/ContactDetailsViewHolder.java
index 0ffe3f9..eec3013 100644
--- a/src/com/android/car/dialer/ui/contact/ContactDetailsViewHolder.java
+++ b/src/com/android/car/dialer/ui/contact/ContactDetailsViewHolder.java
@@ -67,7 +67,7 @@
     @Nullable
     private final View mCallActionView;
     @Nullable
-    private final View mFavoriteActionView;
+    private final ImageView mFavoriteActionView;
 
     // Applies to address items
     @Nullable
@@ -152,26 +152,42 @@
     }
 
     public void bind(Context context, Contact contact, PhoneNumber phoneNumber) {
-
         mTitle.setText(phoneNumber.getRawNumber());
 
-        // Present the phone number type.
+        // Present the phone number type and local favorite state.
         CharSequence readableLabel = phoneNumber.getReadableLabel(context.getResources());
+        CharSequence favoriteLabel = phoneNumber.isFavorite() ? context.getString(
+                R.string.local_favorite_number_description, readableLabel) : readableLabel;
         if (phoneNumber.isPrimary()) {
-            mText.setText(context.getString(R.string.primary_number_description, readableLabel));
+            mText.setText(context.getString(R.string.primary_number_description, favoriteLabel));
             mText.setTextAppearance(R.style.TextAppearance_DefaultNumberLabel);
         } else {
-            mText.setText(readableLabel);
+            mText.setText(favoriteLabel);
             mText.setTextAppearance(R.style.TextAppearance_ContactDetailsListSubtitle);
         }
 
         mCallActionView.setOnClickListener(
                 v -> UiCallManager.get().placeCall(phoneNumber.getRawNumber()));
-        mFavoriteActionView.setActivated(phoneNumber.isFavorite());
-        mFavoriteActionView.setOnClickListener(v -> {
-            mPhoneNumberPresenter.onClick(contact, phoneNumber);
-            mFavoriteActionView.setActivated(!mFavoriteActionView.isActivated());
-        });
+
+        if (phoneNumber.isFavorite() || !contact.isStarred()) {
+            // If the phone number is marked as favorite locally, enable the action button to
+            // allow users to remove from local favorites.
+            // If the phone number is not a local favorite or a favorite from phone, allow users
+            // to mark it as favorite locally.
+            ViewUtils.setActivated(mFavoriteActionView, phoneNumber.isFavorite());
+            ViewUtils.setEnabled(mFavoriteActionView, true);
+            ViewUtils.setOnClickListener(mFavoriteActionView, v -> {
+                mPhoneNumberPresenter.onClick(contact, phoneNumber);
+                mFavoriteActionView.setActivated(!mFavoriteActionView.isActivated());
+            });
+        } else {
+            // The contact is favorite contact downloaded from phone. Disable the favorite action
+            // button so the phone numbers can not be marked as favorite locally as it is not
+            // necessary.
+            ViewUtils.setActivated(mFavoriteActionView, false);
+            ViewUtils.setEnabled(mFavoriteActionView, false);
+            ViewUtils.setOnClickListener(mFavoriteActionView, null);
+        }
     }
 
     public void bind(Context context, @NonNull PostalAddress postalAddress) {
diff --git a/src/com/android/car/dialer/ui/contact/ContactListFragment.java b/src/com/android/car/dialer/ui/contact/ContactListFragment.java
index d638b6c..954806a 100644
--- a/src/com/android/car/dialer/ui/contact/ContactListFragment.java
+++ b/src/com/android/car/dialer/ui/contact/ContactListFragment.java
@@ -42,6 +42,7 @@
 
     @Override
     public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
         // Don't recreate the adapter if we already have one, so that the list items
         // will display immediately upon the view being recreated. If they're not displayed
         // immediately, we won't remember our scroll position.
diff --git a/src/com/android/car/dialer/ui/contact/ContactListViewHolder.java b/src/com/android/car/dialer/ui/contact/ContactListViewHolder.java
index 8a35f87..9619151 100644
--- a/src/com/android/car/dialer/ui/contact/ContactListViewHolder.java
+++ b/src/com/android/car/dialer/ui/contact/ContactListViewHolder.java
@@ -126,8 +126,7 @@
             return;
         }
 
-        boolean hasContactDetail = contact != null
-                && (!contact.getNumbers().isEmpty() || hasPostalAddress(contact));
+        boolean hasContactDetail = DialerUtils.hasContactDetail(itemView.getResources(), contact);
 
         ViewUtils.setEnabled(mShowContactDetailView, hasContactDetail);
         ViewUtils.setVisible(mShowContactDetailView, hasContactDetail || forceShowButton);
@@ -140,12 +139,6 @@
         }
     }
 
-    private boolean hasPostalAddress(@NonNull Contact contact) {
-        boolean showPostalAddress = itemView.getResources().getBoolean(
-                R.bool.config_show_postal_address);
-        return showPostalAddress && (!contact.getPostalAddresses().isEmpty());
-    }
-
     /**
      * Recycles views.
      */
diff --git a/src/com/android/car/dialer/ui/dialpad/AbstractDialpadFragment.java b/src/com/android/car/dialer/ui/dialpad/AbstractDialpadFragment.java
index 2333414..93cc34b 100644
--- a/src/com/android/car/dialer/ui/dialpad/AbstractDialpadFragment.java
+++ b/src/com/android/car/dialer/ui/dialpad/AbstractDialpadFragment.java
@@ -89,6 +89,7 @@
     @CallSuper
     @Override
     public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
         mTitleView = view.findViewById(R.id.title);
         if (mTitleView != null && getResources().getBoolean(R.bool.config_enable_dial_motion)) {
             mInputMotionAnimator = (ValueAnimator) AnimatorInflater.loadAnimator(getContext(),
diff --git a/src/com/android/car/dialer/ui/dialpad/DialpadFragment.java b/src/com/android/car/dialer/ui/dialpad/DialpadFragment.java
index 4438980..3368a81 100644
--- a/src/com/android/car/dialer/ui/dialpad/DialpadFragment.java
+++ b/src/com/android/car/dialer/ui/dialpad/DialpadFragment.java
@@ -32,6 +32,7 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.lifecycle.ViewModelProviders;
 
 import com.android.car.apps.common.util.ViewUtils;
 import com.android.car.dialer.R;
@@ -42,6 +43,7 @@
 import com.android.car.telephony.common.InMemoryPhoneBook;
 import com.android.car.telephony.common.PhoneNumber;
 import com.android.car.telephony.common.TelecomUtils;
+import com.android.car.ui.recyclerview.CarUiRecyclerView;
 import com.android.car.ui.toolbar.Toolbar;
 
 import com.google.common.annotations.VisibleForTesting;
@@ -77,11 +79,15 @@
                     .put(KeyEvent.KEYCODE_STAR, ToneGenerator.TONE_DTMF_S)
                     .put(KeyEvent.KEYCODE_POUND, ToneGenerator.TONE_DTMF_P)
                     .build();
+    private final TypeDownResultsAdapter mAdapter = new TypeDownResultsAdapter();
 
+    private TypeDownResultsViewModel mTypeDownResultsViewModel;
     private TextView mTitleView;
     @Nullable
     private TextView mDisplayName;
     @Nullable
+    private CarUiRecyclerView mRecyclerView;
+    @Nullable
     private TextView mLabel;
     @Nullable
     private ImageView mAvatar;
@@ -120,6 +126,11 @@
         mMode = getArguments().getInt(DIALPAD_MODE_KEY);
         L.d(TAG, "onCreate mode: %s", mMode);
         mToneGenerator = new ToneGenerator(AudioManager.STREAM_MUSIC, TONE_RELATIVE_VOLUME);
+
+        mTypeDownResultsViewModel = ViewModelProviders.of(this).get(
+                TypeDownResultsViewModel.class);
+        mTypeDownResultsViewModel.getContactSearchResults().observe(this,
+                contactResults -> mAdapter.setData(contactResults));
     }
 
     @Override
@@ -132,6 +143,8 @@
                 mMode == MODE_EMERGENCY ? R.style.TextAppearance_EmergencyDialNumber
                         : R.style.TextAppearance_DialNumber);
         mDisplayName = rootView.findViewById(R.id.display_name);
+        mRecyclerView = rootView.findViewById(R.id.list_view);
+        mRecyclerView.setAdapter(mAdapter);
         mLabel = rootView.findViewById(R.id.label);
         mAvatar = rootView.findViewById(R.id.dialpad_contact_avatar);
         if (mAvatar != null) {
@@ -174,7 +187,7 @@
     }
 
     @Override
-    public void setupToolbar(Toolbar toolbar) {
+    protected void setupToolbar(Toolbar toolbar) {
         // Only setup the actionbar if we're in dial mode.
         // In all the other modes, there will be another fragment in the activity
         // at the same time, and we don't want to mess up it's action bar.
@@ -238,7 +251,13 @@
             ViewUtils.setVisible(mDeleteButton, true);
         }
 
-        presentContactInfo(number.toString());
+        if (getResources().getBoolean(R.bool.config_show_type_down_list_on_dialpad)) {
+            resetContactInfo();
+            ViewUtils.setVisible(mRecyclerView, true);
+            mTypeDownResultsViewModel.setSearchQuery(number.toString());
+        } else {
+            presentContactInfo(number.toString());
+        }
     }
 
     @Override
diff --git a/src/com/android/car/dialer/ui/dialpad/InCallDialpadFragment.java b/src/com/android/car/dialer/ui/dialpad/InCallDialpadFragment.java
index 22079c9..8d8e5aa 100644
--- a/src/com/android/car/dialer/ui/dialpad/InCallDialpadFragment.java
+++ b/src/com/android/car/dialer/ui/dialpad/InCallDialpadFragment.java
@@ -114,7 +114,7 @@
     }
 
     @Override
-    public void setupToolbar(Toolbar toolbar) {
+    protected void setupToolbar(Toolbar toolbar) {
         // No-op
     }
 
diff --git a/src/com/android/car/dialer/ui/dialpad/TypeDownResultsAdapter.java b/src/com/android/car/dialer/ui/dialpad/TypeDownResultsAdapter.java
new file mode 100644
index 0000000..30ac4f3
--- /dev/null
+++ b/src/com/android/car/dialer/ui/dialpad/TypeDownResultsAdapter.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car.dialer.ui.dialpad;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.car.dialer.R;
+import com.android.car.dialer.ui.search.ContactResultViewHolder;
+import com.android.car.dialer.ui.search.ContactResultsAdapter;
+
+/**
+ * An adapter used for type down functionality.
+ */
+public class TypeDownResultsAdapter extends ContactResultsAdapter {
+
+    public TypeDownResultsAdapter() {
+        super(null);
+    }
+
+    @Override
+    public ContactResultViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+        View view = LayoutInflater.from(parent.getContext())
+                .inflate(R.layout.type_down_list_item, parent, false);
+        return new ContactResultViewHolder(view, null);
+    }
+
+    @Override
+    public void onBindViewHolder(ContactResultViewHolder holder, int position) {
+        holder.bindTypeDownResult(getContactResults().get(position));
+    }
+}
diff --git a/src/com/android/car/dialer/ui/dialpad/TypeDownResultsViewModel.java b/src/com/android/car/dialer/ui/dialpad/TypeDownResultsViewModel.java
new file mode 100644
index 0000000..8696fab
--- /dev/null
+++ b/src/com/android/car/dialer/ui/dialpad/TypeDownResultsViewModel.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car.dialer.ui.dialpad;
+
+import android.app.Application;
+
+import androidx.annotation.NonNull;
+import androidx.lifecycle.LiveData;
+
+import com.android.car.dialer.ui.common.ContactResultsLiveData;
+import com.android.car.dialer.ui.search.ContactResultsViewModel;
+
+import java.util.List;
+
+/**
+ * {link AndroidViewModel} used for type down functionality.
+ */
+public class TypeDownResultsViewModel extends ContactResultsViewModel {
+
+    private final ContactResultsLiveData mContactSearchResultsLiveData;
+
+    public TypeDownResultsViewModel(@NonNull Application application) {
+        super(application);
+        mContactSearchResultsLiveData = new ContactResultsLiveData(application,
+                getSearchQueryLiveData(), getSharedPreferencesLiveData(),
+                /* showOnlyOneEntry */ false);
+    }
+
+    @Override
+    public LiveData<List<ContactResultsLiveData.ContactResultListItem>> getContactSearchResults() {
+        return mContactSearchResultsLiveData;
+    }
+}
diff --git a/src/com/android/car/dialer/ui/favorite/AddFavoriteFragment.java b/src/com/android/car/dialer/ui/favorite/AddFavoriteFragment.java
index 574cee0..a45d5bc 100644
--- a/src/com/android/car/dialer/ui/favorite/AddFavoriteFragment.java
+++ b/src/com/android/car/dialer/ui/favorite/AddFavoriteFragment.java
@@ -17,18 +17,24 @@
 package com.android.car.dialer.ui.favorite;
 
 import android.app.AlertDialog;
+import android.graphics.drawable.Drawable;
 import android.os.Bundle;
+import android.widget.Toast;
 
 import androidx.lifecycle.ViewModelProviders;
 
 import com.android.car.dialer.R;
-import com.android.car.dialer.ui.common.FavoritePhoneNumberListAdapter;
 import com.android.car.dialer.ui.search.ContactResultsFragment;
 import com.android.car.telephony.common.Contact;
 import com.android.car.telephony.common.PhoneNumber;
 import com.android.car.ui.AlertDialogBuilder;
+import com.android.car.ui.recyclerview.CarUiContentListItem;
+import com.android.car.ui.recyclerview.CarUiListItem;
+import com.android.car.ui.recyclerview.CarUiListItemAdapter;
 
+import java.util.ArrayList;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Set;
 
 /**
@@ -44,7 +50,12 @@
     }
 
     private AlertDialog mCurrentDialog;
-    private FavoritePhoneNumberListAdapter mDialogAdapter;
+    private CarUiListItemAdapter mDialogAdapter;
+    private List<CarUiListItem> mFavoritePhoneNumberList;
+    private Set<PhoneNumber> mSelectedNumbers;
+    private Contact mSelectedContact;
+    private Drawable mFavoriteIcon;
+    private Drawable mFavoriteIconEmpty;
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
@@ -52,36 +63,77 @@
 
         FavoriteViewModel favoriteViewModel = ViewModelProviders.of(getActivity()).get(
                 FavoriteViewModel.class);
-        Set<PhoneNumber> selectedNumbers = new HashSet<>();
+        mSelectedNumbers = new HashSet<>();
 
-        mDialogAdapter = new FavoritePhoneNumberListAdapter(getContext(),
-                (phoneNumber, itemView) -> {
-                    boolean isActivated = itemView.isActivated();
-                    itemView.setActivated(!isActivated);
-                    if (isActivated) {
-                        selectedNumbers.remove(phoneNumber);
-                    } else {
-                        selectedNumbers.add(phoneNumber);
-                    }
-                }
-        );
+        mFavoriteIcon = getResources().getDrawable(R.drawable.ic_favorite_activatable, null);
+        mFavoriteIcon.setTintList(
+                getResources().getColorStateList(R.color.primary_icon_selector, null));
+
+        mFavoritePhoneNumberList = new ArrayList<>();
+        mDialogAdapter = new CarUiListItemAdapter(mFavoritePhoneNumberList);
 
         mCurrentDialog = new AlertDialogBuilder(getContext())
                 .setTitle(R.string.select_number_dialog_title)
-                .setAdapter(mDialogAdapter, null)
+                .setAdapter(mDialogAdapter)
                 .setNegativeButton(R.string.cancel_add_favorites_dialog, null)
                 .setPositiveButton(R.string.confirm_add_favorites_dialog,
                         (d, which) -> {
-                            for (PhoneNumber number : selectedNumbers) {
-                                favoriteViewModel.addToFavorite(mDialogAdapter.getContact(),
+                            for (PhoneNumber number : mSelectedNumbers) {
+                                favoriteViewModel.addToFavorite(mSelectedContact,
                                         number);
                             }
-                            selectedNumbers.clear();
+                            mSelectedNumbers.clear();
                             getFragmentManager().popBackStackImmediate();
                         })
                 .create();
     }
 
+    private void setPhoneNumbers(List<PhoneNumber> phoneNumbers) {
+        mFavoritePhoneNumberList.clear();
+        for (PhoneNumber number : phoneNumbers) {
+            CarUiContentListItem item = new CarUiContentListItem(CarUiContentListItem.Action.ICON);
+            item.setTitle(number.getNumber());
+            item.setSupplementalIcon(mFavoriteIcon.getConstantState().newDrawable());
+            setFavoriteItemState(item, number);
+
+            item.setOnItemClickedListener((listItem) -> {
+                if (mSelectedNumbers.contains(number)) {
+                    mSelectedNumbers.remove(number);
+                    listItem.setActivated(false);
+                } else {
+                    mSelectedNumbers.add(number);
+                    listItem.setActivated(true);
+                }
+                mDialogAdapter.notifyItemChanged(mFavoritePhoneNumberList.indexOf(listItem));
+            });
+            mFavoritePhoneNumberList.add(item);
+        }
+        mDialogAdapter.notifyDataSetChanged();
+    }
+
+    private void setFavoriteItemState(CarUiContentListItem item, PhoneNumber number) {
+        CharSequence readableLabel = number.getReadableLabel(getResources());
+
+        if (number.isFavorite()) {
+            // This phone number is marked as favorite locally. Disable the favorite action button.
+            item.setEnabled(false);
+            item.setActivated(true);
+            item.setBody(getResources().getString(R.string.favorite_number_description,
+                    readableLabel));
+        } else if (mSelectedContact.isStarred()) {
+            // This contact is downloaded from phone, all phone numbers under this contact will show
+            // under the favorite tab. Disable the favorite action button.
+            item.setActivated(false);
+            item.setEnabled(false);
+            item.setBody(getResources().getString(R.string.favorite_number_description,
+                    readableLabel));
+        } else {
+            item.setEnabled(true);
+            item.setActivated(false);
+            item.setBody(readableLabel);
+        }
+    }
+
     @Override
     public void onShowContactDetail(Contact contact) {
         if (contact == null) {
@@ -89,7 +141,13 @@
             return;
         }
 
-        mDialogAdapter.setPhoneNumbers(contact, contact.getNumbers());
-        mCurrentDialog.show();
+        mSelectedContact = contact;
+        if (contact.getNumbers().isEmpty()) {
+            Toast.makeText(getContext(), R.string.no_phone_numbers, Toast.LENGTH_SHORT).show();
+            mCurrentDialog.dismiss();
+        } else {
+            setPhoneNumbers(contact.getNumbers());
+            mCurrentDialog.show();
+        }
     }
 }
diff --git a/src/com/android/car/dialer/ui/favorite/BluetoothFavoriteContactsLiveData.java b/src/com/android/car/dialer/ui/favorite/BluetoothFavoriteContactsLiveData.java
new file mode 100644
index 0000000..a030b3b
--- /dev/null
+++ b/src/com/android/car/dialer/ui/favorite/BluetoothFavoriteContactsLiveData.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car.dialer.ui.favorite;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.provider.ContactsContract;
+
+import androidx.annotation.NonNull;
+
+import com.android.car.telephony.common.AsyncQueryLiveData;
+import com.android.car.telephony.common.Contact;
+import com.android.car.telephony.common.QueryParam;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** Presents the favorite contacts downloaded from phone. It reads the contacts provider. */
+class BluetoothFavoriteContactsLiveData extends AsyncQueryLiveData<List<Contact>> {
+    private final Context mContext;
+
+    BluetoothFavoriteContactsLiveData(Context context) {
+        super(context, QueryParam.of(new FavoriteQueryParam()));
+        mContext = context;
+    }
+
+    @Override
+    protected List<Contact> convertToEntity(@NonNull Cursor cursor) {
+        List<Contact> resultList = new ArrayList<>();
+
+        while (cursor.moveToNext()) {
+            Contact favoriteEntry = Contact.fromCursor(mContext, cursor);
+            // If there is already a contact with the same phone number, don't add duplicate
+            // entries.
+            boolean alreadyExists = false;
+            for (Contact contact : resultList) {
+                if (favoriteEntry.equals(contact) && favoriteEntry.getNumbers().containsAll(
+                        contact.getNumbers())) {
+                    alreadyExists = true;
+                    break;
+                }
+            }
+            if (!alreadyExists) {
+                resultList.add(favoriteEntry);
+            }
+        }
+        return resultList;
+    }
+
+    private static class FavoriteQueryParam extends QueryParam {
+        FavoriteQueryParam() {
+            super(ContactsContract.Data.CONTENT_URI,
+                    null,
+                    ContactsContract.Data.MIMETYPE + " = ? AND "
+                            + ContactsContract.CommonDataKinds.Phone.STARRED + " = ? ",
+                    new String[]{
+                            ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE,
+                            String.valueOf(1)},
+                    ContactsContract.Contacts.DISPLAY_NAME + " ASC ");
+        }
+    }
+}
diff --git a/src/com/android/car/dialer/ui/favorite/FavoriteAdapter.java b/src/com/android/car/dialer/ui/favorite/FavoriteAdapter.java
index da7ebe5..a140c0d 100644
--- a/src/com/android/car/dialer/ui/favorite/FavoriteAdapter.java
+++ b/src/com/android/car/dialer/ui/favorite/FavoriteAdapter.java
@@ -25,6 +25,7 @@
 import com.android.car.dialer.R;
 import com.android.car.dialer.log.L;
 import com.android.car.dialer.ui.common.OnItemClickedListener;
+import com.android.car.dialer.ui.common.entity.Header;
 import com.android.car.telephony.common.Contact;
 
 import java.util.Collections;
@@ -35,21 +36,28 @@
  */
 public class FavoriteAdapter extends RecyclerView.Adapter<FavoriteContactViewHolder> {
     private static final String TAG = "CD.FavoriteAdapter";
-    private static final int TYPE_CONTACT = 0;
-    private static final int TYPE_ADD_FAVORITE = 1;
+    static final int TYPE_CONTACT = 0;
+    static final int TYPE_HEADER = 1;
+    static final int TYPE_ADD_FAVORITE = 2;
 
-    /** Listener interface for when the add favorite button is clicked */
+    /**
+     * Listener interface for when the add favorite button is clicked
+     */
     public interface OnAddFavoriteClickedListener {
-        /** Called when the add favorite button is clicked */
+        /**
+         * Called when the add favorite button is clicked
+         */
         void onAddFavoriteClicked();
     }
 
-    private List<Contact> mFavoriteContacts = Collections.emptyList();
+    private List<Object> mFavoriteContacts = Collections.emptyList();
     private OnItemClickedListener<Contact> mListener;
     private OnAddFavoriteClickedListener mAddFavoriteListener;
 
-    /** Sets the favorite contact list. */
-    public void setFavoriteContacts(List<Contact> favoriteContacts) {
+    /**
+     * Sets the favorite contact list.
+     */
+    public void setFavoriteContacts(List<Object> favoriteContacts) {
         L.d(TAG, "setFavoriteContacts %s", favoriteContacts);
         mFavoriteContacts = (favoriteContacts != null) ? favoriteContacts : Collections.emptyList();
         notifyDataSetChanged();
@@ -57,42 +65,57 @@
 
     @Override
     public int getItemCount() {
-        return mFavoriteContacts.size() + 1; // +1 for the "Add a favorite" button
+        return mFavoriteContacts.size();
     }
 
     @Override
     public int getItemViewType(int position) {
-        return position < mFavoriteContacts.size()
-                ? TYPE_CONTACT
-                : TYPE_ADD_FAVORITE;
+        Object item = mFavoriteContacts.get(position);
+        if (item instanceof Contact) {
+            return TYPE_CONTACT;
+        } else if (item instanceof Header) {
+            return TYPE_HEADER;
+        } else {
+            return TYPE_ADD_FAVORITE;
+        }
     }
 
     @Override
     public FavoriteContactViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
-        View view;
+        int layoutId;
         if (viewType == TYPE_CONTACT) {
-            view = LayoutInflater.from(parent.getContext())
-                    .inflate(R.layout.favorite_contact_list_item, parent, false);
+            layoutId = R.layout.favorite_contact_list_item;
+        } else if (viewType == TYPE_HEADER) {
+            layoutId = R.layout.header_item;
         } else {
-            view = LayoutInflater.from(parent.getContext())
-                    .inflate(R.layout.add_favorite_list_item, parent, false);
+            layoutId = R.layout.add_favorite_list_item;
         }
 
+        View view = LayoutInflater.from(parent.getContext()).inflate(layoutId, parent, false);
         return new FavoriteContactViewHolder(view);
     }
 
     @Override
     public void onBindViewHolder(FavoriteContactViewHolder viewHolder, int position) {
-        if (getItemViewType(position) == TYPE_CONTACT) {
-            Contact contact = mFavoriteContacts.get(position);
-            viewHolder.onBind(contact);
-            viewHolder.itemView.setOnClickListener((v) -> onItemViewClicked(contact));
-        } else {
-            viewHolder.itemView.setOnClickListener((v) -> {
-                if (mAddFavoriteListener != null) {
-                    mAddFavoriteListener.onAddFavoriteClicked();
-                }
-            });
+        int itemViewType = getItemViewType(position);
+        switch (itemViewType) {
+            case TYPE_CONTACT:
+                Contact contact = (Contact) mFavoriteContacts.get(position);
+                viewHolder.onBind(contact);
+                viewHolder.itemView.setOnClickListener(v -> onItemViewClicked(contact));
+                break;
+            case TYPE_HEADER:
+                Header header = (Header) mFavoriteContacts.get(position);
+                viewHolder.onBind(header);
+                viewHolder.itemView.setOnClickListener(null);
+                break;
+            case TYPE_ADD_FAVORITE:
+                viewHolder.itemView.setOnClickListener(v -> {
+                    if (mAddFavoriteListener != null) {
+                        mAddFavoriteListener.onAddFavoriteClicked();
+                    }
+                });
+                break;
         }
     }
 
@@ -110,8 +133,8 @@
     }
 
     /**
-     * Sets a {@link OnAddFavoriteClickedListener listener} which will be called when the
-     * "Add favorite" button is clicked.
+     * Sets a {@link OnAddFavoriteClickedListener listener} which will be called when the "Add
+     * favorite" button is clicked.
      */
     public void setOnAddFavoriteClickedListener(OnAddFavoriteClickedListener listener) {
         mAddFavoriteListener = listener;
diff --git a/src/com/android/car/dialer/ui/favorite/FavoriteContactViewHolder.java b/src/com/android/car/dialer/ui/favorite/FavoriteContactViewHolder.java
index 137a150..f9c17e2 100644
--- a/src/com/android/car/dialer/ui/favorite/FavoriteContactViewHolder.java
+++ b/src/com/android/car/dialer/ui/favorite/FavoriteContactViewHolder.java
@@ -21,10 +21,13 @@
 import android.widget.ImageView;
 import android.widget.TextView;
 
+import androidx.annotation.NonNull;
 import androidx.recyclerview.widget.RecyclerView;
 
+import com.android.car.apps.common.util.ViewUtils;
 import com.android.car.dialer.R;
 import com.android.car.dialer.log.L;
+import com.android.car.dialer.ui.common.entity.Header;
 import com.android.car.dialer.ui.view.ContactAvatarOutputlineProvider;
 import com.android.car.telephony.common.Contact;
 import com.android.car.telephony.common.PhoneNumber;
@@ -32,8 +35,6 @@
 
 import java.util.List;
 
-import javax.annotation.Nonnull;
-
 /**
  * A {@link RecyclerView.ViewHolder ViewHolder} that will hold layouts for favorite contacts list
  * items.
@@ -58,7 +59,7 @@
     /**
      * Binds view with favorite contact.
      */
-    public void onBind(@Nonnull Contact contact) {
+    public void onBind(@NonNull Contact contact) {
         Context context = itemView.getContext();
         String displayName = contact.getDisplayName();
         mTitle.setText(displayName);
@@ -85,4 +86,8 @@
 
         TelecomUtils.setContactBitmapAsync(context, mIcon, contact);
     }
+
+    public void onBind(@NonNull Header header) {
+        ViewUtils.setText(mTitle, header.getHeader());
+    }
 }
diff --git a/src/com/android/car/dialer/ui/favorite/FavoriteFragment.java b/src/com/android/car/dialer/ui/favorite/FavoriteFragment.java
index 7e76f32..c870dcf 100644
--- a/src/com/android/car/dialer/ui/favorite/FavoriteFragment.java
+++ b/src/com/android/car/dialer/ui/favorite/FavoriteFragment.java
@@ -16,6 +16,7 @@
 
 package com.android.car.dialer.ui.favorite;
 
+import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Rect;
 import android.os.Bundle;
@@ -38,6 +39,7 @@
  * Contains a list of favorite contacts.
  */
 public class FavoriteFragment extends DialerListBaseFragment {
+    private FavoriteAdapter mFavoriteAdapter;
 
     /**
      * Constructs a new {@link FavoriteFragment}
@@ -48,15 +50,16 @@
 
     @Override
     public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
         getRecyclerView().addItemDecoration(new ItemSpacingDecoration());
         getRecyclerView().setItemAnimator(null);
 
-        FavoriteAdapter adapter = new FavoriteAdapter();
-        adapter.setOnAddFavoriteClickedListener(this::onAddFavoriteClicked);
+        mFavoriteAdapter = new FavoriteAdapter();
+        mFavoriteAdapter.setOnAddFavoriteClickedListener(this::onAddFavoriteClicked);
 
         FavoriteViewModel favoriteViewModel = ViewModelProviders.of(getActivity()).get(
                 FavoriteViewModel.class);
-        adapter.setOnListItemClickedListener(this::onItemClicked);
+        mFavoriteAdapter.setOnListItemClickedListener(this::onItemClicked);
         favoriteViewModel.getFavoriteContacts().observe(this, contacts -> {
             if (contacts.isLoading()) {
                 showLoading();
@@ -65,20 +68,18 @@
                         R.string.no_favorites_added, R.string.add_favorite_button,
                         v -> onAddFavoriteClicked(), true);
             } else {
-                adapter.setFavoriteContacts(contacts.getData());
+                mFavoriteAdapter.setFavoriteContacts(contacts.getData());
                 showContent();
             }
         });
 
-        getRecyclerView().setAdapter(adapter);
+        getRecyclerView().setAdapter(mFavoriteAdapter);
     }
 
     @NonNull
     @Override
     protected RecyclerView.LayoutManager createLayoutManager() {
-        int numOfColumn = getContext().getResources().getInteger(
-                R.integer.favorite_fragment_grid_column);
-        return new GridLayoutManager(getContext(), numOfColumn);
+        return new FavoriteLayoutManager(getContext());
     }
 
     private void onItemClicked(Contact contact) {
@@ -110,4 +111,21 @@
             outRect.set(leftPadding, verticalPadding, 0, verticalPadding);
         }
     }
+
+    private class FavoriteLayoutManager extends GridLayoutManager {
+        private FavoriteLayoutManager(Context context) {
+            super(context,
+                    context.getResources().getInteger(R.integer.favorite_fragment_grid_column));
+            SpanSizeLookup spanSizeLookup = new SpanSizeLookup() {
+                @Override
+                public int getSpanSize(int position) {
+                    if (mFavoriteAdapter.getItemViewType(position) == FavoriteAdapter.TYPE_HEADER) {
+                        return getSpanCount();
+                    }
+                    return 1;
+                }
+            };
+            setSpanSizeLookup(spanSizeLookup);
+        }
+    }
 }
diff --git a/src/com/android/car/dialer/ui/favorite/FavoriteViewModel.java b/src/com/android/car/dialer/ui/favorite/FavoriteViewModel.java
index 3f55d42..6e938ca 100644
--- a/src/com/android/car/dialer/ui/favorite/FavoriteViewModel.java
+++ b/src/com/android/car/dialer/ui/favorite/FavoriteViewModel.java
@@ -20,13 +20,18 @@
 
 import androidx.lifecycle.AndroidViewModel;
 import androidx.lifecycle.LiveData;
+import androidx.lifecycle.MediatorLiveData;
 
 import com.android.car.arch.common.FutureData;
 import com.android.car.arch.common.LiveDataFunctions;
+import com.android.car.dialer.R;
 import com.android.car.dialer.storage.FavoriteNumberRepository;
+import com.android.car.dialer.ui.common.entity.ActionButton;
+import com.android.car.dialer.ui.common.entity.Header;
 import com.android.car.telephony.common.Contact;
 import com.android.car.telephony.common.PhoneNumber;
 
+import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -34,18 +39,52 @@
  */
 public class FavoriteViewModel extends AndroidViewModel {
     private final FavoriteNumberRepository mFavoriteNumberRepository;
-    private final LiveData<FutureData<List<Contact>>> mFavoriteContacts;
+    private final LiveData<FutureData<List<Object>>> mFavoriteContacts;
+    private final LiveData<List<Contact>> mBluetoothFavoriteContacts;
 
     public FavoriteViewModel(Application application) {
         super(application);
         mFavoriteNumberRepository = FavoriteNumberRepository.getRepository(application);
+        mBluetoothFavoriteContacts = new BluetoothFavoriteContactsLiveData(application);
+
+        MediatorLiveData<List<Object>> favoriteContacts = new MediatorLiveData<>();
+        favoriteContacts.addSource(mFavoriteNumberRepository.getFavoriteContacts(), contacts -> {
+            List<Object> contactList = new ArrayList<>();
+            if (mBluetoothFavoriteContacts.getValue() != null
+                    && !mBluetoothFavoriteContacts.getValue().isEmpty()) {
+                contactList.add(new Header(application.getString(R.string.phone_favorites)));
+                contactList.addAll(mBluetoothFavoriteContacts.getValue());
+            }
+            contactList.add(new Header(application.getString(R.string.local_favorites)));
+            if (contacts != null) {
+                contactList.addAll(contacts);
+            }
+            contactList.add(new ActionButton());
+            favoriteContacts.setValue(contactList);
+        });
+        favoriteContacts.addSource(mBluetoothFavoriteContacts, contacts -> {
+            List<Object> contactList = new ArrayList<>();
+            if (contacts != null && !contacts.isEmpty()) {
+                contactList.add(new Header(application.getString(R.string.phone_favorites)));
+                contactList.addAll(contacts);
+            }
+            contactList.add(new Header(application.getString(R.string.local_favorites)));
+            if (mFavoriteNumberRepository.getFavoriteContacts().getValue() != null) {
+                contactList.addAll(mFavoriteNumberRepository.getFavoriteContacts().getValue());
+            }
+            contactList.add(new ActionButton());
+            favoriteContacts.setValue(contactList);
+        });
         mFavoriteContacts = LiveDataFunctions.loadingSwitchMap(
-                mFavoriteNumberRepository.getFavoriteContacts(),
+                favoriteContacts,
                 input -> LiveDataFunctions.dataOf(input == null || input.isEmpty() ? null : input));
+
     }
 
-    /** Returns favorite contact list live data. */
-    public LiveData<FutureData<List<Contact>>> getFavoriteContacts() {
+    /**
+     * Returns favorite contact list live data.
+     */
+    public LiveData<FutureData<List<Object>>> getFavoriteContacts() {
         return mFavoriteContacts;
     }
 
diff --git a/src/com/android/car/dialer/ui/search/ContactResultViewHolder.java b/src/com/android/car/dialer/ui/search/ContactResultViewHolder.java
index ad1d139..19127e9 100644
--- a/src/com/android/car/dialer/ui/search/ContactResultViewHolder.java
+++ b/src/com/android/car/dialer/ui/search/ContactResultViewHolder.java
@@ -21,32 +21,46 @@
 import android.widget.ImageView;
 import android.widget.TextView;
 
+import androidx.annotation.Nullable;
 import androidx.recyclerview.widget.RecyclerView;
 
+import com.android.car.apps.common.util.ViewUtils;
 import com.android.car.dialer.R;
+import com.android.car.dialer.telecom.UiCallManager;
+import com.android.car.dialer.ui.common.ContactResultsLiveData;
+import com.android.car.dialer.ui.common.DialerUtils;
 import com.android.car.dialer.ui.view.ContactAvatarOutputlineProvider;
 import com.android.car.telephony.common.Contact;
 import com.android.car.telephony.common.TelecomUtils;
 
+import com.bumptech.glide.Glide;
+
 /**
- * A {@link androidx.recyclerview.widget.RecyclerView.ViewHolder} that will parse relevant
- * views out of a {@code contact_result} layout.
+ * A {@link androidx.recyclerview.widget.RecyclerView.ViewHolder} that will parse relevant views out
+ * of a {@code contact_result} layout.
  */
 public class ContactResultViewHolder extends RecyclerView.ViewHolder {
+    private static final String TAG = "CD.ContactResultVH";
+
     private final Context mContext;
     private final View mContactCard;
     private final TextView mContactName;
+    private final TextView mContactNumber;
     private final ImageView mContactPicture;
     private final ContactResultsAdapter.OnShowContactDetailListener mOnShowContactDetailListener;
 
     public ContactResultViewHolder(View view,
-            ContactResultsAdapter.OnShowContactDetailListener onShowContactDetailListener) {
+            @Nullable ContactResultsAdapter.OnShowContactDetailListener
+                    onShowContactDetailListener) {
         super(view);
         mContext = view.getContext();
         mContactCard = view.findViewById(R.id.contact_result);
         mContactName = view.findViewById(R.id.contact_name);
+        mContactNumber = view.findViewById(R.id.phone_number);
         mContactPicture = view.findViewById(R.id.contact_picture);
-        mContactPicture.setOutlineProvider(ContactAvatarOutputlineProvider.get());
+        if (mContactPicture != null) {
+            mContactPicture.setOutlineProvider(ContactAvatarOutputlineProvider.get());
+        }
         mOnShowContactDetailListener = onShowContactDetailListener;
     }
 
@@ -54,11 +68,40 @@
      * Populates the view that is represented by this ViewHolder with the information in the
      * provided {@link Contact}.
      */
-    public void bind(Contact contact) {
-        mContactCard.setOnClickListener(
-                v -> mOnShowContactDetailListener.onShowContactDetail(contact));
+    public void bindSearchResult(ContactResultsLiveData.ContactResultListItem contactResult) {
+        Contact contact = contactResult.getContact();
 
-        mContactName.setText(contact.getDisplayName());
+        ViewUtils.setText(mContactName, contact.getDisplayName());
         TelecomUtils.setContactBitmapAsync(mContext, mContactPicture, contact);
+
+        if (DialerUtils.hasContactDetail(itemView.getResources(), contact)) {
+            mContactCard.setOnClickListener(
+                    v -> mOnShowContactDetailListener.onShowContactDetail(contact));
+        } else {
+            itemView.setEnabled(false);
+        }
+    }
+
+    /**
+     * Populates the view that is represented by this ViewHolder with the information in the
+     * provided {@link Contact}.
+     */
+    public void bindTypeDownResult(ContactResultsLiveData.ContactResultListItem contactResult) {
+        Contact contact = contactResult.getContact();
+        String number = contactResult.getNumber();
+
+        ViewUtils.setText(mContactNumber, number);
+        ViewUtils.setText(mContactName, contact.getDisplayName());
+        mContactCard.setOnClickListener(
+                v -> UiCallManager.get().placeCall(mContactNumber.getText().toString()));
+        TelecomUtils.setContactBitmapAsync(mContext, mContactPicture, contact);
+    }
+
+    void recycle() {
+        itemView.setEnabled(true);
+        mContactCard.setOnClickListener(null);
+        if (mContactPicture != null) {
+            Glide.with(mContext).clear(mContactPicture);
+        }
     }
 }
diff --git a/src/com/android/car/dialer/ui/search/ContactResultsAdapter.java b/src/com/android/car/dialer/ui/search/ContactResultsAdapter.java
index 7b1ac1f..cb35f2b 100644
--- a/src/com/android/car/dialer/ui/search/ContactResultsAdapter.java
+++ b/src/com/android/car/dialer/ui/search/ContactResultsAdapter.java
@@ -24,6 +24,7 @@
 import androidx.recyclerview.widget.RecyclerView;
 
 import com.android.car.dialer.R;
+import com.android.car.dialer.ui.common.ContactResultsLiveData;
 import com.android.car.telephony.common.Contact;
 
 import java.util.ArrayList;
@@ -39,7 +40,8 @@
         void onShowContactDetail(Contact contact);
     }
 
-    private final List<Contact> mContacts = new ArrayList<>();
+    private final List<ContactResultsLiveData.ContactResultListItem> mContactResults =
+            new ArrayList<>();
     private final OnShowContactDetailListener mOnShowContactDetailListener;
 
     public ContactResultsAdapter(OnShowContactDetailListener onShowContactDetailListener) {
@@ -50,7 +52,7 @@
      * Clears all contact results from this adapter.
      */
     public void clear() {
-        mContacts.clear();
+        mContactResults.clear();
         notifyDataSetChanged();
     }
 
@@ -58,9 +60,9 @@
      * Sets the list of contacts that should be displayed. The given {@link Cursor} can be safely
      * closed after this call.
      */
-    public void setData(List<Contact> data) {
-        mContacts.clear();
-        mContacts.addAll(data);
+    public void setData(List<ContactResultsLiveData.ContactResultListItem> data) {
+        mContactResults.clear();
+        mContactResults.addAll(data);
         notifyDataSetChanged();
     }
 
@@ -73,7 +75,12 @@
 
     @Override
     public void onBindViewHolder(ContactResultViewHolder holder, int position) {
-        holder.bind(mContacts.get(position));
+        holder.bindSearchResult(mContactResults.get(position));
+    }
+
+    @Override
+    public void onViewRecycled(ContactResultViewHolder holder) {
+        holder.recycle();
     }
 
     @Override
@@ -84,6 +91,13 @@
 
     @Override
     public int getItemCount() {
-        return mContacts.size();
+        return mContactResults.size();
+    }
+
+    /**
+     * Returns contact results.
+     */
+    public List<ContactResultsLiveData.ContactResultListItem> getContactResults() {
+        return mContactResults;
     }
 }
diff --git a/src/com/android/car/dialer/ui/search/ContactResultsFragment.java b/src/com/android/car/dialer/ui/search/ContactResultsFragment.java
index b35378e..3603e94 100644
--- a/src/com/android/car/dialer/ui/search/ContactResultsFragment.java
+++ b/src/com/android/car/dialer/ui/search/ContactResultsFragment.java
@@ -89,6 +89,7 @@
 
     @Override
     public void onViewCreated(View view, Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
         getRecyclerView().setAdapter(mAdapter);
 
         mOnScrollChangeListener = new RecyclerView.OnScrollListener() {
@@ -114,7 +115,7 @@
     }
 
     @Override
-    public void setupToolbar(@NonNull Toolbar toolbar) {
+    protected void setupToolbar(@NonNull Toolbar toolbar) {
         super.setupToolbar(toolbar);
         mToolbar = toolbar;
         mToolbar.registerOnSearchListener(this);
diff --git a/src/com/android/car/dialer/ui/search/ContactResultsViewModel.java b/src/com/android/car/dialer/ui/search/ContactResultsViewModel.java
index 43969af..7678da4 100644
--- a/src/com/android/car/dialer/ui/search/ContactResultsViewModel.java
+++ b/src/com/android/car/dialer/ui/search/ContactResultsViewModel.java
@@ -17,40 +17,23 @@
 package com.android.car.dialer.ui.search;
 
 import android.app.Application;
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.database.Cursor;
-import android.net.Uri;
-import android.provider.ContactsContract;
 import android.text.TextUtils;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 import androidx.lifecycle.AndroidViewModel;
 import androidx.lifecycle.LiveData;
-import androidx.lifecycle.MediatorLiveData;
 import androidx.lifecycle.MutableLiveData;
 
 import com.android.car.dialer.R;
 import com.android.car.dialer.livedata.SharedPreferencesLiveData;
-import com.android.car.dialer.ui.common.entity.ContactSortingInfo;
-import com.android.car.telephony.common.Contact;
-import com.android.car.telephony.common.InMemoryPhoneBook;
-import com.android.car.telephony.common.ObservableAsyncQuery;
-import com.android.car.telephony.common.QueryParam;
+import com.android.car.dialer.ui.common.ContactResultsLiveData;
 
-import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
 
 /**
  * {link AndroidViewModel} used for search functionality.
  */
 public class ContactResultsViewModel extends AndroidViewModel {
-    private static final String[] CONTACT_DETAILS_PROJECTION = {
-            ContactsContract.Contacts._ID,
-            ContactsContract.Contacts.LOOKUP_KEY
-    };
 
     private final ContactResultsLiveData mContactSearchResultsLiveData;
     private final MutableLiveData<String> mSearchQueryLiveData;
@@ -65,7 +48,10 @@
                 mSearchQueryLiveData, mSharedPreferencesLiveData);
     }
 
-    void setSearchQuery(String searchQuery) {
+    /**
+     * Sets search query.
+     */
+    public void setSearchQuery(String searchQuery) {
         if (TextUtils.equals(mSearchQueryLiveData.getValue(), searchQuery)) {
             return;
         }
@@ -73,105 +59,31 @@
         mSearchQueryLiveData.setValue(searchQuery);
     }
 
-    LiveData<List<Contact>> getContactSearchResults() {
+    /**
+     * Returns live data of search results.
+     */
+    public LiveData<List<ContactResultsLiveData.ContactResultListItem>> getContactSearchResults() {
         return mContactSearchResultsLiveData;
     }
 
-    String getSearchQuery() {
+    /**
+     * Returns search query.
+     */
+    public String getSearchQuery() {
         return mSearchQueryLiveData.getValue();
     }
 
-    private static class ContactResultsLiveData extends MediatorLiveData<List<Contact>> {
-        private final Context mContext;
-        private final SearchQueryParamProvider mSearchQueryParamProvider;
-        private final ObservableAsyncQuery mObservableAsyncQuery;
-        private final LiveData<String> mSearchQueryLiveData;
-        private final LiveData<List<Contact>> mContactListLiveData;
-        private final SharedPreferencesLiveData mSharedPreferencesLiveData;
-
-        ContactResultsLiveData(Context context,
-                LiveData<String> searchQueryLiveData,
-                SharedPreferencesLiveData sharedPreferencesLiveData) {
-            mContext = context;
-            mSearchQueryParamProvider = new SearchQueryParamProvider(searchQueryLiveData);
-            mObservableAsyncQuery = new ObservableAsyncQuery(mSearchQueryParamProvider,
-                    context.getContentResolver(), this::onQueryFinished);
-
-            mContactListLiveData = InMemoryPhoneBook.get().getContactsLiveData();
-            addSource(mContactListLiveData, this::onContactsChange);
-            mSearchQueryLiveData = searchQueryLiveData;
-            addSource(mSearchQueryLiveData, this::onSearchQueryChanged);
-
-            mSharedPreferencesLiveData = sharedPreferencesLiveData;
-            addSource(mSharedPreferencesLiveData, this::onSortOrderChanged);
-        }
-
-        private void onContactsChange(List<Contact> contactList) {
-            if (contactList == null || contactList.isEmpty()) {
-                mObservableAsyncQuery.stopQuery();
-                setValue(Collections.emptyList());
-            } else {
-                onSearchQueryChanged(mSearchQueryLiveData.getValue());
-            }
-        }
-
-        private void onSearchQueryChanged(String searchQuery) {
-            if (TextUtils.isEmpty(searchQuery)) {
-                mObservableAsyncQuery.stopQuery();
-                List<Contact> contacts = mContactListLiveData.getValue();
-                setValue(contacts == null ? Collections.emptyList() : contacts);
-            } else {
-                mObservableAsyncQuery.startQuery();
-            }
-        }
-
-        private void onSortOrderChanged(SharedPreferences unusedSharedPreferences) {
-            setValue(getValue());
-        }
-
-        private void onQueryFinished(@Nullable Cursor cursor) {
-            if (cursor == null) {
-                setValue(Collections.emptyList());
-                return;
-            }
-
-            List<Contact> contacts = new ArrayList<>();
-            while (cursor.moveToNext()) {
-                int lookupKeyColIdx = cursor.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY);
-                List<Contact> lookupResults = InMemoryPhoneBook.get().lookupContactByKey(
-                        cursor.getString(lookupKeyColIdx));
-                contacts.addAll(lookupResults);
-            }
-            setValue(contacts);
-            cursor.close();
-        }
-
-        @Override
-        public void setValue(List<Contact> contacts) {
-            if (contacts != null && !contacts.isEmpty()) {
-                Collections.sort(contacts,
-                        ContactSortingInfo.getSortingInfo(mContext,
-                                mSharedPreferencesLiveData).first);
-            }
-            super.setValue(contacts);
-        }
+    /**
+     * Returns Search Query LiveData.
+     */
+    public MutableLiveData<String> getSearchQueryLiveData() {
+        return mSearchQueryLiveData;
     }
 
-    private static class SearchQueryParamProvider implements QueryParam.Provider {
-        private final LiveData<String> mSearchQueryLiveData;
-
-        private SearchQueryParamProvider(LiveData<String> searchQueryLiveData) {
-            mSearchQueryLiveData = searchQueryLiveData;
-        }
-
-        @Nullable
-        @Override
-        public QueryParam getQueryParam() {
-            Uri lookupUri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_FILTER_URI,
-                    Uri.encode(mSearchQueryLiveData.getValue()));
-            return new QueryParam(lookupUri, CONTACT_DETAILS_PROJECTION,
-                    ContactsContract.Contacts.HAS_PHONE_NUMBER + "!=0",
-                    /* selectionArgs= */null, /* orderBy= */null);
-        }
+    /**
+     * Returns Shared Preferences LiveData.
+     */
+    public SharedPreferencesLiveData getSharedPreferencesLiveData() {
+        return mSharedPreferencesLiveData;
     }
 }
diff --git a/src/com/android/car/dialer/widget/LoadingFrameLayout.java b/src/com/android/car/dialer/widget/LoadingFrameLayout.java
index 1011bed..0f21a47 100644
--- a/src/com/android/car/dialer/widget/LoadingFrameLayout.java
+++ b/src/com/android/car/dialer/widget/LoadingFrameLayout.java
@@ -228,7 +228,7 @@
         if (mState != state) {
             L.d(TAG, "Switch to state: %d", state);
             // Hides, or shows, all the children, including the loading and error views.
-            setChildVisibility(state == State.CONTENT ? View.VISIBLE : View.GONE);
+            ViewUtils.setVisible((View) findViewById(R.id.list_view), state == State.CONTENT);
 
             // Corrects the visibility setting for error and loading views since they are
             // shown independently of the views content.
@@ -240,13 +240,6 @@
         }
     }
 
-    private void setChildVisibility(int visibility) {
-        int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            getChildAt(i).setVisibility(visibility);
-        }
-    }
-
     /**
      * Container for views held by this LoadingFrameLayout. Used for deferring view inflation until
      * the view is about to be shown.
diff --git a/tests/robotests/res/layout/test_activity.xml b/tests/robotests/res/layout/test_activity.xml
index e7e8291..0bc280c 100644
--- a/tests/robotests/res/layout/test_activity.xml
+++ b/tests/robotests/res/layout/test_activity.xml
@@ -19,7 +19,7 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent">
     <com.android.car.ui.toolbar.Toolbar
-        android:id="@+id/car_ui_toolbar"
+        android:id="@+id/toolbar"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"/>
 </FrameLayout>
diff --git a/tests/robotests/src/com/android/car/dialer/telecom/InCallServiceImplTest.java b/tests/robotests/src/com/android/car/dialer/telecom/InCallServiceImplTest.java
index e0d8a48..f4f54b9 100644
--- a/tests/robotests/src/com/android/car/dialer/telecom/InCallServiceImplTest.java
+++ b/tests/robotests/src/com/android/car/dialer/telecom/InCallServiceImplTest.java
@@ -33,9 +33,11 @@
 import android.content.Context;
 import android.content.Intent;
 import android.telecom.Call;
+import android.telecom.CallAudioState;
 
 import com.android.car.dialer.CarDialerRobolectricTestRunner;
 import com.android.car.dialer.testutils.ShadowCar;
+import com.android.car.dialer.ui.activecall.InCallActivity;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -47,7 +49,9 @@
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.android.controller.ServiceController;
 import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
 import org.robolectric.shadows.ShadowContextWrapper;
+import org.robolectric.shadows.ShadowIntent;
 import org.robolectric.shadows.ShadowLooper;
 
 /**
@@ -70,7 +74,9 @@
     @Mock
     private Call.Details mMockCallDetails;
     @Mock
-    private InCallServiceImpl.Callback mCallback;
+    private CallAudioState mMockCallAudioState;
+    @Mock
+    private InCallServiceImpl.CallAudioStateCallback mCallAudioStateCallback;
     @Mock
     private InCallServiceImpl.ActiveCallListChangedCallback mActiveCallListChangedCallback;
 
@@ -88,7 +94,6 @@
         inCallServiceController.create().bind();
         mInCallServiceImpl = inCallServiceController.get();
 
-        mInCallServiceImpl.registerCallback(mCallback);
         mInCallServiceImpl.addActiveCallListChangedCallback(mActiveCallListChangedCallback);
         ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
 
@@ -102,8 +107,6 @@
         mInCallServiceImpl.onCallAdded(mMockTelecomCall);
 
         ArgumentCaptor<Call> callCaptor = ArgumentCaptor.forClass(Call.class);
-        verify(mCallback).onTelecomCallAdded(callCaptor.capture());
-        assertThat(callCaptor.getValue()).isEqualTo(mMockTelecomCall);
 
         verify(mActiveCallListChangedCallback).onTelecomCallAdded(callCaptor.capture());
         assertThat(callCaptor.getValue()).isEqualTo(mMockTelecomCall);
@@ -119,8 +122,6 @@
         mInCallServiceImpl.onCallRemoved(mMockTelecomCall);
 
         ArgumentCaptor<Call> callCaptor = ArgumentCaptor.forClass(Call.class);
-        verify(mCallback).onTelecomCallRemoved(callCaptor.capture());
-        assertThat(callCaptor.getValue()).isEqualTo(mMockTelecomCall);
 
         verify(mActiveCallListChangedCallback).onTelecomCallRemoved(callCaptor.capture());
         assertThat(callCaptor.getValue()).isEqualTo(mMockTelecomCall);
@@ -132,8 +133,6 @@
         mInCallServiceImpl.onCallAdded(mMockTelecomCall);
 
         ArgumentCaptor<Call> callCaptor = ArgumentCaptor.forClass(Call.class);
-        verify(mCallback).onTelecomCallAdded(callCaptor.capture());
-        assertThat(callCaptor.getValue()).isEqualTo(mMockTelecomCall);
 
         verify(mActiveCallListChangedCallback).onTelecomCallAdded(callCaptor.capture());
         assertThat(callCaptor.getValue()).isEqualTo(mMockTelecomCall);
@@ -148,17 +147,6 @@
     }
 
     @Test
-    public void testUnregisterCallback() {
-        mInCallServiceImpl.unregisterCallback(mCallback);
-
-        mInCallServiceImpl.onCallAdded(mMockTelecomCall);
-        verify(mCallback, never()).onTelecomCallAdded(any());
-
-        mInCallServiceImpl.onCallRemoved(mMockTelecomCall);
-        verify(mCallback, never()).onTelecomCallRemoved(any());
-    }
-
-    @Test
     public void testRemoveActiveCallListChangedCallback() {
         mInCallServiceImpl.removeActiveCallListChangedCallback(mActiveCallListChangedCallback);
 
@@ -168,4 +156,24 @@
         mInCallServiceImpl.onCallRemoved(mMockTelecomCall);
         verify(mActiveCallListChangedCallback, never()).onTelecomCallRemoved(any());
     }
+
+    @Test
+    public void testAddCallAudioStateChangedCallback() {
+        mInCallServiceImpl.addCallAudioStateChangedCallback(mCallAudioStateCallback);
+
+        mInCallServiceImpl.onCallAudioStateChanged(mMockCallAudioState);
+        verify(mCallAudioStateCallback).onCallAudioStateChanged(any());
+    }
+
+    @Test
+    public void testOnBringToForeground() {
+        ShadowApplication shadow = shadowOf(mInCallServiceImpl.getApplication());
+
+        mInCallServiceImpl.onCallAdded(mMockTelecomCall);
+        mInCallServiceImpl.onBringToForeground(false);
+
+        Intent intent = shadow.getNextStartedActivity();
+        ShadowIntent shadowIntent = shadowOf(intent);
+        assertThat(InCallActivity.class).isEqualTo(shadowIntent.getIntentClass());
+    }
 }
diff --git a/tests/robotests/src/com/android/car/dialer/ui/activecall/OnGoingCallControllerBarFragmentTest.java b/tests/robotests/src/com/android/car/dialer/ui/activecall/OnGoingCallControllerBarFragmentTest.java
index 0af882b..4eaafa7 100644
--- a/tests/robotests/src/com/android/car/dialer/ui/activecall/OnGoingCallControllerBarFragmentTest.java
+++ b/tests/robotests/src/com/android/car/dialer/ui/activecall/OnGoingCallControllerBarFragmentTest.java
@@ -66,6 +66,7 @@
     private MutableLiveData<CallDetail> mCallDetailLiveData;
     private MutableLiveData<Boolean> mDialpadStateLiveData;
     private MutableLiveData<List<Call>> mCallListLiveData;
+    private MutableLiveData<CallAudioState> mCallAudioStateLiveData;
     private List<Call> mCallList;
     @Mock
     private Call mMockCall;
@@ -89,6 +90,7 @@
         mCallDetailLiveData = new MutableLiveData<>();
         mDialpadStateLiveData = new MutableLiveData<>();
         mCallListLiveData = new MutableLiveData<>();
+        mCallAudioStateLiveData = new MutableLiveData<>();
         mCallList = new ArrayList<>();
         mCallList.add(mMockCall);
         mCallListLiveData.setValue(mCallList);
@@ -238,6 +240,7 @@
         when(mMockInCallViewModel.getPrimaryCallState()).thenReturn(mCallStateLiveData);
         when(mMockInCallViewModel.getDialpadOpenState()).thenReturn(mDialpadStateLiveData);
         when(mMockInCallViewModel.getAllCallList()).thenReturn(mCallListLiveData);
+        when(mMockInCallViewModel.getCallAudioState()).thenReturn(mCallAudioStateLiveData);
 
         MutableLiveData<Integer> audioRouteLiveData = new MutableLiveData<>();
         audioRouteLiveData.setValue(CallAudioState.ROUTE_BLUETOOTH);
diff --git a/tests/robotests/src/com/android/car/dialer/ui/calllog/CallHistoryFragmentTest.java b/tests/robotests/src/com/android/car/dialer/ui/calllog/CallHistoryFragmentTest.java
index fa7a8f1..8ada441 100644
--- a/tests/robotests/src/com/android/car/dialer/ui/calllog/CallHistoryFragmentTest.java
+++ b/tests/robotests/src/com/android/car/dialer/ui/calllog/CallHistoryFragmentTest.java
@@ -21,7 +21,6 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.net.Uri;
 import android.view.View;
 import android.widget.TextView;
 
@@ -38,6 +37,7 @@
 import com.android.car.dialer.ui.common.entity.HeaderViewHolder;
 import com.android.car.dialer.ui.common.entity.UiCallLog;
 import com.android.car.dialer.widget.CallTypeIconsView;
+import com.android.car.telephony.common.Contact;
 import com.android.car.telephony.common.InMemoryPhoneBook;
 import com.android.car.telephony.common.PhoneCallLog;
 import com.android.car.ui.recyclerview.CarUiRecyclerView;
@@ -72,9 +72,9 @@
     @Mock
     private UiCallManager mMockUiCallManager;
     @Mock
-    private Uri mMockUri;
-    @Mock
     private CallHistoryViewModel mMockCallHistoryViewModel;
+    @Mock
+    private Contact mMockContact;
 
     @Before
     public void setup() {
@@ -87,8 +87,8 @@
                 CallHistoryLiveData.CallType.INCOMING_TYPE);
         PhoneCallLog.Record record2 = new PhoneCallLog.Record(TIME_STAMP_2,
                 CallHistoryLiveData.CallType.OUTGOING_TYPE);
-        UiCallLog uiCallLog = new UiCallLog(UI_CALLOG_TITLE, UI_CALLOG_TEXT, PHONE_NUMBER, null,
-                mMockUri, Arrays.asList(record1, record2));
+        UiCallLog uiCallLog = new UiCallLog(UI_CALLOG_TITLE, UI_CALLOG_TEXT, PHONE_NUMBER,
+                mMockContact, Arrays.asList(record1, record2));
 
         MutableLiveData<FutureData<List<Object>>> callLog = new MutableLiveData<>();
         callLog.setValue(new FutureData<>(false, Arrays.asList(HEADER, uiCallLog)));
@@ -103,7 +103,7 @@
         CarUiRecyclerView recyclerView = mCallHistoryFragment.getView()
                 .findViewById(R.id.list_view);
         // set up layout for recyclerView
-        recyclerView.layoutBothForTesting(0, 0, 100, 1000);
+        recyclerView.layout(0, 0, 100, 1000);
         mHeaderViewHolder = recyclerView.findViewHolderForLayoutPosition(0);
         mCalllogViewHolder = recyclerView.findViewHolderForLayoutPosition(1);
     }
diff --git a/tests/robotests/src/com/android/car/dialer/ui/contact/ContactDetailsFragmentTest.java b/tests/robotests/src/com/android/car/dialer/ui/contact/ContactDetailsFragmentTest.java
index 65f00a7..40af9a4 100644
--- a/tests/robotests/src/com/android/car/dialer/ui/contact/ContactDetailsFragmentTest.java
+++ b/tests/robotests/src/com/android/car/dialer/ui/contact/ContactDetailsFragmentTest.java
@@ -116,7 +116,7 @@
 
         mListView = mContactDetailsFragment.getView().findViewById(R.id.list_view);
         // Set up layout for recyclerView
-        mListView.layoutBothForTesting(0, 0, 100, 1000);
+        mListView.layout(0, 0, 100, 1000);
     }
 
     /**
diff --git a/tests/robotests/src/com/android/car/dialer/ui/contact/ContactListFragmentTest.java b/tests/robotests/src/com/android/car/dialer/ui/contact/ContactListFragmentTest.java
index 1ae465b..c5cb8f4 100644
--- a/tests/robotests/src/com/android/car/dialer/ui/contact/ContactListFragmentTest.java
+++ b/tests/robotests/src/com/android/car/dialer/ui/contact/ContactListFragmentTest.java
@@ -216,7 +216,7 @@
         CarUiRecyclerView recyclerView = mContactListFragment.getView()
                 .findViewById(R.id.list_view);
         //Force RecyclerView to layout to ensure findViewHolderForLayoutPosition works.
-        recyclerView.layoutBothForTesting(0, 0, 100, 1000);
+        recyclerView.layout(0, 0, 100, 1000);
         mViewHolder = (ContactListViewHolder) recyclerView.findViewHolderForLayoutPosition(0);
     }
 
diff --git a/tests/robotests/src/com/android/car/dialer/ui/dialpad/DialpadFragmentTest.java b/tests/robotests/src/com/android/car/dialer/ui/dialpad/DialpadFragmentTest.java
index 5f7df33..ae3fb08 100644
--- a/tests/robotests/src/com/android/car/dialer/ui/dialpad/DialpadFragmentTest.java
+++ b/tests/robotests/src/com/android/car/dialer/ui/dialpad/DialpadFragmentTest.java
@@ -28,13 +28,18 @@
 import android.widget.ImageButton;
 import android.widget.TextView;
 
+import androidx.lifecycle.MutableLiveData;
+
 import com.android.car.dialer.CarDialerRobolectricTestRunner;
 import com.android.car.dialer.FragmentTestActivity;
 import com.android.car.dialer.R;
 import com.android.car.dialer.TestDialerApplication;
 import com.android.car.dialer.telecom.UiCallManager;
+import com.android.car.dialer.testutils.ShadowAndroidViewModelFactory;
 import com.android.car.dialer.testutils.ShadowCallLogCalls;
 import com.android.car.dialer.testutils.ShadowInMemoryPhoneBook;
+import com.android.car.dialer.ui.common.ContactResultsLiveData;
+import com.android.car.dialer.ui.search.ContactResultsViewModel;
 import com.android.car.telephony.common.Contact;
 import com.android.car.telephony.common.InMemoryPhoneBook;
 import com.android.car.telephony.common.PhoneNumber;
@@ -51,8 +56,11 @@
 import org.robolectric.annotation.Config;
 import org.robolectric.shadow.api.Shadow;
 
+import java.util.List;
+
 @RunWith(CarDialerRobolectricTestRunner.class)
-@Config(shadows = {ShadowCallLogCalls.class, ShadowInMemoryPhoneBook.class})
+@Config(shadows = {ShadowCallLogCalls.class, ShadowInMemoryPhoneBook.class,
+        ShadowAndroidViewModelFactory.class})
 public class DialpadFragmentTest {
     private static final String DIAL_NUMBER = "6505551234";
     private static final String DIAL_NUMBER_LONG = "650555123465055512346505551234";
@@ -60,7 +68,12 @@
     private static final String SPEC_CHAR = "123=_=%^&";
     private static final String DISPALY_NAME = "Display Name";
 
+    private Context mContext;
     private DialpadFragment mDialpadFragment;
+    private MutableLiveData<List<ContactResultsLiveData.ContactResultListItem>>
+            mContactResultsLiveData;
+    @Mock
+    private ContactResultsViewModel mMockContactResultsViewModel;
     @Mock
     private Contact mMockContact;
 
@@ -68,10 +81,16 @@
     public void setup() {
         MockitoAnnotations.initMocks(this);
 
-        Context context = RuntimeEnvironment.application;
-        ((TestDialerApplication) context).setupInCallServiceImpl();
-        ((TestDialerApplication) context).initUiCallManager();
-        InMemoryPhoneBook.init(context);
+        mContext = RuntimeEnvironment.application;
+        ((TestDialerApplication) mContext).setupInCallServiceImpl();
+        ((TestDialerApplication) mContext).initUiCallManager();
+        InMemoryPhoneBook.init(mContext);
+
+        mContactResultsLiveData = new MutableLiveData<>();
+        when(mMockContactResultsViewModel.getContactSearchResults())
+                .thenReturn(mContactResultsLiveData);
+        ShadowAndroidViewModelFactory.add(
+                ContactResultsViewModel.class, mMockContactResultsViewModel);
     }
 
     @After
@@ -212,7 +231,13 @@
         mDialpadFragment.setDialedNumber(DIAL_NUMBER);
 
         TextView displayName = mDialpadFragment.getView().findViewById(R.id.display_name);
-        assertThat(displayName.getText()).isEqualTo(DISPALY_NAME);
+        if (!mContext.getResources().getBoolean(R.bool.config_show_type_down_list_on_dialpad)
+                && mContext.getResources()
+                .getBoolean(R.bool.config_show_type_down_list_on_dialpad)) {
+            assertThat(displayName.getText()).isEqualTo(DISPALY_NAME);
+        } else {
+            assertThat(displayName.getText()).isEqualTo("");
+        }
     }
 
     private void startPlaceCallActivity() {
diff --git a/tests/robotests/src/com/android/car/dialer/ui/favorite/FavoriteFragmentTest.java b/tests/robotests/src/com/android/car/dialer/ui/favorite/FavoriteFragmentTest.java
index 5fcb006..e6c326e 100644
--- a/tests/robotests/src/com/android/car/dialer/ui/favorite/FavoriteFragmentTest.java
+++ b/tests/robotests/src/com/android/car/dialer/ui/favorite/FavoriteFragmentTest.java
@@ -71,7 +71,7 @@
         UiCallManager.set(mMockUiCallManager);
 
         when(mMockPhoneNumber.getRawNumber()).thenReturn(RAW_NUMBER);
-        MutableLiveData<FutureData<List<Contact>>> favoriteContacts = new MutableLiveData<>();
+        MutableLiveData<FutureData<List<Object>>> favoriteContacts = new MutableLiveData<>();
         favoriteContacts.setValue(new FutureData<>(false, Arrays.asList(mMockContact)));
         ShadowAndroidViewModelFactory.add(FavoriteViewModel.class, mMockFavoriteViewModel);
         when(mMockFavoriteViewModel.getFavoriteContacts()).thenReturn(favoriteContacts);
@@ -83,7 +83,7 @@
 
         CarUiRecyclerView recyclerView = mFavoriteFragment.getView().findViewById(R.id.list_view);
         // set up layout for recyclerView
-        recyclerView.layoutBothForTesting(0, 0, 100, 1000);
+        recyclerView.layout(0, 0, 100, 1000);
         mViewHolder = (FavoriteContactViewHolder) recyclerView.findViewHolderForLayoutPosition(0);
     }
 
diff --git a/tests/robotests/src/com/android/car/dialer/ui/search/ContactResultsFragmentTest.java b/tests/robotests/src/com/android/car/dialer/ui/search/ContactResultsFragmentTest.java
index 83c8e79..c6f4d1c 100644
--- a/tests/robotests/src/com/android/car/dialer/ui/search/ContactResultsFragmentTest.java
+++ b/tests/robotests/src/com/android/car/dialer/ui/search/ContactResultsFragmentTest.java
@@ -33,10 +33,12 @@
 import com.android.car.dialer.FragmentTestActivity;
 import com.android.car.dialer.R;
 import com.android.car.dialer.testutils.ShadowAndroidViewModelFactory;
+import com.android.car.dialer.ui.common.ContactResultsLiveData;
 import com.android.car.dialer.ui.contact.ContactDetailsFragment;
 import com.android.car.dialer.ui.contact.ContactDetailsViewModel;
 import com.android.car.telephony.common.Contact;
 import com.android.car.telephony.common.InMemoryPhoneBook;
+import com.android.car.telephony.common.PhoneNumber;
 import com.android.car.ui.recyclerview.CarUiRecyclerView;
 
 import org.junit.After;
@@ -50,6 +52,7 @@
 import org.robolectric.annotation.Config;
 
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 
 @Config(shadows = {ShadowAndroidViewModelFactory.class})
@@ -62,15 +65,19 @@
     private ContactResultsFragment mContactResultsFragment;
     private FragmentTestActivity mFragmentTestActivity;
     private CarUiRecyclerView mListView;
-    private MutableLiveData<List<Contact>> mContactSearchResultsLiveData;
+    private MutableLiveData<List<ContactResultsLiveData.ContactResultListItem>>
+            mContactSearchResultsLiveData;
     @Mock
     private ContactResultsViewModel mMockContactResultsViewModel;
     @Mock
     private ContactDetailsViewModel mMockContactDetailsViewModel;
     @Mock
-    private Contact mMockContact;
+    private Contact mMockContact, mContact1, mContact2, mContact3;
     @Mock
-    private Contact mContact1, mContact2, mContact3;
+    private ContactResultsLiveData.ContactResultListItem mContactResult1, mContactResult2,
+            mContactResult3;
+    @Mock
+    private PhoneNumber mPhoneNumber;
 
     @Before
     public void setUp() {
@@ -83,9 +90,15 @@
         ShadowAndroidViewModelFactory.add(
                 ContactResultsViewModel.class, mMockContactResultsViewModel);
 
+        when(mContactResult1.getContact()).thenReturn(mContact1);
         when(mContact1.getDisplayName()).thenReturn(DISPLAY_NAMES[0]);
+        when(mContact1.getNumbers()).thenReturn(Collections.singletonList(mPhoneNumber));
+        when(mContactResult2.getContact()).thenReturn(mContact2);
         when(mContact2.getDisplayName()).thenReturn(DISPLAY_NAMES[1]);
+        when(mContact2.getNumbers()).thenReturn(Collections.singletonList(mPhoneNumber));
+        when(mContactResult3.getContact()).thenReturn(mContact3);
         when(mContact3.getDisplayName()).thenReturn(DISPLAY_NAMES[2]);
+        when(mContact3.getNumbers()).thenReturn(Collections.singletonList(mPhoneNumber));
     }
 
     @After
@@ -104,7 +117,7 @@
     @Test
     public void testDisplaySearchResults_multipleResults() {
         mContactSearchResultsLiveData.setValue(
-                Arrays.asList(mContact1, mContact2, mContact3));
+                Arrays.asList(mContactResult1, mContactResult2, mContactResult3));
 
         mContactResultsFragment = ContactResultsFragment.newInstance(INITIAL_SEARCH_QUERY);
         setUpFragment();
@@ -117,7 +130,7 @@
     @Test
     public void testClickSearchResult_showContactDetailPage() {
         mContactSearchResultsLiveData.setValue(
-                Arrays.asList(mContact1, mContact2, mContact3));
+                Arrays.asList(mContactResult1, mContactResult2, mContactResult3));
 
         MutableLiveData<FutureData<Contact>> contactDetailLiveData = new MutableLiveData<>();
         contactDetailLiveData.setValue(new FutureData<>(false, mMockContact));
@@ -143,7 +156,7 @@
 
         mListView = mContactResultsFragment.getView().findViewById(R.id.list_view);
         // Set up layout for recyclerView
-        mListView.layoutBothForTesting(0, 0, 100, 1000);
+        mListView.layout(0, 0, 100, 1000);
     }
 
     private void verifyChildAt(int position) {