Merge "Update Special App Access Compose Components for ECM" into main
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 3acc9ad..b6d9242 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 @@
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 @@
fun sendShowAdminSupportDetailsIntent()
}
+interface BlockedByEcm : RestrictedMode {
+ fun showRestrictedSettingsDetails()
+}
+
+
internal data class BlockedByAdminImpl(
private val context: Context,
private val enforcedAdmin: RestrictedLockUtils.EnforcedAdmin,
@@ -55,3 +61,13 @@
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 550966b..9432d59 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.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 @@
.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 06b3eab..25c3bc5 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 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 5655436..1c830c1 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.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 @@
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 8704f20..916d83a 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 @@
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 36c91f4..4b47437 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.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 @@
@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 d5c5574..cd72025 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 @@
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 fa44ecb..aba3460 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.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 @@
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 @@
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 @@
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 f9abefc..977615b 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 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 @@
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 e77964c..4454b71 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 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.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 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 @@
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 @@
}
/**
+ * 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.
*