Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f16fa584 authored by Shraddha Basantwani's avatar Shraddha Basantwani Committed by Android Build Coastguard Worker
Browse files

[SPA] Add biometric authentication for package modification

Add an extra step of Lock Screen for disabling, force-stopping or
uninstalling updates for protected packages

Bug: 352504490, 344865740
Test: atest AppButtonsPreferenceControllerTest PackageInfoPresenterTest
Flag: EXEMPT High Security Bug
(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:32e388ad3199de3c062bb2e2db5d3239f934d0eb)
(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:d31280f0aa61c4e19d667ad4d4ffb175291c7b1d)
Merged-In: I0c494e307b02229d751de118abcc89e4e61a6861
Change-Id: I0c494e307b02229d751de118abcc89e4e61a6861
parent 2f92c177
Loading
Loading
Loading
Loading
+16 −0
Original line number Diff line number Diff line
@@ -125,6 +125,7 @@ import com.android.settings.password.ChooseLockSettingsHelper;
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;
@@ -1482,4 +1483,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);
    }
}
+46 −18
Original line number Diff line number Diff line
@@ -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;
@@ -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));
@@ -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;
        }
    }
@@ -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);
@@ -543,6 +570,7 @@ public class AppButtonsPreferenceController extends BasePreferenceController imp

            mMetricsFeatureProvider.action(mActivity, SettingsEnums.ACTION_SETTINGS_UNINSTALL_APP);
            mFragment.startActivityForResult(uninstallIntent, mRequestUninstall);
        });
    }

    @VisibleForTesting
+33 −15
Original line number Diff line number Diff line
@@ -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
@@ -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)
@@ -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() {
@@ -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()) {
@@ -166,6 +183,7 @@ class PackageInfoPresenter(
                context.activityManager.forceStopPackageAsUser(packageName, userId)
            }
        }
    }

    fun logAction(category: Int) {
        metricsFeatureProvider.action(context, category, packageName)
+3 −0
Original line number Diff line number Diff line
@@ -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;
@@ -85,6 +86,7 @@ import org.robolectric.util.ReflectionHelpers;

import java.util.Set;

@Config(shadows = {ShadowUtils.class})
@RunWith(RobolectricTestRunner.class)
public class AppButtonsPreferenceControllerTest {

@@ -168,6 +170,7 @@ public class AppButtonsPreferenceControllerTest {
    @After
    public void tearDown() {
        ShadowAppUtils.reset();
        ShadowUtils.reset();
    }

    @Test
+11 −0
Original line number Diff line number Diff line
@@ -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) {
@@ -84,6 +85,7 @@ public class ShadowUtils {
        sIsBatteryPresent = true;
        sIsMultipleBiometricsSupported = false;
        sIsPrivateProfile = false;
        sIsProtectedPackage = false;
    }

    public static void setIsDemoUser(boolean isDemoUser) {
@@ -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