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

Commit bc915e7b authored by Diya Bera's avatar Diya Bera Committed by Android (Google) Code Review
Browse files

Merge "Add mandatory biometric prompt to platform surfaces (4/N)" into main

parents 39a56459 8f0c77bb
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();
    }
}