Loading src/com/android/settings/Utils.java +16 −0 Original line number Diff line number Diff line Loading @@ -131,6 +131,7 @@ import com.android.settings.password.ConfirmDeviceCredentialActivity; import com.android.settingslib.widget.ActionBarShadowController; import com.android.settingslib.widget.AdaptiveIcon; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Locale; Loading Loading @@ -1600,4 +1601,19 @@ public final class Utils extends com.android.settingslib.Utils { pm.setComponentEnabledSetting(componentName, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP); } /** * Returns {@code true} if the supplied package is a protected package. Otherwise, returns * {@code false}. * * @param context the context * @param packageName the package name */ public static boolean isProtectedPackage( @NonNull Context context, @NonNull String packageName) { final List<String> protectedPackageNames = Arrays.asList(context.getResources() .getStringArray(com.android.internal.R.array .config_biometric_protected_package_names)); return protectedPackageNames != null && protectedPackageNames.contains(packageName); } } src/com/android/settings/applications/appinfo/AppButtonsPreferenceController.java +46 −18 Original line number Diff line number Diff line Loading @@ -53,6 +53,7 @@ import com.android.settings.R; import com.android.settings.SettingsActivity; import com.android.settings.Utils; import com.android.settings.applications.ApplicationFeatureProvider; import com.android.settings.applications.appinfo.AppInfoDashboardFragment; import com.android.settings.applications.specialaccess.deviceadmin.DeviceAdminAdd; import com.android.settings.core.BasePreferenceController; import com.android.settings.core.InstrumentedPreferenceFragment; Loading Loading @@ -240,12 +241,20 @@ public class AppButtonsPreferenceController extends BasePreferenceController imp } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { if (mAppEntry.info.enabled && !isDisabledUntilUsed()) { showDialogInner(ButtonActionDialogFragment.DialogType.DISABLE); } else if (mAppEntry.info.enabled) { requireAuthAndExecute(() -> { mMetricsFeatureProvider.action( mActivity, SettingsEnums.ACTION_SETTINGS_DISABLE_APP, getPackageNameForMetric()); AsyncTask.execute(new DisableChangerRunnable(mPm, mAppEntry.info.packageName, PackageManager.COMPONENT_ENABLED_STATE_DEFAULT)); }); } else { mMetricsFeatureProvider.action( mActivity, mAppEntry.info.enabled ? SettingsEnums.ACTION_SETTINGS_DISABLE_APP : SettingsEnums.ACTION_SETTINGS_ENABLE_APP, SettingsEnums.ACTION_SETTINGS_ENABLE_APP, getPackageNameForMetric()); AsyncTask.execute(new DisableChangerRunnable(mPm, mAppEntry.info.packageName, PackageManager.COMPONENT_ENABLED_STATE_DEFAULT)); Loading Loading @@ -289,17 +298,34 @@ public class AppButtonsPreferenceController extends BasePreferenceController imp } } /** * Runs the given action with restricted lock authentication if it is a protected package. * * @param action The action to run. */ private void requireAuthAndExecute(Runnable action) { if (Utils.isProtectedPackage(mContext, mAppEntry.info.packageName)) { AppInfoDashboardFragment.showLockScreen(mContext, () -> action.run()); } else { action.run(); } } public void handleDialogClick(int id) { switch (id) { case ButtonActionDialogFragment.DialogType.DISABLE: requireAuthAndExecute(() -> { mMetricsFeatureProvider.action(mActivity, SettingsEnums.ACTION_SETTINGS_DISABLE_APP, getPackageNameForMetric()); AsyncTask.execute(new DisableChangerRunnable(mPm, mAppEntry.info.packageName, PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER)); }); break; case ButtonActionDialogFragment.DialogType.FORCE_STOP: requireAuthAndExecute(() -> { forceStopPackage(mAppEntry.info.packageName); }); break; } } Loading Loading @@ -535,6 +561,7 @@ public class AppButtonsPreferenceController extends BasePreferenceController imp @VisibleForTesting void uninstallPkg(String packageName, boolean allUsers) { requireAuthAndExecute(() -> { stopListeningToPackageRemove(); // Create new intent to launch Uninstaller activity Uri packageUri = Uri.parse("package:" + packageName); Loading @@ -543,6 +570,7 @@ public class AppButtonsPreferenceController extends BasePreferenceController imp mMetricsFeatureProvider.action(mActivity, SettingsEnums.ACTION_SETTINGS_UNINSTALL_APP); mFragment.startActivityForResult(uninstallIntent, mRequestUninstall); }); } @VisibleForTesting Loading src/com/android/settings/spa/app/appinfo/PackageInfoPresenter.kt +33 −15 Original line number Diff line number Diff line Loading @@ -29,6 +29,8 @@ import android.os.UserHandle import android.util.Log import androidx.annotation.VisibleForTesting import androidx.compose.runtime.Composable import com.android.settings.Utils import com.android.settings.applications.appinfo.AppInfoDashboardFragment import com.android.settings.flags.FeatureFlags import com.android.settings.flags.FeatureFlagsImpl import com.android.settings.overlay.FeatureFactory.Companion.featureFactory Loading Loading @@ -116,6 +118,16 @@ class PackageInfoPresenter( private fun isForThisApp(intent: Intent) = packageName == intent.data?.schemeSpecificPart private fun requireAuthAndExecute(action: () -> Unit) { if (Utils.isProtectedPackage(context, packageName)) { AppInfoDashboardFragment.showLockScreen(context) { action() } } else { action() } } /** Enables this package. */ fun enable() { logAction(SettingsEnums.ACTION_SETTINGS_ENABLE_APP) Loading @@ -129,18 +141,22 @@ class PackageInfoPresenter( /** Disables this package. */ fun disable() { logAction(SettingsEnums.ACTION_SETTINGS_DISABLE_APP) requireAuthAndExecute { coroutineScope.launch(Dispatchers.IO) { userPackageManager.setApplicationEnabledSetting( packageName, PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER, 0 ) } } } /** Starts the uninstallation activity. */ fun startUninstallActivity(forAllUsers: Boolean = false) { logAction(SettingsEnums.ACTION_SETTINGS_UNINSTALL_APP) requireAuthAndExecute { context.startUninstallActivity(packageName, userHandle, forAllUsers) } } /** Clears this instant app. */ fun clearInstantApp() { Loading @@ -153,6 +169,7 @@ class PackageInfoPresenter( /** Force stops this package. */ fun forceStop() { logAction(SettingsEnums.ACTION_APP_FORCE_STOP) requireAuthAndExecute { coroutineScope.launch(Dispatchers.Default) { Log.d(TAG, "Stopping package $packageName") if (android.app.Flags.appRestrictionsApi()) { Loading @@ -166,6 +183,7 @@ class PackageInfoPresenter( context.activityManager.forceStopPackageAsUser(packageName, userId) } } } fun logAction(category: Int) { metricsFeatureProvider.action(context, category, packageName) Loading tests/robotests/src/com/android/settings/applications/appinfo/AppButtonsPreferenceControllerTest.java +3 −0 Original line number Diff line number Diff line Loading @@ -60,6 +60,7 @@ import com.android.settings.R; import com.android.settings.SettingsActivity; import com.android.settings.core.InstrumentedPreferenceFragment; import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.shadow.ShadowUtils; import com.android.settingslib.applications.AppUtils; import com.android.settingslib.applications.ApplicationsState; import com.android.settingslib.applications.instantapps.InstantAppDataProvider; Loading @@ -85,6 +86,7 @@ import org.robolectric.util.ReflectionHelpers; import java.util.Set; @Config(shadows = {ShadowUtils.class}) @RunWith(RobolectricTestRunner.class) public class AppButtonsPreferenceControllerTest { Loading Loading @@ -168,6 +170,7 @@ public class AppButtonsPreferenceControllerTest { @After public void tearDown() { ShadowAppUtils.reset(); ShadowUtils.reset(); } @Test Loading tests/robotests/testutils/com/android/settings/testutils/shadow/ShadowUtils.java +11 −0 Original line number Diff line number Diff line Loading @@ -51,6 +51,7 @@ public class ShadowUtils { private static boolean sIsBatteryPresent; private static boolean sIsMultipleBiometricsSupported; private static boolean sIsPrivateProfile; private static boolean sIsProtectedPackage; @Implementation protected static int enforceSameOwner(Context context, int userId) { Loading Loading @@ -84,6 +85,7 @@ public class ShadowUtils { sIsBatteryPresent = true; sIsMultipleBiometricsSupported = false; sIsPrivateProfile = false; sIsProtectedPackage = false; } public static void setIsDemoUser(boolean isDemoUser) { Loading Loading @@ -199,4 +201,13 @@ public class ShadowUtils { public static void setIsPrivateProfile(boolean isPrivateProfile) { sIsPrivateProfile = isPrivateProfile; } @Implementation protected static boolean isProtectedPackage(Context context, String packageName) { return sIsProtectedPackage; } public static void setIsProtectedPackage(boolean isProtectedPackage) { sIsProtectedPackage = isProtectedPackage; } } Loading
src/com/android/settings/Utils.java +16 −0 Original line number Diff line number Diff line Loading @@ -131,6 +131,7 @@ import com.android.settings.password.ConfirmDeviceCredentialActivity; import com.android.settingslib.widget.ActionBarShadowController; import com.android.settingslib.widget.AdaptiveIcon; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Locale; Loading Loading @@ -1600,4 +1601,19 @@ public final class Utils extends com.android.settingslib.Utils { pm.setComponentEnabledSetting(componentName, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP); } /** * Returns {@code true} if the supplied package is a protected package. Otherwise, returns * {@code false}. * * @param context the context * @param packageName the package name */ public static boolean isProtectedPackage( @NonNull Context context, @NonNull String packageName) { final List<String> protectedPackageNames = Arrays.asList(context.getResources() .getStringArray(com.android.internal.R.array .config_biometric_protected_package_names)); return protectedPackageNames != null && protectedPackageNames.contains(packageName); } }
src/com/android/settings/applications/appinfo/AppButtonsPreferenceController.java +46 −18 Original line number Diff line number Diff line Loading @@ -53,6 +53,7 @@ import com.android.settings.R; import com.android.settings.SettingsActivity; import com.android.settings.Utils; import com.android.settings.applications.ApplicationFeatureProvider; import com.android.settings.applications.appinfo.AppInfoDashboardFragment; import com.android.settings.applications.specialaccess.deviceadmin.DeviceAdminAdd; import com.android.settings.core.BasePreferenceController; import com.android.settings.core.InstrumentedPreferenceFragment; Loading Loading @@ -240,12 +241,20 @@ public class AppButtonsPreferenceController extends BasePreferenceController imp } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { if (mAppEntry.info.enabled && !isDisabledUntilUsed()) { showDialogInner(ButtonActionDialogFragment.DialogType.DISABLE); } else if (mAppEntry.info.enabled) { requireAuthAndExecute(() -> { mMetricsFeatureProvider.action( mActivity, SettingsEnums.ACTION_SETTINGS_DISABLE_APP, getPackageNameForMetric()); AsyncTask.execute(new DisableChangerRunnable(mPm, mAppEntry.info.packageName, PackageManager.COMPONENT_ENABLED_STATE_DEFAULT)); }); } else { mMetricsFeatureProvider.action( mActivity, mAppEntry.info.enabled ? SettingsEnums.ACTION_SETTINGS_DISABLE_APP : SettingsEnums.ACTION_SETTINGS_ENABLE_APP, SettingsEnums.ACTION_SETTINGS_ENABLE_APP, getPackageNameForMetric()); AsyncTask.execute(new DisableChangerRunnable(mPm, mAppEntry.info.packageName, PackageManager.COMPONENT_ENABLED_STATE_DEFAULT)); Loading Loading @@ -289,17 +298,34 @@ public class AppButtonsPreferenceController extends BasePreferenceController imp } } /** * Runs the given action with restricted lock authentication if it is a protected package. * * @param action The action to run. */ private void requireAuthAndExecute(Runnable action) { if (Utils.isProtectedPackage(mContext, mAppEntry.info.packageName)) { AppInfoDashboardFragment.showLockScreen(mContext, () -> action.run()); } else { action.run(); } } public void handleDialogClick(int id) { switch (id) { case ButtonActionDialogFragment.DialogType.DISABLE: requireAuthAndExecute(() -> { mMetricsFeatureProvider.action(mActivity, SettingsEnums.ACTION_SETTINGS_DISABLE_APP, getPackageNameForMetric()); AsyncTask.execute(new DisableChangerRunnable(mPm, mAppEntry.info.packageName, PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER)); }); break; case ButtonActionDialogFragment.DialogType.FORCE_STOP: requireAuthAndExecute(() -> { forceStopPackage(mAppEntry.info.packageName); }); break; } } Loading Loading @@ -535,6 +561,7 @@ public class AppButtonsPreferenceController extends BasePreferenceController imp @VisibleForTesting void uninstallPkg(String packageName, boolean allUsers) { requireAuthAndExecute(() -> { stopListeningToPackageRemove(); // Create new intent to launch Uninstaller activity Uri packageUri = Uri.parse("package:" + packageName); Loading @@ -543,6 +570,7 @@ public class AppButtonsPreferenceController extends BasePreferenceController imp mMetricsFeatureProvider.action(mActivity, SettingsEnums.ACTION_SETTINGS_UNINSTALL_APP); mFragment.startActivityForResult(uninstallIntent, mRequestUninstall); }); } @VisibleForTesting Loading
src/com/android/settings/spa/app/appinfo/PackageInfoPresenter.kt +33 −15 Original line number Diff line number Diff line Loading @@ -29,6 +29,8 @@ import android.os.UserHandle import android.util.Log import androidx.annotation.VisibleForTesting import androidx.compose.runtime.Composable import com.android.settings.Utils import com.android.settings.applications.appinfo.AppInfoDashboardFragment import com.android.settings.flags.FeatureFlags import com.android.settings.flags.FeatureFlagsImpl import com.android.settings.overlay.FeatureFactory.Companion.featureFactory Loading Loading @@ -116,6 +118,16 @@ class PackageInfoPresenter( private fun isForThisApp(intent: Intent) = packageName == intent.data?.schemeSpecificPart private fun requireAuthAndExecute(action: () -> Unit) { if (Utils.isProtectedPackage(context, packageName)) { AppInfoDashboardFragment.showLockScreen(context) { action() } } else { action() } } /** Enables this package. */ fun enable() { logAction(SettingsEnums.ACTION_SETTINGS_ENABLE_APP) Loading @@ -129,18 +141,22 @@ class PackageInfoPresenter( /** Disables this package. */ fun disable() { logAction(SettingsEnums.ACTION_SETTINGS_DISABLE_APP) requireAuthAndExecute { coroutineScope.launch(Dispatchers.IO) { userPackageManager.setApplicationEnabledSetting( packageName, PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER, 0 ) } } } /** Starts the uninstallation activity. */ fun startUninstallActivity(forAllUsers: Boolean = false) { logAction(SettingsEnums.ACTION_SETTINGS_UNINSTALL_APP) requireAuthAndExecute { context.startUninstallActivity(packageName, userHandle, forAllUsers) } } /** Clears this instant app. */ fun clearInstantApp() { Loading @@ -153,6 +169,7 @@ class PackageInfoPresenter( /** Force stops this package. */ fun forceStop() { logAction(SettingsEnums.ACTION_APP_FORCE_STOP) requireAuthAndExecute { coroutineScope.launch(Dispatchers.Default) { Log.d(TAG, "Stopping package $packageName") if (android.app.Flags.appRestrictionsApi()) { Loading @@ -166,6 +183,7 @@ class PackageInfoPresenter( context.activityManager.forceStopPackageAsUser(packageName, userId) } } } fun logAction(category: Int) { metricsFeatureProvider.action(context, category, packageName) Loading
tests/robotests/src/com/android/settings/applications/appinfo/AppButtonsPreferenceControllerTest.java +3 −0 Original line number Diff line number Diff line Loading @@ -60,6 +60,7 @@ import com.android.settings.R; import com.android.settings.SettingsActivity; import com.android.settings.core.InstrumentedPreferenceFragment; import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.shadow.ShadowUtils; import com.android.settingslib.applications.AppUtils; import com.android.settingslib.applications.ApplicationsState; import com.android.settingslib.applications.instantapps.InstantAppDataProvider; Loading @@ -85,6 +86,7 @@ import org.robolectric.util.ReflectionHelpers; import java.util.Set; @Config(shadows = {ShadowUtils.class}) @RunWith(RobolectricTestRunner.class) public class AppButtonsPreferenceControllerTest { Loading Loading @@ -168,6 +170,7 @@ public class AppButtonsPreferenceControllerTest { @After public void tearDown() { ShadowAppUtils.reset(); ShadowUtils.reset(); } @Test Loading
tests/robotests/testutils/com/android/settings/testutils/shadow/ShadowUtils.java +11 −0 Original line number Diff line number Diff line Loading @@ -51,6 +51,7 @@ public class ShadowUtils { private static boolean sIsBatteryPresent; private static boolean sIsMultipleBiometricsSupported; private static boolean sIsPrivateProfile; private static boolean sIsProtectedPackage; @Implementation protected static int enforceSameOwner(Context context, int userId) { Loading Loading @@ -84,6 +85,7 @@ public class ShadowUtils { sIsBatteryPresent = true; sIsMultipleBiometricsSupported = false; sIsPrivateProfile = false; sIsProtectedPackage = false; } public static void setIsDemoUser(boolean isDemoUser) { Loading Loading @@ -199,4 +201,13 @@ public class ShadowUtils { public static void setIsPrivateProfile(boolean isPrivateProfile) { sIsPrivateProfile = isPrivateProfile; } @Implementation protected static boolean isProtectedPackage(Context context, String packageName) { return sIsProtectedPackage; } public static void setIsProtectedPackage(boolean isProtectedPackage) { sIsProtectedPackage = isProtectedPackage; } }