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

Commit 8f0c77bb authored by Diya Bera's avatar Diya Bera
Browse files

Add mandatory biometric prompt to platform surfaces (4/N)

1. Enable developer options via build info
2. Enable developer options via toggle under system -> developer options

Flag: android.hardware.biometrics.flags.mandatory_biometrics
Fixes: 355500452
Test: atest BuildNumberPreferenceControllerTest
DevelopmentSettingsDashboardFragmentTest

Change-Id: Iecbe34024d287e71e235becec3ce5a2bd5c1697f
parent 2e12b95c
Loading
Loading
Loading
Loading
+22 −1
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.settings.development;

import static android.app.Activity.RESULT_OK;
import static android.provider.Settings.Global.DEVELOPMENT_SETTINGS_ENABLED;
import static android.service.quicksettings.TileService.ACTION_QS_TILE_PREFERENCES;
import static android.view.flags.Flags.sensitiveContentAppProtectionApi;
@@ -100,11 +101,13 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra
        NfcRebootDialog.OnNfcRebootDialogConfirmedListener, BluetoothSnoopLogHost {

    private static final String TAG = "DevSettingsDashboard";
    @VisibleForTesting static final int REQUEST_BIOMETRIC_PROMPT = 100;

    private final BluetoothA2dpConfigStore mBluetoothA2dpConfigStore =
            new BluetoothA2dpConfigStore();

    private boolean mIsAvailable = true;
    private boolean mIsBiometricsAuthenticated;
    private SettingsMainSwitchBar mSwitchBar;
    private DevelopmentSwitchBarController mSwitchBarController;
    private List<AbstractPreferenceController> mPreferenceControllers = new ArrayList<>();
@@ -216,6 +219,7 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra
    public void onStart() {
        super.onStart();
        final ContentResolver cr = getContext().getContentResolver();
        mIsBiometricsAuthenticated = false;
        cr.registerContentObserver(mDevelopEnabled, false, mDeveloperSettingsObserver);

        // Restore UI state based on whether developer options is enabled
@@ -360,7 +364,18 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra
                DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(getContext());
        if (isChecked != developmentEnabledState) {
            if (isChecked) {
                final int userId = getContext().getUserId();
                if (Utils.requestBiometricAuthenticationForMandatoryBiometrics(getContext(),
                        mIsBiometricsAuthenticated,
                        false /* biometricsAuthenticationRequested */, userId)) {
                    mSwitchBar.setChecked(false);
                    Utils.launchBiometricPromptForMandatoryBiometrics(this,
                            REQUEST_BIOMETRIC_PROMPT, userId, false /* hideBackground */);
                } else {
                    //Reset biometrics once enable dialog is shown
                    mIsBiometricsAuthenticated = false;
                    EnableDevelopmentSettingWarningDialog.show(this /* host */);
                }
            } else {
                final BluetoothA2dpHwOffloadPreferenceController a2dpController =
                        getDevelopmentOptionsController(
@@ -534,6 +549,12 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra
    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        boolean handledResult = false;
        if (requestCode == REQUEST_BIOMETRIC_PROMPT) {
            if (resultCode == RESULT_OK) {
                mIsBiometricsAuthenticated = true;
                mSwitchBar.setChecked(true);
            }
        }
        for (AbstractPreferenceController controller : mPreferenceControllers) {
            if (controller instanceof OnActivityResultListener) {
                // We do not break early because it is possible for multiple controllers to
+17 −2
Original line number Diff line number Diff line
@@ -55,6 +55,7 @@ public class BuildNumberPreferenceController extends BasePreferenceController im

    static final int TAPS_TO_BE_A_DEVELOPER = 7;
    static final int REQUEST_CONFIRM_PASSWORD_FOR_DEV_PREF = 100;
    static final int REQUEST_IDENTITY_CHECK_FOR_DEV_PREF = 101;

    private Activity mActivity;
    private InstrumentedPreferenceFragment mFragment;
@@ -217,10 +218,24 @@ public class BuildNumberPreferenceController extends BasePreferenceController im
     * @return if activity result is handled.
     */
    public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode != REQUEST_CONFIRM_PASSWORD_FOR_DEV_PREF) {
        if (requestCode != REQUEST_CONFIRM_PASSWORD_FOR_DEV_PREF
                && requestCode != REQUEST_IDENTITY_CHECK_FOR_DEV_PREF) {
            return false;
        }
        if (resultCode == Activity.RESULT_OK) {
        if (requestCode == REQUEST_CONFIRM_PASSWORD_FOR_DEV_PREF
                && resultCode == Activity.RESULT_OK) {
            final int userId = mContext.getUserId();
            if (Utils.requestBiometricAuthenticationForMandatoryBiometrics(mContext,
                    false /* biometricsSuccessfullyAuthenticated */,
                    false /* biometricsAuthenticationRequested */,
                    userId)) {
                Utils.launchBiometricPromptForMandatoryBiometrics(mFragment,
                        REQUEST_IDENTITY_CHECK_FOR_DEV_PREF, userId, false /* hideBackground */);
            } else {
                enableDevelopmentSettings();
            }
        } else if (requestCode == REQUEST_IDENTITY_CHECK_FOR_DEV_PREF
                && resultCode == Activity.RESULT_OK) {
            enableDevelopmentSettings();
        }
        mProcessingLastDevHit = false;
+59 −2
Original line number Diff line number Diff line
@@ -18,13 +18,21 @@ package com.android.settings.development;

import static com.google.common.truth.Truth.assertThat;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.app.Activity;
import android.content.Context;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.Flags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.SearchIndexableResource;
import android.provider.Settings;

@@ -42,6 +50,7 @@ import com.android.settingslib.development.DevelopmentSettingsEnabler;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
@@ -51,6 +60,7 @@ import org.robolectric.annotation.Config;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.ShadowBiometricManager;
import org.robolectric.shadows.androidx.fragment.FragmentController;
import org.robolectric.util.ReflectionHelpers;

@@ -61,22 +71,34 @@ import java.util.List;
        ShadowAlertDialogCompat.class,
        ShadowUserManager.class,
        ShadowUserManager.class,
        ShadowBiometricManager.class,
})
public class DevelopmentSettingsDashboardFragmentTest {
    @Rule
    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();

    private Context mContext;
    private ShadowUserManager mShadowUserManager;
    private ShadowBiometricManager mShadowBiometricManager;
    private DevelopmentSettingsDashboardFragment mDashboard;
    private SettingsMainSwitchBar mSwitchBar;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        mContext = RuntimeEnvironment.application;
        SettingsMainSwitchBar switchBar = new SettingsMainSwitchBar(mContext);
        mSwitchBar = new SettingsMainSwitchBar(mContext);
        mDashboard = spy(new DevelopmentSettingsDashboardFragment());
        ReflectionHelpers.setField(mDashboard, "mSwitchBar", switchBar);
        ReflectionHelpers.setField(mDashboard, "mSwitchBar", mSwitchBar);
        mShadowUserManager = Shadow.extract(mContext.getSystemService(Context.USER_SERVICE));
        mShadowUserManager.setIsAdminUser(true);
        mShadowBiometricManager = Shadow.extract(mContext.getSystemService(
                Context.BIOMETRIC_SERVICE));
        mShadowBiometricManager.setCanAuthenticate(false);
        //TODO(b/352603684): Should be Authenticators.MANDATORY_BIOMETRICS,
        // but it is not supported by ShadowBiometricManager
        mShadowBiometricManager.setAuthenticatorType(
                BiometricManager.Authenticators.BIOMETRIC_STRONG);
    }

    @After
@@ -176,6 +198,41 @@ public class DevelopmentSettingsDashboardFragmentTest {
        assertThat(ShadowEnableDevelopmentSettingWarningDialog.mShown).isTrue();
    }

    @Test
    @Config(shadows = ShadowEnableDevelopmentSettingWarningDialog.class)
    @EnableFlags(Flags.FLAG_MANDATORY_BIOMETRICS)
    public void onSwitchChanged_turnOn_shouldLaunchBiometricPromptIfMandatoryBiometricsEffective() {
        when(mDashboard.getContext()).thenReturn(mContext);
        doNothing().when(mDashboard).startActivityForResult(any(),
                eq(DevelopmentSettingsDashboardFragment.REQUEST_BIOMETRIC_PROMPT));

        Settings.Global.putInt(mContext.getContentResolver(),
                Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0);
        mShadowBiometricManager.setCanAuthenticate(true);
        mDashboard.onCheckedChanged(null, true /* isChecked */);

        assertThat(mSwitchBar.isChecked()).isFalse();
        verify(mDashboard).startActivityForResult(any(),
                eq(DevelopmentSettingsDashboardFragment.REQUEST_BIOMETRIC_PROMPT));
        assertThat(ShadowEnableDevelopmentSettingWarningDialog.mShown).isFalse();
    }

    @Test
    @Config(shadows = ShadowEnableDevelopmentSettingWarningDialog.class)
    @EnableFlags(Flags.FLAG_MANDATORY_BIOMETRICS)
    public void onActivityResult_requestBiometricPrompt_shouldShowWarningDialog() {
        when(mDashboard.getContext()).thenReturn(mContext);

        Settings.Global.putInt(mContext.getContentResolver(),
                Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0);
        mDashboard.onActivityResult(DevelopmentSettingsDashboardFragment.REQUEST_BIOMETRIC_PROMPT,
                Activity.RESULT_OK, null);
        mDashboard.onCheckedChanged(null, true /* isChecked */);

        assertThat(mSwitchBar.isChecked()).isTrue();
        assertThat(ShadowEnableDevelopmentSettingWarningDialog.mShown).isTrue();
    }

    @Test
    @Ignore
    @Config(shadows = ShadowEnableDevelopmentSettingWarningDialog.class)
+51 −1
Original line number Diff line number Diff line
@@ -28,8 +28,13 @@ import static org.mockito.Mockito.when;

import android.app.Activity;
import android.content.Context;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.Flags;
import android.os.Looper;
import android.os.UserManager;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.provider.Settings;

import androidx.lifecycle.LifecycleOwner;
@@ -45,6 +50,7 @@ import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.development.DevelopmentSettingsEnabler;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
@@ -53,6 +59,9 @@ import org.mockito.MockitoAnnotations;

@RunWith(AndroidJUnit4.class)
public class BuildNumberPreferenceControllerTest {
    @Rule
    public final CheckFlagsRule mCheckFlagsRule =
            DeviceFlagsValueProvider.createCheckFlagsRule();

    private static final String KEY_BUILD_NUMBER = "build_number";
    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
@@ -60,6 +69,7 @@ public class BuildNumberPreferenceControllerTest {

    private Context mContext;
    private UserManager mUserManager;
    private BiometricManager mBiometricManager;
    private LifecycleOwner mLifecycleOwner;
    private Lifecycle mLifecycle;
    private FakeFeatureFactory mFactory;
@@ -76,7 +86,13 @@ public class BuildNumberPreferenceControllerTest {

        mContext = spy(ApplicationProvider.getApplicationContext());
        mUserManager = (UserManager) spy(mContext.getSystemService(Context.USER_SERVICE));
        mBiometricManager = spy(mContext.getSystemService(BiometricManager.class));

        doReturn(mUserManager).when(mContext).getSystemService(Context.USER_SERVICE);
        when(mContext.getSystemService(BiometricManager.class)).thenReturn(mBiometricManager);
        when(mBiometricManager.canAuthenticate(mContext.getUserId(),
                BiometricManager.Authenticators.MANDATORY_BIOMETRICS))
                .thenReturn(BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE);

        mFactory = FakeFeatureFactory.setupForTest();
        mLifecycleOwner = () -> mLifecycle;
@@ -156,7 +172,7 @@ public class BuildNumberPreferenceControllerTest {
    @Test
    public void onActivityResult_notConfirmPasswordRequest_doNothing() {
        final boolean activityResultHandled = mController.onActivityResult(
                BuildNumberPreferenceController.REQUEST_CONFIRM_PASSWORD_FOR_DEV_PREF + 1,
                BuildNumberPreferenceController.REQUEST_CONFIRM_PASSWORD_FOR_DEV_PREF + 2,
                Activity.RESULT_OK,
                null);

@@ -188,4 +204,38 @@ public class BuildNumberPreferenceControllerTest {
        assertThat(activityResultHandled).isTrue();
        assertThat(DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext)).isTrue();
    }

    @Test
    @UiThreadTest
    @RequiresFlagsEnabled(Flags.FLAG_MANDATORY_BIOMETRICS)
    public void onActivityResult_confirmPasswordRequestCompleted_launchBiometricPrompt() {
        when(mUserManager.isAdminUser()).thenReturn(true);
        when(mBiometricManager.canAuthenticate(mContext.getUserId(),
                BiometricManager.Authenticators.MANDATORY_BIOMETRICS))
                .thenReturn(BiometricManager.BIOMETRIC_SUCCESS);

        final boolean activityResultHandled = mController.onActivityResult(
                BuildNumberPreferenceController.REQUEST_CONFIRM_PASSWORD_FOR_DEV_PREF,
                Activity.RESULT_OK,
                null);

        assertThat(activityResultHandled).isTrue();
        assertThat(DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext)).isFalse();
        verify(mFragment).startActivityForResult(any(),
                eq(BuildNumberPreferenceController.REQUEST_IDENTITY_CHECK_FOR_DEV_PREF));
    }

    @Test
    public void onActivityResult_confirmBiometricAuthentication_enableDevPref() {
        when(mUserManager.isAdminUser()).thenReturn(true);

        Looper.prepare();
        final boolean activityResultHandled = mController.onActivityResult(
                BuildNumberPreferenceController.REQUEST_IDENTITY_CHECK_FOR_DEV_PREF,
                Activity.RESULT_OK,
                null);

        assertThat(activityResultHandled).isTrue();
        assertThat(DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext)).isTrue();
    }
}