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

Commit f15e98e1 authored by Shawn Lin's avatar Shawn Lin Committed by Android (Google) Code Review
Browse files

Merge "Provide a way to customized the max enroll count for face" into main

parents 8a00a8b2 67b277fd
Loading
Loading
Loading
Loading
+16 −8
Original line number Diff line number Diff line
@@ -312,8 +312,10 @@ public class BiometricEnrollActivity extends InstrumentedActivity {
                    .getInteger(R.integer.suw_max_faces_enrollable);
            if (!faceProperties.isEmpty()) {
                final FaceSensorPropertiesInternal props = faceProperties.get(0);
                final int maxEnrolls =
                        isSetupWizard ? maxFacesEnrollableIfSUW : props.maxEnrollmentsPerUser;
                final int maxEnrolls = isSetupWizard
                                ? maxFacesEnrollableIfSUW
                                : FeatureFactory.getFeatureFactory().getFaceFeatureProvider()
                                        .getMaxEnrollableCount(getApplicationContext());
                final boolean isFaceStrong =
                        props.sensorStrength == SensorProperties.STRENGTH_STRONG;
                mIsFaceEnrollable =
@@ -529,9 +531,10 @@ public class BiometricEnrollActivity extends InstrumentedActivity {
    private void handleOnActivityResultWhileEnrolling(
            int requestCode, int resultCode, Intent data) {

        Log.d(TAG, "handleOnActivityResultWhileEnrolling, request = " + requestCode + ""
        Log.d(TAG, "handleOnActivityResultWhileEnrolling, request = " + requestCode
                + ", resultCode = " + resultCode + ", launchFaceEnrollFirst="
                + mLaunchFaceEnrollFirst);
                + mLaunchFaceEnrollFirst + ", mIsFingerprintEnrollable="
                + mIsFingerprintEnrollable + ", mIsFaceEnrollable=" + mIsFaceEnrollable);
        updateOnboardingEventList(data);
        switch (requestCode) {
            case REQUEST_HANDOFF_PARENT:
@@ -563,10 +566,12 @@ public class BiometricEnrollActivity extends InstrumentedActivity {
                break;
            case REQUEST_SINGLE_ENROLL_FINGERPRINT:
                mIsSingleEnrolling = false;
                if (resultCode == BiometricEnrollBase.RESULT_FINISHED) {
                if (resultCode == BiometricEnrollBase.RESULT_FINISHED
                        || resultCode == Activity.RESULT_CANCELED) {
                    // FingerprintEnrollIntroduction's visibility is determined by
                    // mIsFingerprintEnrollable. Keep this value up-to-date after a successful
                    // enrollment.
                    // enrollment. Also update it when result code is cancel for the case that user
                    // triggers back key in confirmation page.
                    updateFingerprintEnrollable(WizardManagerHelper.isAnySetupWizard(getIntent()));
                }
                if ((resultCode == BiometricEnrollBase.RESULT_SKIP
@@ -589,9 +594,12 @@ public class BiometricEnrollActivity extends InstrumentedActivity {
                break;
            case REQUEST_SINGLE_ENROLL_FACE:
                mIsSingleEnrolling = false;
                if (resultCode == BiometricEnrollBase.RESULT_FINISHED) {
                if (resultCode == BiometricEnrollBase.RESULT_FINISHED
                        || resultCode == Activity.RESULT_CANCELED) {
                    // FaceEnrollIntroduction's visibility is determined by mIsFaceEnrollable.
                    // Keep this value up-to-date after a successful enrollment.
                    // Keep this value up-to-date after a successful enrollment. Also update it when
                    // result code is cancel for the case that user triggers back key in
                    // confirmation page.
                    updateFaceEnrollable(WizardManagerHelper.isAnySetupWizard(getIntent()));
                }
                if ((resultCode == BiometricEnrollBase.RESULT_SKIP
+3 −0
Original line number Diff line number Diff line
@@ -51,4 +51,7 @@ public interface FaceFeatureProvider {
    default FaceSettingsFeatureProvider getFaceSettingsFeatureProvider() {
        return FaceSettingsFeatureProvider.getInstance();
    }

    /** Returns the max enrollable count. */
    int getMaxEnrollableCount(@NonNull Context context);
}
+37 −0
Original line number Diff line number Diff line
@@ -19,14 +19,42 @@ package com.android.settings.biometrics.face;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.hardware.face.FaceManager;
import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.face.IFaceAuthenticatorsRegisteredCallback;
import android.text.TextUtils;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.android.settings.R;
import com.android.settings.Utils;

import java.util.List;

public class FaceFeatureProviderImpl implements FaceFeatureProvider {
    private static final String TAG = "FaceFeatureProvider";

    private int mMaxEnrollableCount = -1;

    public FaceFeatureProviderImpl(@NonNull Context context) {
        final FaceManager faceManager = Utils.getFaceManagerOrNull(context);
        if (faceManager != null) {
            faceManager.addAuthenticatorsRegisteredCallback(
                    new IFaceAuthenticatorsRegisteredCallback.Stub() {
                        @Override
                        public void onAllAuthenticatorsRegistered(
                                @NonNull List<FaceSensorPropertiesInternal> sensors) {
                            Log.d(TAG, "onAllAuthenticatorsRegistered sensors=" + sensors);
                            if (sensors.isEmpty()) {
                                return;
                            }
                            mMaxEnrollableCount = sensors.get(0).maxEnrollmentsPerUser;
                        }
                    });
        }
    }

    /**
     * Returns the guidance page intent if device support {@link FoldingFeature}, and we want to
@@ -60,4 +88,13 @@ public class FaceFeatureProviderImpl implements FaceFeatureProvider {
    public boolean isSetupWizardSupported(@NonNull Context context) {
        return true;
    }

    @Override
    public int getMaxEnrollableCount(@NonNull Context context) {
        if (mMaxEnrollableCount == -1) {
            Log.e(TAG, "The max enrollable count is not yet initialized.");
            return 0;
        }
        return mMaxEnrollableCount;
    }
}
+3 −1
Original line number Diff line number Diff line
@@ -157,7 +157,9 @@ open class FeatureFactoryImpl : FeatureFactory() {
        BiometricsFeatureProviderImpl()
    }

    override val faceFeatureProvider: FaceFeatureProvider by lazy { FaceFeatureProviderImpl() }
    override val faceFeatureProvider: FaceFeatureProvider by lazy {
        FaceFeatureProviderImpl(appContext)
    }

    override val fingerprintFeatureProvider: FingerprintFeatureProvider by lazy {
        FingerprintFeatureProviderImpl()
+100 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.settings.biometrics.face;

import static android.hardware.biometrics.SensorProperties.STRENGTH_STRONG;
import static android.hardware.face.FaceSensorProperties.TYPE_RGB;

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

import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.face.FaceManager;
import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.face.IFaceAuthenticatorsRegisteredCallback;
import android.os.RemoteException;

import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;

import java.util.ArrayList;

@RunWith(AndroidJUnit4.class)
public class FaceFeatureProviderImplTest {

    @Rule
    public final MockitoRule mMockitoRule = MockitoJUnit.rule();
    @Spy
    Context mContext = ApplicationProvider.getApplicationContext();
    @Mock
    private FaceManager mFaceManager;
    @Mock
    private PackageManager mPackageManager;

    private FaceFeatureProvider mProvider;
    private ArgumentCaptor<IFaceAuthenticatorsRegisteredCallback> mCallbackCaptor =
            ArgumentCaptor.forClass(IFaceAuthenticatorsRegisteredCallback.class);
    private FaceSensorPropertiesInternal mSensorProperty =
            new FaceSensorPropertiesInternal(
                    0 /* sensor id */,
                    STRENGTH_STRONG,
                    1 /* maxEnrollmentsPerUser */,
                    new ArrayList<ComponentInfoInternal>(),
                    TYPE_RGB,
                    true /* supportsFaceDetection */,
                    true /* supportsSelfIllumination */,
                    true /* resetLockoutRequiresChallenge */
            );

    @Before
    public void setUp() {
        when(mContext.getPackageManager()).thenReturn(mPackageManager);
        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(true);
        when(mContext.getSystemService(Context.FACE_SERVICE)).thenReturn(mFaceManager);

        mProvider = new FaceFeatureProviderImpl(mContext);
        verify(mFaceManager).addAuthenticatorsRegisteredCallback(mCallbackCaptor.capture());
    }

    @Test
    public void getMaxEnrollableCount_callbackCalled() throws RemoteException {
        final ArrayList<FaceSensorPropertiesInternal> list = new ArrayList<>();
        list.add(mSensorProperty);
        mCallbackCaptor.getValue().onAllAuthenticatorsRegistered(list);

        assertThat(mProvider.getMaxEnrollableCount(mContext)).isEqualTo(1);
    }

    @Test
    public void getMaxEnrollableCount_callbackNotCalled() {
        assertThat(mProvider.getMaxEnrollableCount(mContext)).isEqualTo(0);
    }
}