Merge "Add 'Archive' button to AppInfo screen" into main
diff --git a/res/drawable/audio_sharing_guidance.png b/res/drawable/audio_sharing_guidance.png
new file mode 100644
index 0000000..c0ab637
--- /dev/null
+++ b/res/drawable/audio_sharing_guidance.png
Binary files differ
diff --git a/res/layout/dialog_audio_sharing.xml b/res/layout/dialog_audio_sharing.xml
index 3b11020..3ea2c01 100644
--- a/res/layout/dialog_audio_sharing.xml
+++ b/res/layout/dialog_audio_sharing.xml
@@ -19,38 +19,64 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:padding="24dp"
-    android:orientation="vertical">
+    android:orientation="vertical"
+    android:paddingHorizontal="?android:dialogPreferredPadding"
+    android:paddingBottom="?android:dialogPreferredPadding">
 
     <TextView
-        style="@style/DeviceAudioSharingText"
         android:id="@+id/share_audio_subtitle1"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:textAlignment="center"
-        android:layout_gravity="center"/>
-
-    <TextView
         style="@style/DeviceAudioSharingText"
-        android:id="@+id/share_audio_subtitle2"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:textAlignment="center"
-        android:layout_gravity="center"/>
-
-    <com.android.internal.widget.RecyclerView
-        android:visibility="visible"
-        android:id="@+id/btn_list"
-        android:nestedScrollingEnabled="false"
-        android:overScrollMode="never"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center"/>
-
-    <Button
-        android:id="@+id/cancel_btn"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_gravity="center"
-        android:text="@string/cancel"/>
+        android:paddingBottom="14dp"
+        android:textFontWeight="500"
+        android:visibility="gone" />
+
+    <TextView
+        android:id="@+id/share_audio_subtitle2"
+        style="@style/DeviceAudioSharingText"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:paddingBottom="24dp"
+        android:textFontWeight="400" />
+
+    <ImageView
+        android:id="@+id/share_audio_guidance"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:src="@drawable/audio_sharing_guidance"
+        android:visibility="gone" />
+
+    <com.android.internal.widget.RecyclerView
+        android:id="@+id/btn_list"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:nestedScrollingEnabled="false"
+        android:overScrollMode="never"
+        android:visibility="gone" />
+
+    <Button
+        android:id="@+id/share_btn"
+        style="@style/SettingsLibActionButton"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:layout_marginTop="4dp"
+        android:background="@drawable/audio_sharing_rounded_bg_ripple"
+        android:visibility="gone" />
+
+    <Button
+        android:id="@+id/cancel_btn"
+        style="@style/SettingsLibActionButton"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:layout_marginTop="4dp"
+        android:background="@drawable/audio_sharing_rounded_bg_ripple"
+        android:text="Not now"
+        android:visibility="gone" />
 </LinearLayout>
\ No newline at end of file
diff --git a/res/layout/dialog_audio_sharing_disconnect.xml b/res/layout/dialog_audio_sharing_disconnect.xml
index 09bac40..54dee40 100644
--- a/res/layout/dialog_audio_sharing_disconnect.xml
+++ b/res/layout/dialog_audio_sharing_disconnect.xml
@@ -19,29 +19,31 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:padding="24dp"
-    android:orientation="vertical">
+    android:orientation="vertical"
+    android:paddingHorizontal="?android:dialogPreferredPadding"
+    android:paddingBottom="?android:dialogPreferredPadding">
 
     <TextView
         android:id="@+id/share_audio_disconnect_description"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:textAlignment="center"
-        android:layout_gravity="center"/>
-
-    <com.android.internal.widget.RecyclerView
-        android:visibility="visible"
-        android:id="@+id/device_btn_list"
-        android:nestedScrollingEnabled="false"
-        android:overScrollMode="never"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center"/>
-
-    <Button
-        android:id="@+id/cancel_btn"
+        style="@style/DeviceAudioSharingText"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_gravity="center"
-        android:text="@string/cancel"/>
+        android:paddingBottom="24dp" />
+
+    <com.android.internal.widget.RecyclerView
+        android:id="@+id/device_btn_list"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center" />
+
+    <Button
+        android:id="@+id/cancel_btn"
+        style="@style/SettingsLibActionButton"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:layout_marginTop="4dp"
+        android:background="@drawable/audio_sharing_rounded_bg_ripple"
+        android:text="@string/cancel" />
 </LinearLayout>
\ No newline at end of file
diff --git a/res/layout/dialog_audio_sharing_join.xml b/res/layout/dialog_audio_sharing_join.xml
index 42d964a..4bdab7f 100644
--- a/res/layout/dialog_audio_sharing_join.xml
+++ b/res/layout/dialog_audio_sharing_join.xml
@@ -19,35 +19,44 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:padding="24dp"
-    android:orientation="vertical">
+    android:orientation="vertical"
+    android:paddingHorizontal="?android:dialogPreferredPadding"
+    android:paddingBottom="?android:dialogPreferredPadding">
 
     <TextView
         android:id="@+id/share_audio_subtitle1"
+        style="@style/DeviceAudioSharingText"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:textAlignment="center"
-        android:layout_gravity="center"/>
+        android:layout_gravity="center"
+        android:paddingBottom="14dp"
+        android:textFontWeight="500" />
 
     <TextView
         android:id="@+id/share_audio_subtitle2"
+        style="@style/DeviceAudioSharingText"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:textAlignment="center"
-        android:layout_gravity="center"/>
+        android:layout_gravity="center"
+        android:paddingBottom="24dp"
+        android:textFontWeight="400" />
 
     <Button
         android:id="@+id/share_btn"
+        style="@style/SettingsLibActionButton"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_gravity="center"
-        android:text=""/>
+        android:layout_marginTop="4dp"
+        android:background="@drawable/audio_sharing_rounded_bg_ripple" />
 
     <Button
         android:id="@+id/cancel_btn"
+        style="@style/SettingsLibActionButton"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_gravity="center"
-        android:text="@string/cancel"/>
-
+        android:layout_marginTop="4dp"
+        android:background="@drawable/audio_sharing_rounded_bg_ripple"
+        android:text="Not now" />
 </LinearLayout>
\ No newline at end of file
diff --git a/res/layout/privatespace_account_login_error.xml b/res/layout/privatespace_account_login_error.xml
index a38dd50..beae22f 100644
--- a/res/layout/privatespace_account_login_error.xml
+++ b/res/layout/privatespace_account_login_error.xml
@@ -22,7 +22,7 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:filterTouchesWhenObscured="true"
-    app:sucHeaderText="@string/privatespace_retry_signin_title"
-    app:sudDescriptionText="@string/privatespace_retry_summary"
+    app:sucHeaderText="@string/private_space_retry_signin_title"
+    app:sudDescriptionText="@string/private_space_retry_summary"
     android:icon="@drawable/ic_error_red">
 </com.google.android.setupdesign.GlifLayout>
diff --git a/res/layout/privatespace_advancing_screen.xml b/res/layout/privatespace_advancing_screen.xml
index 5b69593..3a85b16 100644
--- a/res/layout/privatespace_advancing_screen.xml
+++ b/res/layout/privatespace_advancing_screen.xml
@@ -17,7 +17,7 @@
 <com.google.android.setupdesign.GlifLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
-    android:id="@+id/privatesapce_autoadvance_screen"
+    android:id="@+id/private_space_autoadvance_screen"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:icon="@drawable/ic_privatespace_icon">
@@ -39,7 +39,7 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:textSize="14sp"
-            android:text="@string/privatespace_setting_up_text"
+            android:text="@string/private_space_setting_up_text"
             android:layout_marginBottom="24dp"/>
 
     </LinearLayout>
diff --git a/res/layout/privatespace_creation_error.xml b/res/layout/privatespace_creation_error.xml
index af11f3a..0ec0cb6 100644
--- a/res/layout/privatespace_creation_error.xml
+++ b/res/layout/privatespace_creation_error.xml
@@ -17,11 +17,10 @@
 <com.google.android.setupdesign.GlifLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
-    android:id="@+id/privatespace_setup_error"
+    android:id="@+id/private_space_setup_error"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:filterTouchesWhenObscured="true"
-    app:sucHeaderText="@string/privatespace_error_screen_title"
-    app:sudDescriptionText="@string/privatespace_error_screen_summary"
+    app:sucHeaderText="@string/private_space_error_screen_title"
     android:icon="@drawable/ic_warning_circle_red">
 </com.google.android.setupdesign.GlifLayout>
diff --git a/res/layout/privatespace_education_screen.xml b/res/layout/privatespace_education_screen.xml
index adb65c9..350e780 100644
--- a/res/layout/privatespace_education_screen.xml
+++ b/res/layout/privatespace_education_screen.xml
@@ -21,8 +21,8 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:filterTouchesWhenObscured="true"
-    app:sucHeaderText="@string/privatespace_setup_title"
-    app:sudDescriptionText="@string/privatespace_hide_apps_summary"
+    app:sucHeaderText="@string/private_space_setup_title"
+    app:sudDescriptionText="@string/private_space_hide_apps_summary"
     android:icon="@drawable/ic_privatespace_icon">
     <ScrollView
         android:id="@+id/main_clear_scrollview"
@@ -43,7 +43,29 @@
             android:src="@drawable/privatespace_placeholder_image"/>
         <TextView
             style="@style/PrivateSpaceSetupSubHeaderStyle"
-            android:text="@string/privatespace_how_title"/>
+            android:text="@string/private_space_how_title"/>
+        <RelativeLayout
+            style="@style/PrivateSpaceSetupBulletPointLayoutStyle">
+            <ImageView
+                android:id="@+id/lockIcon"
+                style="@style/PrivateSpaceBulletPointIconStyle"
+                android:src="@drawable/ic_lock_closed" />
+            <TextView
+                style="@style/PrivateSpaceBulletPointTextFontStyle"
+                android:layout_toRightOf="@+id/lockIcon"
+                android:text="@string/private_space_protected_lock_text"/>
+        </RelativeLayout>
+        <RelativeLayout
+            style="@style/PrivateSpaceSetupBulletPointLayoutStyle">
+            <ImageView
+                android:id="@+id/bellIcon"
+                style="@style/PrivateSpaceBulletPointIconStyle"
+                android:src="@drawable/ic_notifications" />
+            <TextView
+                style="@style/PrivateSpaceBulletPointTextFontStyle"
+                android:layout_toRightOf="@+id/bellIcon"
+                android:text="@string/private_space_hidden_notifications_text"/>
+        </RelativeLayout>
         <RelativeLayout
             style="@style/PrivateSpaceSetupBulletPointLayoutStyle"
             android:layout_width="fill_parent"
@@ -55,29 +77,7 @@
             <TextView
                 style="@style/PrivateSpaceBulletPointTextFontStyle"
                 android:layout_toRightOf="@+id/appsIcon"
-                android:text="@string/privatespace_access_bottom_text"/>
-        </RelativeLayout>
-        <RelativeLayout
-            style="@style/PrivateSpaceSetupBulletPointLayoutStyle">
-            <ImageView
-                android:id="@+id/lockIcon"
-                style="@style/PrivateSpaceBulletPointIconStyle"
-                android:src="@drawable/ic_lock_closed" />
-            <TextView
-                style="@style/PrivateSpaceBulletPointTextFontStyle"
-                android:layout_toRightOf="@+id/lockIcon"
-                android:text="@string/privatespace_protected_lock_text"/>
-        </RelativeLayout>
-        <RelativeLayout
-            style="@style/PrivateSpaceSetupBulletPointLayoutStyle">
-            <ImageView
-                android:id="@+id/bellIcon"
-                style="@style/PrivateSpaceBulletPointIconStyle"
-                android:src="@drawable/ic_notifications" />
-            <TextView
-                style="@style/PrivateSpaceBulletPointTextFontStyle"
-                android:layout_toRightOf="@+id/bellIcon"
-                android:text="@string/privatespace_hidden_notifications_text"/>
+                android:text="@string/private_space_access_bottom_text"/>
         </RelativeLayout>
         <Space
             android:layout_width="wrap_content"
@@ -94,7 +94,7 @@
                 style="@style/PrivateSpaceBulletPointTextFontStyle"
                 android:textSize = "14sp"
                 android:layout_toRightOf="@+id/infoIcon"
-                android:text="@string/privatespace_apps_permission_text"/>
+                android:text="@string/private_space_apps_permission_text"/>
         </RelativeLayout>
     </LinearLayout>
     </ScrollView>
diff --git a/res/layout/privatespace_setlock_screen.xml b/res/layout/privatespace_setlock_screen.xml
index 5caf4ae..7211948 100644
--- a/res/layout/privatespace_setlock_screen.xml
+++ b/res/layout/privatespace_setlock_screen.xml
@@ -21,8 +21,8 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:filterTouchesWhenObscured="true"
-    app:sucHeaderText="@string/privatespace_lockscreen_title"
-    app:sudDescriptionText="@string/privatespace_lockscreen_summary"
+    app:sucHeaderText="@string/private_space_lockscreen_title"
+    app:sudDescriptionText="@string/private_space_lockscreen_summary"
     android:icon="@drawable/ic_lock">
     <com.google.android.setupdesign.view.FillContentLayout
         style="@style/SudContentFrame"
diff --git a/res/layout/privatespace_setup_success.xml b/res/layout/privatespace_setup_success.xml
index 00b6fec..e3e6b68 100644
--- a/res/layout/privatespace_setup_success.xml
+++ b/res/layout/privatespace_setup_success.xml
@@ -17,11 +17,11 @@
 <com.google.android.setupdesign.GlifLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
-    android:id="@+id/privatespace_setup_success"
+    android:id="@+id/private_space_setup_success"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:filterTouchesWhenObscured="true"
-    app:sucHeaderText="@string/privatespace_success_title"
-    app:sudDescriptionText="@string/privatespace_access_text"
+    app:sucHeaderText="@string/private_space_success_title"
+    app:sudDescriptionText="@string/private_space_access_text"
     android:icon="@drawable/ic_privatespace_done">
 </com.google.android.setupdesign.GlifLayout>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 20d96b5..9de79d4 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1253,63 +1253,61 @@
     <!-- Title of the Alert Dialog when no screen lock is set [CHAR LIMIT=30] -->
     <string name="no_device_lock_title">Set a screen lock</string>
     <!-- Summary of the alert when no screen lock is set [CHAR LIMIT=90] -->
-    <string name="no_device_lock_summary">To use Private Space, set a screen lock on this device.</string>
+    <string name="no_device_lock_summary">To use your private space, set a screen lock on this device</string>
     <!-- Action label for dialog when no screen lock is set [CHAR LIMIT=30] -->
     <string name="no_device_lock_action_label">Set screen lock</string>
     <!-- Action label to cancel Alert dialog when no screen lock is set [CHAR LIMIT=30] -->
     <string name="no_device_lock_cancel">Cancel</string>
-    <!-- Action label to cancel Private Space Setup flow [CHAR LIMIT=30] -->
-    <string name="privatespace_cancel_label">Cancel</string>
-    <!-- Label for Private Space setup button to create Private Space [CHAR LIMIT=30] -->
-    <string name="privatespace_setup_button_label">Set up</string>
+    <!-- Action label to cancel private space Setup flow [CHAR LIMIT=30] -->
+    <string name="private_space_cancel_label">Cancel</string>
+    <!-- Label for private space setup button to create private space [CHAR LIMIT=30] -->
+    <string name="private_space_setup_button_label">Set up</string>
     <!-- Title for Private Space setup education screen. [CHAR LIMIT=50] -->
-    <string name="privatespace_setup_title">Set up Private Space</string>
-    <!-- Summary for the Private Space setup education screen. [CHAR LIMIT=NONE] -->
-    <string name="privatespace_hide_apps_summary">Hide private apps in a secure space that only you can access</string>
-    <!-- Text shown in Private Space setup screen which explains how the Private Space works [CHAR LIMIT=50] -->
-    <string name="privatespace_how_title">How it works</string>
-    <!-- Text shown in Private Space setup screen which explains Private Space can be accessed from bottom of all apps list. [CHAR LIMIT=NONE] -->
-    <string name="privatespace_access_bottom_text">You can access Private Space from the bottom of your apps list</string>
-    <!-- Text shown in Private Space setup screen which explains Private Space apps are protected by a lock. [CHAR LIMIT=60] -->
-    <string name="privatespace_protected_lock_text">Apps in Private Space are protected by a lock</string>
-    <!-- Text shown in Private Space setup screen which explains notifications from Private Space apps will not be shown when Private Space is locked. [CHAR LIMIT=NONE] -->
-    <string name="privatespace_hidden_notifications_text">Notifications from apps in Private Space are hidden when it\u2019s locked</string>
-    <!-- Text shown in Private Space setup screen which explains that the permissions granted to Private Space apps will not be shown in settings when Private Space is locked. [CHAR LIMIT=NONE] -->
-    <string name="privatespace_apps_permission_text">Private Space apps won\u2019t appear in permission manager, privacy dashboard, and other settings when Private Space is locked</string>
-    <!-- Text shown at the bottom in Private Space auto advancing  screens. [CHAR LIMIT=60] -->
-    <string name="privatespace_setting_up_text">Setting up Private Space\u2026</string>
-    <!-- Title for Private Space setup in auto advancing screen informing private space is hidden when locked. [CHAR LIMIT=NONE] -->
-    <string name="privatespace_apps_hidden_title">Usage info for Private Space apps is hidden when it\u2019s locked</string>
-    <!-- Title for Private Space setup in auto advancing screen informing private space can be accessed from apps list. [CHAR LIMIT=60] -->
-    <string name="privatespace_access_from_apps_title">Access Private Space from your apps list</string>
-    <!-- Title for Private Space setup in auto advancing screen informing some system apps are already installed in Private Space. [CHAR LIMIT=NONE] -->
-    <string name="privatespace_system_apps_installed_title">Some system apps are already installed in Private Space</string>
-    <!-- Title for Private Space creation error screen. [CHAR LIMIT=60] -->
-    <string name="privatespace_error_screen_title">Couldn\u2019t set up Private Space</string>
-    <!-- Summary for the Private Space creation error screen. [CHAR LIMIT=60] -->
-    <string name="privatespace_error_screen_summary">Try again now, or come back later</string>
+    <string name="private_space_setup_title">Set up a private space</string>
+    <!-- Summary for the private space setup education screen. [CHAR LIMIT=NONE] -->
+    <string name="private_space_hide_apps_summary">Keep private apps in a separate space that you can hide or lock</string>
+    <!-- Text shown in private space setup screen which explains how the private space works [CHAR LIMIT=50] -->
+    <string name="private_space_how_title">How it works</string>
+    <!-- Text shown in private space setup screen which explains private space can be accessed from bottom of all apps list. [CHAR LIMIT=NONE] -->
+    <string name="private_space_access_bottom_text">You can access your private space from the bottom of your apps list</string>
+    <!-- Text shown in private space setup screen which explains private space apps are protected by a lock. [CHAR LIMIT=60] -->
+    <string name="private_space_protected_lock_text">Apps in your private space are protected by a lock</string>
+    <!-- Text shown in private space setup screen which explains notifications from private space apps will not be shown when private space is locked. [CHAR LIMIT=NONE] -->
+    <string name="private_space_hidden_notifications_text">Notifications from apps in your private space are hidden when it\u2019s locked</string>
+    <!-- This is info text to help explain in private space setup screen that the permissions granted to private space apps will not be shown in settings when private space is locked. [CHAR LIMIT=NONE] -->
+    <string name="private_space_apps_permission_text">Apps in your private space won\'t appear in permission manager, privacy dashboard, and other settings when your private space is locked.\n\nYour private space can\'t be moved to a new device. You\'ll need to set up another private space if you want to use it on another device.\n\nAnyone that connects your device to a computer or installs harmful apps on your device may be able to access your private space.</string>
+    <!-- Text shown at the bottom in private space auto advancing  screens. [CHAR LIMIT=60] -->
+    <string name="private_space_setting_up_text">Setting up private space\u2026</string>
+    <!-- Title for private space setup in auto advancing screen informing private space notifications are hidden when locked. [CHAR LIMIT=NONE] -->
+    <string name="private_space_notifications_hidden_title">Notifications from apps in private space are hidden when it\u2019s locked</string>
+    <!-- Title for private space setup in auto advancing screen informing photos/files from private space can be shared when unlocked. [CHAR LIMIT=NONE] -->
+    <string name="private_space_share_photos_title">Unlock your space to share photos or files from private space apps</string>
+    <!-- Title for private space setup in auto advancing screen informing some system apps are already installed in private space. [CHAR LIMIT=NONE] -->
+    <string name="private_space_apps_installed_title">Some apps are already installed in your private space</string>
+    <!-- Title for private space creation error screen. [CHAR LIMIT=60] -->
+    <string name="private_space_error_screen_title">Couldn\u2019t set up private space</string>
     <!-- Label for button to retry creating private space again on creation error. [CHAR LIMIT=30] -->
-    <string name="privatespace_tryagain_label">Try Again</string>
-    <!-- Title for Private Space lock setup screen. [CHAR LIMIT=50] -->
-    <string name="privatespace_lockscreen_title">Use screen lock to unlock?</string>
-    <!-- Summary for the Private Space lock setup screen. [CHAR LIMIT=NONE] -->
-    <string name="privatespace_lockscreen_summary">You can unlock Private Space the same way you unlock your device, or choose a different lock</string>
-    <!-- Action label to use existing device lock for Private Space. [CHAR LIMIT=50] -->
-    <string name="privatespace_use_screenlock_label">Use screen lock</string>
-    <!-- Label for Private Space lock setup button to choose a new lock. [CHAR LIMIT=50] -->
-    <string name="privatespace_set_lock_label">Choose new lock</string>
-    <!-- Title for Private Space setup success screen. [CHAR LIMIT=30] -->
-    <string name="privatespace_success_title">All set!</string>
-    <!-- Summary for the Private Space setup success screen. [CHAR LIMIT=NONE] -->
-    <string name="privatespace_access_text">You can access Private Space from your apps list</string>
-    <!-- Label for Private Space done button to show a toast, finish setup and launch All apps [CHAR LIMIT=30] -->
-    <string name="privatespace_done_label">Done</string>
+    <string name="private_space_tryagain_label">Try Again</string>
+    <!-- Title for private space lock setup screen. [CHAR LIMIT=50] -->
+    <string name="private_space_lockscreen_title">Use screen lock to unlock private space?</string>
+    <!-- Summary for the private space lock setup screen. [CHAR LIMIT=NONE] -->
+    <string name="private_space_lockscreen_summary">You can unlock your private space the same way you unlock your device, or choose a different lock</string>
+    <!-- Action label to use existing device lock for private space. [CHAR LIMIT=50] -->
+    <string name="private_space_use_screenlock_label">Use screen lock</string>
+    <!-- Label for private space lock setup button to choose a new lock. [CHAR LIMIT=50] -->
+    <string name="private_space_set_lock_label">Choose new lock</string>
+    <!-- Title for private space setup success screen. [CHAR LIMIT=30] -->
+    <string name="private_space_success_title">All set!</string>
+    <!-- Summary for the private space setup success screen. [CHAR LIMIT=NONE] -->
+    <string name="private_space_access_text">To access your private space, go to your apps list then scroll down</string>
+    <!-- Label for private space done button to show a toast, finish setup and launch All apps [CHAR LIMIT=30] -->
+    <string name="private_space_done_label">Done</string>
     <!-- Toast to show on private space setup completion informing user to scroll down All apps to access private space. [CHAR LIMIT=60] -->
-    <string name="scrolldown_to_access">Scroll down to access Private Space</string>
-    <!-- Title for Private Space account login error screen. [CHAR LIMIT=60] -->
-    <string name="privatespace_retry_signin_title">Sign in to set up Private Space</string>
-    <!-- Summary for the Private Space account login error screen. [CHAR LIMIT=NONE] -->
-    <string name="privatespace_retry_summary">You need to sign in to an account to set up Private Space</string>
+    <string name="private_space_scrolldown_to_access">Scroll down to find private space</string>
+    <!-- Title for private space account login error screen. [CHAR LIMIT=60] -->
+    <string name="private_space_retry_signin_title">Sign in to set up a private space</string>
+    <!-- Summary for the private space account login error screen. [CHAR LIMIT=NONE] -->
+    <string name="private_space_retry_summary">You need to sign in to an account to set up a private space</string>
     <!-- private space lock setup screen title. This title is asking the user to choose a type of screen lock (such as a pattern, PIN, or password) that they need to enter to unlock private space. [CHAR LIMIT=60] -->
     <string name="private_space_lock_setup_title">Choose a lock for your private space</string>
     <!-- private space lock setup screen description [CHAR LIMIT=NONE] -->
@@ -3270,6 +3268,8 @@
     <string name="error_mnc_not23">MNC field must be 2 or 3 digits.</string>
     <!-- APN error dialog messages: -->
     <string name="error_adding_apn_type">Carrier does not allow adding APNs of type %s.</string>
+    <!-- APN error messages: -->
+    <string name="error_mmsc_valid">MMSC field must be valid.</string>
     <!-- The message of dialog indicated restoring default APN settings in progress -->
     <string name="restore_default_apn">Restoring default APN settings.</string>
     <!-- APNs screen menu option to reset default APN settings -->
diff --git a/res/xml/privatespace_setup_education_entries.xml b/res/xml/privatespace_setup_education_entries.xml
deleted file mode 100644
index 18fb19d..0000000
--- a/res/xml/privatespace_setup_education_entries.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2023 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.
-  -->
-
-<ItemGroup
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto">
-
-    <Item
-        android:enabled="false"
-        android:icon="@drawable/ic_apps"
-        android:title="@string/privatespace_access_bottom_text"
-        app:sudIconTint="?android:attr/textColorSecondary"/>
-
-    <Item
-        android:enabled="false"
-        android:icon="@drawable/ic_lock_24dp"
-        android:title="@string/privatespace_protected_lock_text"
-        app:sudIconTint="?android:attr/textColorSecondary"/>
-
-    <Item
-        android:enabled="false"
-        android:icon="@drawable/ic_notifications"
-        android:title="@string/privatespace_hidden_notifications_text"
-        app:sudIconTint="?android:attr/textColorSecondary"/>
-</ItemGroup>
diff --git a/src/com/android/settings/biometrics/BiometricEnrollBase.java b/src/com/android/settings/biometrics/BiometricEnrollBase.java
index 7df1fe1..c9c8cff 100644
--- a/src/com/android/settings/biometrics/BiometricEnrollBase.java
+++ b/src/com/android/settings/biometrics/BiometricEnrollBase.java
@@ -67,7 +67,6 @@
     public static final String EXTRA_FINISHED_ENROLL_FACE = "finished_enrolling_face";
     public static final String EXTRA_FINISHED_ENROLL_FINGERPRINT = "finished_enrolling_fingerprint";
     public static final String EXTRA_LAUNCHED_POSTURE_GUIDANCE = "launched_posture_guidance";
-    public static final String KEY_CALIBRATOR_UUID = "calibrator_uuid";
 
     /**
      * Used by the choose fingerprint wizard to indicate the wizard is
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
index 063d55d..6e90885 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
@@ -70,6 +70,7 @@
 import com.android.settings.biometrics.BiometricsEnrollEnrolling;
 import com.android.settings.biometrics.fingerprint.feature.SfpsEnrollmentFeature;
 import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+import com.android.settings.flags.Flags;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settingslib.display.DisplayDensityUtils;
 
@@ -197,6 +198,8 @@
 
     @NonNull
     private SfpsEnrollmentFeature mSfpsEnrollmentFeature = new EmptySfpsEnrollmentFeature();
+    @Nullable
+    private UdfpsEnrollCalibrator mCalibrator;
 
     @VisibleForTesting
     protected boolean shouldShowLottie() {
@@ -245,6 +248,12 @@
 
             setContentView(layout);
             setDescriptionText(R.string.security_settings_udfps_enroll_start_message);
+
+            if (Flags.udfpsEnrollCalibration()) {
+                mCalibrator = FeatureFactory.getFeatureFactory().getFingerprintFeatureProvider()
+                        .getUdfpsEnrollCalibrator(getApplicationContext(), savedInstanceState,
+                                getIntent());
+            }
         } else if (mCanAssumeSfps) {
             mSfpsEnrollmentFeature = FeatureFactory.getFeatureFactory()
                     .getFingerprintFeatureProvider().getSfpsEnrollmentFeature();
@@ -364,6 +373,11 @@
         super.onSaveInstanceState(outState);
         outState.putBoolean(KEY_STATE_CANCELED, mIsCanceled);
         outState.putInt(KEY_STATE_PREVIOUS_ROTATION, mPreviousRotation);
+        if (Flags.udfpsEnrollCalibration()) {
+            if (mCalibrator != null) {
+                mCalibrator.onSaveInstanceState(outState);
+            }
+        }
     }
 
     private void restoreSavedState(Bundle savedInstanceState) {
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java
index 276845c..b71330a 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java
@@ -25,8 +25,6 @@
 import android.hardware.fingerprint.FingerprintManager;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
 import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
 import android.util.Log;
 import android.view.OrientationEventListener;
 import android.view.Surface;
@@ -35,15 +33,12 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-import androidx.lifecycle.Observer;
 
 import com.android.settings.R;
 import com.android.settings.Utils;
 import com.android.settings.biometrics.BiometricEnrollBase;
 import com.android.settings.biometrics.BiometricEnrollSidecar;
 import com.android.settings.biometrics.BiometricUtils;
-import com.android.settings.biometrics.fingerprint.UdfpsEnrollCalibrator.Result;
-import com.android.settings.biometrics.fingerprint.UdfpsEnrollCalibrator.Status;
 import com.android.settings.flags.Flags;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.password.ChooseLockSettingsHelper;
@@ -56,7 +51,6 @@
 import com.google.android.setupcompat.template.FooterButton;
 
 import java.util.List;
-import java.util.UUID;
 
 /**
  * Activity explaining the fingerprint sensor location for fingerprint enrollment.
@@ -85,8 +79,6 @@
     private boolean mIsReverseDefaultRotation;
     @Nullable
     private UdfpsEnrollCalibrator mCalibrator;
-    @Nullable
-    private Observer<Status> mCalibratorStatusObserver;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -172,13 +164,15 @@
         if (mCanAssumeUdfps) {
             if (Flags.udfpsEnrollCalibration()) {
                 mCalibrator = FeatureFactory.getFeatureFactory().getFingerprintFeatureProvider()
-                        .getUdfpsEnrollCalibrator(
-                                (savedInstanceState != null)
-                                ? savedInstanceState.getParcelable(KEY_CALIBRATOR_UUID, UUID.class)
-                                : getIntent().getSerializableExtra(KEY_CALIBRATOR_UUID, UUID.class)
-                        );
-                if (mCalibrator == null
-                        || mCalibrator.getStatusLiveData().getValue() == Status.FINISHED) {
+                        .getUdfpsEnrollCalibrator(getApplicationContext(), savedInstanceState,
+                                getIntent());
+                if (mCalibrator != null) {
+                    mCalibrator.onFindSensorPage(
+                            getLifecycle(),
+                            getSupportFragmentManager(),
+                            this::enableUdfpsLottieAndNextButton
+                    );
+                } else {
                     enableUdfpsLottieAndNextButton();
                 }
             } else {
@@ -193,14 +187,19 @@
     }
 
     private void enableUdfpsLottieAndNextButton() {
-        mFooterBarMixin.setPrimaryButton(
-                new FooterButton.Builder(this)
-                        .setText(R.string.security_settings_udfps_enroll_find_sensor_start_button)
-                        .setListener(this::onStartButtonClick)
-                        .setButtonType(FooterButton.ButtonType.NEXT)
-                        .setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Primary)
-                        .build()
-        );
+        if (isFinishing()) {
+            return;
+        }
+
+        if (mFooterBarMixin.getPrimaryButton() == null) {
+            mFooterBarMixin.setPrimaryButton(new FooterButton.Builder(this)
+                    .setText(R.string.security_settings_udfps_enroll_find_sensor_start_button)
+                    .setListener(this::onStartButtonClick)
+                    .setButtonType(FooterButton.ButtonType.NEXT)
+                    .setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Primary)
+                    .build()
+            );
+        }
         if (mIllustrationLottie != null) {
             mIllustrationLottie.setOnClickListener(this::onStartButtonClick);
         }
@@ -285,7 +284,7 @@
         outState.putBoolean(SAVED_STATE_IS_NEXT_CLICKED, mNextClicked);
         if (Flags.udfpsEnrollCalibration()) {
             if (mCalibrator != null) {
-                outState.putSerializable(KEY_CALIBRATOR_UUID, mCalibrator.getUuid());
+                mCalibrator.onSaveInstanceState(outState);
             }
         }
     }
@@ -317,39 +316,6 @@
         if (mAnimation != null) {
             mAnimation.startAnimation();
         }
-        if (Flags.udfpsEnrollCalibration()) {
-            if (mCalibrator != null) {
-                final Status current = mCalibrator.getStatusLiveData().getValue();
-                if (current == Status.PROCESSING) {
-                    if (mCalibratorStatusObserver == null) {
-                        mCalibratorStatusObserver = status -> {
-                            if (status == Status.GOT_RESULT) {
-                                onGotCalibrationResult();
-                            }
-                        };
-                    }
-                    mCalibrator.getStatusLiveData().observe(this, mCalibratorStatusObserver);
-                } else if (current == Status.GOT_RESULT) {
-                    onGotCalibrationResult();
-                }
-            }
-        }
-    }
-
-    private void onGotCalibrationResult() {
-        if (Flags.udfpsEnrollCalibration()) {
-            if (mCalibrator != null) {
-                mCalibrator.setFinished();
-                if (mCalibrator.getResult() == Result.NEED_CALIBRATION) {
-                    UdfpsEnrollCalibrationDialog.newInstance(
-                            mCalibrator.getCalibrationDialogTitleTextId(),
-                            mCalibrator.getCalibrationDialogMessageTextId(),
-                            mCalibrator.getCalibrationDialogDismissButtonTextId()
-                    ).show(getSupportFragmentManager(), "findsensor-calibration-dialog");
-                }
-            }
-            new Handler(Looper.getMainLooper()).post(this::enableUdfpsLottieAndNextButton);
-        }
     }
 
     private void stopLookingForFingerprint() {
@@ -407,12 +373,6 @@
         if (mAnimation != null) {
             mAnimation.pauseAnimation();
         }
-        if (Flags.udfpsEnrollCalibration()) {
-            if (mCalibrator != null && mCalibratorStatusObserver != null) {
-                mCalibrator.getStatusLiveData().removeObserver(mCalibratorStatusObserver);
-                mCalibratorStatusObserver = null;
-            }
-        }
     }
 
     @Override
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java
index dc3c65e..bd52b64 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java
@@ -57,7 +57,6 @@
 import com.google.android.setupdesign.util.DeviceHelper;
 
 import java.util.List;
-import java.util.UUID;
 
 public class FingerprintEnrollIntroduction extends BiometricEnrollIntroduction {
 
@@ -92,12 +91,7 @@
 
         if (Flags.udfpsEnrollCalibration()) {
             mCalibrator = FeatureFactory.getFeatureFactory().getFingerprintFeatureProvider()
-                    .getUdfpsEnrollCalibrator(
-                            (savedInstanceState != null)
-                                    ? savedInstanceState.getParcelable(
-                                        KEY_CALIBRATOR_UUID, UUID.class)
-                                    : null
-                        );
+                    .getUdfpsEnrollCalibrator(getApplicationContext(), savedInstanceState, null);
         }
 
         final ImageView iconFingerprint = findViewById(R.id.icon_fingerprint);
@@ -175,7 +169,7 @@
         super.onSaveInstanceState(outState);
         if (Flags.udfpsEnrollCalibration()) {
             if (mCalibrator != null) {
-                outState.putSerializable(KEY_CALIBRATOR_UUID, mCalibrator.getUuid());
+                mCalibrator.onSaveInstanceState(outState);
             }
         }
     }
@@ -391,7 +385,7 @@
         }
         if (Flags.udfpsEnrollCalibration()) {
             if (mCalibrator != null) {
-                intent.putExtra(KEY_CALIBRATOR_UUID, mCalibrator.getUuid());
+                intent.putExtras(mCalibrator.getExtrasForNextIntent());
             }
         }
         return intent;
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintFeatureProvider.java b/src/com/android/settings/biometrics/fingerprint/FingerprintFeatureProvider.java
index 5a2bf8b..e770220 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintFeatureProvider.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintFeatureProvider.java
@@ -16,12 +16,15 @@
 
 package com.android.settings.biometrics.fingerprint;
 
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.android.settings.biometrics.fingerprint.feature.SfpsEnrollmentFeature;
 
-import java.util.UUID;
-
 public interface FingerprintFeatureProvider {
     /**
      * Gets the feature implementation of SFPS enrollment.
@@ -29,11 +32,16 @@
      */
     SfpsEnrollmentFeature getSfpsEnrollmentFeature();
 
+
     /**
-     * Gets calibrator to calibrate the FPS before enrolling udfps
-     * @param uuid unique id for passed between different activities
-     * @return udfps calibrator
+     * Gets calibrator for udfps pre-enroll
+     * @param appContext application context
+     * @param activitySavedInstanceState activity savedInstanceState
+     * @param activityIntent activity intent
      */
     @Nullable
-    UdfpsEnrollCalibrator getUdfpsEnrollCalibrator(@Nullable UUID uuid);
+    default UdfpsEnrollCalibrator getUdfpsEnrollCalibrator(@NonNull Context appContext,
+            @Nullable Bundle activitySavedInstanceState, @Nullable Intent activityIntent) {
+        return null;
+    }
 }
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintFeatureProviderImpl.java b/src/com/android/settings/biometrics/fingerprint/FingerprintFeatureProviderImpl.java
index 1baabc6..9745ca3 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintFeatureProviderImpl.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintFeatureProviderImpl.java
@@ -21,8 +21,6 @@
 import com.android.settings.biometrics.fingerprint.feature.SfpsEnrollmentFeature;
 import com.android.settings.biometrics.fingerprint.feature.SfpsEnrollmentFeatureImpl;
 
-import java.util.UUID;
-
 public class FingerprintFeatureProviderImpl implements FingerprintFeatureProvider {
 
     @Nullable
@@ -35,10 +33,4 @@
         }
         return mSfpsEnrollmentFeatureImpl;
     }
-
-    @Nullable
-    @Override
-    public UdfpsEnrollCalibrator getUdfpsEnrollCalibrator(@Nullable UUID uuid) {
-        return null;
-    }
 }
diff --git a/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollCalibrationDialog.kt b/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollCalibrationDialog.kt
deleted file mode 100644
index 892996a..0000000
--- a/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollCalibrationDialog.kt
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2023 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.settings.biometrics.fingerprint
-
-import android.app.Dialog
-import android.content.DialogInterface
-import android.os.Bundle
-import androidx.annotation.StringRes
-import androidx.appcompat.app.AlertDialog
-import androidx.fragment.app.DialogFragment
-import com.android.settings.R
-
-class UdfpsEnrollCalibrationDialog : DialogFragment() {
-
-    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog =
-            AlertDialog.Builder(requireActivity(), R.style.Theme_AlertDialog)
-                    .setTitle(arguments!!.getInt(KEY_TITLE_TEXT_ID))
-                    .setMessage(arguments!!.getInt(KEY_MESSAGE_TEXT_ID))
-                    .setPositiveButton(arguments!!.getInt(KEY_DISMISS_BUTTON_TEXT_ID)) {
-                        dialog: DialogInterface?, _: Int -> dialog?.dismiss()
-                    }
-                    .create().also {
-                        isCancelable = false
-                    }
-
-    companion object {
-
-        private const val KEY_TITLE_TEXT_ID = "title_text_id"
-        private const val KEY_MESSAGE_TEXT_ID = "message_text_id"
-        private const val KEY_DISMISS_BUTTON_TEXT_ID = "dismiss_button_text_id"
-
-        @JvmStatic
-        fun newInstance(
-                @StringRes titleTextId: Int,
-                @StringRes messageTextId: Int,
-                @StringRes dismissButtonTextId: Int
-        ) = UdfpsEnrollCalibrationDialog().apply {
-            arguments = Bundle().apply {
-                putInt(KEY_TITLE_TEXT_ID, titleTextId)
-                putInt(KEY_MESSAGE_TEXT_ID, messageTextId)
-                putInt(KEY_DISMISS_BUTTON_TEXT_ID, dismissButtonTextId)
-            }
-        }
-    }
-}
diff --git a/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollCalibrator.kt b/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollCalibrator.kt
index c0626d3..9809bcc 100644
--- a/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollCalibrator.kt
+++ b/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollCalibrator.kt
@@ -1,36 +1,18 @@
 package com.android.settings.biometrics.fingerprint
 
-import androidx.annotation.StringRes
-import androidx.lifecycle.LiveData
-import java.util.UUID
+import android.os.Bundle
+import androidx.fragment.app.FragmentManager
+import androidx.lifecycle.Lifecycle
 
 interface UdfpsEnrollCalibrator {
 
-    enum class Status {
-        PROCESSING,
-        GOT_RESULT,
-        FINISHED,
-    }
+    val extrasForNextIntent: Bundle
 
-    enum class Result {
-        NEED_CALIBRATION,
-        NO_NEED_CALIBRATION,
-    }
+    fun onSaveInstanceState(outState: Bundle)
 
-    val uuid: UUID
-
-    val statusLiveData: LiveData<Status>
-
-    val result: Result?
-
-    fun setFinished()
-
-    @get:StringRes
-    val calibrationDialogTitleTextId: Int
-
-    @get:StringRes
-    val calibrationDialogMessageTextId: Int
-
-    @get:StringRes
-    val calibrationDialogDismissButtonTextId: Int
+    fun onFindSensorPage(
+            lifecycle: Lifecycle,
+            fragmentManager: FragmentManager,
+            enableEnrollingRunnable: Runnable
+    )
 }
\ No newline at end of file
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFragment.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFragment.java
index 5cd86b4..e44939d 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFragment.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFragment.java
@@ -22,6 +22,7 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.Button;
+import android.widget.ImageView;
 import android.widget.TextView;
 
 import androidx.appcompat.app.AlertDialog;
@@ -33,8 +34,10 @@
 import com.android.settings.R;
 import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
 
+import com.google.common.collect.Iterables;
+
 import java.util.ArrayList;
-import java.util.Locale;
+import java.util.stream.Collectors;
 
 public class AudioSharingDialogFragment extends InstrumentedDialogFragment {
     private static final String TAG = "AudioSharingDialog";
@@ -54,8 +57,6 @@
 
     private static DialogEventListener sListener;
 
-    private View mRootView;
-
     @Override
     public int getMetricsCategory() {
         return SettingsEnums.DIALOG_START_AUDIO_SHARING;
@@ -90,40 +91,62 @@
         ArrayList<AudioSharingDeviceItem> deviceItems =
                 arguments.getParcelableArrayList(BUNDLE_KEY_DEVICE_ITEMS);
         final AlertDialog.Builder builder =
-                new AlertDialog.Builder(getActivity()).setTitle("Share audio").setCancelable(false);
-        mRootView =
-                LayoutInflater.from(builder.getContext())
-                        .inflate(R.layout.dialog_audio_sharing, /* parent= */ null);
-        TextView subTitle1 = mRootView.findViewById(R.id.share_audio_subtitle1);
-        TextView subTitle2 = mRootView.findViewById(R.id.share_audio_subtitle2);
+                new AlertDialog.Builder(getActivity()).setCancelable(false);
+        LayoutInflater inflater = LayoutInflater.from(builder.getContext());
+        View customTitle = inflater.inflate(R.layout.dialog_custom_title_audio_sharing, null);
+        ImageView icon = customTitle.findViewById(R.id.title_icon);
+        icon.setImageResource(R.drawable.ic_bt_audio_sharing);
+        TextView title = customTitle.findViewById(R.id.title_text);
+        View rootView = inflater.inflate(R.layout.dialog_audio_sharing, /* parent= */ null);
+        TextView subTitle1 = rootView.findViewById(R.id.share_audio_subtitle1);
+        TextView subTitle2 = rootView.findViewById(R.id.share_audio_subtitle2);
+        RecyclerView recyclerView = rootView.findViewById(R.id.btn_list);
+        Button shareBtn = rootView.findViewById(R.id.share_btn);
+        Button cancelBtn = rootView.findViewById(R.id.cancel_btn);
         if (deviceItems.isEmpty()) {
-            subTitle1.setVisibility(View.INVISIBLE);
+            title.setText("Share your audio");
             subTitle2.setText(
-                    "To start sharing audio, connect additional headphones that support LE audio");
-        } else {
+                    "To start sharing audio, "
+                            + "connect two pairs of headphones that support LE Audio");
+            ImageView image = rootView.findViewById(R.id.share_audio_guidance);
+            image.setVisibility(View.VISIBLE);
+            builder.setNegativeButton("Close", null);
+        } else if (deviceItems.size() == 1) {
+            title.setText("Share your audio");
             subTitle1.setText(
-                    String.format(
-                            Locale.US,
-                            "%d additional device%s connected",
-                            deviceItems.size(),
-                            deviceItems.size() > 1 ? "" : "s"));
+                    deviceItems.stream()
+                            .map(AudioSharingDeviceItem::getName)
+                            .collect(Collectors.joining(" and ")));
             subTitle2.setText(
-                    "The headphones you share audio with will hear videos and music playing on this"
-                            + " phone");
+                    "This device's music and videos will play on both pairs of headphones");
+            shareBtn.setText("Share audio");
+            shareBtn.setOnClickListener(
+                    v -> {
+                        sListener.onItemClick(Iterables.getOnlyElement(deviceItems));
+                        dismiss();
+                    });
+            cancelBtn.setOnClickListener(v -> dismiss());
+            subTitle1.setVisibility(View.VISIBLE);
+            shareBtn.setVisibility(View.VISIBLE);
+            cancelBtn.setVisibility(View.VISIBLE);
+        } else {
+            title.setText("Share audio with another device");
+            subTitle2.setText(
+                    "This device's music and videos will play on the headphones you connect");
+            recyclerView.setAdapter(
+                    new AudioSharingDeviceAdapter(
+                            deviceItems,
+                            (AudioSharingDeviceItem item) -> {
+                                sListener.onItemClick(item);
+                                dismiss();
+                            }));
+            recyclerView.setLayoutManager(
+                    new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false));
+            recyclerView.setVisibility(View.VISIBLE);
+            cancelBtn.setOnClickListener(v -> dismiss());
+            cancelBtn.setVisibility(View.VISIBLE);
         }
-        RecyclerView recyclerView = mRootView.findViewById(R.id.btn_list);
-        recyclerView.setAdapter(
-                new AudioSharingDeviceAdapter(
-                        deviceItems,
-                        (AudioSharingDeviceItem item) -> {
-                            sListener.onItemClick(item);
-                            dismiss();
-                        }));
-        recyclerView.setLayoutManager(
-                new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false));
-        Button cancelBtn = mRootView.findViewById(R.id.cancel_btn);
-        cancelBtn.setOnClickListener(v -> dismiss());
-        AlertDialog dialog = builder.setView(mRootView).create();
+        AlertDialog dialog = builder.setCustomTitle(customTitle).setView(rootView).create();
         dialog.setCanceledOnTouchOutside(false);
         return dialog;
     }
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragment.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragment.java
index 461c230..7eedb9a 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragment.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragment.java
@@ -22,6 +22,7 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.Button;
+import android.widget.ImageView;
 import android.widget.TextView;
 
 import androidx.appcompat.app.AlertDialog;
@@ -90,17 +91,18 @@
         Bundle arguments = requireArguments();
         ArrayList<AudioSharingDeviceItem> deviceItems =
                 arguments.getParcelableArrayList(BUNDLE_KEY_DEVICE_TO_DISCONNECT_ITEMS);
-        String newDeviceName = arguments.getString(BUNDLE_KEY_NEW_DEVICE_NAME);
         final AlertDialog.Builder builder =
-                new AlertDialog.Builder(getActivity())
-                        .setTitle("Choose headphone to disconnect")
-                        .setCancelable(false);
+                new AlertDialog.Builder(getActivity()).setCancelable(false);
+        LayoutInflater inflater = LayoutInflater.from(builder.getContext());
+        View customTitle = inflater.inflate(R.layout.dialog_custom_title_audio_sharing, null);
+        ImageView icon = customTitle.findViewById(R.id.title_icon);
+        icon.setImageResource(R.drawable.ic_bt_audio_sharing);
+        TextView title = customTitle.findViewById(R.id.title_text);
+        title.setText("Choose a device to disconnect");
         View rootView =
-                LayoutInflater.from(builder.getContext())
-                        .inflate(R.layout.dialog_audio_sharing_disconnect, /* parent= */ null);
+                inflater.inflate(R.layout.dialog_audio_sharing_disconnect, /* parent= */ null);
         TextView subTitle = rootView.findViewById(R.id.share_audio_disconnect_description);
-        subTitle.setText(
-                "To share audio with " + newDeviceName + ", disconnect another pair of headphone");
+        subTitle.setText("Only 2 devices can share audio at a time");
         RecyclerView recyclerView = rootView.findViewById(R.id.device_btn_list);
         recyclerView.setAdapter(
                 new AudioSharingDeviceAdapter(
@@ -116,7 +118,7 @@
                 v -> {
                     dismiss();
                 });
-        AlertDialog dialog = builder.setView(rootView).create();
+        AlertDialog dialog = builder.setCustomTitle(customTitle).setView(rootView).create();
         dialog.setCanceledOnTouchOutside(false);
         return dialog;
     }
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragment.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragment.java
index 2d7b4c4..f3f0fe4 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragment.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragment.java
@@ -22,6 +22,7 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.Button;
+import android.widget.ImageView;
 import android.widget.TextView;
 
 import androidx.appcompat.app.AlertDialog;
@@ -48,7 +49,6 @@
     }
 
     private static DialogEventListener sListener;
-    private View mRootView;
 
     @Override
     public int getMetricsCategory() {
@@ -86,14 +86,17 @@
                 arguments.getParcelableArrayList(BUNDLE_KEY_DEVICE_ITEMS);
         String newDeviceName = arguments.getString(BUNDLE_KEY_NEW_DEVICE_NAME);
         final AlertDialog.Builder builder =
-                new AlertDialog.Builder(getActivity())
-                        .setTitle("Share audio?")
-                        .setCancelable(false);
-        mRootView =
-                LayoutInflater.from(builder.getContext())
-                        .inflate(R.layout.dialog_audio_sharing_join, null /* parent */);
-        TextView subtitle1 = mRootView.findViewById(R.id.share_audio_subtitle1);
-        TextView subtitle2 = mRootView.findViewById(R.id.share_audio_subtitle2);
+                new AlertDialog.Builder(getActivity()).setCancelable(false);
+        LayoutInflater inflater = LayoutInflater.from(builder.getContext());
+        View customTitle =
+                inflater.inflate(R.layout.dialog_custom_title_audio_sharing, /* parent= */ null);
+        ImageView icon = customTitle.findViewById(R.id.title_icon);
+        icon.setImageResource(R.drawable.ic_bt_audio_sharing);
+        TextView title = customTitle.findViewById(R.id.title_text);
+        title.setText("Share your audio");
+        View rootView = inflater.inflate(R.layout.dialog_audio_sharing_join, /* parent= */ null);
+        TextView subtitle1 = rootView.findViewById(R.id.share_audio_subtitle1);
+        TextView subtitle2 = rootView.findViewById(R.id.share_audio_subtitle2);
         if (deviceItems.isEmpty()) {
             subtitle1.setText(newDeviceName);
         } else {
@@ -106,10 +109,9 @@
                                     .collect(Collectors.joining(", ")),
                             newDeviceName));
         }
-        subtitle2.setText(
-                "Connected eligible headphones will hear videos ad music playing on this phone");
-        Button shareBtn = mRootView.findViewById(R.id.share_btn);
-        Button cancelBtn = mRootView.findViewById(R.id.cancel_btn);
+        subtitle2.setText("This device's music and videos will play on both pairs of headphones");
+        Button shareBtn = rootView.findViewById(R.id.share_btn);
+        Button cancelBtn = rootView.findViewById(R.id.cancel_btn);
         shareBtn.setOnClickListener(
                 v -> {
                     sListener.onShareClick();
@@ -117,7 +119,7 @@
                 });
         shareBtn.setText("Share audio");
         cancelBtn.setOnClickListener(v -> dismiss());
-        Dialog dialog = builder.setView(mRootView).create();
+        Dialog dialog = builder.setCustomTitle(customTitle).setView(rootView).create();
         dialog.setCanceledOnTouchOutside(false);
         return dialog;
     }
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragment.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragment.java
index 31125de..1454f76 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragment.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragment.java
@@ -19,11 +19,16 @@
 import android.app.Dialog;
 import android.app.settings.SettingsEnums;
 import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
 
 import androidx.appcompat.app.AlertDialog;
 import androidx.fragment.app.Fragment;
 import androidx.fragment.app.FragmentManager;
 
+import com.android.settings.R;
 import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
 
 public class AudioSharingStopDialogFragment extends InstrumentedDialogFragment {
@@ -68,22 +73,20 @@
         Bundle arguments = requireArguments();
         String newDeviceName = arguments.getString(BUNDLE_KEY_NEW_DEVICE_NAME);
         final AlertDialog.Builder builder =
-                new AlertDialog.Builder(getActivity())
-                        .setTitle("Stop sharing audio?")
-                        .setCancelable(false);
+                new AlertDialog.Builder(getActivity()).setCancelable(false);
+        LayoutInflater inflater = LayoutInflater.from(builder.getContext());
+        View customTitle =
+                inflater.inflate(R.layout.dialog_custom_title_audio_sharing, /* parent= */ null);
+        ImageView icon = customTitle.findViewById(R.id.title_icon);
+        icon.setImageResource(R.drawable.ic_warning_24dp);
+        TextView title = customTitle.findViewById(R.id.title_text);
+        title.setText("Stop sharing audio?");
         builder.setMessage(
-                newDeviceName + " is connected, devices in audio sharing will disconnect.");
+                newDeviceName + " wants to connect, headphones in audio sharing will disconnect.");
         builder.setPositiveButton(
-                "Stop sharing",
-                (dialog, which) -> {
-                    sListener.onStopSharingClick();
-                });
-        builder.setNegativeButton(
-                "Cancel",
-                (dialog, which) -> {
-                    dismiss();
-                });
-        AlertDialog dialog = builder.create();
+                "Stop sharing", (dialog, which) -> sListener.onStopSharingClick());
+        builder.setNegativeButton("Cancel", (dialog, which) -> dismiss());
+        AlertDialog dialog = builder.setCustomTitle(customTitle).create();
         dialog.setCanceledOnTouchOutside(false);
         return dialog;
     }
diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamPreference.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamPreference.java
index 8f701a3..ffb0b88 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamPreference.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamPreference.java
@@ -16,6 +16,9 @@
 
 package com.android.settings.connecteddevice.audiosharing.audiostreams;
 
+import android.bluetooth.BluetoothLeAudioContentMetadata;
+import android.bluetooth.BluetoothLeBroadcastMetadata;
+import android.bluetooth.BluetoothLeBroadcastReceiveState;
 import android.content.Context;
 import android.util.AttributeSet;
 
@@ -24,11 +27,13 @@
 import com.android.settings.R;
 import com.android.settingslib.widget.TwoTargetPreference;
 
+import com.google.common.base.Strings;
+
 /**
  * Custom preference class for managing audio stream preferences with an optional lock icon. Extends
  * {@link TwoTargetPreference}.
  */
-public class AudioStreamPreference extends TwoTargetPreference {
+class AudioStreamPreference extends TwoTargetPreference {
     private boolean mIsConnected = false;
 
     /**
@@ -36,7 +41,7 @@
      *
      * @param isConnected Is this streams connected
      */
-    public void setIsConnected(
+    void setIsConnected(
             boolean isConnected, @Nullable OnPreferenceClickListener onPreferenceClickListener) {
         if (mIsConnected == isConnected
                 && getOnPreferenceClickListener() == onPreferenceClickListener) {
@@ -50,7 +55,7 @@
         notifyChanged();
     }
 
-    public AudioStreamPreference(Context context, @Nullable AttributeSet attrs) {
+    AudioStreamPreference(Context context, @Nullable AttributeSet attrs) {
         super(context, attrs);
         setIcon(R.drawable.ic_bt_audio_sharing);
     }
@@ -64,4 +69,34 @@
     protected int getSecondTargetResId() {
         return R.layout.preference_widget_lock;
     }
+
+    static AudioStreamPreference fromMetadata(
+            Context context, BluetoothLeBroadcastMetadata source) {
+        AudioStreamPreference preference = new AudioStreamPreference(context, /* attrs= */ null);
+        preference.setTitle(getBroadcastName(source));
+        return preference;
+    }
+
+    static AudioStreamPreference fromReceiveState(
+            Context context, BluetoothLeBroadcastReceiveState state) {
+        AudioStreamPreference preference = new AudioStreamPreference(context, /* attrs= */ null);
+        preference.setTitle(getBroadcastName(state));
+        return preference;
+    }
+
+    private static String getBroadcastName(BluetoothLeBroadcastMetadata source) {
+        return source.getSubgroups().stream()
+                .map(s -> s.getContentMetadata().getProgramInfo())
+                .filter(i -> !Strings.isNullOrEmpty(i))
+                .findFirst()
+                .orElse("Broadcast Id: " + source.getBroadcastId());
+    }
+
+    private static String getBroadcastName(BluetoothLeBroadcastReceiveState state) {
+        return state.getSubgroupMetadata().stream()
+                .map(BluetoothLeAudioContentMetadata::getProgramInfo)
+                .filter(i -> !Strings.isNullOrEmpty(i))
+                .findFirst()
+                .orElse("Broadcast Id: " + state.getBroadcastId());
+    }
 }
diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsBroadcastAssistantCallback.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsBroadcastAssistantCallback.java
index 788b253..84e753c 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsBroadcastAssistantCallback.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsBroadcastAssistantCallback.java
@@ -32,7 +32,7 @@
     private static final String TAG = "AudioStreamsBroadcastAssistantCallback";
     private static final boolean DEBUG = BluetoothUtils.D;
 
-    private AudioStreamsProgressCategoryController mCategoryController;
+    private final AudioStreamsProgressCategoryController mCategoryController;
 
     public AudioStreamsBroadcastAssistantCallback(
             AudioStreamsProgressCategoryController audioStreamsProgressCategoryController) {
@@ -52,6 +52,7 @@
                             + " state: "
                             + state);
         }
+        mCategoryController.handleSourceConnected(state);
     }
 
     @Override
@@ -94,7 +95,20 @@
 
     @Override
     public void onSourceAddFailed(
-            BluetoothDevice sink, BluetoothLeBroadcastMetadata source, int reason) {}
+            BluetoothDevice sink, BluetoothLeBroadcastMetadata source, int reason) {
+        if (DEBUG) {
+            Log.d(
+                    TAG,
+                    "onSourceAddFailed() sink : "
+                            + sink.getAddress()
+                            + " source: "
+                            + source
+                            + " reason: "
+                            + reason);
+        }
+        mCategoryController.showToast(
+                String.format(Locale.US, "Failed to join broadcast, reason %d", reason));
+    }
 
     @Override
     public void onSourceAdded(BluetoothDevice sink, int sourceId, int reason) {
@@ -119,7 +133,7 @@
         if (DEBUG) {
             Log.d(TAG, "onSourceFound() broadcastId : " + source.getBroadcastId());
         }
-        mCategoryController.addSourceFound(source);
+        mCategoryController.handleSourceFound(source);
     }
 
     @Override
@@ -127,7 +141,7 @@
         if (DEBUG) {
             Log.d(TAG, "onSourceLost() broadcastId : " + broadcastId);
         }
-        mCategoryController.removeSourceLost(broadcastId);
+        mCategoryController.handleSourceLost(broadcastId);
     }
 
     @Override
@@ -137,8 +151,23 @@
     public void onSourceModifyFailed(BluetoothDevice sink, int sourceId, int reason) {}
 
     @Override
-    public void onSourceRemoveFailed(BluetoothDevice sink, int sourceId, int reason) {}
+    public void onSourceRemoveFailed(BluetoothDevice sink, int sourceId, int reason) {
+        Log.w(TAG, "onSourceRemoveFailed() sourceId : " + sourceId + " reason : " + reason);
+        mCategoryController.showToast(
+                String.format(
+                        Locale.US,
+                        "Failed to remove source %d for sink %s",
+                        sourceId,
+                        sink.getAddress()));
+    }
 
     @Override
-    public void onSourceRemoved(BluetoothDevice sink, int sourceId, int reason) {}
+    public void onSourceRemoved(BluetoothDevice sink, int sourceId, int reason) {
+        if (DEBUG) {
+            Log.d(TAG, "onSourceRemoved() sourceId : " + sourceId + " reason : " + reason);
+        }
+        mCategoryController.showToast(
+                String.format(
+                        Locale.US, "Source %d removed for sink %s", sourceId, sink.getAddress()));
+    }
 }
diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsDashboardFragment.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsDashboardFragment.java
index a673cb4..a418415 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsDashboardFragment.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsDashboardFragment.java
@@ -16,15 +16,25 @@
 
 package com.android.settings.connecteddevice.audiosharing.audiostreams;
 
+import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsScanQrCodeController.REQUEST_SCAN_BT_BROADCAST_QR_CODE;
+
+import android.app.Activity;
+import android.bluetooth.BluetoothLeBroadcastMetadata;
 import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
+import android.util.Log;
 
 import com.android.settings.R;
+import com.android.settings.connecteddevice.audiosharing.audiostreams.qrcode.QrCodeScanModeFragment;
 import com.android.settings.dashboard.DashboardFragment;
+import com.android.settingslib.bluetooth.BluetoothLeBroadcastMetadataExt;
+import com.android.settingslib.bluetooth.BluetoothUtils;
 
 public class AudioStreamsDashboardFragment extends DashboardFragment {
     private static final String TAG = "AudioStreamsDashboardFrag";
+    private static final boolean DEBUG = BluetoothUtils.D;
+    private AudioStreamsScanQrCodeController mAudioStreamsScanQrCodeController;
 
     public AudioStreamsDashboardFragment() {
         super();
@@ -59,7 +69,8 @@
     @Override
     public void onAttach(Context context) {
         super.onAttach(context);
-        use(AudioStreamsScanQrCodeController.class).setFragment(this);
+        mAudioStreamsScanQrCodeController = use(AudioStreamsScanQrCodeController.class);
+        mAudioStreamsScanQrCodeController.setFragment(this);
     }
 
     @Override
@@ -70,6 +81,34 @@
     @Override
     public void onActivityResult(int requestCode, int resultCode, Intent data) {
         super.onActivityResult(requestCode, resultCode, data);
-        // TODO(chelseahao): implementation.
+        if (DEBUG) {
+            Log.d(
+                    TAG,
+                    "onActivityResult() requestCode : "
+                            + requestCode
+                            + " resultCode : "
+                            + resultCode);
+        }
+        if (requestCode == REQUEST_SCAN_BT_BROADCAST_QR_CODE) {
+            if (resultCode == Activity.RESULT_OK) {
+                String broadcastMetadata =
+                        data.getStringExtra(QrCodeScanModeFragment.KEY_BROADCAST_METADATA);
+                BluetoothLeBroadcastMetadata source =
+                        BluetoothLeBroadcastMetadataExt.INSTANCE.convertToBroadcastMetadata(
+                                broadcastMetadata);
+                if (source == null) {
+                    Log.w(TAG, "onActivityResult() source is null!");
+                    return;
+                }
+                if (DEBUG) {
+                    Log.d(TAG, "onActivityResult() broadcastId : " + source.getBroadcastId());
+                }
+                if (mAudioStreamsScanQrCodeController == null) {
+                    Log.w(TAG, "onActivityResult() AudioStreamsScanQrCodeController is null!");
+                    return;
+                }
+                mAudioStreamsScanQrCodeController.addSource(source);
+            }
+        }
     }
 }
diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsHelper.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsHelper.java
new file mode 100644
index 0000000..5acbc1f
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsHelper.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2023 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.settings.connecteddevice.audiosharing.audiostreams;
+
+import static java.util.Collections.emptyList;
+
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothLeBroadcastMetadata;
+import android.bluetooth.BluetoothLeBroadcastReceiveState;
+import android.util.Log;
+
+import com.android.settings.connecteddevice.audiosharing.AudioSharingUtils;
+import com.android.settingslib.bluetooth.BluetoothUtils;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
+import com.android.settingslib.utils.ThreadUtils;
+
+import java.util.List;
+import java.util.stream.Stream;
+
+import javax.annotation.Nullable;
+
+/**
+ * A helper class that adds, removes and retrieves LE broadcast sources for all active sink devices.
+ */
+class AudioStreamsHelper {
+
+    private static final String TAG = "AudioStreamsHelper";
+    private static final boolean DEBUG = BluetoothUtils.D;
+
+    private final @Nullable LocalBluetoothManager mBluetoothManager;
+    private final @Nullable LocalBluetoothLeBroadcastAssistant mLeBroadcastAssistant;
+
+    AudioStreamsHelper(@Nullable LocalBluetoothManager bluetoothManager) {
+        mBluetoothManager = bluetoothManager;
+        mLeBroadcastAssistant = getLeBroadcastAssistant(mBluetoothManager);
+    }
+
+    /**
+     * Adds the specified LE broadcast source to all active sinks.
+     *
+     * @param source The LE broadcast metadata representing the audio source.
+     */
+    void addSource(BluetoothLeBroadcastMetadata source) {
+        if (mLeBroadcastAssistant == null) {
+            Log.w(TAG, "addSource(): LeBroadcastAssistant is null!");
+            return;
+        }
+        var unused =
+                ThreadUtils.postOnBackgroundThread(
+                        () -> {
+                            for (var sink : getActiveSinksOnAssistant(mBluetoothManager)) {
+                                if (DEBUG) {
+                                    Log.d(
+                                            TAG,
+                                            "addSource(): join broadcast broadcastId"
+                                                    + " : "
+                                                    + source.getBroadcastId()
+                                                    + " sink : "
+                                                    + sink.getAddress());
+                                }
+                                mLeBroadcastAssistant.addSource(sink, source, false);
+                            }
+                        });
+    }
+
+    /** Removes all sources from LE broadcasts associated for all active sinks. */
+    void removeSource() {
+        if (mLeBroadcastAssistant == null) {
+            Log.w(TAG, "removeSource(): LeBroadcastAssistant is null!");
+            return;
+        }
+        var unused =
+                ThreadUtils.postOnBackgroundThread(
+                        () -> {
+                            for (var sink : getActiveSinksOnAssistant(mBluetoothManager)) {
+                                if (DEBUG) {
+                                    Log.d(
+                                            TAG,
+                                            "removeSource(): remove all sources from sink : "
+                                                    + sink.getAddress());
+                                }
+                                var sources = mLeBroadcastAssistant.getAllSources(sink);
+                                if (!sources.isEmpty()) {
+                                    mLeBroadcastAssistant.removeSource(
+                                            sink, sources.get(0).getSourceId());
+                                }
+                            }
+                        });
+    }
+
+    /** Retrieves a list of all LE broadcast receive states from active sinks. */
+    List<BluetoothLeBroadcastReceiveState> getAllSources() {
+        if (mLeBroadcastAssistant == null) {
+            Log.w(TAG, "getAllSources(): LeBroadcastAssistant is null!");
+            return emptyList();
+        }
+        return getActiveSinksOnAssistant(mBluetoothManager).stream()
+                .flatMap(sink -> mLeBroadcastAssistant.getAllSources(sink).stream())
+                .toList();
+    }
+
+    @Nullable
+    LocalBluetoothLeBroadcastAssistant getLeBroadcastAssistant() {
+        return mLeBroadcastAssistant;
+    }
+
+    private static List<BluetoothDevice> getActiveSinksOnAssistant(
+            @Nullable LocalBluetoothManager manager) {
+        if (manager == null) {
+            Log.w(TAG, "getActiveSinksOnAssistant(): LocalBluetoothManager is null!");
+            return emptyList();
+        }
+        return AudioSharingUtils.getActiveSinkOnAssistant(manager)
+                .map(
+                        cachedBluetoothDevice ->
+                                Stream.concat(
+                                                Stream.of(cachedBluetoothDevice.getDevice()),
+                                                cachedBluetoothDevice.getMemberDevice().stream()
+                                                        .map(CachedBluetoothDevice::getDevice))
+                                        .toList())
+                .orElse(emptyList());
+    }
+
+    private static @Nullable LocalBluetoothLeBroadcastAssistant getLeBroadcastAssistant(
+            @Nullable LocalBluetoothManager manager) {
+        if (manager == null) {
+            Log.w(TAG, "getLeBroadcastAssistant(): LocalBluetoothManager is null!");
+            return null;
+        }
+
+        LocalBluetoothProfileManager profileManager = manager.getProfileManager();
+        if (profileManager == null) {
+            Log.w(TAG, "getLeBroadcastAssistant(): LocalBluetoothProfileManager is null!");
+            return null;
+        }
+
+        return profileManager.getLeAudioBroadcastAssistantProfile();
+    }
+}
diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryController.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryController.java
index fef1e7b..6cf69c5 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryController.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryController.java
@@ -18,8 +18,6 @@
 
 import static java.util.Collections.emptyList;
 
-import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothLeAudioContentMetadata;
 import android.bluetooth.BluetoothLeBroadcastMetadata;
 import android.bluetooth.BluetoothLeBroadcastReceiveState;
 import android.content.Context;
@@ -35,19 +33,12 @@
 import com.android.settings.connecteddevice.audiosharing.AudioSharingUtils;
 import com.android.settings.core.BasePreferenceController;
 import com.android.settingslib.bluetooth.BluetoothUtils;
-import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
-import com.android.settingslib.bluetooth.LocalBluetoothManager;
-import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
 import com.android.settingslib.utils.ThreadUtils;
 
-import com.google.common.base.Strings;
-
-import java.util.List;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
-import java.util.stream.Stream;
 
 import javax.annotation.Nullable;
 
@@ -58,17 +49,17 @@
 
     private final Executor mExecutor;
     private final AudioStreamsBroadcastAssistantCallback mBroadcastAssistantCallback;
-    private final LocalBluetoothManager mBluetoothManager;
+    private final AudioStreamsHelper mAudioStreamsHelper;
     private final @Nullable LocalBluetoothLeBroadcastAssistant mLeBroadcastAssistant;
     private final ConcurrentHashMap<Integer, AudioStreamPreference> mBroadcastIdToPreferenceMap =
             new ConcurrentHashMap<>();
-    private @Nullable AudioStreamsProgressCategoryPreference mCategoryPreference;
+    private AudioStreamsProgressCategoryPreference mCategoryPreference;
 
     public AudioStreamsProgressCategoryController(Context context, String preferenceKey) {
         super(context, preferenceKey);
         mExecutor = Executors.newSingleThreadExecutor();
-        mBluetoothManager = Utils.getLocalBtManager(mContext);
-        mLeBroadcastAssistant = getLeBroadcastAssistant(mBluetoothManager);
+        mAudioStreamsHelper = new AudioStreamsHelper(Utils.getLocalBtManager(mContext));
+        mLeBroadcastAssistant = mAudioStreamsHelper.getLeBroadcastAssistant();
         mBroadcastAssistantCallback = new AudioStreamsBroadcastAssistantCallback(this);
     }
 
@@ -104,14 +95,10 @@
                     // Display currently connected streams
                     var unused =
                             ThreadUtils.postOnBackgroundThread(
-                                    () -> {
-                                        for (var sink :
-                                                getActiveSinksOnAssistant(mBluetoothManager)) {
-                                            mLeBroadcastAssistant
-                                                    .getAllSources(sink)
-                                                    .forEach(this::addSourceConnected);
-                                        }
-                                    });
+                                    () ->
+                                            mAudioStreamsHelper
+                                                    .getAllSources()
+                                                    .forEach(this::handleSourceConnected));
                 });
     }
 
@@ -140,31 +127,36 @@
                 });
     }
 
-    void addSourceFound(BluetoothLeBroadcastMetadata source) {
-        Preference.OnPreferenceClickListener onClickListener =
+    void handleSourceFound(BluetoothLeBroadcastMetadata source) {
+        Preference.OnPreferenceClickListener addSourceOrShowDialog =
                 preference -> {
                     if (DEBUG) {
                         Log.d(TAG, "preferenceClicked(): attempt to join broadcast");
                     }
-
-                    // TODO(chelseahao): add source to sink
+                    if (source.isEncrypted()) {
+                        ThreadUtils.postOnMainThread(
+                                () -> launchPasswordDialog(source, preference));
+                    } else {
+                        mAudioStreamsHelper.addSource(source);
+                    }
                     return true;
                 };
         mBroadcastIdToPreferenceMap.computeIfAbsent(
                 source.getBroadcastId(),
                 k -> {
-                    var p = createPreference(source, onClickListener);
+                    var preference = AudioStreamPreference.fromMetadata(mContext, source);
                     ThreadUtils.postOnMainThread(
                             () -> {
+                                preference.setIsConnected(false, addSourceOrShowDialog);
                                 if (mCategoryPreference != null) {
-                                    mCategoryPreference.addPreference(p);
+                                    mCategoryPreference.addPreference(preference);
                                 }
                             });
-                    return p;
+                    return preference;
                 });
     }
 
-    void removeSourceLost(int broadcastId) {
+    void handleSourceLost(int broadcastId) {
         var toRemove = mBroadcastIdToPreferenceMap.remove(broadcastId);
         if (toRemove != null) {
             ThreadUtils.postOnMainThread(
@@ -174,92 +166,43 @@
                         }
                     });
         }
-        // TODO(chelseahao): remove source from sink
+        mAudioStreamsHelper.removeSource();
     }
 
-    private void addSourceConnected(BluetoothLeBroadcastReceiveState state) {
+    void handleSourceConnected(BluetoothLeBroadcastReceiveState state) {
+        // TODO(chelseahao): only continue when the state indicates a successful connection
         mBroadcastIdToPreferenceMap.compute(
                 state.getBroadcastId(),
                 (k, v) -> {
-                    if (v == null) {
-                        // Create a new preference as the source has not been added.
-                        var p = createPreference(state);
-                        ThreadUtils.postOnMainThread(
-                                () -> {
-                                    if (mCategoryPreference != null) {
-                                        mCategoryPreference.addPreference(p);
-                                    }
-                                });
-                        return p;
-                    } else {
-                        // This source has been added either by scanning, or it's currently
-                        // connected to another active sink. Update its connection status to true
-                        // if needed.
-                        ThreadUtils.postOnMainThread(() -> v.setIsConnected(true, null));
-                        return v;
-                    }
+                    // True if this source has been added either by scanning, or it's currently
+                    // connected to another active sink.
+                    boolean existed = v != null;
+                    AudioStreamPreference preference =
+                            existed ? v : AudioStreamPreference.fromReceiveState(mContext, state);
+
+                    ThreadUtils.postOnMainThread(
+                            () -> {
+                                preference.setIsConnected(
+                                        true, p -> launchDetailFragment((AudioStreamPreference) p));
+                                if (mCategoryPreference != null && !existed) {
+                                    mCategoryPreference.addPreference(preference);
+                                }
+                            });
+
+                    return preference;
                 });
     }
 
-    private AudioStreamPreference createPreference(
-            BluetoothLeBroadcastMetadata source,
-            Preference.OnPreferenceClickListener onPreferenceClickListener) {
-        AudioStreamPreference preference = new AudioStreamPreference(mContext, /* attrs= */ null);
-        preference.setTitle(
-                source.getSubgroups().stream()
-                        .map(s -> s.getContentMetadata().getProgramInfo())
-                        .filter(i -> !Strings.isNullOrEmpty(i))
-                        .findFirst()
-                        .orElse("Broadcast Id: " + source.getBroadcastId()));
-        preference.setIsConnected(false, onPreferenceClickListener);
-        return preference;
-    }
-
-    private AudioStreamPreference createPreference(BluetoothLeBroadcastReceiveState state) {
-        AudioStreamPreference preference = new AudioStreamPreference(mContext, /* attrs= */ null);
-        preference.setTitle(
-                state.getSubgroupMetadata().stream()
-                        .map(BluetoothLeAudioContentMetadata::getProgramInfo)
-                        .filter(i -> !Strings.isNullOrEmpty(i))
-                        .findFirst()
-                        .orElse("Broadcast Id: " + state.getBroadcastId()));
-        preference.setIsConnected(true, null);
-        return preference;
-    }
-
-    private static List<BluetoothDevice> getActiveSinksOnAssistant(LocalBluetoothManager manager) {
-        if (manager == null) {
-            Log.w(TAG, "getActiveSinksOnAssistant(): LocalBluetoothManager is null!");
-            return emptyList();
-        }
-        return AudioSharingUtils.getActiveSinkOnAssistant(manager)
-                .map(
-                        cachedBluetoothDevice ->
-                                Stream.concat(
-                                                Stream.of(cachedBluetoothDevice.getDevice()),
-                                                cachedBluetoothDevice.getMemberDevice().stream()
-                                                        .map(CachedBluetoothDevice::getDevice))
-                                        .toList())
-                .orElse(emptyList());
-    }
-
-    private static @Nullable LocalBluetoothLeBroadcastAssistant getLeBroadcastAssistant(
-            LocalBluetoothManager manager) {
-        if (manager == null) {
-            Log.w(TAG, "getLeBroadcastAssistant(): LocalBluetoothManager is null!");
-            return null;
-        }
-
-        LocalBluetoothProfileManager profileManager = manager.getProfileManager();
-        if (profileManager == null) {
-            Log.w(TAG, "getLeBroadcastAssistant(): LocalBluetoothProfileManager is null!");
-            return null;
-        }
-
-        return profileManager.getLeAudioBroadcastAssistantProfile();
-    }
-
     void showToast(String msg) {
         AudioSharingUtils.toastMessage(mContext, msg);
     }
+
+    private boolean launchDetailFragment(AudioStreamPreference preference) {
+        // TODO(chelseahao): impl
+        return true;
+    }
+
+    private void launchPasswordDialog(BluetoothLeBroadcastMetadata source, Preference preference) {
+        // TODO(chelseahao): impl
+    }
 }
diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsScanQrCodeController.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsScanQrCodeController.java
index 12b46e5..549e725 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsScanQrCodeController.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsScanQrCodeController.java
@@ -16,6 +16,7 @@
 
 package com.android.settings.connecteddevice.audiosharing.audiostreams;
 
+import android.bluetooth.BluetoothLeBroadcastMetadata;
 import android.bluetooth.BluetoothProfile;
 import android.content.Context;
 import android.content.Intent;
@@ -41,10 +42,10 @@
 
 public class AudioStreamsScanQrCodeController extends BasePreferenceController
         implements DefaultLifecycleObserver {
+    static final int REQUEST_SCAN_BT_BROADCAST_QR_CODE = 0;
     private static final String TAG = "AudioStreamsProgressCategoryController";
     private static final boolean DEBUG = BluetoothUtils.D;
     private static final String KEY = "audio_streams_scan_qr_code";
-    private static final int REQUEST_SCAN_BT_BROADCAST_QR_CODE = 0;
     private final BluetoothCallback mBluetoothCallback =
             new BluetoothCallback() {
                 @Override
@@ -57,12 +58,14 @@
             };
 
     private final LocalBluetoothManager mLocalBtManager;
+    private final AudioStreamsHelper mAudioStreamsHelper;
     private AudioStreamsDashboardFragment mFragment;
     private Preference mPreference;
 
     public AudioStreamsScanQrCodeController(Context context, String preferenceKey) {
         super(context, preferenceKey);
         mLocalBtManager = Utils.getLocalBtManager(mContext);
+        mAudioStreamsHelper = new AudioStreamsHelper(mLocalBtManager);
     }
 
     public void setFragment(AudioStreamsDashboardFragment fragment) {
@@ -121,6 +124,10 @@
                 });
     }
 
+    void addSource(BluetoothLeBroadcastMetadata source) {
+        mAudioStreamsHelper.addSource(source);
+    }
+
     private void updateVisibility() {
         ThreadUtils.postOnBackgroundThread(
                 () -> {
diff --git a/src/com/android/settings/network/apn/ApnEditPageProvider.kt b/src/com/android/settings/network/apn/ApnEditPageProvider.kt
index 5026958..bb19c59 100644
--- a/src/com/android/settings/network/apn/ApnEditPageProvider.kt
+++ b/src/com/android/settings/network/apn/ApnEditPageProvider.kt
@@ -150,6 +150,7 @@
             SettingsOutlinedTextField(
                 value = apnData.mmsc,
                 label = stringResource(R.string.apn_mmsc),
+                errorMessage = validateMMSC(apnData.mmsc, context),
                 enabled = apnData.mmscEnabled
             ) { apnData = apnData.copy(mmsc = it) }
             SettingsOutlinedTextField(
diff --git a/src/com/android/settings/network/apn/ApnStatus.kt b/src/com/android/settings/network/apn/ApnStatus.kt
index d81a8e3..668ea9b 100644
--- a/src/com/android/settings/network/apn/ApnStatus.kt
+++ b/src/com/android/settings/network/apn/ApnStatus.kt
@@ -239,7 +239,12 @@
     if (apnData.customizedConfig.readOnlyApn) {
         return true
     }
-    val errorMsg = validateApnData(apnData, context)
+    var errorMsg = validateApnData(apnData, context)
+    if (errorMsg != null) {
+        //TODO: showError(this)
+        return false
+    }
+    errorMsg = validateMMSC(apnData.mmsc, context)
     if (errorMsg != null) {
         //TODO: showError(this)
         return false
@@ -529,4 +534,9 @@
 fun deleteApn(uri: Uri, context: Context) {
     val contentResolver = context.contentResolver
     contentResolver.delete(uri, null, null)
+}
+
+fun validateMMSC(mmsc: String, context: Context): String? {
+    return if (mmsc.matches(Regex("^https?:\\/\\/.+"))) null
+            else context.resources.getString(R.string.error_mmsc_valid)
 }
\ No newline at end of file
diff --git a/src/com/android/settings/privatespace/AutoAdvanceSetupFragment.java b/src/com/android/settings/privatespace/AutoAdvanceSetupFragment.java
index 77d57f7..9e905f2 100644
--- a/src/com/android/settings/privatespace/AutoAdvanceSetupFragment.java
+++ b/src/com/android/settings/privatespace/AutoAdvanceSetupFragment.java
@@ -52,16 +52,17 @@
     private static final String TITLE_INDEX = "title_index";
     private static final int DELAY_BETWEEN_SCREENS = 5000; // 5 seconds in millis
     private static final int ANIMATION_DURATION_MILLIS = 500;
+    private static final int HEADER_TEXT_MAX_LINES = 4;
     private GlifLayout mRootView;
     private Handler mHandler;
     private int mScreenTitleIndex;
     private static final List<Pair<Integer, Integer>> HEADER_IMAGE_PAIRS =
             ImmutableList.of(
-                    new Pair(R.string.privatespace_apps_hidden_title,
+                    new Pair(R.string.private_space_notifications_hidden_title,
                             R.drawable.privatespace_setup_flow_placeholder),
-                    new Pair(R.string.privatespace_access_from_apps_title,
+                    new Pair(R.string.private_space_share_photos_title,
                             R.drawable.privatespace_setup_flow_placeholder),
-                    new Pair(R.string.privatespace_system_apps_installed_title,
+                    new Pair(R.string.private_space_apps_installed_title,
                             R.drawable.privatespace_setup_flow_placeholder));
 
     private Runnable mUpdateScreenResources =
@@ -116,6 +117,7 @@
         mRootView =
                 (GlifLayout)
                         inflater.inflate(R.layout.privatespace_advancing_screen, container, false);
+        mRootView.getHeaderTextView().setMaxLines(HEADER_TEXT_MAX_LINES);
         updateHeaderAndImage();
         mHandler = new Handler(Looper.getMainLooper());
         mHandler.postDelayed(mUpdateScreenResources, DELAY_BETWEEN_SCREENS);
diff --git a/src/com/android/settings/privatespace/PrivateProfileCreationError.java b/src/com/android/settings/privatespace/PrivateProfileCreationError.java
index 91c35a3..74beef4 100644
--- a/src/com/android/settings/privatespace/PrivateProfileCreationError.java
+++ b/src/com/android/settings/privatespace/PrivateProfileCreationError.java
@@ -47,14 +47,14 @@
         final FooterBarMixin mixin = rootView.getMixin(FooterBarMixin.class);
         mixin.setPrimaryButton(
                 new FooterButton.Builder(getContext())
-                        .setText(R.string.privatespace_tryagain_label)
+                        .setText(R.string.private_space_tryagain_label)
                         .setListener(onTryAgain())
                         .setButtonType(FooterButton.ButtonType.NEXT)
                         .setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Primary)
                         .build());
         mixin.setSecondaryButton(
                 new FooterButton.Builder(getContext())
-                        .setText(R.string.privatespace_cancel_label)
+                        .setText(R.string.private_space_cancel_label)
                         .setListener(onCancel())
                         .setButtonType(FooterButton.ButtonType.CANCEL)
                         .setTheme(
diff --git a/src/com/android/settings/privatespace/PrivateSpaceAccountLoginError.java b/src/com/android/settings/privatespace/PrivateSpaceAccountLoginError.java
index ac3518e..e445a7f 100644
--- a/src/com/android/settings/privatespace/PrivateSpaceAccountLoginError.java
+++ b/src/com/android/settings/privatespace/PrivateSpaceAccountLoginError.java
@@ -51,7 +51,7 @@
         final FooterBarMixin mixin = rootView.getMixin(FooterBarMixin.class);
         mixin.setPrimaryButton(
                 new FooterButton.Builder(getContext())
-                        .setText(R.string.privatespace_tryagain_label)
+                        .setText(R.string.private_space_tryagain_label)
                         .setListener(nextScreen())
                         .setButtonType(FooterButton.ButtonType.NEXT)
                         .setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Primary)
diff --git a/src/com/android/settings/privatespace/PrivateSpaceEducation.java b/src/com/android/settings/privatespace/PrivateSpaceEducation.java
index 887854b..eb562af 100644
--- a/src/com/android/settings/privatespace/PrivateSpaceEducation.java
+++ b/src/com/android/settings/privatespace/PrivateSpaceEducation.java
@@ -50,14 +50,14 @@
         final FooterBarMixin mixin = rootView.getMixin(FooterBarMixin.class);
         mixin.setPrimaryButton(
                 new FooterButton.Builder(getContext())
-                        .setText(R.string.privatespace_setup_button_label)
+                        .setText(R.string.private_space_setup_button_label)
                         .setListener(onSetup())
                         .setButtonType(FooterButton.ButtonType.NEXT)
                         .setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Primary)
                         .build());
         mixin.setSecondaryButton(
                 new FooterButton.Builder(getContext())
-                        .setText(R.string.privatespace_cancel_label)
+                        .setText(R.string.private_space_cancel_label)
                         .setListener(onCancel())
                         .setButtonType(FooterButton.ButtonType.CANCEL)
                         .setTheme(
diff --git a/src/com/android/settings/privatespace/PrivateSpaceSetLockFragment.java b/src/com/android/settings/privatespace/PrivateSpaceSetLockFragment.java
index dd4bc7f..a256ea6 100644
--- a/src/com/android/settings/privatespace/PrivateSpaceSetLockFragment.java
+++ b/src/com/android/settings/privatespace/PrivateSpaceSetLockFragment.java
@@ -60,14 +60,14 @@
         final FooterBarMixin mixin = rootView.getMixin(FooterBarMixin.class);
         mixin.setPrimaryButton(
                 new FooterButton.Builder(getContext())
-                        .setText(R.string.privatespace_use_screenlock_label)
+                        .setText(R.string.private_space_use_screenlock_label)
                         .setListener(onClickUse())
                         .setButtonType(FooterButton.ButtonType.NEXT)
                         .setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Primary)
                         .build());
         mixin.setSecondaryButton(
                 new FooterButton.Builder(getContext())
-                        .setText(R.string.privatespace_set_lock_label)
+                        .setText(R.string.private_space_set_lock_label)
                         .setListener(onClickNewLock())
                         .setButtonType(FooterButton.ButtonType.NEXT)
                         .setTheme(
diff --git a/src/com/android/settings/privatespace/SetupSuccessFragment.java b/src/com/android/settings/privatespace/SetupSuccessFragment.java
index fc2ce46..0b1b9d9 100644
--- a/src/com/android/settings/privatespace/SetupSuccessFragment.java
+++ b/src/com/android/settings/privatespace/SetupSuccessFragment.java
@@ -55,7 +55,7 @@
         final FooterBarMixin mixin = rootView.getMixin(FooterBarMixin.class);
         mixin.setPrimaryButton(
                 new FooterButton.Builder(getContext())
-                        .setText(R.string.privatespace_done_label)
+                        .setText(R.string.private_space_done_label)
                         .setListener(onClickNext())
                         .setButtonType(FooterButton.ButtonType.NEXT)
                         .setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Primary)
@@ -104,6 +104,7 @@
     }
 
     private void accessPrivateSpaceToast() {
-        Toast.makeText(getContext(), R.string.scrolldown_to_access, Toast.LENGTH_SHORT).show();
+        Toast.makeText(getContext(), R.string.private_space_scrolldown_to_access,
+                Toast.LENGTH_SHORT).show();
     }
 }
diff --git a/src/com/android/settings/privatespace/onelock/UseOneLockControllerSwitch.java b/src/com/android/settings/privatespace/onelock/UseOneLockControllerSwitch.java
index 04101b2..54b0374 100644
--- a/src/com/android/settings/privatespace/onelock/UseOneLockControllerSwitch.java
+++ b/src/com/android/settings/privatespace/onelock/UseOneLockControllerSwitch.java
@@ -192,7 +192,7 @@
         new AlertDialog.Builder(mContext)
                   .setMessage(R.string.private_space_new_lock_title)
                   .setPositiveButton(
-                            R.string.privatespace_set_lock_label,
+                            R.string.private_space_set_lock_label,
                             (dialog, which) -> {
                                 Intent intent = new Intent(mContext,
                                           PrivateProfileContextHelperActivity.class);
@@ -201,7 +201,7 @@
                                           UNUNIFY_PRIVATE_LOCK_FROM_DEVICE_REQUEST,
                                           /*Options*/ null, mUserHandle);
                             })
-                  .setNegativeButton(R.string.privatespace_cancel_label,
+                  .setNegativeButton(R.string.private_space_cancel_label,
                             (DialogInterface dialog, int which) -> {
                                 mUnifyProfile.setChecked(true);
                                 dialog.dismiss();
diff --git a/src/com/android/settings/spa/app/specialaccess/AlarmsAndRemindersAppList.kt b/src/com/android/settings/spa/app/specialaccess/AlarmsAndRemindersAppList.kt
index c990927..3e48aa5 100644
--- a/src/com/android/settings/spa/app/specialaccess/AlarmsAndRemindersAppList.kt
+++ b/src/com/android/settings/spa/app/specialaccess/AlarmsAndRemindersAppList.kt
@@ -18,6 +18,7 @@
 
 import android.Manifest
 import android.app.AlarmManager
+import android.app.AppOpsManager
 import android.app.compat.CompatChanges
 import android.app.settings.SettingsEnums
 import android.content.Context
@@ -56,6 +57,7 @@
     override val pageTitleResId = R.string.alarms_and_reminders_title
     override val switchTitleResId = R.string.alarms_and_reminders_switch_title
     override val footerResId = R.string.alarms_and_reminders_footer_title
+    override val enhancedConfirmationKey: String = AppOpsManager.OPSTR_SCHEDULE_EXACT_ALARM
 
     override fun transform(userIdFlow: Flow<Int>, appListFlow: Flow<List<ApplicationInfo>>) =
         userIdFlow.map { userId ->
diff --git a/src/com/android/settings/spa/app/specialaccess/InstallUnknownApps.kt b/src/com/android/settings/spa/app/specialaccess/InstallUnknownApps.kt
index 7f63e38..dc98330 100644
--- a/src/com/android/settings/spa/app/specialaccess/InstallUnknownApps.kt
+++ b/src/com/android/settings/spa/app/specialaccess/InstallUnknownApps.kt
@@ -18,6 +18,7 @@
 
 import android.Manifest
 import android.app.AppGlobals
+import android.app.AppOpsManager
 import android.app.AppOpsManager.MODE_DEFAULT
 import android.app.AppOpsManager.OP_REQUEST_INSTALL_PACKAGES
 import android.content.Context
@@ -55,6 +56,7 @@
             UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
             UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY,
         )
+    override val enhancedConfirmationKey: String = AppOpsManager.OPSTR_REQUEST_INSTALL_PACKAGES
 
     override fun transformItem(app: ApplicationInfo) =
         InstallUnknownAppsRecord(
diff --git a/src/com/android/settings/spa/app/specialaccess/PictureInPicture.kt b/src/com/android/settings/spa/app/specialaccess/PictureInPicture.kt
index cd615919..fe8f103 100644
--- a/src/com/android/settings/spa/app/specialaccess/PictureInPicture.kt
+++ b/src/com/android/settings/spa/app/specialaccess/PictureInPicture.kt
@@ -16,6 +16,7 @@
 
 package com.android.settings.spa.app.specialaccess
 
+import android.app.AppOpsManager
 import android.app.AppOpsManager.OP_PICTURE_IN_PICTURE
 import android.content.Context
 import android.content.pm.ActivityInfo
@@ -53,6 +54,7 @@
     override val pageTitleResId = R.string.picture_in_picture_title
     override val switchTitleResId = R.string.picture_in_picture_app_detail_switch
     override val footerResId = R.string.picture_in_picture_app_detail_summary
+    override val enhancedConfirmationKey: String = AppOpsManager.OPSTR_PICTURE_IN_PICTURE
 
     private val packageManager = context.packageManager
 
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFragmentTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFragmentTest.java
new file mode 100644
index 0000000..86c724c
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFragmentTest.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2023 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.settings.connecteddevice.audiosharing;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.robolectric.shadows.ShadowLooper.shadowMainLooper;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothStatusCodes;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.view.View;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentActivity;
+
+import com.android.settings.R;
+import com.android.settings.flags.Flags;
+import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
+import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadow.api.Shadow;
+import org.robolectric.shadows.androidx.fragment.FragmentController;
+
+import java.util.ArrayList;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(
+        shadows = {
+            ShadowAlertDialogCompat.class,
+            ShadowBluetoothAdapter.class,
+        })
+public class AudioSharingDialogFragmentTest {
+
+    @Rule public final MockitoRule mocks = MockitoJUnit.rule();
+
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
+    private static final String TEST_DEVICE_NAME1 = "test1";
+
+    private static final String TEST_DEVICE_NAME2 = "test2";
+    private static final String TEST_DEVICE_NAME3 = "test3";
+    private static final AudioSharingDeviceItem TEST_DEVICE_ITEM1 =
+            new AudioSharingDeviceItem(TEST_DEVICE_NAME1, /* groupId= */ 1, /* isActive= */ false);
+    private static final AudioSharingDeviceItem TEST_DEVICE_ITEM2 =
+            new AudioSharingDeviceItem(TEST_DEVICE_NAME2, /* groupId= */ 2, /* isActive= */ false);
+    private static final AudioSharingDeviceItem TEST_DEVICE_ITEM3 =
+            new AudioSharingDeviceItem(TEST_DEVICE_NAME3, /* groupId= */ 3, /* isActive= */ false);
+
+    private Fragment mParent;
+    private AudioSharingDialogFragment mFragment;
+    private ShadowBluetoothAdapter mShadowBluetoothAdapter;
+
+    @Before
+    public void setUp() {
+        mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+        mShadowBluetoothAdapter.setEnabled(true);
+        mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
+                BluetoothStatusCodes.FEATURE_SUPPORTED);
+        mShadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
+                BluetoothStatusCodes.FEATURE_SUPPORTED);
+        mFragment = new AudioSharingDialogFragment();
+        mParent = new Fragment();
+        FragmentController.setupFragment(
+                mParent, FragmentActivity.class, /* containerViewId= */ 0, /* bundle= */ null);
+    }
+
+    @After
+    public void tearDown() {
+        ShadowAlertDialogCompat.reset();
+    }
+
+    @Test
+    @RequiresFlagsDisabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
+    public void onCreateDialog_flagOff_dialogNotExist() {
+        mFragment.show(mParent, new ArrayList<>(), (item) -> {});
+        shadowMainLooper().idle();
+        AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
+        assertThat(dialog).isNull();
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
+    public void onCreateDialog_flagOn_noConnectedDevice() {
+        mFragment.show(mParent, new ArrayList<>(), (item) -> {});
+        shadowMainLooper().idle();
+        AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
+        ShadowAlertDialogCompat shadowDialog = ShadowAlertDialogCompat.shadowOf(dialog);
+        View rootView = shadowDialog.getView();
+        TextView subtitle1 = rootView.findViewById(R.id.share_audio_subtitle1);
+        ImageView guidance = rootView.findViewById(R.id.share_audio_guidance);
+        Button shareBtn = rootView.findViewById(R.id.share_btn);
+        assertThat(dialog.isShowing()).isTrue();
+        assertThat(subtitle1.getVisibility()).isEqualTo(View.GONE);
+        assertThat(guidance.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(shareBtn.getVisibility()).isEqualTo(View.GONE);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
+    public void onCreateDialog_noConnectedDevice_dialogDismiss() {
+        mFragment.show(mParent, new ArrayList<>(), (item) -> {});
+        shadowMainLooper().idle();
+        AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
+        dialog.findViewById(android.R.id.button2).performClick();
+        shadowMainLooper().idle();
+        assertThat(dialog.isShowing()).isFalse();
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
+    public void onCreateDialog_flagOn_singleConnectedDevice() {
+        ArrayList<AudioSharingDeviceItem> list = new ArrayList<>();
+        list.add(TEST_DEVICE_ITEM1);
+        mFragment.show(mParent, list, (item) -> {});
+        shadowMainLooper().idle();
+        AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
+        ShadowAlertDialogCompat shadowDialog = ShadowAlertDialogCompat.shadowOf(dialog);
+        View rootView = shadowDialog.getView();
+        TextView subtitle1 = rootView.findViewById(R.id.share_audio_subtitle1);
+        ImageView guidance = rootView.findViewById(R.id.share_audio_guidance);
+        Button shareBtn = rootView.findViewById(R.id.share_btn);
+        assertThat(dialog.isShowing()).isTrue();
+        assertThat(subtitle1.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(TEST_DEVICE_NAME1).isEqualTo(subtitle1.getText());
+        assertThat(guidance.getVisibility()).isEqualTo(View.GONE);
+        assertThat(shareBtn.getVisibility()).isEqualTo(View.VISIBLE);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
+    public void onCreateDialog_singleConnectedDevice_dialogDismiss() {
+        mFragment.show(mParent, new ArrayList<>(), (item) -> {});
+        shadowMainLooper().idle();
+        AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
+        ShadowAlertDialogCompat shadowDialog = ShadowAlertDialogCompat.shadowOf(dialog);
+        View rootView = shadowDialog.getView();
+        rootView.findViewById(R.id.cancel_btn).performClick();
+        assertThat(dialog.isShowing()).isFalse();
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
+    public void onCreateDialog_singleConnectedDevice_shareClicked() {
+        AtomicBoolean isShareBtnClicked = new AtomicBoolean(false);
+        mFragment.show(
+                mParent,
+                new ArrayList<>(),
+                (item) -> {
+                    isShareBtnClicked.set(true);
+                });
+        shadowMainLooper().idle();
+        AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
+        ShadowAlertDialogCompat shadowDialog = ShadowAlertDialogCompat.shadowOf(dialog);
+        View rootView = shadowDialog.getView();
+        rootView.findViewById(R.id.share_btn).performClick();
+        assertThat(dialog.isShowing()).isFalse();
+        assertThat(isShareBtnClicked.get()).isTrue();
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
+    public void onCreateDialog_flagOn_multipleConnectedDevice() {
+        ArrayList<AudioSharingDeviceItem> list = new ArrayList<>();
+        list.add(TEST_DEVICE_ITEM1);
+        list.add(TEST_DEVICE_ITEM2);
+        list.add(TEST_DEVICE_ITEM3);
+        mFragment.show(mParent, list, (item) -> {});
+        shadowMainLooper().idle();
+        AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
+        ShadowAlertDialogCompat shadowDialog = ShadowAlertDialogCompat.shadowOf(dialog);
+        View rootView = shadowDialog.getView();
+        TextView subtitle1 = rootView.findViewById(R.id.share_audio_subtitle1);
+        ImageView guidance = rootView.findViewById(R.id.share_audio_guidance);
+        Button shareBtn = rootView.findViewById(R.id.share_btn);
+        assertThat(dialog.isShowing()).isTrue();
+        assertThat(subtitle1.getVisibility()).isEqualTo(View.GONE);
+        assertThat(guidance.getVisibility()).isEqualTo(View.GONE);
+        assertThat(shareBtn.getVisibility()).isEqualTo(View.GONE);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
+    public void onCreateDialog_multipleConnectedDevice_dialogDismiss() {
+        ArrayList<AudioSharingDeviceItem> list = new ArrayList<>();
+        list.add(TEST_DEVICE_ITEM1);
+        list.add(TEST_DEVICE_ITEM2);
+        list.add(TEST_DEVICE_ITEM3);
+        mFragment.show(mParent, list, (item) -> {});
+        shadowMainLooper().idle();
+        AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
+        ShadowAlertDialogCompat shadowDialog = ShadowAlertDialogCompat.shadowOf(dialog);
+        View rootView = shadowDialog.getView();
+        rootView.findViewById(R.id.cancel_btn).performClick();
+        assertThat(dialog.isShowing()).isFalse();
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragmentTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragmentTest.java
new file mode 100644
index 0000000..976e164
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragmentTest.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2023 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.settings.connecteddevice.audiosharing;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.robolectric.shadows.ShadowLooper.shadowMainLooper;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothStatusCodes;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.view.View;
+
+import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentActivity;
+
+import com.android.internal.widget.RecyclerView;
+import com.android.settings.R;
+import com.android.settings.flags.Flags;
+import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
+import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadow.api.Shadow;
+import org.robolectric.shadows.androidx.fragment.FragmentController;
+
+import java.util.ArrayList;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(
+        shadows = {
+            ShadowAlertDialogCompat.class,
+            ShadowBluetoothAdapter.class,
+        })
+public class AudioSharingDisconnectDialogFragmentTest {
+
+    @Rule public final MockitoRule mocks = MockitoJUnit.rule();
+
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
+    private static final String TEST_DEVICE_NAME1 = "test1";
+    private static final String TEST_DEVICE_NAME2 = "test2";
+    private static final String TEST_DEVICE_NAME3 = "test3";
+    private static final AudioSharingDeviceItem TEST_DEVICE_ITEM1 =
+            new AudioSharingDeviceItem(TEST_DEVICE_NAME1, /* groupId= */ 1, /* isActive= */ true);
+    private static final AudioSharingDeviceItem TEST_DEVICE_ITEM2 =
+            new AudioSharingDeviceItem(TEST_DEVICE_NAME2, /* groupId= */ 2, /* isActive= */ false);
+
+    private Fragment mParent;
+    private AudioSharingDisconnectDialogFragment mFragment;
+    private ShadowBluetoothAdapter mShadowBluetoothAdapter;
+
+    @Before
+    public void setUp() {
+        mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+        mShadowBluetoothAdapter.setEnabled(true);
+        mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
+                BluetoothStatusCodes.FEATURE_SUPPORTED);
+        mShadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
+                BluetoothStatusCodes.FEATURE_SUPPORTED);
+        mFragment = new AudioSharingDisconnectDialogFragment();
+        mParent = new Fragment();
+        FragmentController.setupFragment(
+                mParent, FragmentActivity.class, /* containerViewId= */ 0, /* bundle= */ null);
+        ArrayList<AudioSharingDeviceItem> list = new ArrayList<>();
+        list.add(TEST_DEVICE_ITEM1);
+        list.add(TEST_DEVICE_ITEM2);
+        mFragment.show(mParent, list, TEST_DEVICE_NAME3, (item) -> {});
+        shadowMainLooper().idle();
+    }
+
+    @After
+    public void tearDown() {
+        ShadowAlertDialogCompat.reset();
+    }
+
+    @Test
+    @RequiresFlagsDisabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
+    public void onCreateDialog_flagOff_dialogNotExist() {
+        AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
+        assertThat(dialog).isNotNull();
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
+    public void onCreateDialog_flagOn_dialogShowBtnForTwoDevices() {
+        AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
+        ShadowAlertDialogCompat shadowDialog = ShadowAlertDialogCompat.shadowOf(dialog);
+        View rootView = shadowDialog.getView();
+        RecyclerView view = rootView.findViewById(R.id.device_btn_list);
+        assertThat(view.getAdapter().getItemCount()).isEqualTo(2);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
+    public void onCreateDialog_clickCancel_dialogDismiss() {
+        AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
+        ShadowAlertDialogCompat shadowDialog = ShadowAlertDialogCompat.shadowOf(dialog);
+        View rootView = shadowDialog.getView();
+        rootView.findViewById(R.id.cancel_btn).performClick();
+        assertThat(dialog.isShowing()).isFalse();
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragmentTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragmentTest.java
new file mode 100644
index 0000000..5eb0e8a
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragmentTest.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2023 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.settings.connecteddevice.audiosharing;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.robolectric.shadows.ShadowLooper.shadowMainLooper;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothStatusCodes;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.view.View;
+import android.widget.TextView;
+
+import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentActivity;
+
+import com.android.settings.R;
+import com.android.settings.flags.Flags;
+import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
+import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadow.api.Shadow;
+import org.robolectric.shadows.androidx.fragment.FragmentController;
+
+import java.util.ArrayList;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(
+        shadows = {
+            ShadowAlertDialogCompat.class,
+            ShadowBluetoothAdapter.class,
+        })
+public class AudioSharingJoinDialogFragmentTest {
+
+    @Rule public final MockitoRule mocks = MockitoJUnit.rule();
+
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
+    private static final String TEST_DEVICE_NAME1 = "test1";
+    private static final String TEST_DEVICE_NAME2 = "test2";
+    private static final AudioSharingDeviceItem TEST_DEVICE_ITEM =
+            new AudioSharingDeviceItem(TEST_DEVICE_NAME1, /* groupId= */ 1, /* isActive= */ true);
+
+    private Fragment mParent;
+    private AudioSharingJoinDialogFragment mFragment;
+    private ShadowBluetoothAdapter mShadowBluetoothAdapter;
+
+    @Before
+    public void setUp() {
+        mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+        mShadowBluetoothAdapter.setEnabled(true);
+        mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
+                BluetoothStatusCodes.FEATURE_SUPPORTED);
+        mShadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
+                BluetoothStatusCodes.FEATURE_SUPPORTED);
+        mFragment = new AudioSharingJoinDialogFragment();
+        mParent = new Fragment();
+        FragmentController.setupFragment(
+                mParent, FragmentActivity.class, /* containerViewId= */ 0, /* bundle= */ null);
+    }
+
+    @After
+    public void tearDown() {
+        ShadowAlertDialogCompat.reset();
+    }
+
+    @Test
+    @RequiresFlagsDisabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
+    public void onCreateDialog_flagOff_dialogNotExist() {
+        mFragment.show(mParent, new ArrayList<>(), TEST_DEVICE_NAME2, () -> {});
+        shadowMainLooper().idle();
+        AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
+        assertThat(dialog).isNull();
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
+    public void onCreateDialog_flagOn_dialogShowTextForSingleDevice() {
+        mFragment.show(mParent, new ArrayList<>(), TEST_DEVICE_NAME2, () -> {});
+        shadowMainLooper().idle();
+        AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
+        assertThat(dialog).isNotNull();
+        assertThat(dialog.isShowing()).isTrue();
+        ShadowAlertDialogCompat shadowDialog = ShadowAlertDialogCompat.shadowOf(dialog);
+        View rootView = shadowDialog.getView();
+        TextView subtitle1 = rootView.findViewById(R.id.share_audio_subtitle1);
+        assertThat(subtitle1.getText()).isEqualTo(TEST_DEVICE_NAME2);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
+    public void onCreateDialog_flagOn_dialogShowTextForTwoDevice() {
+        ArrayList<AudioSharingDeviceItem> list = new ArrayList<>();
+        list.add(TEST_DEVICE_ITEM);
+        mFragment.show(mParent, list, TEST_DEVICE_NAME2, () -> {});
+        shadowMainLooper().idle();
+        AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
+        assertThat(dialog).isNotNull();
+        assertThat(dialog.isShowing()).isTrue();
+        ShadowAlertDialogCompat shadowDialog = ShadowAlertDialogCompat.shadowOf(dialog);
+        View rootView = shadowDialog.getView();
+        TextView subtitle1 = rootView.findViewById(R.id.share_audio_subtitle1);
+        assertThat(subtitle1.getText()).isEqualTo(TEST_DEVICE_NAME1 + " and " + TEST_DEVICE_NAME2);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
+    public void onCreateDialog_clickCancel_dialogDismiss() {
+        mFragment.show(mParent, new ArrayList<>(), TEST_DEVICE_NAME2, () -> {});
+        shadowMainLooper().idle();
+        AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
+        ShadowAlertDialogCompat shadowDialog = ShadowAlertDialogCompat.shadowOf(dialog);
+        View rootView = shadowDialog.getView();
+        rootView.findViewById(R.id.cancel_btn).performClick();
+        assertThat(dialog.isShowing()).isFalse();
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
+    public void onCreateDialog_clickShare_callbackTriggered() {
+        AtomicBoolean isShareBtnClicked = new AtomicBoolean(false);
+        mFragment.show(
+                mParent, new ArrayList<>(), TEST_DEVICE_NAME2, () -> isShareBtnClicked.set(true));
+        shadowMainLooper().idle();
+        AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
+        ShadowAlertDialogCompat shadowDialog = ShadowAlertDialogCompat.shadowOf(dialog);
+        View rootView = shadowDialog.getView();
+        rootView.findViewById(R.id.share_btn).performClick();
+        assertThat(dialog.isShowing()).isFalse();
+        assertThat(isShareBtnClicked.get()).isTrue();
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragmentTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragmentTest.java
new file mode 100644
index 0000000..1de7b2d
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragmentTest.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2023 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.settings.connecteddevice.audiosharing;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.robolectric.shadows.ShadowLooper.shadowMainLooper;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothStatusCodes;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+
+import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentActivity;
+
+import com.android.settings.flags.Flags;
+import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
+import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadow.api.Shadow;
+import org.robolectric.shadows.androidx.fragment.FragmentController;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(
+        shadows = {
+            ShadowAlertDialogCompat.class,
+            ShadowBluetoothAdapter.class,
+        })
+public class AudioSharingStopDialogFragmentTest {
+
+    @Rule public final MockitoRule mocks = MockitoJUnit.rule();
+
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
+    private static final String TEST_DEVICE_NAME = "test";
+
+    private Fragment mParent;
+    private AudioSharingStopDialogFragment mFragment;
+    private ShadowBluetoothAdapter mShadowBluetoothAdapter;
+
+    @Before
+    public void setUp() {
+        mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+        mShadowBluetoothAdapter.setEnabled(true);
+        mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
+                BluetoothStatusCodes.FEATURE_SUPPORTED);
+        mShadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
+                BluetoothStatusCodes.FEATURE_SUPPORTED);
+        mFragment = new AudioSharingStopDialogFragment();
+        mParent = new Fragment();
+        FragmentController.setupFragment(
+                mParent, FragmentActivity.class, /* containerViewId= */ 0, /* bundle= */ null);
+    }
+
+    @After
+    public void tearDown() {
+        ShadowAlertDialogCompat.reset();
+    }
+
+    @Test
+    @RequiresFlagsDisabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
+    public void onCreateDialog_flagOff_dialogNotExist() {
+        mFragment.show(mParent, TEST_DEVICE_NAME, () -> {});
+        shadowMainLooper().idle();
+        AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
+        assertThat(dialog).isNull();
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
+    public void onCreateDialog_clickCancel_dialogDismiss() {
+        mFragment.show(mParent, TEST_DEVICE_NAME, () -> {});
+        shadowMainLooper().idle();
+        AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
+        dialog.findViewById(android.R.id.button2).performClick();
+        shadowMainLooper().idle();
+        assertThat(dialog.isShowing()).isFalse();
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
+    public void onCreateDialog_clickShare_callbackTriggered() {
+        AtomicBoolean isStopBtnClicked = new AtomicBoolean(false);
+        mFragment.show(mParent, TEST_DEVICE_NAME, () -> isStopBtnClicked.set(true));
+        shadowMainLooper().idle();
+        AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
+        dialog.findViewById(android.R.id.button1).performClick();
+        shadowMainLooper().idle();
+        assertThat(dialog.isShowing()).isFalse();
+        assertThat(isStopBtnClicked.get()).isTrue();
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/BluetoothUpdateWorkerTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/BluetoothUpdateWorkerTest.java
index e34737b..f306693 100644
--- a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/BluetoothUpdateWorkerTest.java
+++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/BluetoothUpdateWorkerTest.java
@@ -29,6 +29,7 @@
 import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.robolectric.RobolectricTestRunner;
@@ -60,6 +61,7 @@
         verify(mResolver).notifyChange(URI, null);
     }
 
+    @Ignore("b/315399487")
     @Test
     public void onActiveDeviceChanged_shouldNotifyChange() {
         mBluetoothUpdateWorker.onActiveDeviceChanged(null, 0);
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppInstallerInfoPreferenceTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppInstallerInfoPreferenceTest.kt
index d1c318a..31722c9 100644
--- a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppInstallerInfoPreferenceTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppInstallerInfoPreferenceTest.kt
@@ -21,14 +21,11 @@
 import android.content.pm.ApplicationInfo
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.test.assertIsDisplayed
-import androidx.compose.ui.test.assertIsEnabled
 import androidx.compose.ui.test.assertIsNotDisplayed
-import androidx.compose.ui.test.assertIsNotEnabled
 import androidx.compose.ui.test.hasText
 import androidx.compose.ui.test.isEnabled
+import androidx.compose.ui.test.isNotEnabled
 import androidx.compose.ui.test.junit4.createComposeRule
-import androidx.compose.ui.test.onNodeWithText
 import androidx.compose.ui.test.onRoot
 import androidx.compose.ui.test.performClick
 import androidx.test.core.app.ApplicationProvider
@@ -122,9 +119,8 @@
             .thenReturn(null)
 
         setContent()
-        waitUntilDisplayed()
 
-        composeTestRule.onNode(preferenceNode).assertIsNotEnabled()
+        composeTestRule.waitUntilExists(preferenceNode.and(isNotEnabled()))
     }
 
     @Test
@@ -143,17 +139,16 @@
     @Test
     fun whenNotInstantApp() {
         setContent()
-        waitUntilDisplayed()
 
-        composeTestRule.onNodeWithText("App installed from installer label")
-            .assertIsDisplayed()
-            .assertIsEnabled()
+        composeTestRule.waitUntilExists(hasText("App installed from installer label"))
+        composeTestRule.waitUntilExists(preferenceNode.and(isEnabled()))
     }
 
     @Test
     fun whenClick_startActivity() {
         setContent()
-        waitUntilDisplayed()
+        composeTestRule.delay()
+
         composeTestRule.onRoot().performClick()
         composeTestRule.delay()
 
@@ -168,14 +163,10 @@
         }
     }
 
-    private fun waitUntilDisplayed() {
-        composeTestRule.waitUntilExists(preferenceNode)
-    }
-
     private val preferenceNode = hasText(context.getString(R.string.app_install_details_title))
 
     private companion object {
-        const val PACKAGE_NAME = "packageName"
+        const val PACKAGE_NAME = "package.name"
         const val INSTALLER_PACKAGE_NAME = "installer"
         const val INSTALLER_PACKAGE_LABEL = "installer label"
         val STORE_LINK = Intent("store/link")