Merge "[Audiosharing] Update media devices section." into main
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 85a443e..f88c55d 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -1741,6 +1741,8 @@
                         android:targetActivity=".spa.SpaBridgeActivity">
             <meta-data android:name="com.android.settings.spa.DESTINATION"
                        android:value="TogglePermissionAppList/UseFullScreenIntent"/>
+            <meta-data android:name="com.android.settings.HIGHLIGHT_MENU_KEY"
+                android:value="@string/menu_key_apps"/>
         </activity-alias>
 
         <activity-alias android:name="AppManageFullScreenIntent"
@@ -1753,6 +1755,8 @@
             </intent-filter>
             <meta-data android:name="com.android.settings.spa.DESTINATION"
                        android:value="TogglePermissionAppInfoPage/UseFullScreenIntent"/>
+            <meta-data android:name="com.android.settings.HIGHLIGHT_MENU_KEY"
+                android:value="@string/menu_key_apps"/>
         </activity-alias>
 
         <activity android:name=".applications.InstalledAppOpenByDefaultActivity"
@@ -4967,6 +4971,8 @@
             </intent-filter>
             <meta-data android:name="com.android.settings.spa.DESTINATION"
                        android:value="UsageStats"/>
+            <meta-data android:name="com.android.settings.HIGHLIGHT_MENU_KEY"
+                android:value="@string/menu_key_apps"/>
         </activity-alias>
 
         <!-- [b/197780098] Disable eager initialization of Jetpack libraries. -->
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 6ed0d82..71a7b30 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -4786,6 +4786,10 @@
     <string name="accessibility_toggle_large_pointer_icon_title">Large mouse pointer</string>
     <!-- Summary for the accessibility preference for enabling/disabling large icons for mouse/trackpad pointers. [CHAR LIMIT=60] -->
     <string name="accessibility_toggle_large_pointer_icon_summary">Make the mouse pointer more noticeable</string>
+    <!-- Title for the accessibility preference for forcing all apps to use dark theme. [CHAR LIMIT=35] -->
+    <string name="accessibility_force_invert_title">Make all apps dark</string>
+    <!-- Summary for the accessibility preference for forcing all apps to use dark theme. [CHAR LIMIT=100] -->
+    <string name="accessibility_force_invert_summary">Applies to apps without their own dark theme. Some apps may have display issues, like inverted colors.</string>
     <!-- Title for the accessibility preference for disabling animations. [CHAR LIMIT=35] -->
     <string name="accessibility_disable_animations">Remove animations</string>
     <!-- Summary for the accessibility preference for disabling animations. [CHAR LIMIT=60] -->
@@ -6071,10 +6075,6 @@
     <!-- Backup and reset Settings screen --><skip />
     <!-- Backup and reset settings menu and activity title -->
     <string name="privacy_settings_title">Backup</string>
-    <!-- Summary for the Backup settings when it is turned on. -->
-    <string name="backup_summary_state_on">On</string>
-    <!-- Summary for the Backup settings when it is turned off. -->
-    <string name="backup_summary_state_off">Off</string>
     <!-- Backup section title -->
     <string name="backup_section_title">Backup &amp; restore</string>
     <!-- Personal data section title -->
diff --git a/res/xml/accessibility_color_and_motion.xml b/res/xml/accessibility_color_and_motion.xml
index 3b2e78d..a500b72 100644
--- a/res/xml/accessibility_color_and_motion.xml
+++ b/res/xml/accessibility_color_and_motion.xml
@@ -50,6 +50,14 @@
         settings:searchable="false"/>
 
     <SwitchPreferenceCompat
+        android:icon="@drawable/ic_dark_ui"
+        android:key="toggle_force_invert"
+        android:persistent="false"
+        android:summary="@string/accessibility_force_invert_summary"
+        android:title="@string/accessibility_force_invert_title"
+        settings:controller="com.android.settings.accessibility.ToggleForceInvertPreferenceController"/>
+
+    <SwitchPreferenceCompat
         android:icon="@drawable/ic_accessibility_animation"
         android:key="toggle_disable_animations"
         android:persistent="false"
diff --git a/res/xml/bluetooth_audio_streams.xml b/res/xml/bluetooth_audio_streams.xml
index b419eaa..95ee710 100644
--- a/res/xml/bluetooth_audio_streams.xml
+++ b/res/xml/bluetooth_audio_streams.xml
@@ -34,6 +34,7 @@
 
     <com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsProgressCategoryPreference
         android:key="audio_streams_nearby_category"
-        android:title="@string/audio_streams_pref_title" />
+        android:title="@string/audio_streams_pref_title"
+        settings:controller="com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsProgressCategoryController" />
 
 </PreferenceScreen>
\ No newline at end of file
diff --git a/src/com/android/settings/accessibility/ColorAndMotionFragment.java b/src/com/android/settings/accessibility/ColorAndMotionFragment.java
index 7933eb1..28c5335 100644
--- a/src/com/android/settings/accessibility/ColorAndMotionFragment.java
+++ b/src/com/android/settings/accessibility/ColorAndMotionFragment.java
@@ -21,11 +21,13 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.provider.Settings;
+import android.view.accessibility.Flags;
 
 import androidx.preference.Preference;
 import androidx.preference.PreferenceCategory;
 import androidx.preference.TwoStatePreference;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.settings.R;
 import com.android.settings.dashboard.DashboardFragment;
 import com.android.settings.search.BaseSearchIndexProvider;
@@ -47,6 +49,8 @@
     private static final String DISPLAY_DALTONIZER_PREFERENCE_SCREEN = "daltonizer_preference";
     private static final String TOGGLE_DISABLE_ANIMATIONS = "toggle_disable_animations";
     private static final String TOGGLE_LARGE_POINTER_ICON = "toggle_large_pointer_icon";
+    @VisibleForTesting
+    static final String TOGGLE_FORCE_INVERT = "toggle_force_invert";
 
     private Preference mDisplayDaltonizerPreferenceScreen;
     private TwoStatePreference mToggleDisableAnimationsPreference;
@@ -70,6 +74,9 @@
         mShortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED);
         mShortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE);
         mShortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS);
+        if (Flags.forceInvertColor()) {
+            mShortcutFeatureKeys.add(ToggleForceInvertPreferenceController.SETTINGS_KEY);
+        }
 
         mSettingsContentObserver = new AccessibilitySettingsContentObserver(new Handler());
         mSettingsContentObserver.registerKeysToObserverCallback(mShortcutFeatureKeys,
diff --git a/src/com/android/settings/accessibility/ToggleForceInvertPreferenceController.java b/src/com/android/settings/accessibility/ToggleForceInvertPreferenceController.java
new file mode 100644
index 0000000..3f2cc13
--- /dev/null
+++ b/src/com/android/settings/accessibility/ToggleForceInvertPreferenceController.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.accessibility;
+
+import android.content.Context;
+import android.provider.Settings;
+import android.view.accessibility.Flags;
+
+import androidx.annotation.VisibleForTesting;
+
+import com.android.settings.R;
+import com.android.settings.core.TogglePreferenceController;
+
+/** A toggle preference controller for force invert (force dark). */
+public class ToggleForceInvertPreferenceController extends TogglePreferenceController {
+
+    public static final String SETTINGS_KEY =
+            Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED;
+
+    @VisibleForTesting
+    static final int ON = 1;
+    @VisibleForTesting
+    static final int OFF = 0;
+
+    public ToggleForceInvertPreferenceController(Context context, String preferenceKey) {
+        super(context, preferenceKey);
+    }
+
+    @Override
+    public boolean isChecked() {
+        return Settings.Secure.getInt(mContext.getContentResolver(), SETTINGS_KEY, OFF) != OFF;
+    }
+
+    @Override
+    public boolean setChecked(boolean isChecked) {
+        return Settings.Secure.putInt(mContext.getContentResolver(),
+                SETTINGS_KEY, isChecked ? ON : OFF);
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        return Flags.forceInvertColor() ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
+    }
+
+    @Override
+    public int getSliceHighlightMenuRes() {
+        return R.string.menu_key_accessibility;
+    }
+}
diff --git a/src/com/android/settings/applications/AppInfoBase.java b/src/com/android/settings/applications/AppInfoBase.java
index 6f8597e..2c41be4 100644
--- a/src/com/android/settings/applications/AppInfoBase.java
+++ b/src/com/android/settings/applications/AppInfoBase.java
@@ -144,10 +144,14 @@
         if (mAppEntry != null) {
             // Get application info again to refresh changed properties of application
             try {
-                mPackageInfo = mPm.getPackageInfoAsUser(mAppEntry.info.packageName,
-                        PackageManager.MATCH_DISABLED_COMPONENTS |
-                                PackageManager.GET_SIGNING_CERTIFICATES |
-                                PackageManager.GET_PERMISSIONS, mUserId);
+                mPackageInfo = mPm.getPackageInfoAsUser(
+                        mAppEntry.info.packageName,
+                        PackageManager.PackageInfoFlags.of(
+                                PackageManager.MATCH_DISABLED_COMPONENTS
+                                        | PackageManager.GET_SIGNING_CERTIFICATES
+                                        | PackageManager.GET_PERMISSIONS
+                                        | PackageManager.MATCH_ARCHIVED_PACKAGES),
+                        mUserId);
             } catch (NameNotFoundException e) {
                 Log.e(TAG, "Exception when retrieving package:" + mAppEntry.info.packageName, e);
             }
diff --git a/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java
index e771ff4..82d55f3 100644
--- a/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java
+++ b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java
@@ -731,10 +731,12 @@
             try {
                 mPackageInfo = activity.getPackageManager().getPackageInfo(
                         mAppEntry.info.packageName,
-                        PackageManager.MATCH_DISABLED_COMPONENTS |
-                                PackageManager.MATCH_ANY_USER |
-                                PackageManager.GET_SIGNATURES |
-                                PackageManager.GET_PERMISSIONS);
+                        PackageManager.PackageInfoFlags.of(
+                                PackageManager.MATCH_DISABLED_COMPONENTS
+                                        | PackageManager.MATCH_ANY_USER
+                                        | PackageManager.GET_SIGNATURES
+                                        | PackageManager.GET_PERMISSIONS
+                                        | PackageManager.MATCH_ARCHIVED_PACKAGES));
             } catch (NameNotFoundException e) {
                 Log.e(TAG, "Exception when retrieving package:" + mAppEntry.info.packageName, e);
             }
diff --git a/src/com/android/settings/backup/BackupSettingsHelper.java b/src/com/android/settings/backup/BackupSettingsHelper.java
index b55172e..258f54d 100644
--- a/src/com/android/settings/backup/BackupSettingsHelper.java
+++ b/src/com/android/settings/backup/BackupSettingsHelper.java
@@ -24,7 +24,6 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
-import android.os.UserManager;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -51,24 +50,6 @@
     }
 
     /**
-     * If there is only one profile, show whether the backup is on or off.
-     * Otherwise, show nothing.
-     */
-    public String getSummary() {
-        UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
-        if (userManager.getUserProfiles().size() == 1) {
-            try {
-                int resId = mBackupManager.isBackupEnabled()
-                        ? R.string.backup_summary_state_on : R.string.backup_summary_state_off;
-                return mContext.getText(resId).toString();
-            } catch (RemoteException e) {
-                Log.e(TAG, "Error getting isBackupEnabled", e);
-            }
-        }
-        return null;
-    }
-
-    /**
      * Returns an intent to launch backup settings from backup transport if the intent was provided
      * by the transport. Otherwise returns the intent to launch the default backup settings screen.
      *
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java
index 483a74b..9329cc29 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java
@@ -27,6 +27,8 @@
 import android.util.Log;
 
 import androidx.annotation.NonNull;
+import androidx.fragment.app.DialogFragment;
+import androidx.fragment.app.Fragment;
 import androidx.lifecycle.DefaultLifecycleObserver;
 import androidx.lifecycle.LifecycleOwner;
 import androidx.preference.Preference;
@@ -39,6 +41,7 @@
 import com.android.settings.core.BasePreferenceController;
 import com.android.settings.dashboard.DashboardFragment;
 import com.android.settingslib.bluetooth.BluetoothCallback;
+import com.android.settingslib.bluetooth.BluetoothUtils;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 import com.android.settingslib.bluetooth.LeAudioProfile;
 import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
@@ -54,6 +57,7 @@
 
 public class AudioSharingDevicePreferenceController extends BasePreferenceController
         implements DefaultLifecycleObserver, DevicePreferenceCallback, BluetoothCallback {
+    private static final boolean DEBUG = BluetoothUtils.D;
 
     private static final String TAG = "AudioSharingDevicePrefController";
     private static final String KEY = "audio_sharing_device_list";
@@ -372,19 +376,31 @@
                             + " non le audio device");
             return;
         }
+        if (DEBUG) {
+            Log.d(
+                    TAG,
+                    "Start handling onProfileConnectionStateChanged for "
+                            + cachedDevice.getDevice().getAnonymizedAddress());
+        }
         if (!isLeAudioSupported) {
             // Handle connected ineligible (non LE audio) remote device
             if (isBroadcasting()) {
                 // Show stop audio sharing dialog when an ineligible (non LE audio) remote device
                 // connected during a sharing session.
+                closeOpeningDialogs();
                 AudioSharingStopDialogFragment.show(
                         mFragment,
                         cachedDevice.getName(),
-                        () -> {
-                            mBroadcast.stopBroadcast(mBroadcast.getLatestBroadcastId());
-                        });
+                        () -> mBroadcast.stopBroadcast(mBroadcast.getLatestBroadcastId()));
+            } else {
+                // Do nothing for ineligible (non LE audio) remote device when no sharing session.
+                if (DEBUG) {
+                    Log.d(
+                            TAG,
+                            "Ignore onProfileConnectionStateChanged for non LE audio without"
+                                + " sharing session");
+                }
             }
-            // Do nothing for ineligible (non LE audio) remote device when no sharing session.
         } else {
             Map<Integer, List<CachedBluetoothDevice>> groupedDevices =
                     AudioSharingUtils.fetchConnectedDevicesByGroupId(mLocalBtManager);
@@ -398,6 +414,7 @@
                 // Show audio sharing switch dialog when the third eligible (LE audio) remote device
                 // connected during a sharing session.
                 if (deviceItemsInSharingSession.size() >= 2) {
+                    closeOpeningDialogs();
                     AudioSharingDisconnectDialogFragment.show(
                             mFragment,
                             deviceItemsInSharingSession,
@@ -421,6 +438,7 @@
                 } else {
                     // Show audio sharing join dialog when the first or second eligible (LE audio)
                     // remote device connected during a sharing session.
+                    closeOpeningDialogs();
                     AudioSharingJoinDialogFragment.show(
                             mFragment,
                             deviceItemsInSharingSession,
@@ -447,6 +465,7 @@
                 // Show audio sharing join dialog when the second eligible (LE audio) remote device
                 // connect and no sharing session.
                 if (deviceItems.size() == 1) {
+                    closeOpeningDialogs();
                     AudioSharingJoinDialogFragment.show(
                             mFragment,
                             deviceItems,
@@ -524,4 +543,15 @@
             mAssistant.addSource(sink, broadcastMetadata, /* isGroupOp= */ false);
         }
     }
+
+    private void closeOpeningDialogs() {
+        if (mFragment == null) return;
+        List<Fragment> fragments = mFragment.getChildFragmentManager().getFragments();
+        for (Fragment fragment : fragments) {
+            if (fragment instanceof DialogFragment) {
+                Log.d(TAG, "Remove staled opening dialog " + fragment.getTag());
+                ((DialogFragment) fragment).dismiss();
+            }
+        }
+    }
 }
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragment.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragment.java
index 365aed5..461c230 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragment.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragment.java
@@ -77,15 +77,12 @@
         if (!AudioSharingUtils.isFeatureEnabled()) return;
         final FragmentManager manager = host.getChildFragmentManager();
         sListener = listener;
-        if (manager.findFragmentByTag(TAG) == null) {
-            final Bundle bundle = new Bundle();
-            bundle.putParcelableArrayList(BUNDLE_KEY_DEVICE_TO_DISCONNECT_ITEMS, deviceItems);
-            bundle.putString(BUNDLE_KEY_NEW_DEVICE_NAME, newDeviceName);
-            AudioSharingDisconnectDialogFragment dialog =
-                    new AudioSharingDisconnectDialogFragment();
-            dialog.setArguments(bundle);
-            dialog.show(manager, TAG);
-        }
+        final Bundle bundle = new Bundle();
+        bundle.putParcelableArrayList(BUNDLE_KEY_DEVICE_TO_DISCONNECT_ITEMS, deviceItems);
+        bundle.putString(BUNDLE_KEY_NEW_DEVICE_NAME, newDeviceName);
+        AudioSharingDisconnectDialogFragment dialog = new AudioSharingDisconnectDialogFragment();
+        dialog.setArguments(bundle);
+        dialog.show(manager, TAG);
     }
 
     @Override
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragment.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragment.java
index 589c955..2d7b4c4 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragment.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragment.java
@@ -71,14 +71,12 @@
         if (!AudioSharingUtils.isFeatureEnabled()) return;
         final FragmentManager manager = host.getChildFragmentManager();
         sListener = listener;
-        if (manager.findFragmentByTag(TAG) == null) {
-            final Bundle bundle = new Bundle();
-            bundle.putParcelableArrayList(BUNDLE_KEY_DEVICE_ITEMS, deviceItems);
-            bundle.putString(BUNDLE_KEY_NEW_DEVICE_NAME, newDeviceName);
-            final AudioSharingJoinDialogFragment dialog = new AudioSharingJoinDialogFragment();
-            dialog.setArguments(bundle);
-            dialog.show(manager, TAG);
-        }
+        final Bundle bundle = new Bundle();
+        bundle.putParcelableArrayList(BUNDLE_KEY_DEVICE_ITEMS, deviceItems);
+        bundle.putString(BUNDLE_KEY_NEW_DEVICE_NAME, newDeviceName);
+        final AudioSharingJoinDialogFragment dialog = new AudioSharingJoinDialogFragment();
+        dialog.setArguments(bundle);
+        dialog.show(manager, TAG);
     }
 
     @Override
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragment.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragment.java
index 13416aa..31125de 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragment.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragment.java
@@ -56,13 +56,11 @@
         if (!AudioSharingUtils.isFeatureEnabled()) return;
         final FragmentManager manager = host.getChildFragmentManager();
         sListener = listener;
-        if (manager.findFragmentByTag(TAG) == null) {
-            final Bundle bundle = new Bundle();
-            bundle.putString(BUNDLE_KEY_NEW_DEVICE_NAME, newDeviceName);
-            AudioSharingStopDialogFragment dialog = new AudioSharingStopDialogFragment();
-            dialog.setArguments(bundle);
-            dialog.show(manager, TAG);
-        }
+        final Bundle bundle = new Bundle();
+        bundle.putString(BUNDLE_KEY_NEW_DEVICE_NAME, newDeviceName);
+        AudioSharingStopDialogFragment dialog = new AudioSharingStopDialogFragment();
+        dialog.setArguments(bundle);
+        dialog.show(manager, TAG);
     }
 
     @Override
diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamPreference.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamPreference.java
index 6809c7d..8f701a3 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamPreference.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamPreference.java
@@ -19,6 +19,8 @@
 import android.content.Context;
 import android.util.AttributeSet;
 
+import androidx.annotation.Nullable;
+
 import com.android.settings.R;
 import com.android.settingslib.widget.TwoTargetPreference;
 
@@ -27,25 +29,35 @@
  * {@link TwoTargetPreference}.
  */
 public class AudioStreamPreference extends TwoTargetPreference {
-    private boolean mShowLock = true;
+    private boolean mIsConnected = false;
 
     /**
-     * Sets whether to display the lock icon.
+     * Update preference UI based on connection status
      *
-     * @param showLock Should show / hide the lock icon
+     * @param isConnected Is this streams connected
      */
-    public void setShowLock(boolean showLock) {
-        mShowLock = showLock;
+    public void setIsConnected(
+            boolean isConnected, @Nullable OnPreferenceClickListener onPreferenceClickListener) {
+        if (mIsConnected == isConnected
+                && getOnPreferenceClickListener() == onPreferenceClickListener) {
+            // Nothing to update.
+            return;
+        }
+        mIsConnected = isConnected;
+        setSummary(isConnected ? "Listening now" : "");
+        setOrder(isConnected ? 0 : 1);
+        setOnPreferenceClickListener(onPreferenceClickListener);
         notifyChanged();
     }
 
-    public AudioStreamPreference(Context context, AttributeSet attrs) {
+    public AudioStreamPreference(Context context, @Nullable AttributeSet attrs) {
         super(context, attrs);
+        setIcon(R.drawable.ic_bt_audio_sharing);
     }
 
     @Override
     protected boolean shouldHideSecondTarget() {
-        return !mShowLock;
+        return mIsConnected;
     }
 
     @Override
diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsBroadcastAssistantCallback.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsBroadcastAssistantCallback.java
new file mode 100644
index 0000000..788b253
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsBroadcastAssistantCallback.java
@@ -0,0 +1,144 @@
+/*
+ * 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 android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothLeBroadcastAssistant;
+import android.bluetooth.BluetoothLeBroadcastMetadata;
+import android.bluetooth.BluetoothLeBroadcastReceiveState;
+import android.util.Log;
+
+import com.android.settingslib.bluetooth.BluetoothUtils;
+
+import java.util.Locale;
+
+public class AudioStreamsBroadcastAssistantCallback
+        implements BluetoothLeBroadcastAssistant.Callback {
+
+    private static final String TAG = "AudioStreamsBroadcastAssistantCallback";
+    private static final boolean DEBUG = BluetoothUtils.D;
+
+    private AudioStreamsProgressCategoryController mCategoryController;
+
+    public AudioStreamsBroadcastAssistantCallback(
+            AudioStreamsProgressCategoryController audioStreamsProgressCategoryController) {
+        mCategoryController = audioStreamsProgressCategoryController;
+    }
+
+    @Override
+    public void onReceiveStateChanged(
+            BluetoothDevice sink, int sourceId, BluetoothLeBroadcastReceiveState state) {
+        if (DEBUG) {
+            Log.d(
+                    TAG,
+                    "onReceiveStateChanged() sink : "
+                            + sink.getAddress()
+                            + " sourceId: "
+                            + sourceId
+                            + " state: "
+                            + state);
+        }
+    }
+
+    @Override
+    public void onSearchStartFailed(int reason) {
+        Log.w(TAG, "onSearchStartFailed() reason : " + reason);
+        mCategoryController.showToast(
+                String.format(Locale.US, "Failed to start scanning, reason %d", reason));
+    }
+
+    @Override
+    public void onSearchStarted(int reason) {
+        if (mCategoryController == null) {
+            Log.w(TAG, "onSearchStarted() : mCategoryController is null!");
+            return;
+        }
+        if (DEBUG) {
+            Log.d(TAG, "onSearchStarted() reason : " + reason);
+        }
+        mCategoryController.setScanning(true);
+    }
+
+    @Override
+    public void onSearchStopFailed(int reason) {
+        Log.w(TAG, "onSearchStopFailed() reason : " + reason);
+        mCategoryController.showToast(
+                String.format(Locale.US, "Failed to stop scanning, reason %d", reason));
+    }
+
+    @Override
+    public void onSearchStopped(int reason) {
+        if (mCategoryController == null) {
+            Log.w(TAG, "onSearchStopped() : mCategoryController is null!");
+            return;
+        }
+        if (DEBUG) {
+            Log.d(TAG, "onSearchStopped() reason : " + reason);
+        }
+        mCategoryController.setScanning(false);
+    }
+
+    @Override
+    public void onSourceAddFailed(
+            BluetoothDevice sink, BluetoothLeBroadcastMetadata source, int reason) {}
+
+    @Override
+    public void onSourceAdded(BluetoothDevice sink, int sourceId, int reason) {
+        if (DEBUG) {
+            Log.d(
+                    TAG,
+                    "onSourceAdded() sink : "
+                            + sink.getAddress()
+                            + " sourceId: "
+                            + sourceId
+                            + " reason: "
+                            + reason);
+        }
+    }
+
+    @Override
+    public void onSourceFound(BluetoothLeBroadcastMetadata source) {
+        if (mCategoryController == null) {
+            Log.w(TAG, "onSourceFound() : mCategoryController is null!");
+            return;
+        }
+        if (DEBUG) {
+            Log.d(TAG, "onSourceFound() broadcastId : " + source.getBroadcastId());
+        }
+        mCategoryController.addSourceFound(source);
+    }
+
+    @Override
+    public void onSourceLost(int broadcastId) {
+        if (DEBUG) {
+            Log.d(TAG, "onSourceLost() broadcastId : " + broadcastId);
+        }
+        mCategoryController.removeSourceLost(broadcastId);
+    }
+
+    @Override
+    public void onSourceModified(BluetoothDevice sink, int sourceId, int reason) {}
+
+    @Override
+    public void onSourceModifyFailed(BluetoothDevice sink, int sourceId, int reason) {}
+
+    @Override
+    public void onSourceRemoveFailed(BluetoothDevice sink, int sourceId, int reason) {}
+
+    @Override
+    public void onSourceRemoved(BluetoothDevice sink, int sourceId, int reason) {}
+}
diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryController.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryController.java
new file mode 100644
index 0000000..fef1e7b
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryController.java
@@ -0,0 +1,265 @@
+/*
+ * 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.BluetoothLeAudioContentMetadata;
+import android.bluetooth.BluetoothLeBroadcastMetadata;
+import android.bluetooth.BluetoothLeBroadcastReceiveState;
+import android.content.Context;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.lifecycle.DefaultLifecycleObserver;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.bluetooth.Utils;
+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;
+
+public class AudioStreamsProgressCategoryController extends BasePreferenceController
+        implements DefaultLifecycleObserver {
+    private static final String TAG = "AudioStreamsProgressCategoryController";
+    private static final boolean DEBUG = BluetoothUtils.D;
+
+    private final Executor mExecutor;
+    private final AudioStreamsBroadcastAssistantCallback mBroadcastAssistantCallback;
+    private final LocalBluetoothManager mBluetoothManager;
+    private final @Nullable LocalBluetoothLeBroadcastAssistant mLeBroadcastAssistant;
+    private final ConcurrentHashMap<Integer, AudioStreamPreference> mBroadcastIdToPreferenceMap =
+            new ConcurrentHashMap<>();
+    private @Nullable AudioStreamsProgressCategoryPreference mCategoryPreference;
+
+    public AudioStreamsProgressCategoryController(Context context, String preferenceKey) {
+        super(context, preferenceKey);
+        mExecutor = Executors.newSingleThreadExecutor();
+        mBluetoothManager = Utils.getLocalBtManager(mContext);
+        mLeBroadcastAssistant = getLeBroadcastAssistant(mBluetoothManager);
+        mBroadcastAssistantCallback = new AudioStreamsBroadcastAssistantCallback(this);
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        return AVAILABLE;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        mCategoryPreference = screen.findPreference(getPreferenceKey());
+    }
+
+    @Override
+    public void onStart(@NonNull LifecycleOwner owner) {
+        if (mLeBroadcastAssistant == null) {
+            Log.w(TAG, "onStart(): LeBroadcastAssistant is null!");
+            return;
+        }
+        mBroadcastIdToPreferenceMap.clear();
+        if (mCategoryPreference != null) {
+            mCategoryPreference.removeAll();
+        }
+        mExecutor.execute(
+                () -> {
+                    mLeBroadcastAssistant.registerServiceCallBack(
+                            mExecutor, mBroadcastAssistantCallback);
+                    if (DEBUG) {
+                        Log.d(TAG, "scanAudioStreamsStart()");
+                    }
+                    mLeBroadcastAssistant.startSearchingForSources(emptyList());
+                    // Display currently connected streams
+                    var unused =
+                            ThreadUtils.postOnBackgroundThread(
+                                    () -> {
+                                        for (var sink :
+                                                getActiveSinksOnAssistant(mBluetoothManager)) {
+                                            mLeBroadcastAssistant
+                                                    .getAllSources(sink)
+                                                    .forEach(this::addSourceConnected);
+                                        }
+                                    });
+                });
+    }
+
+    @Override
+    public void onStop(@NonNull LifecycleOwner owner) {
+        if (mLeBroadcastAssistant == null) {
+            Log.w(TAG, "onStop(): LeBroadcastAssistant is null!");
+            return;
+        }
+        mExecutor.execute(
+                () -> {
+                    if (mLeBroadcastAssistant.isSearchInProgress()) {
+                        if (DEBUG) {
+                            Log.d(TAG, "scanAudioStreamsStop()");
+                        }
+                        mLeBroadcastAssistant.stopSearchingForSources();
+                    }
+                    mLeBroadcastAssistant.unregisterServiceCallBack(mBroadcastAssistantCallback);
+                });
+    }
+
+    void setScanning(boolean isScanning) {
+        ThreadUtils.postOnMainThread(
+                () -> {
+                    if (mCategoryPreference != null) mCategoryPreference.setProgress(isScanning);
+                });
+    }
+
+    void addSourceFound(BluetoothLeBroadcastMetadata source) {
+        Preference.OnPreferenceClickListener onClickListener =
+                preference -> {
+                    if (DEBUG) {
+                        Log.d(TAG, "preferenceClicked(): attempt to join broadcast");
+                    }
+
+                    // TODO(chelseahao): add source to sink
+                    return true;
+                };
+        mBroadcastIdToPreferenceMap.computeIfAbsent(
+                source.getBroadcastId(),
+                k -> {
+                    var p = createPreference(source, onClickListener);
+                    ThreadUtils.postOnMainThread(
+                            () -> {
+                                if (mCategoryPreference != null) {
+                                    mCategoryPreference.addPreference(p);
+                                }
+                            });
+                    return p;
+                });
+    }
+
+    void removeSourceLost(int broadcastId) {
+        var toRemove = mBroadcastIdToPreferenceMap.remove(broadcastId);
+        if (toRemove != null) {
+            ThreadUtils.postOnMainThread(
+                    () -> {
+                        if (mCategoryPreference != null) {
+                            mCategoryPreference.removePreference(toRemove);
+                        }
+                    });
+        }
+        // TODO(chelseahao): remove source from sink
+    }
+
+    private void addSourceConnected(BluetoothLeBroadcastReceiveState state) {
+        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;
+                    }
+                });
+    }
+
+    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);
+    }
+}
diff --git a/src/com/android/settings/datausage/lib/AppPreferenceRepository.kt b/src/com/android/settings/datausage/lib/AppPreferenceRepository.kt
index a71bc8f..3bfb24a 100644
--- a/src/com/android/settings/datausage/lib/AppPreferenceRepository.kt
+++ b/src/com/android/settings/datausage/lib/AppPreferenceRepository.kt
@@ -19,10 +19,13 @@
 import android.content.Context
 import android.content.pm.PackageManager
 import android.os.UserHandle
+import android.util.IconDrawableFactory
 import androidx.preference.Preference
-import com.android.settingslib.Utils
 
-class AppPreferenceRepository(private val context: Context) {
+class AppPreferenceRepository(
+    private val context: Context,
+    private val iconDrawableFactory: IconDrawableFactory = IconDrawableFactory.newInstance(context),
+) {
     private val packageManager = context.packageManager
 
     fun loadAppPreferences(uids: List<Int>): List<Preference> = uids.flatMap { uid ->
@@ -38,7 +41,7 @@
     private fun getPreference(packageName: String, userId: Int): Preference? = try {
         val app = packageManager.getApplicationInfoAsUser(packageName, 0, userId)
         Preference(context).apply {
-            icon = Utils.getBadgedIcon(context, app)
+            icon = iconDrawableFactory.getBadgedIcon(app)
             title = app.loadLabel(packageManager)
             isSelectable = false
         }
diff --git a/src/com/android/settings/inputmethod/NewKeyboardLayoutPickerController.java b/src/com/android/settings/inputmethod/NewKeyboardLayoutPickerController.java
index 879f134..9545276 100644
--- a/src/com/android/settings/inputmethod/NewKeyboardLayoutPickerController.java
+++ b/src/com/android/settings/inputmethod/NewKeyboardLayoutPickerController.java
@@ -83,6 +83,7 @@
         mFinalSelectedLayout = mLayout;
         mKeyboardLayouts = mIm.getKeyboardLayoutListForInputDevice(
                 mInputDeviceIdentifier, mUserId, mInputMethodInfo, mInputMethodSubtype);
+        NewKeyboardSettingsUtils.sortKeyboardLayoutsByLabel(mKeyboardLayouts);
         parent.getActivity().setTitle(mTitle);
     }
 
diff --git a/src/com/android/settings/inputmethod/NewKeyboardSettingsUtils.java b/src/com/android/settings/inputmethod/NewKeyboardSettingsUtils.java
index f3dc0a3..a927165 100644
--- a/src/com/android/settings/inputmethod/NewKeyboardSettingsUtils.java
+++ b/src/com/android/settings/inputmethod/NewKeyboardSettingsUtils.java
@@ -27,6 +27,8 @@
 import android.view.inputmethod.InputMethodSubtype;
 
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
 import java.util.List;
 
 /**
@@ -123,4 +125,11 @@
             InputDeviceIdentifier identifier, InputMethodInfo info, InputMethodSubtype subtype) {
         return inputManager.getKeyboardLayoutForInputDevice(identifier, userId, info, subtype);
     }
+
+    static void sortKeyboardLayoutsByLabel(KeyboardLayout[] keyboardLayouts) {
+        Arrays.sort(
+                keyboardLayouts,
+                Comparator.comparing(KeyboardLayout::getLabel)
+        );
+    }
 }
diff --git a/src/com/android/settings/network/telephony/ConvertToEsimPreferenceController.java b/src/com/android/settings/network/telephony/ConvertToEsimPreferenceController.java
index b4e768c..a17144f 100644
--- a/src/com/android/settings/network/telephony/ConvertToEsimPreferenceController.java
+++ b/src/com/android/settings/network/telephony/ConvertToEsimPreferenceController.java
@@ -19,7 +19,19 @@
 import static androidx.lifecycle.Lifecycle.Event.ON_START;
 import static androidx.lifecycle.Lifecycle.Event.ON_STOP;
 
+import android.Manifest;
+import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ComponentInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.service.euicc.EuiccService;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.euicc.EuiccManager;
 import android.text.TextUtils;
 
 import androidx.annotation.VisibleForTesting;
@@ -29,12 +41,12 @@
 import androidx.preference.Preference;
 import androidx.preference.PreferenceScreen;
 
+import com.android.internal.telephony.util.TelephonyUtils;
 import com.android.settings.network.MobileNetworkRepository;
 import com.android.settingslib.core.lifecycle.Lifecycle;
-import com.android.settingslib.mobile.dataservice.DataServiceUtils;
-import com.android.settingslib.mobile.dataservice.MobileNetworkInfoEntity;
 import com.android.settingslib.mobile.dataservice.SubscriptionInfoEntity;
-import com.android.settingslib.mobile.dataservice.UiccInfoEntity;
+
+import org.jetbrains.annotations.NotNull;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -47,6 +59,9 @@
     private MobileNetworkRepository mMobileNetworkRepository;
     private List<SubscriptionInfoEntity> mSubscriptionInfoEntityList = new ArrayList<>();
     private SubscriptionInfoEntity mSubscriptionInfoEntity;
+    private static int sQueryFlag =
+            PackageManager.MATCH_SYSTEM_ONLY | PackageManager.MATCH_DIRECT_BOOT_AUTO
+                    | PackageManager.GET_RESOLVED_FILTER;
 
     public ConvertToEsimPreferenceController(Context context, String key, Lifecycle lifecycle,
             LifecycleOwner lifecycleOwner, int subId) {
@@ -83,11 +98,14 @@
 
     @Override
     public int getAvailabilityStatus(int subId) {
-        return CONDITIONALLY_UNAVAILABLE;
         // TODO(b/262195754): Need the intent to enabled the feature.
-//        return mSubscriptionInfoEntity != null && mSubscriptionInfoEntity.isActiveSubscriptionId
-//                && !mSubscriptionInfoEntity.isEmbedded ? AVAILABLE
-//                : CONDITIONALLY_UNAVAILABLE;
+        if (findConversionSupportComponent()) {
+            return mSubscriptionInfoEntity != null && mSubscriptionInfoEntity.isActiveSubscriptionId
+                    && !mSubscriptionInfoEntity.isEmbedded && isActiveSubscription(subId)
+                    ? AVAILABLE
+                    : CONDITIONALLY_UNAVAILABLE;
+        }
+        return CONDITIONALLY_UNAVAILABLE;
     }
 
     @VisibleForTesting
@@ -104,6 +122,9 @@
             return false;
         }
         // Send intent to launch LPA
+        Intent intent = new Intent(EuiccManager.ACTION_CONVERT_TO_EMBEDDED_SUBSCRIPTION);
+        intent.putExtra("subId", mSubId);
+        mContext.startActivity(intent);
         return true;
     }
 
@@ -115,12 +136,73 @@
     @Override
     public void onActiveSubInfoChanged(List<SubscriptionInfoEntity> subInfoEntityList) {
         // TODO(b/262195754): Need the intent to enabled the feature.
-//        mSubscriptionInfoEntityList = subInfoEntityList;
-//        mSubscriptionInfoEntityList.forEach(entity -> {
-//            if (Integer.parseInt(entity.subId) == mSubId) {
-//                mSubscriptionInfoEntity = entity;
-//                update();
-//            }
-//        });
+        mSubscriptionInfoEntityList = subInfoEntityList;
+        mSubscriptionInfoEntityList.forEach(entity -> {
+            if (Integer.parseInt(entity.subId) == mSubId) {
+                mSubscriptionInfoEntity = entity;
+                update();
+            }
+        });
+    }
+
+    private boolean isActiveSubscription(int subId) {
+        SubscriptionManager subscriptionManager = mContext.getSystemService(
+                SubscriptionManager.class);
+        SubscriptionInfo subInfo = subscriptionManager.getActiveSubscriptionInfo(subId);
+        if (subInfo == null) {
+            return false;
+        }
+        return true;
+    }
+
+    private boolean findConversionSupportComponent() {
+        Intent intent = new Intent(EuiccService.ACTION_CONVERT_TO_EMBEDDED_SUBSCRIPTION);
+        PackageManager packageManager = mContext.getPackageManager();
+        List<ResolveInfo> resolveInfoList = packageManager
+                .queryIntentActivities(intent, sQueryFlag);
+        if (resolveInfoList == null || resolveInfoList.isEmpty()) {
+            return false;
+        }
+        for (ResolveInfo resolveInfo : resolveInfoList) {
+            if (!isValidEuiccComponent(packageManager, resolveInfo)) {
+                continue;
+            } else {
+                return true;
+            }
+        }
+        return true;
+    }
+
+    private boolean isValidEuiccComponent(
+            PackageManager packageManager, @NotNull ResolveInfo resolveInfo) {
+        ComponentInfo componentInfo = TelephonyUtils.getComponentInfo(resolveInfo);
+        String packageName = new ComponentName(componentInfo.packageName, componentInfo.name)
+                .getPackageName();
+
+        // Verify that the app is privileged (via granting of a privileged permission).
+        if (packageManager.checkPermission(
+                Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS, packageName)
+                != PackageManager.PERMISSION_GRANTED) {
+            return false;
+        }
+
+        // Verify that only the system can access the component.
+        final String permission;
+        if (componentInfo instanceof ServiceInfo) {
+            permission = ((ServiceInfo) componentInfo).permission;
+        } else if (componentInfo instanceof ActivityInfo) {
+            permission = ((ActivityInfo) componentInfo).permission;
+        } else {
+            return false;
+        }
+        if (!TextUtils.equals(permission, Manifest.permission.BIND_EUICC_SERVICE)) {
+            return false;
+        }
+
+        // Verify that the component declares a priority.
+        if (resolveInfo.filter == null || resolveInfo.filter.getPriority() == 0) {
+            return false;
+        }
+        return true;
     }
 }
diff --git a/src/com/android/settings/spa/SpaAppBridgeActivity.kt b/src/com/android/settings/spa/SpaAppBridgeActivity.kt
index 1a77442..a68d220 100644
--- a/src/com/android/settings/spa/SpaAppBridgeActivity.kt
+++ b/src/com/android/settings/spa/SpaAppBridgeActivity.kt
@@ -20,7 +20,6 @@
 import android.content.Intent
 import android.os.Bundle
 import android.os.UserHandle
-import com.android.settings.spa.SpaBridgeActivity.Companion.getDestination
 import com.android.settings.spa.SpaBridgeActivity.Companion.startSpaActivityFromBridge
 
 /**
@@ -33,11 +32,7 @@
 class SpaAppBridgeActivity : Activity() {
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
-        getDestination()?.let { destinationPrefix ->
-            getDestinationForApp(destinationPrefix, intent)?.let { destination ->
-                startSpaActivityFromBridge(destination)
-            }
-        }
+        startSpaActivityFromBridge { getDestinationForApp(it, intent) }
         finish()
     }
 
diff --git a/src/com/android/settings/spa/SpaBridgeActivity.kt b/src/com/android/settings/spa/SpaBridgeActivity.kt
index 0e239ae..61d8f51 100644
--- a/src/com/android/settings/spa/SpaBridgeActivity.kt
+++ b/src/com/android/settings/spa/SpaBridgeActivity.kt
@@ -18,12 +18,10 @@
 
 import android.app.Activity
 import android.content.Intent
-import android.content.pm.PackageManager
-import android.content.pm.PackageManager.ComponentInfoFlags
 import android.os.Bundle
-import androidx.annotation.VisibleForTesting
 import com.android.settings.activityembedding.ActivityEmbeddingUtils
 import com.android.settings.activityembedding.EmbeddedDeepLinkUtils.tryStartMultiPaneDeepLink
+import com.android.settings.spa.SpaDestination.Companion.getDestination
 import com.android.settingslib.spa.framework.util.SESSION_EXTERNAL
 import com.android.settingslib.spa.framework.util.appendSpaParams
 
@@ -37,29 +35,23 @@
 class SpaBridgeActivity : Activity() {
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
-        getDestination()?.let { destination ->
-            startSpaActivityFromBridge(destination)
-        }
+        startSpaActivityFromBridge()
         finish()
     }
 
     companion object {
-        fun Activity.startSpaActivityFromBridge(destination: String) {
+        fun Activity.startSpaActivityFromBridge(destinationFactory: (String) -> String? = { it }) {
+            val (destination, highlightMenuKey) = getDestination(destinationFactory) ?: return
             val intent = Intent(this, SpaActivity::class.java)
-                .appendSpaParams(destination = destination)
-                .appendSpaParams(sessionName = SESSION_EXTERNAL)
+                .appendSpaParams(
+                    destination = destination,
+                    sessionName = SESSION_EXTERNAL,
+                )
             if (!ActivityEmbeddingUtils.isEmbeddingActivityEnabled(this) ||
-                !tryStartMultiPaneDeepLink(intent)) {
+                !tryStartMultiPaneDeepLink(intent, highlightMenuKey)
+            ) {
                 startActivity(intent)
             }
         }
-
-        fun Activity.getDestination(): String? =
-            packageManager.getActivityInfo(
-                componentName, ComponentInfoFlags.of(PackageManager.GET_META_DATA.toLong())
-            ).metaData.getString(META_DATA_KEY_DESTINATION)
-
-        @VisibleForTesting
-        const val META_DATA_KEY_DESTINATION = "com.android.settings.spa.DESTINATION"
     }
 }
diff --git a/src/com/android/settings/spa/SpaDestination.kt b/src/com/android/settings/spa/SpaDestination.kt
new file mode 100644
index 0000000..bdec1d8
--- /dev/null
+++ b/src/com/android/settings/spa/SpaDestination.kt
@@ -0,0 +1,49 @@
+/*
+ * 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.spa
+
+import android.app.Activity
+import android.content.pm.PackageManager
+import androidx.annotation.VisibleForTesting
+import com.android.settings.SettingsActivity.META_DATA_KEY_HIGHLIGHT_MENU_KEY
+
+data class SpaDestination(
+    val destination: String,
+    val highlightMenuKey: String?,
+) {
+    companion object {
+        fun Activity.getDestination(
+            destinationFactory: (String) -> String? = { it },
+        ): SpaDestination? {
+            val metaData = packageManager.getActivityInfo(
+                componentName,
+                PackageManager.ComponentInfoFlags.of(PackageManager.GET_META_DATA.toLong())
+            ).metaData
+            val destination = metaData.getString(META_DATA_KEY_DESTINATION)
+            if (destination.isNullOrBlank()) return null
+            val finalDestination = destinationFactory(destination)
+            if (finalDestination.isNullOrBlank()) return null
+            return SpaDestination(
+                destination = finalDestination,
+                highlightMenuKey = metaData.getString(META_DATA_KEY_HIGHLIGHT_MENU_KEY),
+            )
+        }
+
+        @VisibleForTesting
+        const val META_DATA_KEY_DESTINATION = "com.android.settings.spa.DESTINATION"
+    }
+}
diff --git a/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt b/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt
index 6882963..85e59de 100644
--- a/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt
@@ -119,16 +119,19 @@
 
 @Composable
 private fun AppInfoSettings(packageInfoPresenter: PackageInfoPresenter) {
-    val packageInfo = packageInfoPresenter.flow.collectAsStateWithLifecycle().value ?:return
-    val app = checkNotNull(packageInfo.applicationInfo)
+    val packageInfoState = packageInfoPresenter.flow.collectAsStateWithLifecycle()
     val featureFlags: FeatureFlags = FeatureFlagsImpl()
     RegularScaffold(
         title = stringResource(R.string.application_info_label),
         actions = {
-            if (featureFlags.archiving()) TopBarAppLaunchButton(packageInfoPresenter, app)
-            AppInfoSettingsMoreOptions(packageInfoPresenter, app)
+            packageInfoState.value?.applicationInfo?.let { app ->
+                if (featureFlags.archiving()) TopBarAppLaunchButton(packageInfoPresenter, app)
+                AppInfoSettingsMoreOptions(packageInfoPresenter, app)
+            }
         }
     ) {
+        val packageInfo = packageInfoState.value ?: return@RegularScaffold
+        val app = packageInfo.applicationInfo ?: return@RegularScaffold
         val appInfoProvider = remember(packageInfo) { AppInfoProvider(packageInfo) }
 
         appInfoProvider.AppInfo()
diff --git a/src/com/android/settings/spa/app/appinfo/AppUninstallButton.kt b/src/com/android/settings/spa/app/appinfo/AppUninstallButton.kt
index 6b3535b..5f6f097 100644
--- a/src/com/android/settings/spa/app/appinfo/AppUninstallButton.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppUninstallButton.kt
@@ -23,8 +23,10 @@
 import android.os.UserHandle
 import android.os.UserManager
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
 import androidx.compose.ui.graphics.vector.ImageVector
 import androidx.compose.ui.res.vectorResource
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.settings.R
 import com.android.settings.Utils
 import com.android.settings.applications.specialaccess.deviceadmin.DeviceAdminAdd
@@ -33,6 +35,9 @@
 import com.android.settingslib.spaprivileged.model.app.hasFlag
 import com.android.settingslib.spaprivileged.model.app.isActiveAdmin
 import com.android.settingslib.spaprivileged.model.app.userHandle
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.flowOn
 
 class AppUninstallButton(private val packageInfoPresenter: PackageInfoPresenter) {
     private val context = packageInfoPresenter.context
@@ -43,7 +48,7 @@
     @Composable
     fun getActionButton(app: ApplicationInfo): ActionButton? {
         if (app.isSystemApp || app.isInstantApp) return null
-        return uninstallButton(app = app, enabled = isUninstallButtonEnabled(app))
+        return uninstallButton(app)
     }
 
     /** Gets whether a package can be uninstalled. */
@@ -90,11 +95,15 @@
             overlayManager.getOverlayInfo(packageName, userHandle)?.isEnabled == true
 
     @Composable
-    private fun uninstallButton(app: ApplicationInfo, enabled: Boolean) = ActionButton(
+    private fun uninstallButton(app: ApplicationInfo) = ActionButton(
         text = if (isCloneApp(app)) context.getString(R.string.delete) else
             context.getString(R.string.uninstall_text),
         imageVector = ImageVector.vectorResource(R.drawable.ic_settings_delete),
-        enabled = enabled,
+        enabled = remember(app) {
+            flow {
+                emit(isUninstallButtonEnabled(app))
+            }.flowOn(Dispatchers.Default)
+        }.collectAsStateWithLifecycle(false).value,
     ) { onUninstallClicked(app) }
 
     private fun onUninstallClicked(app: ApplicationInfo) {
diff --git a/src/com/android/settings/spa/app/appinfo/PackageInfoPresenter.kt b/src/com/android/settings/spa/app/appinfo/PackageInfoPresenter.kt
index 8d0f0bb..a6bd8f0 100644
--- a/src/com/android/settings/spa/app/appinfo/PackageInfoPresenter.kt
+++ b/src/com/android/settings/spa/app/appinfo/PackageInfoPresenter.kt
@@ -26,6 +26,7 @@
 import android.content.pm.PackageManager
 import android.os.UserHandle
 import android.util.Log
+import androidx.annotation.VisibleForTesting
 import androidx.compose.runtime.Composable
 import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
 import com.android.settings.spa.app.startUninstallActivity
@@ -40,6 +41,7 @@
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.merge
@@ -65,18 +67,42 @@
     val userContext by lazy { context.asUser(userHandle) }
     val userPackageManager: PackageManager by lazy { userContext.packageManager }
 
-    val flow: StateFlow<PackageInfo?> = merge(
-        flowOf(null), // kick an initial value
-        context.broadcastReceiverAsUserFlow(
-            intentFilter = IntentFilter().apply {
-                addAction(Intent.ACTION_PACKAGE_CHANGED)
-                addAction(Intent.ACTION_PACKAGE_REPLACED)
-                addAction(Intent.ACTION_PACKAGE_RESTARTED)
-                addDataScheme("package")
-            },
-            userHandle = userHandle,
-        ),
-    ).map { getPackageInfo() }
+    private val appChangeFlow = context.broadcastReceiverAsUserFlow(
+        intentFilter = IntentFilter().apply {
+            // App enabled / disabled
+            addAction(Intent.ACTION_PACKAGE_CHANGED)
+
+            // App archived
+            addAction(Intent.ACTION_PACKAGE_REMOVED)
+
+            // App updated / the updates are uninstalled (system app)
+            addAction(Intent.ACTION_PACKAGE_REPLACED)
+
+            // App force-stopped
+            addAction(Intent.ACTION_PACKAGE_RESTARTED)
+
+            addDataScheme("package")
+        },
+        userHandle = userHandle,
+    ).filter(::isInterestedAppChange).filter(::isForThisApp)
+
+    @VisibleForTesting
+    fun isInterestedAppChange(intent: Intent) = when {
+        intent.action != Intent.ACTION_PACKAGE_REMOVED -> true
+
+        // filter out the fully removed case, in which the page will be closed, so no need to
+        // refresh
+        intent.getBooleanExtra(Intent.EXTRA_DATA_REMOVED, false) -> false
+
+        // filter out the updates are uninstalled (system app), which will followed by a replacing
+        // broadcast, we can refresh at that time
+        intent.getBooleanExtra(Intent.EXTRA_REPLACING, false) -> false
+
+        else -> true // App archived
+    }
+
+    val flow: StateFlow<PackageInfo?> = merge(flowOf(null), appChangeFlow)
+        .map { getPackageInfo() }
         .stateIn(coroutineScope + Dispatchers.Default, SharingStarted.Eagerly, null)
 
     /**
@@ -89,12 +115,14 @@
         }
         val navController = LocalNavController.current
         DisposableBroadcastReceiverAsUser(intentFilter, userHandle) { intent ->
-            if (packageName == intent.data?.schemeSpecificPart) {
+            if (isForThisApp(intent)) {
                 navController.navigateBack()
             }
         }
     }
 
+    private fun isForThisApp(intent: Intent) = packageName == intent.data?.schemeSpecificPart
+
     /** Enables this package. */
     fun enable() {
         logAction(SettingsEnums.ACTION_SETTINGS_ENABLE_APP)
diff --git a/tests/robotests/Android.bp b/tests/robotests/Android.bp
index 6d3c748..95a78c7 100644
--- a/tests/robotests/Android.bp
+++ b/tests/robotests/Android.bp
@@ -50,7 +50,7 @@
         "src/**/*.kt",
     ],
 
-    test_suites: ["general-tests"],
+    // test_suites attribute is not needed. This module will be configured in ATP GCL file.
 
     static_libs: [
         "Robolectric_shadows_androidx_fragment_upstream",
diff --git a/tests/robotests/src/com/android/settings/accessibility/ColorAndMotionFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/ColorAndMotionFragmentTest.java
index a30dbff..d3f5457 100644
--- a/tests/robotests/src/com/android/settings/accessibility/ColorAndMotionFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/ColorAndMotionFragmentTest.java
@@ -16,10 +16,16 @@
 
 package com.android.settings.accessibility;
 
+import static android.view.accessibility.Flags.FLAG_FORCE_INVERT_COLOR;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import android.app.settings.SettingsEnums;
 import android.content.Context;
+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.test.core.app.ApplicationProvider;
 
@@ -27,6 +33,7 @@
 import com.android.settings.testutils.XmlTestUtils;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.robolectric.RobolectricTestRunner;
@@ -37,6 +44,9 @@
 @RunWith(RobolectricTestRunner.class)
 public class ColorAndMotionFragmentTest {
 
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
     private final Context mContext = ApplicationProvider.getApplicationContext();
     private ColorAndMotionFragment mFragment;
 
@@ -63,6 +73,20 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(FLAG_FORCE_INVERT_COLOR)
+    public void forceInvertEnabled_getNonIndexableKeys_existInXmlLayout() {
+        final List<String> niks = ColorAndMotionFragment.SEARCH_INDEX_DATA_PROVIDER
+                .getNonIndexableKeys(mContext);
+        final List<String> keys =
+                XmlTestUtils.getKeysFromPreferenceXml(mContext,
+                        R.xml.accessibility_color_and_motion);
+
+        assertThat(niks).doesNotContain(ColorAndMotionFragment.TOGGLE_FORCE_INVERT);
+        assertThat(keys).containsAtLeastElementsIn(niks);
+    }
+
+    @Test
+    @RequiresFlagsDisabled(FLAG_FORCE_INVERT_COLOR)
     public void getNonIndexableKeys_existInXmlLayout() {
         final List<String> niks = ColorAndMotionFragment.SEARCH_INDEX_DATA_PROVIDER
                 .getNonIndexableKeys(mContext);
@@ -70,6 +94,7 @@
                 XmlTestUtils.getKeysFromPreferenceXml(mContext,
                         R.xml.accessibility_color_and_motion);
 
+        assertThat(niks).contains(ColorAndMotionFragment.TOGGLE_FORCE_INVERT);
         assertThat(keys).containsAtLeastElementsIn(niks);
     }
 }
diff --git a/tests/robotests/src/com/android/settings/accessibility/ToggleForceInvertPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/ToggleForceInvertPreferenceControllerTest.java
new file mode 100644
index 0000000..d8197ef
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/accessibility/ToggleForceInvertPreferenceControllerTest.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.accessibility;
+
+import static android.view.accessibility.Flags.FLAG_FORCE_INVERT_COLOR;
+
+import static com.android.settings.accessibility.AccessibilityUtil.State.OFF;
+import static com.android.settings.accessibility.AccessibilityUtil.State.ON;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+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.provider.Settings;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.core.BasePreferenceController;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+/** Tests for {@link ToggleForceInvertPreferenceController}. */
+@RunWith(RobolectricTestRunner.class)
+public class ToggleForceInvertPreferenceControllerTest {
+
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
+    private final Context mContext = ApplicationProvider.getApplicationContext();
+    private ToggleForceInvertPreferenceController mController;
+
+    @Before
+    public void setUp() {
+        mController = new ToggleForceInvertPreferenceController(
+                mContext,
+                ColorAndMotionFragment.TOGGLE_FORCE_INVERT
+        );
+    }
+
+    @Test
+    @RequiresFlagsDisabled(FLAG_FORCE_INVERT_COLOR)
+    public void flagOff_getAvailabilityStatus_shouldReturnUnsupported() {
+        assertThat(mController.getAvailabilityStatus())
+                .isEqualTo(BasePreferenceController.UNSUPPORTED_ON_DEVICE);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(FLAG_FORCE_INVERT_COLOR)
+    public void flagOn_getAvailabilityStatus_shouldReturnAvailable() {
+        assertThat(mController.getAvailabilityStatus())
+                .isEqualTo(BasePreferenceController.AVAILABLE);
+    }
+
+    @Test
+    public void settingOff_reflectsCorrectValue() {
+        setEnabled(false);
+        assertThat(mController.isChecked()).isFalse();
+    }
+
+    @Test
+    public void settingOn_reflectsCorrectValue() {
+        setEnabled(true);
+        assertThat(mController.isChecked()).isTrue();
+    }
+
+    @Test
+    public void onCheck_settingChanges() {
+        setEnabled(false);
+
+        mController.setChecked(true);
+        assertThat(isEnabled()).isTrue();
+
+        mController.setChecked(false);
+        assertThat(isEnabled()).isFalse();
+    }
+
+    private boolean isEnabled() {
+        return Settings.Secure.getInt(mContext.getContentResolver(),
+                Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED, /* def=*/ -1) == ON;
+    }
+
+    private void setEnabled(boolean enabled) {
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED, enabled ? ON : OFF);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/backup/BackupSettingsHelperTest.java b/tests/robotests/src/com/android/settings/backup/BackupSettingsHelperTest.java
index b6ef32c..17f17c1 100644
--- a/tests/robotests/src/com/android/settings/backup/BackupSettingsHelperTest.java
+++ b/tests/robotests/src/com/android/settings/backup/BackupSettingsHelperTest.java
@@ -34,7 +34,6 @@
 import android.content.res.Resources;
 import android.os.IBinder;
 import android.os.RemoteException;
-import android.os.UserHandle;
 
 import com.android.settings.R;
 
@@ -48,8 +47,6 @@
 import org.robolectric.annotation.Config;
 import org.robolectric.annotation.Implementation;
 import org.robolectric.annotation.Implements;
-import org.robolectric.shadow.api.Shadow;
-import org.robolectric.shadows.ShadowUserManager;
 
 @RunWith(RobolectricTestRunner.class)
 @Config(shadows = BackupSettingsHelperTest.ShadowBackupManagerStub.class)
@@ -72,46 +69,12 @@
     @Mock
     private static IBackupManager mBackupManager;
 
-    private ShadowUserManager mUserManager;
-
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
         mContext = spy(RuntimeEnvironment.application.getApplicationContext());
         when(mBackupManager.getCurrentTransport()).thenReturn("test_transport");
         mBackupSettingsHelper = new BackupSettingsHelper(mContext);
-        mUserManager = Shadow.extract(mContext.getSystemService(Context.USER_SERVICE));
-    }
-
-    @Test
-    public void testGetSummary_backupEnabledOnlyOneProfile_showsOn() throws Exception {
-        mUserManager.addUserProfile(new UserHandle(0));
-        when(mBackupManager.isBackupEnabled()).thenReturn(true);
-
-        String backupSummary = mBackupSettingsHelper.getSummary();
-
-        assertThat(backupSummary).isEqualTo(mContext.getString(R.string.backup_summary_state_on));
-    }
-
-    @Test
-    public void testGetSummary_backupDisabledOnlyOneProfile_showsOff() throws Exception {
-        mUserManager.addUserProfile(new UserHandle(0));
-        when(mBackupManager.isBackupEnabled()).thenReturn(false);
-
-        String backupSummary = mBackupSettingsHelper.getSummary();
-
-        assertThat(backupSummary).isEqualTo(mContext.getString(R.string.backup_summary_state_off));
-    }
-
-    @Test
-    public void testGetSummary_TwoProfiles_returnsNull() throws Exception {
-        mUserManager.addUserProfile(new UserHandle(0));
-        mUserManager.addUserProfile(new UserHandle(10));
-        when(mBackupManager.isBackupEnabled()).thenReturn(true);
-
-        String backupSummary = mBackupSettingsHelper.getSummary();
-
-        assertThat(backupSummary).isNull();
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/biometrics/BiometricSettingsProviderTest.kt b/tests/robotests/src/com/android/settings/biometrics/BiometricSettingsProviderTest.kt
index cdad338..152258d 100644
--- a/tests/robotests/src/com/android/settings/biometrics/BiometricSettingsProviderTest.kt
+++ b/tests/robotests/src/com/android/settings/biometrics/BiometricSettingsProviderTest.kt
@@ -26,7 +26,6 @@
 import com.android.settings.flags.Flags
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
-import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -46,7 +45,9 @@
 
   @Before
   fun setUp() {
+    whenever(context.resources).thenReturn(resources)
     provider = BiometricSettingsProvider()
+    provider.attachInfo(context, null)
   }
 
   @Test(expected = UnsupportedOperationException::class)
@@ -74,16 +75,14 @@
     provider.update(Uri.EMPTY, null, null, null)
   }
 
-  @Ignore("b/313342682")
   @Test
   @RequiresFlagsEnabled(Flags.FLAG_BIOMETRIC_SETTINGS_PROVIDER)
   fun getSuggestionState_shouldQueryFeatureProvider() {
     val expectedValue = false
     setSupportFaceEnroll(expectedValue)
 
-    val bundle =
-      provider.call(BiometricSettingsProvider.GET_SUW_FACE_ENABLED, null, Bundle())
-    assertThat(bundle!!.getString(BiometricSettingsProvider.SUW_FACE_ENABLED))
+    val bundle = provider.call(BiometricSettingsProvider.GET_SUW_FACE_ENABLED, null, Bundle())
+    assertThat(bundle!!.getBoolean(BiometricSettingsProvider.SUW_FACE_ENABLED))
       .isEqualTo(expectedValue)
   }
 
diff --git a/tests/robotests/src/com/android/settings/development/SelectDebugAppPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/SelectDebugAppPreferenceControllerTest.java
index a998ddc..94fce17 100644
--- a/tests/robotests/src/com/android/settings/development/SelectDebugAppPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/development/SelectDebugAppPreferenceControllerTest.java
@@ -36,6 +36,7 @@
 import androidx.preference.PreferenceScreen;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -71,6 +72,7 @@
         mController.displayPreference(mPreferenceScreen);
     }
 
+    @Ignore("b/314919354")
     @Test
     public void handlePreferenceTreeClick_preferenceClicked_launchActivity() {
         final Intent activityStartIntent = new Intent(mContext, AppPicker.class);
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiverTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiverTest.java
index 118c3ed..df330a3 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiverTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiverTest.java
@@ -35,6 +35,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.robolectric.RobolectricTestRunner;
@@ -128,6 +129,7 @@
         assertThat(mShadowAlarmManager.peekNextScheduledAlarm()).isNull();
     }
 
+    @Ignore("b/314921894")
     @Test
     public void onReceive_withTimeChangedIntent_clearsAllDataAndRefreshesJob()
             throws InterruptedException {
diff --git a/tests/robotests/src/com/android/settings/shortcut/CreateShortcutPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/shortcut/CreateShortcutPreferenceControllerTest.java
index 3705ba8..9727dd1 100644
--- a/tests/robotests/src/com/android/settings/shortcut/CreateShortcutPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/shortcut/CreateShortcutPreferenceControllerTest.java
@@ -116,6 +116,7 @@
                 .isEqualTo(SHORTCUT_ID_PREFIX + intent.getComponent().flattenToShortString());
     }
 
+    @Ignore("b/314924127")
     @Test
     public void queryShortcuts_shouldOnlyIncludeSystemApp() {
         final ResolveInfo ri1 = new ResolveInfo();
@@ -139,6 +140,7 @@
         assertThat(info.get(0).activityInfo).isEqualTo(ri2.activityInfo);
     }
 
+    @Ignore("b/314924127")
     @Test
     public void queryShortcuts_shouldSortBasedOnPriority() {
         final ResolveInfo ri1 = new ResolveInfo();
diff --git a/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java b/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java
index 57f4e17..84c8586 100644
--- a/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java
+++ b/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java
@@ -64,6 +64,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -200,6 +201,7 @@
                 .registerIntentToUri(eq(FakeToggleController.INTENT_FILTER), eq(INTENT_SLICE_URI));
     }
 
+    @Ignore("b/314925256")
     @Test
     public void loadSlice_registersBackgroundListener() {
         SliceTestUtils.insertSliceToDb(mContext, KEY);
diff --git a/tests/robotests/src/com/android/settings/support/SupportDashboardActivityTest.java b/tests/robotests/src/com/android/settings/support/SupportDashboardActivityTest.java
index f510994..16df9ba 100644
--- a/tests/robotests/src/com/android/settings/support/SupportDashboardActivityTest.java
+++ b/tests/robotests/src/com/android/settings/support/SupportDashboardActivityTest.java
@@ -29,6 +29,7 @@
 import com.android.settingslib.search.SearchIndexableRaw;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.robolectric.RobolectricTestRunner;
@@ -64,6 +65,7 @@
         assertThat(value.intentAction).isEqualTo(ACTION_SUPPORT_SETTINGS);
     }
 
+    @Ignore("b/314927625")
     @Test
     public void shouldHandleIntentAction() {
         PackageManager packageManager = mContext.getPackageManager();
diff --git a/tests/robotests/src/com/android/settings/survey/SurveyMixinTest.java b/tests/robotests/src/com/android/settings/survey/SurveyMixinTest.java
index 8f43268..635343e 100644
--- a/tests/robotests/src/com/android/settings/survey/SurveyMixinTest.java
+++ b/tests/robotests/src/com/android/settings/survey/SurveyMixinTest.java
@@ -13,6 +13,7 @@
 import com.android.settings.testutils.FakeFeatureFactory;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -39,6 +40,7 @@
         mProvider = FakeFeatureFactory.setupForTest().getSurveyFeatureProvider(mContext);
     }
 
+    @Ignore("b/314929422")
     @Test
     public void onResume_noActionIfActivityDoesNotExist() {
         // Pretend we are an activity that is starting up
diff --git a/tests/robotests/src/com/android/settings/system/FactoryResetPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/system/FactoryResetPreferenceControllerTest.java
index 6e6fad8..321fcf5 100644
--- a/tests/robotests/src/com/android/settings/system/FactoryResetPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/system/FactoryResetPreferenceControllerTest.java
@@ -27,6 +27,7 @@
 
 import org.junit.After;
 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 @@
         Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.DEVICE_DEMO_MODE, 0);
     }
 
+    @Ignore("b/314930928")
     @Test
     public void isAvailable_systemUser() {
         mShadowUserManager.setIsAdminUser(true);
diff --git a/tests/robotests/src/com/android/settings/wifi/WifiDialogActivityTest.java b/tests/robotests/src/com/android/settings/wifi/WifiDialogActivityTest.java
index 5a39368..82aff4e 100644
--- a/tests/robotests/src/com/android/settings/wifi/WifiDialogActivityTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/WifiDialogActivityTest.java
@@ -50,6 +50,7 @@
 import com.google.android.setupcompat.util.WizardManagerHelper;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -89,6 +90,7 @@
 
     WifiDialogActivity mActivity;
 
+    @Ignore("b/314867581")
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
diff --git a/tests/robotests/src/com/android/settings/wifi/WifiNoInternetDialogTest.java b/tests/robotests/src/com/android/settings/wifi/WifiNoInternetDialogTest.java
index 8e52210..64bf525 100644
--- a/tests/robotests/src/com/android/settings/wifi/WifiNoInternetDialogTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/WifiNoInternetDialogTest.java
@@ -45,6 +45,7 @@
 import com.android.settings.R;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -55,6 +56,7 @@
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
+@Ignore("b/314867581")
 @RunWith(RobolectricTestRunner.class)
 public class WifiNoInternetDialogTest {
 
diff --git a/tests/robotests/src/com/android/settings/wifi/dpp/WifiDppConfiguratorActivityTest.java b/tests/robotests/src/com/android/settings/wifi/dpp/WifiDppConfiguratorActivityTest.java
index d8605de..73e4811 100644
--- a/tests/robotests/src/com/android/settings/wifi/dpp/WifiDppConfiguratorActivityTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/dpp/WifiDppConfiguratorActivityTest.java
@@ -29,6 +29,7 @@
 import androidx.test.core.app.ApplicationProvider;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -39,6 +40,7 @@
 import org.robolectric.Robolectric;
 import org.robolectric.RobolectricTestRunner;
 
+@Ignore("b/314867581")
 @RunWith(RobolectricTestRunner.class)
 public class WifiDppConfiguratorActivityTest {
 
diff --git a/tests/robotests/src/com/android/settings/wifi/dpp/WifiDppEnrolleeActivityTest.java b/tests/robotests/src/com/android/settings/wifi/dpp/WifiDppEnrolleeActivityTest.java
index 67d4678..c099060 100644
--- a/tests/robotests/src/com/android/settings/wifi/dpp/WifiDppEnrolleeActivityTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/dpp/WifiDppEnrolleeActivityTest.java
@@ -28,6 +28,7 @@
 import com.android.settingslib.wifi.WifiRestrictionsCache;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -37,6 +38,7 @@
 import org.robolectric.Robolectric;
 import org.robolectric.RobolectricTestRunner;
 
+@Ignore("b/314867581")
 @RunWith(RobolectricTestRunner.class)
 public class WifiDppEnrolleeActivityTest {
 
diff --git a/tests/robotests/src/com/android/settings/wifi/savedaccesspoints2/SavedAccessPointsWifiSettings2Test.java b/tests/robotests/src/com/android/settings/wifi/savedaccesspoints2/SavedAccessPointsWifiSettings2Test.java
index 3e6c64b..fc0b946 100644
--- a/tests/robotests/src/com/android/settings/wifi/savedaccesspoints2/SavedAccessPointsWifiSettings2Test.java
+++ b/tests/robotests/src/com/android/settings/wifi/savedaccesspoints2/SavedAccessPointsWifiSettings2Test.java
@@ -47,6 +47,7 @@
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
 
+@Ignore("b/314867581")
 @RunWith(RobolectricTestRunner.class)
 @Config(shadows = ShadowInteractionJankMonitor.class)
 public class SavedAccessPointsWifiSettings2Test {
diff --git a/tests/spa_unit/src/com/android/settings/datausage/lib/AppPreferenceRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/datausage/lib/AppPreferenceRepositoryTest.kt
index c7371ee..05fce8f 100644
--- a/tests/spa_unit/src/com/android/settings/datausage/lib/AppPreferenceRepositoryTest.kt
+++ b/tests/spa_unit/src/com/android/settings/datausage/lib/AppPreferenceRepositoryTest.kt
@@ -21,6 +21,7 @@
 import android.content.pm.PackageManager
 import android.graphics.drawable.Drawable
 import android.os.UserHandle
+import android.util.IconDrawableFactory
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.google.common.truth.Truth.assertThat
@@ -43,7 +44,9 @@
         on { packageManager } doReturn packageManager
     }
 
-    private val repository = AppPreferenceRepository(context)
+    private val mockIconDrawableFactory = mock<IconDrawableFactory>()
+
+    private val repository = AppPreferenceRepository(context, mockIconDrawableFactory)
 
     @Test
     fun loadAppPreferences_packageNotFound_returnEmpty() {
@@ -61,9 +64,11 @@
     @Test
     fun loadAppPreferences_packageFound_returnPreference() {
         val app = mock<ApplicationInfo> {
-            on { loadUnbadgedIcon(any()) } doReturn UNBADGED_ICON
             on { loadLabel(any()) } doReturn LABEL
         }
+        mockIconDrawableFactory.stub {
+            on { getBadgedIcon(app) } doReturn UNBADGED_ICON
+        }
         packageManager.stub {
             on {
                 getApplicationInfoAsUser(PACKAGE_NAME, 0, UserHandle.getUserId(UID))
diff --git a/tests/spa_unit/src/com/android/settings/spa/SpaBridgeActivityTest.kt b/tests/spa_unit/src/com/android/settings/spa/SpaBridgeActivityTest.kt
deleted file mode 100644
index 48fa823..0000000
--- a/tests/spa_unit/src/com/android/settings/spa/SpaBridgeActivityTest.kt
+++ /dev/null
@@ -1,63 +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.spa
-
-import android.app.Activity
-import android.content.ComponentName
-import android.content.pm.ActivityInfo
-import android.content.pm.PackageManager
-import android.content.pm.PackageManager.ComponentInfoFlags
-import androidx.core.os.bundleOf
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import com.android.settings.spa.SpaBridgeActivity.Companion.META_DATA_KEY_DESTINATION
-import com.android.settings.spa.SpaBridgeActivity.Companion.getDestination
-import com.google.common.truth.Truth.assertThat
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.kotlin.any
-import org.mockito.kotlin.doReturn
-import org.mockito.kotlin.eq
-import org.mockito.kotlin.mock
-
-@RunWith(AndroidJUnit4::class)
-class SpaBridgeActivityTest {
-    private val mockPackageManager = mock<PackageManager> {
-        on { getActivityInfo(eq(COMPONENT_NAME), any<ComponentInfoFlags>()) } doReturn
-            ActivityInfo().apply {
-                metaData = bundleOf(META_DATA_KEY_DESTINATION to DESTINATION)
-            }
-    }
-
-    private val activity = mock<Activity> {
-        on { componentName } doReturn COMPONENT_NAME
-        on { packageManager } doReturn mockPackageManager
-    }
-
-    @Test
-    fun getDestination() {
-        val destination = activity.getDestination()
-
-        assertThat(destination).isEqualTo(DESTINATION)
-    }
-
-    private companion object {
-        const val PACKAGE_NAME = "package.name"
-        const val ACTIVITY_NAME = "ActivityName"
-        val COMPONENT_NAME = ComponentName(PACKAGE_NAME, ACTIVITY_NAME)
-        const val DESTINATION = "Destination"
-    }
-}
diff --git a/tests/spa_unit/src/com/android/settings/spa/SpaDestinationTest.kt b/tests/spa_unit/src/com/android/settings/spa/SpaDestinationTest.kt
new file mode 100644
index 0000000..0b9eb22
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/spa/SpaDestinationTest.kt
@@ -0,0 +1,97 @@
+/*
+ * 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.spa
+
+import android.app.Activity
+import android.content.ComponentName
+import android.content.pm.ActivityInfo
+import android.content.pm.PackageManager
+import android.os.Bundle
+import androidx.core.os.bundleOf
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settings.SettingsActivity.META_DATA_KEY_HIGHLIGHT_MENU_KEY
+import com.android.settings.spa.SpaDestination.Companion.META_DATA_KEY_DESTINATION
+import com.android.settings.spa.SpaDestination.Companion.getDestination
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doAnswer
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+
+@RunWith(AndroidJUnit4::class)
+class SpaDestinationTest {
+    private var activityMetadata: Bundle = bundleOf()
+
+    private val mockPackageManager = mock<PackageManager> {
+        on {
+            getActivityInfo(
+                eq(COMPONENT_NAME),
+                any<PackageManager.ComponentInfoFlags>()
+            )
+        } doAnswer {
+            ActivityInfo().apply { metaData = activityMetadata }
+        }
+    }
+
+    private val activity = mock<Activity> {
+        on { componentName } doReturn COMPONENT_NAME
+        on { packageManager } doReturn mockPackageManager
+    }
+
+    @Test
+    fun getDestination_noDestination_returnNull() {
+        activityMetadata = bundleOf()
+
+        val destination = activity.getDestination()
+
+        assertThat(destination).isNull()
+    }
+
+    @Test
+    fun getDestination_withoutHighlightMenuKey() {
+        activityMetadata = bundleOf(META_DATA_KEY_DESTINATION to DESTINATION)
+
+        val (destination, highlightMenuKey) = activity.getDestination()!!
+
+        assertThat(destination).isEqualTo(DESTINATION)
+        assertThat(highlightMenuKey).isNull()
+    }
+
+    @Test
+    fun getDestination_withHighlightMenuKey() {
+        activityMetadata = bundleOf(
+            META_DATA_KEY_DESTINATION to DESTINATION,
+            META_DATA_KEY_HIGHLIGHT_MENU_KEY to HIGHLIGHT_MENU_KEY,
+        )
+
+        val (destination, highlightMenuKey) = activity.getDestination()!!
+
+        assertThat(destination).isEqualTo(DESTINATION)
+        assertThat(highlightMenuKey).isEqualTo(HIGHLIGHT_MENU_KEY)
+    }
+
+    private companion object {
+        const val PACKAGE_NAME = "package.name"
+        const val ACTIVITY_NAME = "ActivityName"
+        val COMPONENT_NAME = ComponentName(PACKAGE_NAME, ACTIVITY_NAME)
+        const val DESTINATION = "Destination"
+        const val HIGHLIGHT_MENU_KEY = "apps"
+    }
+}
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/PackageInfoPresenterTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/PackageInfoPresenterTest.kt
index ecb540c..d81bb1a 100644
--- a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/PackageInfoPresenterTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/PackageInfoPresenterTest.kt
@@ -20,7 +20,9 @@
 import android.app.settings.SettingsEnums
 import android.content.Context
 import android.content.Intent
+import android.content.pm.PackageInfo
 import android.content.pm.PackageManager
+import android.net.Uri
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.android.settings.testutils.FakeFeatureFactory
@@ -61,11 +63,57 @@
     private val fakeFeatureFactory = FakeFeatureFactory()
     private val metricsFeatureProvider = fakeFeatureFactory.metricsFeatureProvider
 
+    private val packageInfoPresenter =
+        PackageInfoPresenter(context, PACKAGE_NAME, USER_ID, TestScope(), packageManagers)
+
+    @Test
+    fun isInterestedAppChange_packageChanged_isInterested() {
+        val intent = Intent(Intent.ACTION_PACKAGE_CHANGED).apply {
+            data = Uri.parse("package:$PACKAGE_NAME")
+        }
+
+        val isInterestedAppChange = packageInfoPresenter.isInterestedAppChange(intent)
+
+        assertThat(isInterestedAppChange).isTrue()
+    }
+
+    @Test
+    fun isInterestedAppChange_fullyRemoved_notInterested() {
+        val intent = Intent(Intent.ACTION_PACKAGE_REMOVED).apply {
+            data = Uri.parse("package:$PACKAGE_NAME")
+            putExtra(Intent.EXTRA_DATA_REMOVED, true)
+        }
+
+        val isInterestedAppChange = packageInfoPresenter.isInterestedAppChange(intent)
+
+        assertThat(isInterestedAppChange).isFalse()
+    }
+
+    @Test
+    fun isInterestedAppChange_removedBeforeReplacing_notInterested() {
+        val intent = Intent(Intent.ACTION_PACKAGE_REMOVED).apply {
+            data = Uri.parse("package:$PACKAGE_NAME")
+            putExtra(Intent.EXTRA_REPLACING, true)
+        }
+
+        val isInterestedAppChange = packageInfoPresenter.isInterestedAppChange(intent)
+
+        assertThat(isInterestedAppChange).isFalse()
+    }
+
+    @Test
+    fun isInterestedAppChange_archived_interested() {
+        val intent = Intent(Intent.ACTION_PACKAGE_REMOVED).apply {
+            data = Uri.parse("package:$PACKAGE_NAME")
+        }
+
+        val isInterestedAppChange = packageInfoPresenter.isInterestedAppChange(intent)
+
+        assertThat(isInterestedAppChange).isTrue()
+    }
+
     @Test
     fun enable() = runBlocking {
-        val packageInfoPresenter =
-            PackageInfoPresenter(context, PACKAGE_NAME, USER_ID, TestScope(), packageManagers)
-
         packageInfoPresenter.enable()
         delay(100)
 
@@ -77,9 +125,6 @@
 
     @Test
     fun disable() = runBlocking {
-        val packageInfoPresenter =
-            PackageInfoPresenter(context, PACKAGE_NAME, USER_ID, TestScope(), packageManagers)
-
         packageInfoPresenter.disable()
         delay(100)
 
@@ -91,9 +136,6 @@
 
     @Test
     fun startUninstallActivity() = runBlocking {
-        val packageInfoPresenter =
-            PackageInfoPresenter(context, PACKAGE_NAME, USER_ID, TestScope(), packageManagers)
-
         packageInfoPresenter.startUninstallActivity()
 
         verifyAction(SettingsEnums.ACTION_SETTINGS_UNINSTALL_APP)
@@ -109,9 +151,6 @@
 
     @Test
     fun clearInstantApp() = runBlocking {
-        val packageInfoPresenter =
-            PackageInfoPresenter(context, PACKAGE_NAME, USER_ID, TestScope(), packageManagers)
-
         packageInfoPresenter.clearInstantApp()
         delay(100)
 
@@ -121,9 +160,6 @@
 
     @Test
     fun forceStop() = runBlocking {
-        val packageInfoPresenter =
-            PackageInfoPresenter(context, PACKAGE_NAME, USER_ID, TestScope(), packageManagers)
-
         packageInfoPresenter.forceStop()
         delay(100)
 
@@ -133,9 +169,6 @@
 
     @Test
     fun logAction() = runBlocking {
-        val packageInfoPresenter =
-            PackageInfoPresenter(context, PACKAGE_NAME, USER_ID, TestScope(), packageManagers)
-
         packageInfoPresenter.logAction(123)
 
         verifyAction(123)
@@ -148,5 +181,6 @@
     private companion object {
         const val PACKAGE_NAME = "package.name"
         const val USER_ID = 0
+        val PACKAGE_INFO = PackageInfo()
     }
 }