Merge "Always confirm eSim removal." into main
diff --git a/aconfig/settings_telephony_flag_declarations.aconfig b/aconfig/settings_telephony_flag_declarations.aconfig
index 68e313b..0999a7b 100644
--- a/aconfig/settings_telephony_flag_declarations.aconfig
+++ b/aconfig/settings_telephony_flag_declarations.aconfig
@@ -6,3 +6,10 @@
     description: "Stop honoring CarrierConfigManager.KEY_HIDE_ENABLE_2G. Allow 2G toggle cannot be hidden from users by carriers."
     bug: "300248708"
 }
+
+flag {
+    name: "is_dual_sim_onboarding_enabled"
+    namespace: "settings_experience"
+    description: "Control the Dual SIM onobarding feature"
+    bug: "298898436"
+}
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 2c928ff..8df990b 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -438,11 +438,10 @@
     </style>
 
     <style name="DeviceAudioSharingText">
-        <item name="android:textAlignment">viewStart</item>
+        <item name="android:textAlignment">center</item>
         <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
         <item name="android:textSize">14sp</item>
         <item name="android:textColor">?android:attr/textColorPrimary</item>
-        <item name="android:paddingBottom">24dp</item>
     </style>
 
     <style name="ContextualCardStyle">
diff --git a/src/com/android/settings/accessibility/RestrictedPreferenceHelper.java b/src/com/android/settings/accessibility/RestrictedPreferenceHelper.java
index dfa2f33..6f21fb8 100644
--- a/src/com/android/settings/accessibility/RestrictedPreferenceHelper.java
+++ b/src/com/android/settings/accessibility/RestrictedPreferenceHelper.java
@@ -234,6 +234,32 @@
         // permittedServices null means all accessibility services are allowed.
         boolean serviceAllowed = permittedServices == null || permittedServices.contains(
                 preference.getPackageName());
+
+        if (android.security.Flags.extendEcmToAllSettings()) {
+            preference.checkEcmRestrictionAndSetDisabled(
+                    AppOpsManager.OPSTR_BIND_ACCESSIBILITY_SERVICE,
+                    preference.getPackageName(), preference.getUid());
+            if (preference.isDisabledByEcm()) {
+                serviceAllowed = false;
+            }
+
+            if (serviceAllowed || serviceEnabled) {
+                preference.setEnabled(true);
+            } else {
+                // Disable accessibility service that are not permitted.
+                final RestrictedLockUtils.EnforcedAdmin admin =
+                        RestrictedLockUtilsInternal.checkIfAccessibilityServiceDisallowed(
+                                mContext, preference.getPackageName(), UserHandle.myUserId());
+
+                if (admin != null) {
+                    preference.setDisabledByAdmin(admin);
+                } else if (!preference.isDisabledByEcm()) {
+                    preference.setEnabled(false);
+                }
+            }
+            return;
+        }
+
         boolean appOpsAllowed;
         if (serviceAllowed) {
             try {
diff --git a/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceController.java b/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceController.java
index e3816bf..fb78e3e 100644
--- a/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceController.java
+++ b/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceController.java
@@ -41,6 +41,8 @@
     private PreferenceFragmentCompat mParent;
     private NotificationManager mNm;
     private PackageManager mPm;
+    // The appOp representing this preference
+    private String mAppOpStr;
 
     public ApprovalPreferenceController(Context context, String key) {
         super(context, key);
@@ -71,6 +73,14 @@
         return this;
     }
 
+    /**
+     * Set the associated appOp for the Setting
+     */
+    public ApprovalPreferenceController setAppOpStr(String appOpStr) {
+        mAppOpStr = appOpStr;
+        return this;
+    }
+
     @Override
     public int getAvailabilityStatus() {
         return AVAILABLE;
@@ -107,8 +117,20 @@
                 return false;
             }
         });
-        preference.updateState(
-                mCn.getPackageName(), mPkgInfo.applicationInfo.uid, isAllowedCn, isEnabled);
+
+        if (android.security.Flags.extendEcmToAllSettings()) {
+            if (!isAllowedCn && !isEnabled) {
+                preference.setEnabled(false);
+            } else if (isEnabled) {
+                preference.setEnabled(true);
+            } else {
+                preference.checkEcmRestrictionAndSetDisabled(mAppOpStr,
+                        mCn.getPackageName(), mPkgInfo.applicationInfo.uid);
+            }
+        } else {
+            preference.updateState(
+                    mCn.getPackageName(), mPkgInfo.applicationInfo.uid, isAllowedCn, isEnabled);
+        }
     }
 
     public void disable(final ComponentName cn) {
diff --git a/src/com/android/settings/applications/specialaccess/notificationaccess/NotificationAccessDetails.java b/src/com/android/settings/applications/specialaccess/notificationaccess/NotificationAccessDetails.java
index 17dabe4..89767dd 100644
--- a/src/com/android/settings/applications/specialaccess/notificationaccess/NotificationAccessDetails.java
+++ b/src/com/android/settings/applications/specialaccess/notificationaccess/NotificationAccessDetails.java
@@ -21,6 +21,7 @@
 import static com.android.settings.applications.AppInfoBase.ARG_PACKAGE_NAME;
 
 import android.Manifest;
+import android.app.AppOpsManager;
 import android.app.NotificationManager;
 import android.app.settings.SettingsEnums;
 import android.companion.ICompanionDeviceManager;
@@ -102,6 +103,7 @@
                 .setCn(mComponentName)
                 .setNm(context.getSystemService(NotificationManager.class))
                 .setPm(mPm)
+                .setAppOpStr(AppOpsManager.OPSTR_ACCESS_NOTIFICATIONS)
                 .setParent(this);
         use(HeaderPreferenceController.class)
                 .setFragment(this)
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceVolumeControlUpdater.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceVolumeControlUpdater.java
index e60eabd..5c0a90a 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceVolumeControlUpdater.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceVolumeControlUpdater.java
@@ -51,9 +51,10 @@
     public boolean isFilterMatched(CachedBluetoothDevice cachedDevice) {
         boolean isFilterMatched = false;
         if (isDeviceConnected(cachedDevice) && isDeviceInCachedDevicesList(cachedDevice)) {
-            // If device is LE audio device and has a broadcast source,
-            // it would show in audio sharing devices group.
+            // If device is LE audio device and in a sharing session on current sharing device,
+            // it would show in volume control group.
             if (cachedDevice.isConnectedLeAudioDevice()
+                    && AudioSharingUtils.isBroadcasting(mLocalBtManager)
                     && AudioSharingUtils.hasBroadcastSource(cachedDevice, mLocalBtManager)) {
                 isFilterMatched = true;
             }
diff --git a/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java b/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
index a0db4ce..2a299c5 100644
--- a/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
+++ b/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
@@ -43,6 +43,7 @@
 import android.os.Looper;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.os.storage.StorageManager;
 import android.util.Log;
 import android.view.WindowManager;
 
@@ -292,7 +293,7 @@
             // use, which optionally accepts a challenge.
             mForceVerifyPath = true;
             if (isBiometricAllowed(effectiveUserId, mUserId)) {
-                showBiometricPrompt(promptInfo);
+                showBiometricPrompt(promptInfo, mUserId);
                 launchedBiometric = true;
             } else {
                 showConfirmCredentials();
@@ -302,19 +303,25 @@
                 && userProperties != null
                 && userProperties.isAuthAlwaysRequiredToDisableQuietMode()
                 && isInternalActivity()) {
-            // Force verification path is required to be invoked as we might need to verify the tied
-            // profile challenge if the profile is using the unified challenge mode. This would
-            // result in ConfirmLockPassword.startVerifyPassword/
+            // Force verification path is required to be invoked as we might need to verify the
+            // tied profile challenge if the profile is using the unified challenge mode. This
+            // would result in ConfirmLockPassword.startVerifyPassword/
             // ConfirmLockPattern.startVerifyPattern being called instead of the
             // startCheckPassword/startCheckPattern
             mForceVerifyPath = userProperties.isCredentialShareableWithParent();
-            showConfirmCredentials();
-            launchedCDC = true;
+            if (android.multiuser.Flags.enableBiometricsToUnlockPrivateSpace()
+                    && isBiometricAllowed(effectiveUserId, mUserId)) {
+                showBiometricPrompt(promptInfo, effectiveUserId);
+                launchedBiometric = true;
+            } else {
+                showConfirmCredentials();
+                launchedCDC = true;
+            }
         } else {
             if (isBiometricAllowed(effectiveUserId, mUserId)) {
                 // Don't need to check if biometrics / pin/pattern/pass are enrolled. It will go to
                 // onAuthenticationError and do the right thing automatically.
-                showBiometricPrompt(promptInfo);
+                showBiometricPrompt(promptInfo, mUserId);
                 launchedBiometric = true;
             } else {
                 showConfirmCredentials();
@@ -400,7 +407,19 @@
     // biometric is disabled due to device restart.
     private boolean isStrongAuthRequired(int effectiveUserId) {
         return !mLockPatternUtils.isBiometricAllowedForUser(effectiveUserId)
-                || !mUserManager.isUserUnlocked(mUserId);
+                || doesUserStateEnforceStrongAuth(mUserId);
+    }
+
+    private boolean doesUserStateEnforceStrongAuth(int userId) {
+        if (android.os.Flags.allowPrivateProfile()
+                && android.multiuser.Flags.enableBiometricsToUnlockPrivateSpace()) {
+            // Check if CE storage for user is locked since biometrics can't unlock fbe/keystore of
+            // the profile user using verifyTiedProfileChallenge. Biometrics can still be used if
+            // the user is stopped with delayed locking (i.e., with storage unlocked), so the user
+            // state (whether the user is in the RUNNING_UNLOCKED state) should not be relied upon.
+            return !StorageManager.isUserKeyUnlocked(userId);
+        }
+        return !mUserManager.isUserUnlocked(userId);
     }
 
     private boolean isBiometricAllowed(int effectiveUserId, int realUserId) {
@@ -408,7 +427,7 @@
                 .hasPendingEscrowToken(realUserId);
     }
 
-    private void showBiometricPrompt(PromptInfo promptInfo) {
+    private void showBiometricPrompt(PromptInfo promptInfo, int userId) {
         mBiometricFragment = (BiometricFragment) getSupportFragmentManager()
                 .findFragmentByTag(TAG_BIOMETRIC_FRAGMENT);
         boolean newFragment = false;
@@ -418,7 +437,9 @@
             newFragment = true;
         }
         mBiometricFragment.setCallbacks(mExecutor, mAuthenticationCallback);
-        mBiometricFragment.setUser(mUserId);
+        // TODO(b/315864564): Move the logic of choosing the user id against which the
+        //  authentication needs to happen to the BiometricPrompt API
+        mBiometricFragment.setUser(userId);
 
         if (newFragment) {
             getSupportFragmentManager().beginTransaction()
diff --git a/src/com/android/settings/spa/app/appinfo/AppBatteryPreference.kt b/src/com/android/settings/spa/app/appinfo/AppBatteryPreference.kt
index c707b44b..af4fc17 100644
--- a/src/com/android/settings/spa/app/appinfo/AppBatteryPreference.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppBatteryPreference.kt
@@ -49,7 +49,7 @@
 @Composable
 fun AppBatteryPreference(app: ApplicationInfo) {
     val context = LocalContext.current
-    val presenter = remember { AppBatteryPresenter(context, app) }
+    val presenter = remember(app) { AppBatteryPresenter(context, app) }
     if (!presenter.isAvailable()) return
 
     Preference(object : PreferenceModel {
diff --git a/src/com/android/settings/spa/app/appinfo/AppDataUsagePreference.kt b/src/com/android/settings/spa/app/appinfo/AppDataUsagePreference.kt
index 057f911..7e6e726 100644
--- a/src/com/android/settings/spa/app/appinfo/AppDataUsagePreference.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppDataUsagePreference.kt
@@ -59,7 +59,7 @@
 ) {
     val context = LocalContext.current
     val coroutineScope = rememberCoroutineScope()
-    val presenter = remember {
+    val presenter = remember(app) {
         AppDataUsagePresenter(context, app, coroutineScope, networkTemplates, repositoryFactory)
     }
     if (!presenter.isAvailableFlow.collectAsStateWithLifecycle(initialValue = false).value) return
diff --git a/src/com/android/settings/spa/app/appinfo/AppPermissionSummary.kt b/src/com/android/settings/spa/app/appinfo/AppPermissionSummary.kt
index de6bd10..91c3887 100644
--- a/src/com/android/settings/spa/app/appinfo/AppPermissionSummary.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppPermissionSummary.kt
@@ -39,7 +39,7 @@
 @Composable
 fun rememberAppPermissionSummary(app: ApplicationInfo): AppPermissionSummaryLiveData {
     val context = LocalContext.current
-    return remember { AppPermissionSummaryLiveData(context, app) }
+    return remember(app) { AppPermissionSummaryLiveData(context, app) }
 }
 
 class AppPermissionSummaryLiveData(
@@ -55,7 +55,11 @@
 
     override fun onActive() {
         userPackageManager.addOnPermissionsChangeListener(onPermissionsChangedListener)
-        update()
+        if (app.isArchived) {
+            postValue(noPermissionRequestedState())
+        } else {
+            update()
+        }
     }
 
     override fun onInactive() {
diff --git a/src/com/android/settings/spa/app/appinfo/AppTimeSpentPreference.kt b/src/com/android/settings/spa/app/appinfo/AppTimeSpentPreference.kt
index 7ba61dc..837df67 100644
--- a/src/com/android/settings/spa/app/appinfo/AppTimeSpentPreference.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppTimeSpentPreference.kt
@@ -40,7 +40,7 @@
 @Composable
 fun AppTimeSpentPreference(app: ApplicationInfo) {
     val context = LocalContext.current
-    val presenter = remember { AppTimeSpentPresenter(context, app) }
+    val presenter = remember(app) { AppTimeSpentPresenter(context, app) }
     if (!presenter.isAvailable()) return
 
     val summary by presenter.summaryLiveData.observeAsState(
diff --git a/src/com/android/settings/spa/app/appinfo/HibernationSwitchPreference.kt b/src/com/android/settings/spa/app/appinfo/HibernationSwitchPreference.kt
index efa88b5..324fa06 100644
--- a/src/com/android/settings/spa/app/appinfo/HibernationSwitchPreference.kt
+++ b/src/com/android/settings/spa/app/appinfo/HibernationSwitchPreference.kt
@@ -54,7 +54,7 @@
 @Composable
 fun HibernationSwitchPreference(app: ApplicationInfo) {
     val context = LocalContext.current
-    val presenter = remember { HibernationSwitchPresenter(context, app) }
+    val presenter = remember(app) { HibernationSwitchPresenter(context, app) }
     if (!presenter.isAvailable()) return
 
     val isEligibleState by presenter.isEligibleFlow.collectAsStateWithLifecycle(initialValue = false)
@@ -93,6 +93,10 @@
         DeviceConfig.getBoolean(NAMESPACE_APP_HIBERNATION, PROPERTY_APP_HIBERNATION_ENABLED, true)
 
     val isEligibleFlow = flow {
+        if (app.isArchived) {
+            emit(false)
+            return@flow
+        }
         val eligibility = getEligibility()
         emit(
             eligibility != HIBERNATION_ELIGIBILITY_EXEMPT_BY_SYSTEM &&
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/HibernationSwitchPreferenceTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/HibernationSwitchPreferenceTest.kt
index 6acdcf5..57e31da 100644
--- a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/HibernationSwitchPreferenceTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/HibernationSwitchPreferenceTest.kt
@@ -24,8 +24,8 @@
 import android.apphibernation.AppHibernationManager
 import android.content.Context
 import android.content.pm.ApplicationInfo
+import android.content.pm.Flags
 import android.os.Build
-import android.os.UserHandle
 import android.permission.PermissionControllerManager
 import android.permission.PermissionControllerManager.HIBERNATION_ELIGIBILITY_ELIGIBLE
 import android.permission.PermissionControllerManager.HIBERNATION_ELIGIBILITY_EXEMPT_BY_SYSTEM
@@ -63,7 +63,6 @@
 import org.mockito.Mockito.anyBoolean
 import org.mockito.Mockito.anyString
 import org.mockito.Mockito.doAnswer
-import org.mockito.Mockito.doReturn
 import org.mockito.Mockito.eq
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
@@ -147,12 +146,20 @@
 
         setContent()
 
-        composeTestRule.onNodeWithText(context.getString(R.string.unused_apps_switch))
+        val text = if (isArchivingEnabled()) {
+            context.getString(R.string.unused_apps_switch_v2)
+        } else {
+            context.getString(R.string.unused_apps_switch)
+        }
+        composeTestRule.onNodeWithText(text)
             .assertIsDisplayed()
             .assertIsNotEnabled()
             .assertIsOff()
     }
 
+    private fun isArchivingEnabled() =
+            Flags.archiving() || "true" == System.getProperty("pm.archiving.enabled")
+
     @Test
     fun `An app targets Q with ops mode default when hibernation targets pre S - not exempted`() {
         mockOpsMode(MODE_DEFAULT)