diff options
10 files changed, 144 insertions, 18 deletions
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictedMode.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictedMode.kt index 3acc9ad83d2c..b6d92422c333 100644 --- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictedMode.kt +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictedMode.kt @@ -18,6 +18,7 @@ package com.android.settingslib.spaprivileged.model.enterprise import android.app.admin.DevicePolicyResources.Strings.Settings import android.content.Context +import android.content.Intent import com.android.settingslib.RestrictedLockUtils import com.android.settingslib.widget.restricted.R @@ -32,6 +33,11 @@ interface BlockedByAdmin : RestrictedMode { fun sendShowAdminSupportDetailsIntent() } +interface BlockedByEcm : RestrictedMode { + fun showRestrictedSettingsDetails() +} + + internal data class BlockedByAdminImpl( private val context: Context, private val enforcedAdmin: RestrictedLockUtils.EnforcedAdmin, @@ -55,3 +61,13 @@ internal data class BlockedByAdminImpl( RestrictedLockUtils.sendShowAdminSupportDetailsIntent(context, enforcedAdmin) } } + +internal data class BlockedByEcmImpl( + private val context: Context, + private val intent: Intent, +) : BlockedByEcm { + + override fun showRestrictedSettingsDetails() { + context.startActivity(intent) + } +} diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt index 550966beb0b8..9432d5995151 100644 --- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt @@ -29,10 +29,20 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOn +data class EnhancedConfirmation( + val key: String, + val uid: Int, + val packageName: String, +) data class Restrictions( val userId: Int = UserHandle.myUserId(), val keys: List<String>, -) + val enhancedConfirmation: EnhancedConfirmation? = null, +) { + fun isEmpty(): Boolean { + return keys.isEmpty() && enhancedConfirmation == null + } +} interface RestrictionsProvider { @Composable @@ -77,6 +87,14 @@ internal class RestrictionsProviderImpl( .checkIfRestrictionEnforced(context, key, restrictions.userId) ?.let { return BlockedByAdminImpl(context = context, enforcedAdmin = it) } } + + restrictions.enhancedConfirmation?.let { ec -> + RestrictedLockUtilsInternal + .checkIfRequiresEnhancedConfirmation(context, ec.key, + ec.uid, ec.packageName) + ?.let { intent -> return BlockedByEcmImpl(context = context, intent = intent) } + } + return NoRestricted } } diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppList.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppList.kt index 06b3eabfad26..25c3bc541249 100644 --- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppList.kt +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppList.kt @@ -47,6 +47,9 @@ abstract class AppOpPermissionListModel( abstract val appOp: Int abstract val permission: String + override val enhancedConfirmationKey: String? + get() = AppOpsManager.opToPublicName(appOp) + /** * When set, specifies the broader permission who trumps the [permission]. * diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt index 565543614866..1c830c1c5b06 100644 --- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt @@ -41,6 +41,7 @@ import com.android.settingslib.spaprivileged.model.app.AppRecord import com.android.settingslib.spaprivileged.model.app.IPackageManagers import com.android.settingslib.spaprivileged.model.app.PackageManagers import com.android.settingslib.spaprivileged.model.app.toRoute +import com.android.settingslib.spaprivileged.model.enterprise.EnhancedConfirmation import com.android.settingslib.spaprivileged.model.enterprise.Restrictions import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderFactory import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderImpl @@ -154,7 +155,12 @@ internal fun <T : AppRecord> TogglePermissionAppListModel<T>.TogglePermissionApp override val changeable = { isChangeable } override val onCheckedChange: (Boolean) -> Unit = { setAllowed(record, it) } } - val restrictions = Restrictions(userId, switchRestrictionKeys) + val restrictions = Restrictions(userId = userId, + keys = switchRestrictionKeys, + enhancedConfirmation = enhancedConfirmationKey?.let { EnhancedConfirmation( + key = it, + uid = checkNotNull(applicationInfo).uid, + packageName = packageName) }) RestrictedSwitchPreference(switchModel, restrictions, restrictionsProviderFactory) } } diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt index 8704f20f5c4a..916d83af3f8f 100644 --- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt @@ -36,6 +36,9 @@ interface TogglePermissionAppListModel<T : AppRecord> { val switchRestrictionKeys: List<String> get() = emptyList() + val enhancedConfirmationKey: String? + get() = null + /** * Loads the extra info for the App List, and generates the [AppRecord] List. * diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt index 36c91f463efe..4b474379c54b 100644 --- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt @@ -41,6 +41,7 @@ import com.android.settingslib.spaprivileged.framework.compose.getPlaceholder import com.android.settingslib.spaprivileged.model.app.AppListModel import com.android.settingslib.spaprivileged.model.app.AppRecord import com.android.settingslib.spaprivileged.model.app.userId +import com.android.settingslib.spaprivileged.model.enterprise.EnhancedConfirmation import com.android.settingslib.spaprivileged.model.enterprise.Restrictions import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderFactory import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderImpl @@ -149,11 +150,17 @@ internal class TogglePermissionInternalAppListModel<T : AppRecord>( @Composable fun getSummary(record: T): () -> String { - val restrictions = remember(record.app.userId) { + val restrictions = remember(record.app.userId, + record.app.uid, record.app.packageName) { Restrictions( userId = record.app.userId, keys = listModel.switchRestrictionKeys, - ) + enhancedConfirmation = listModel.enhancedConfirmationKey?.let { + EnhancedConfirmation( + key = it, + uid = record.app.uid, + packageName = record.app.packageName) + }) } val restrictedMode by restrictionsProviderFactory.rememberRestrictedMode(restrictions) val allowed = listModel.isAllowed(record) diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreference.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreference.kt index d5c5574a0450..cd720252e485 100644 --- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreference.kt +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreference.kt @@ -40,7 +40,7 @@ internal fun RestrictedSwitchPreference( restrictions: Restrictions, restrictionsProviderFactory: RestrictionsProviderFactory, ) { - if (restrictions.keys.isEmpty()) { + if (restrictions.isEmpty()) { SwitchPreference(model) return } diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceModel.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceModel.kt index fa44ecb92ed5..aba3460fc1b9 100644 --- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceModel.kt +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceModel.kt @@ -31,6 +31,7 @@ import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel import com.android.settingslib.spaprivileged.framework.compose.getPlaceholder import com.android.settingslib.spaprivileged.model.enterprise.BaseUserRestricted import com.android.settingslib.spaprivileged.model.enterprise.BlockedByAdmin +import com.android.settingslib.spaprivileged.model.enterprise.BlockedByEcm import com.android.settingslib.spaprivileged.model.enterprise.NoRestricted import com.android.settingslib.spaprivileged.model.enterprise.RestrictedMode import com.android.settingslib.spaprivileged.model.enterprise.Restrictions @@ -56,6 +57,7 @@ internal class RestrictedSwitchPreferenceModel( is NoRestricted -> model.checked is BaseUserRestricted -> ({ false }) is BlockedByAdmin -> model.checked + is BlockedByEcm -> model.checked } override val changeable = if (restrictedMode is NoRestricted) model.changeable else ({ false }) @@ -68,24 +70,42 @@ internal class RestrictedSwitchPreferenceModel( is BaseUserRestricted -> model.onCheckedChange // Pass null since semantics ToggleableState is provided in RestrictionWrapper. is BlockedByAdmin -> null + is BlockedByEcm -> null } @Composable fun RestrictionWrapper(content: @Composable () -> Unit) { - if (restrictedMode !is BlockedByAdmin) { - content() - return + when (restrictedMode) { + is BlockedByAdmin -> { + Box( + Modifier + .clickable( + role = Role.Switch, + onClick = { restrictedMode.sendShowAdminSupportDetailsIntent() }, + ) + .semantics { + this.toggleableState = ToggleableState(checked()) + }, + ) { content() } + } + + is BlockedByEcm -> { + Box( + Modifier + .clickable( + role = Role.Switch, + onClick = { restrictedMode.showRestrictedSettingsDetails() }, + ) + .semantics { + this.toggleableState = ToggleableState(checked()) + }, + ) { content() } + } + + else -> { + content() + } } - Box( - Modifier - .clickable( - role = Role.Switch, - onClick = { restrictedMode.sendShowAdminSupportDetailsIntent() }, - ) - .semantics { - this.toggleableState = ToggleableState(checked()) - }, - ) { content() } } private fun ToggleableState(value: Boolean?) = when (value) { @@ -123,6 +143,9 @@ internal class RestrictedSwitchPreferenceModel( context.getString(com.android.settingslib.R.string.disabled) is BlockedByAdmin -> restrictedMode.getSummary(checked()) + is BlockedByEcm -> + context.getString(com.android.settingslib.R.string.disabled) + null -> context.getPlaceholder() } } diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/scaffold/RestrictedMenuItem.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/scaffold/RestrictedMenuItem.kt index f9abefc11e24..977615b55a6a 100644 --- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/scaffold/RestrictedMenuItem.kt +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/scaffold/RestrictedMenuItem.kt @@ -21,6 +21,7 @@ import androidx.compose.runtime.Composable import com.android.settingslib.spa.widget.scaffold.MoreOptionsScope import com.android.settingslib.spaprivileged.model.enterprise.BaseUserRestricted import com.android.settingslib.spaprivileged.model.enterprise.BlockedByAdmin +import com.android.settingslib.spaprivileged.model.enterprise.BlockedByEcm import com.android.settingslib.spaprivileged.model.enterprise.Restrictions import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderFactory import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderImpl @@ -47,6 +48,7 @@ internal fun MoreOptionsScope.RestrictedMenuItemImpl( MenuItem(text = text, enabled = restrictedMode !== BaseUserRestricted) { when (restrictedMode) { is BlockedByAdmin -> restrictedMode.sendShowAdminSupportDetailsIntent() + is BlockedByEcm -> restrictedMode.showRestrictedSettingsDetails() else -> onClick() } } diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java index e77964c6fa0c..4454b710b7e4 100644 --- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java +++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java @@ -22,13 +22,16 @@ import static android.app.admin.DevicePolicyManager.PROFILE_KEYGUARD_FEATURES_AF import static com.android.settingslib.Utils.getColorAttrDefaultColor; +import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.AppGlobals; +import android.app.AppOpsManager; import android.app.admin.DevicePolicyManager; import android.content.ComponentName; import android.content.Context; +import android.content.Intent; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.UserInfo; @@ -39,10 +42,12 @@ import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; import android.os.UserManager.EnforcingUser; +import android.provider.Settings; import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.style.ForegroundColorSpan; import android.text.style.ImageSpan; +import android.util.ArraySet; import android.util.Log; import android.view.MenuItem; import android.widget.TextView; @@ -53,6 +58,7 @@ import androidx.annotation.VisibleForTesting; import com.android.internal.widget.LockPatternUtils; import java.util.List; +import java.util.Set; /** * Utility class to host methods usable in adding a restricted padlock icon and showing admin @@ -62,6 +68,16 @@ public class RestrictedLockUtilsInternal extends RestrictedLockUtils { private static final String LOG_TAG = "RestrictedLockUtils"; private static final boolean DEBUG = Log.isLoggable(LOG_TAG, Log.DEBUG); + private static final Set<String> ECM_KEYS = new ArraySet<>(); + + static { + if (android.security.Flags.extendEcmToAllSettings()) { + ECM_KEYS.add(AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW); + ECM_KEYS.add(AppOpsManager.OPSTR_GET_USAGE_STATS); + ECM_KEYS.add(AppOpsManager.OPSTR_LOADER_USAGE_STATS); + ECM_KEYS.add(Manifest.permission.BIND_DEVICE_ADMIN); + } + } /** * @return drawables for displaying with settings that are locked by a device admin. @@ -81,6 +97,38 @@ public class RestrictedLockUtilsInternal extends RestrictedLockUtils { } /** + * Checks if a given permission requires additional confirmation for the given package + * + * @return An intent to show the user if additional confirmation is required, null otherwise + */ + @Nullable + public static Intent checkIfRequiresEnhancedConfirmation(@NonNull Context context, + @NonNull String restriction, + int uid, + @Nullable String packageName) { + // TODO(b/297372999): Replace with call to mainline module once ready + + if (!ECM_KEYS.contains(restriction)) { + return null; + } + + final AppOpsManager appOps = (AppOpsManager) context + .getSystemService(Context.APP_OPS_SERVICE); + final int mode = appOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS, + uid, packageName, null, null); + final boolean ecmEnabled = context.getResources().getBoolean( + com.android.internal.R.bool.config_enhancedConfirmationModeEnabled); + if (ecmEnabled && mode != AppOpsManager.MODE_ALLOWED) { + final Intent intent = new Intent(Settings.ACTION_SHOW_RESTRICTED_SETTING_DIALOG); + intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName); + intent.putExtra(Intent.EXTRA_UID, uid); + return intent; + } + + return null; + } + + /** * Checks if a restriction is enforced on a user and returns the enforced admin and * admin userId. * |