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)