Loading src/com/android/settings/biometrics/face/FaceEnrollSidecar.java +3 −5 Original line number Diff line number Diff line Loading @@ -19,9 +19,7 @@ package com.android.settings.biometrics.face; import android.app.Activity; import android.app.settings.SettingsEnums; import android.hardware.face.FaceManager; import android.os.UserHandle; import com.android.settings.Utils; import com.android.settings.biometrics.BiometricEnrollSidecar; import java.util.Arrays; Loading @@ -33,7 +31,7 @@ public class FaceEnrollSidecar extends BiometricEnrollSidecar { private final int[] mDisabledFeatures; private FaceManager mFaceManager; private FaceUpdater mFaceUpdater; public FaceEnrollSidecar(int[] disabledFeatures) { mDisabledFeatures = Arrays.copyOf(disabledFeatures, disabledFeatures.length); Loading @@ -42,13 +40,13 @@ public class FaceEnrollSidecar extends BiometricEnrollSidecar { @Override public void onAttach(Activity activity) { super.onAttach(activity); mFaceManager = Utils.getFaceManagerOrNull(activity); mFaceUpdater = new FaceUpdater(activity); } @Override public void startEnrollment() { super.startEnrollment(); mFaceManager.enroll(mUserId, mToken, mEnrollmentCancel, mFaceUpdater.enroll(mUserId, mToken, mEnrollmentCancel, mEnrollmentCallback, mDisabledFeatures); } Loading src/com/android/settings/biometrics/face/FaceSettingsRemoveButtonPreferenceController.java +3 −1 Original line number Diff line number Diff line Loading @@ -103,6 +103,7 @@ public class FaceSettingsRemoveButtonPreferenceController extends BasePreference private final MetricsFeatureProvider mMetricsFeatureProvider; private final Context mContext; private final FaceManager mFaceManager; private final FaceUpdater mFaceUpdater; private final FaceManager.RemovalCallback mRemovalCallback = new FaceManager.RemovalCallback() { @Override public void onRemovalError(Face face, int errMsgId, CharSequence errString) { Loading Loading @@ -144,7 +145,7 @@ public class FaceSettingsRemoveButtonPreferenceController extends BasePreference } // Remove the first/only face mFaceManager.remove(faces.get(0), mUserId, mRemovalCallback); mFaceUpdater.remove(faces.get(0), mUserId, mRemovalCallback); } else { mButton.setEnabled(true); mRemoving = false; Loading @@ -157,6 +158,7 @@ public class FaceSettingsRemoveButtonPreferenceController extends BasePreference mContext = context; mFaceManager = context.getSystemService(FaceManager.class); mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider(); mFaceUpdater = new FaceUpdater(context, mFaceManager); } public FaceSettingsRemoveButtonPreferenceController(Context context) { Loading src/com/android/settings/biometrics/face/FaceUpdater.java 0 → 100644 +137 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 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 android.content.Context; import android.hardware.face.Face; import android.hardware.face.FaceEnrollCell; import android.hardware.face.FaceManager; import android.os.CancellationSignal; import android.view.Surface; import androidx.annotation.Nullable; import com.android.settings.Utils; import com.android.settings.safetycenter.BiometricsSafetySource; /** * Responsible for making {@link FaceManager#enroll} and {@link FaceManager#remove} calls and thus * updating the face setting. */ public class FaceUpdater { private final Context mContext; private final FaceManager mFaceManager; public FaceUpdater(Context context) { mContext = context; mFaceManager = Utils.getFaceManagerOrNull(context); } public FaceUpdater(Context context, FaceManager faceManager) { mContext = context; mFaceManager = faceManager; } /** Wrapper around the {@link FaceManager#enroll} method. */ public void enroll(int userId, byte[] hardwareAuthToken, CancellationSignal cancel, FaceManager.EnrollmentCallback callback, int[] disabledFeatures) { mFaceManager.enroll(userId, hardwareAuthToken, cancel, new NotifyingEnrollmentCallback(mContext, callback), disabledFeatures); } /** Wrapper around the {@link FaceManager#enroll} method. */ public void enroll(int userId, byte[] hardwareAuthToken, CancellationSignal cancel, FaceManager.EnrollmentCallback callback, int[] disabledFeatures, @Nullable Surface previewSurface, boolean debugConsent) { mFaceManager.enroll(userId, hardwareAuthToken, cancel, new NotifyingEnrollmentCallback(mContext, callback), disabledFeatures, previewSurface, debugConsent); } /** Wrapper around the {@link FaceManager#remove} method. */ public void remove(Face face, int userId, FaceManager.RemovalCallback callback) { mFaceManager.remove(face, userId, new NotifyingRemovalCallback(mContext, callback)); } /** * Decorator of the {@link FaceManager.EnrollmentCallback} class that notifies other * interested parties that a face setting has changed. */ private static class NotifyingEnrollmentCallback extends FaceManager.EnrollmentCallback { private final Context mContext; private final FaceManager.EnrollmentCallback mCallback; NotifyingEnrollmentCallback(Context context, FaceManager.EnrollmentCallback callback) { mContext = context; mCallback = callback; } @Override public void onEnrollmentError(int errMsgId, CharSequence errString) { mCallback.onEnrollmentError(errMsgId, errString); } @Override public void onEnrollmentHelp(int helpMsgId, CharSequence helpString) { mCallback.onEnrollmentHelp(helpMsgId, helpString); } @Override public void onEnrollmentFrame(int helpCode, @Nullable CharSequence helpMessage, @Nullable FaceEnrollCell cell, int stage, float pan, float tilt, float distance) { mCallback.onEnrollmentFrame(helpCode, helpMessage, cell, stage, pan, tilt, distance); } @Override public void onEnrollmentProgress(int remaining) { mCallback.onEnrollmentProgress(remaining); if (remaining == 0) { BiometricsSafetySource.sendSafetyData(mContext); // biometrics data changed } } } /** * Decorator of the {@link FaceManager.RemovalCallback} class that notifies other * interested parties that a face setting has changed. */ private static class NotifyingRemovalCallback extends FaceManager.RemovalCallback { private final Context mContext; private final FaceManager.RemovalCallback mCallback; NotifyingRemovalCallback(Context context, FaceManager.RemovalCallback callback) { mContext = context; mCallback = callback; } @Override public void onRemovalError(Face fp, int errMsgId, CharSequence errString) { mCallback.onRemovalError(fp, errMsgId, errString); } @Override public void onRemovalSucceeded(@Nullable Face fp, int remaining) { mCallback.onRemovalSucceeded(fp, remaining); BiometricsSafetySource.sendSafetyData(mContext); // biometrics data changed } } } src/com/android/settings/homepage/contextualcards/FaceReEnrollDialog.java +4 −1 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ import com.android.internal.app.AlertActivity; import com.android.internal.app.AlertController; import com.android.settings.R; import com.android.settings.Utils; import com.android.settings.biometrics.face.FaceUpdater; import com.android.settings.homepage.contextualcards.slices.FaceSetupSlice; /** Loading @@ -43,6 +44,7 @@ public class FaceReEnrollDialog extends AlertActivity implements private static final String BIOMETRIC_ENROLL_ACTION = "android.settings.BIOMETRIC_ENROLL"; private FaceManager mFaceManager; private FaceUpdater mFaceUpdater; /** * The type of re-enrollment that has been requested, * see {@link Settings.Secure#FACE_UNLOCK_RE_ENROLL} for more details. Loading @@ -67,6 +69,7 @@ public class FaceReEnrollDialog extends AlertActivity implements alertParams.mPositiveButtonListener = this; mFaceManager = Utils.getFaceManagerOrNull(getApplicationContext()); mFaceUpdater = new FaceUpdater(getApplicationContext(), mFaceManager); final Context context = getApplicationContext(); mReEnrollType = FaceSetupSlice.getReEnrollSetting(context, getUserId()); Loading Loading @@ -96,7 +99,7 @@ public class FaceReEnrollDialog extends AlertActivity implements if (mFaceManager == null || !mFaceManager.hasEnrolledTemplates(userId)) { finish(); } mFaceManager.remove(new Face("", 0, 0), userId, new FaceManager.RemovalCallback() { mFaceUpdater.remove(new Face("", 0, 0), userId, new FaceManager.RemovalCallback() { @Override public void onRemovalError(Face face, int errMsgId, CharSequence errString) { super.onRemovalError(face, errMsgId, errString); Loading tests/unit/src/com/android/settings/biometrics/face/FaceUpdaterTest.java 0 → 100644 +277 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 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 org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.same; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import android.content.Context; import android.hardware.face.Face; import android.hardware.face.FaceEnrollCell; import android.hardware.face.FaceEnrollStages; import android.hardware.face.FaceManager; import android.os.CancellationSignal; import android.view.Surface; import androidx.annotation.Nullable; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.android.settings.safetycenter.SafetyCenterManagerWrapper; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @RunWith(AndroidJUnit4.class) public class FaceUpdaterTest { private static final byte[] HARDWARE_AUTH_TOKEN = new byte[] {0}; private static final CancellationSignal CANCELLATION_SIGNAL = new CancellationSignal(); private static final int USER_ID = 0; private static final int ERR_MSG_ID = 0; private static final int HELP_MSG_ID = 0; private static final String HELP_STRING = ""; private static final String ERR_STRING = ""; private static final Face FACE = new Face(/* name= */"", /* faceId */ 0, /* deviceId= */ 0L); private static final int[] DISABLED_FEATURES = new int[] {0}; private static final boolean DEBUG_CONSENT = false; private static final Surface PREVIEW_SURFACE = new Surface(); private static final int HELP_CODE = 0; private static final CharSequence HELP_MESSAGE = ""; private static final FaceEnrollCell CELL = new FaceEnrollCell(/* x= */ 0, /* y= */ 0, /* z= */ 0); private static final int STAGE = FaceEnrollStages.UNKNOWN; private static final float PAN = 0; private static final float TILT = 0; private static final float DISTANCE = 0; @Mock private FaceManager mFaceManager; @Mock private SafetyCenterManagerWrapper mSafetyCenterManagerWrapper; private FaceUpdater mFaceUpdater; private Context mContext; private FaceManager.EnrollmentCallback mEnrollmentCallback; private FaceManager.RemovalCallback mRemovalCallback; @Before public void setUp() { MockitoAnnotations.initMocks(this); mContext = ApplicationProvider.getApplicationContext(); mFaceUpdater = new FaceUpdater(mContext, mFaceManager); mEnrollmentCallback = spy(new TestEnrollmentCallback()); mRemovalCallback = spy(new TestRemovalCallback()); SafetyCenterManagerWrapper.sInstance = mSafetyCenterManagerWrapper; } @Test public void enroll_firstVersion_onEnrollmentCallbacks_triggerGivenCallback() { ArgumentCaptor<FaceManager.EnrollmentCallback> callbackCaptor = ArgumentCaptor.forClass(FaceManager.EnrollmentCallback.class); mFaceUpdater.enroll(USER_ID, HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, mEnrollmentCallback, DISABLED_FEATURES); verify(mFaceManager).enroll( eq(USER_ID), same(HARDWARE_AUTH_TOKEN), same(CANCELLATION_SIGNAL), callbackCaptor.capture(), same(DISABLED_FEATURES)); FaceManager.EnrollmentCallback callback = callbackCaptor.getValue(); callback.onEnrollmentError(ERR_MSG_ID, ERR_STRING); callback.onEnrollmentProgress(/* remaining= */ 2); callback.onEnrollmentHelp(HELP_MSG_ID, HELP_STRING); callback.onEnrollmentFrame(HELP_CODE, HELP_MESSAGE, CELL, STAGE, PAN, TILT, DISTANCE); verify(mEnrollmentCallback, atLeast(1)).onEnrollmentError(ERR_MSG_ID, ERR_STRING); verify(mEnrollmentCallback, atLeast(1)).onEnrollmentProgress(/* remaining= */ 2); verify(mEnrollmentCallback, atLeast(1)).onEnrollmentHelp(HELP_MSG_ID, HELP_STRING); verify(mEnrollmentCallback, atLeast(1)) .onEnrollmentFrame(HELP_CODE, HELP_MESSAGE, CELL, STAGE, PAN, TILT, DISTANCE); } @Test public void enroll_firstVersion_onEnrollmentSuccess_invokedInteractionWithSafetyCenter() { ArgumentCaptor<FaceManager.EnrollmentCallback> callbackCaptor = ArgumentCaptor.forClass(FaceManager.EnrollmentCallback.class); mFaceUpdater.enroll(USER_ID, HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, mEnrollmentCallback, DISABLED_FEATURES); verify(mFaceManager).enroll( eq(USER_ID), same(HARDWARE_AUTH_TOKEN), same(CANCELLATION_SIGNAL), callbackCaptor.capture(), same(DISABLED_FEATURES)); FaceManager.EnrollmentCallback callback = callbackCaptor.getValue(); callback.onEnrollmentProgress(/* remaining= */ 0); verify(mSafetyCenterManagerWrapper).isEnabled(mContext); } @Test public void enroll_firstVersion_onEnrollmentNotYetFinished_didntInvokeInteractionWithSafetyCenter() { ArgumentCaptor<FaceManager.EnrollmentCallback> callbackCaptor = ArgumentCaptor.forClass(FaceManager.EnrollmentCallback.class); mFaceUpdater.enroll(USER_ID, HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, mEnrollmentCallback, DISABLED_FEATURES); verify(mFaceManager).enroll( eq(USER_ID), same(HARDWARE_AUTH_TOKEN), same(CANCELLATION_SIGNAL), callbackCaptor.capture(), same(DISABLED_FEATURES)); FaceManager.EnrollmentCallback callback = callbackCaptor.getValue(); callback.onEnrollmentProgress(/* remaining= */ 1); verify(mSafetyCenterManagerWrapper, never()).isEnabled(any()); } @Test public void enroll_secondVersion_onEnrollmentCallbacks_triggerGivenCallback() { ArgumentCaptor<FaceManager.EnrollmentCallback> callbackCaptor = ArgumentCaptor.forClass(FaceManager.EnrollmentCallback.class); mFaceUpdater.enroll(USER_ID, HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, mEnrollmentCallback, DISABLED_FEATURES, PREVIEW_SURFACE, DEBUG_CONSENT); verify(mFaceManager).enroll( eq(USER_ID), same(HARDWARE_AUTH_TOKEN), same(CANCELLATION_SIGNAL), callbackCaptor.capture(), same(DISABLED_FEATURES), same(PREVIEW_SURFACE), eq(DEBUG_CONSENT)); FaceManager.EnrollmentCallback callback = callbackCaptor.getValue(); callback.onEnrollmentError(ERR_MSG_ID, ERR_STRING); callback.onEnrollmentProgress(/* remaining= */ 2); callback.onEnrollmentHelp(HELP_MSG_ID, HELP_STRING); callback.onEnrollmentFrame(HELP_CODE, HELP_MESSAGE, CELL, STAGE, PAN, TILT, DISTANCE); verify(mEnrollmentCallback, atLeast(1)).onEnrollmentError(ERR_MSG_ID, ERR_STRING); verify(mEnrollmentCallback, atLeast(1)).onEnrollmentProgress(/* remaining= */ 2); verify(mEnrollmentCallback, atLeast(1)).onEnrollmentHelp(HELP_MSG_ID, HELP_STRING); verify(mEnrollmentCallback, atLeast(1)) .onEnrollmentFrame(HELP_CODE, HELP_MESSAGE, CELL, STAGE, PAN, TILT, DISTANCE); } @Test public void enroll_secondVersion_onEnrollmentSuccess_invokedInteractionWithSafetyCenter() { ArgumentCaptor<FaceManager.EnrollmentCallback> callbackCaptor = ArgumentCaptor.forClass(FaceManager.EnrollmentCallback.class); mFaceUpdater.enroll(USER_ID, HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, mEnrollmentCallback, DISABLED_FEATURES, PREVIEW_SURFACE, DEBUG_CONSENT); verify(mFaceManager).enroll( eq(USER_ID), same(HARDWARE_AUTH_TOKEN), same(CANCELLATION_SIGNAL), callbackCaptor.capture(), same(DISABLED_FEATURES), same(PREVIEW_SURFACE), eq(DEBUG_CONSENT)); FaceManager.EnrollmentCallback callback = callbackCaptor.getValue(); callback.onEnrollmentProgress(/* remaining= */ 0); verify(mSafetyCenterManagerWrapper).isEnabled(mContext); } @Test public void enroll_secondVersion_onEnrollmentNotYetFinished_didntInvokeInteractionWithSafetyCenter() { ArgumentCaptor<FaceManager.EnrollmentCallback> callbackCaptor = ArgumentCaptor.forClass(FaceManager.EnrollmentCallback.class); mFaceUpdater.enroll(USER_ID, HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, mEnrollmentCallback, DISABLED_FEATURES, PREVIEW_SURFACE, DEBUG_CONSENT); verify(mFaceManager).enroll( eq(USER_ID), same(HARDWARE_AUTH_TOKEN), same(CANCELLATION_SIGNAL), callbackCaptor.capture(), same(DISABLED_FEATURES), same(PREVIEW_SURFACE), eq(DEBUG_CONSENT)); FaceManager.EnrollmentCallback callback = callbackCaptor.getValue(); callback.onEnrollmentProgress(/* remaining= */ 1); verify(mSafetyCenterManagerWrapper, never()).isEnabled(any()); } @Test public void remove_onRemovalCallbacks_triggerGivenCallback() { ArgumentCaptor<FaceManager.RemovalCallback> callbackCaptor = ArgumentCaptor.forClass(FaceManager.RemovalCallback.class); mFaceUpdater.remove(FACE, USER_ID, mRemovalCallback); verify(mFaceManager) .remove(same(FACE), eq(USER_ID), callbackCaptor.capture()); FaceManager.RemovalCallback callback = callbackCaptor.getValue(); callback.onRemovalSucceeded(FACE, /* remaining= */ 1); callback.onRemovalError(FACE, ERR_MSG_ID, ERR_STRING); verify(mRemovalCallback).onRemovalSucceeded(any(), eq(1)); verify(mRemovalCallback).onRemovalError(FACE, ERR_MSG_ID, ERR_STRING); } @Test public void remove_onRemovalSuccess_invokedInteractionWithSafetyCenter() { ArgumentCaptor<FaceManager.RemovalCallback> callbackCaptor = ArgumentCaptor.forClass(FaceManager.RemovalCallback.class); mFaceUpdater.remove(FACE, USER_ID, mRemovalCallback); verify(mFaceManager) .remove(same(FACE), eq(USER_ID), callbackCaptor.capture()); FaceManager.RemovalCallback callback = callbackCaptor.getValue(); callback.onRemovalSucceeded(FACE, /* remaining= */ 0); verify(mSafetyCenterManagerWrapper).isEnabled(mContext); } public static class TestEnrollmentCallback extends FaceManager.EnrollmentCallback { @Override public void onEnrollmentError(int errMsgId, CharSequence errString) {} @Override public void onEnrollmentHelp(int helpMsgId, CharSequence helpString) {} @Override public void onEnrollmentProgress(int remaining) {} @Override public void onEnrollmentFrame(int helpCode, @Nullable CharSequence helpMessage, @Nullable FaceEnrollCell cell, int stage, float pan, float tilt, float distance) {} } public static class TestRemovalCallback extends FaceManager.RemovalCallback { @Override public void onRemovalError(Face fp, int errMsgId, CharSequence errString) {} @Override public void onRemovalSucceeded(@Nullable Face fp, int remaining) {} } } Loading
src/com/android/settings/biometrics/face/FaceEnrollSidecar.java +3 −5 Original line number Diff line number Diff line Loading @@ -19,9 +19,7 @@ package com.android.settings.biometrics.face; import android.app.Activity; import android.app.settings.SettingsEnums; import android.hardware.face.FaceManager; import android.os.UserHandle; import com.android.settings.Utils; import com.android.settings.biometrics.BiometricEnrollSidecar; import java.util.Arrays; Loading @@ -33,7 +31,7 @@ public class FaceEnrollSidecar extends BiometricEnrollSidecar { private final int[] mDisabledFeatures; private FaceManager mFaceManager; private FaceUpdater mFaceUpdater; public FaceEnrollSidecar(int[] disabledFeatures) { mDisabledFeatures = Arrays.copyOf(disabledFeatures, disabledFeatures.length); Loading @@ -42,13 +40,13 @@ public class FaceEnrollSidecar extends BiometricEnrollSidecar { @Override public void onAttach(Activity activity) { super.onAttach(activity); mFaceManager = Utils.getFaceManagerOrNull(activity); mFaceUpdater = new FaceUpdater(activity); } @Override public void startEnrollment() { super.startEnrollment(); mFaceManager.enroll(mUserId, mToken, mEnrollmentCancel, mFaceUpdater.enroll(mUserId, mToken, mEnrollmentCancel, mEnrollmentCallback, mDisabledFeatures); } Loading
src/com/android/settings/biometrics/face/FaceSettingsRemoveButtonPreferenceController.java +3 −1 Original line number Diff line number Diff line Loading @@ -103,6 +103,7 @@ public class FaceSettingsRemoveButtonPreferenceController extends BasePreference private final MetricsFeatureProvider mMetricsFeatureProvider; private final Context mContext; private final FaceManager mFaceManager; private final FaceUpdater mFaceUpdater; private final FaceManager.RemovalCallback mRemovalCallback = new FaceManager.RemovalCallback() { @Override public void onRemovalError(Face face, int errMsgId, CharSequence errString) { Loading Loading @@ -144,7 +145,7 @@ public class FaceSettingsRemoveButtonPreferenceController extends BasePreference } // Remove the first/only face mFaceManager.remove(faces.get(0), mUserId, mRemovalCallback); mFaceUpdater.remove(faces.get(0), mUserId, mRemovalCallback); } else { mButton.setEnabled(true); mRemoving = false; Loading @@ -157,6 +158,7 @@ public class FaceSettingsRemoveButtonPreferenceController extends BasePreference mContext = context; mFaceManager = context.getSystemService(FaceManager.class); mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider(); mFaceUpdater = new FaceUpdater(context, mFaceManager); } public FaceSettingsRemoveButtonPreferenceController(Context context) { Loading
src/com/android/settings/biometrics/face/FaceUpdater.java 0 → 100644 +137 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 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 android.content.Context; import android.hardware.face.Face; import android.hardware.face.FaceEnrollCell; import android.hardware.face.FaceManager; import android.os.CancellationSignal; import android.view.Surface; import androidx.annotation.Nullable; import com.android.settings.Utils; import com.android.settings.safetycenter.BiometricsSafetySource; /** * Responsible for making {@link FaceManager#enroll} and {@link FaceManager#remove} calls and thus * updating the face setting. */ public class FaceUpdater { private final Context mContext; private final FaceManager mFaceManager; public FaceUpdater(Context context) { mContext = context; mFaceManager = Utils.getFaceManagerOrNull(context); } public FaceUpdater(Context context, FaceManager faceManager) { mContext = context; mFaceManager = faceManager; } /** Wrapper around the {@link FaceManager#enroll} method. */ public void enroll(int userId, byte[] hardwareAuthToken, CancellationSignal cancel, FaceManager.EnrollmentCallback callback, int[] disabledFeatures) { mFaceManager.enroll(userId, hardwareAuthToken, cancel, new NotifyingEnrollmentCallback(mContext, callback), disabledFeatures); } /** Wrapper around the {@link FaceManager#enroll} method. */ public void enroll(int userId, byte[] hardwareAuthToken, CancellationSignal cancel, FaceManager.EnrollmentCallback callback, int[] disabledFeatures, @Nullable Surface previewSurface, boolean debugConsent) { mFaceManager.enroll(userId, hardwareAuthToken, cancel, new NotifyingEnrollmentCallback(mContext, callback), disabledFeatures, previewSurface, debugConsent); } /** Wrapper around the {@link FaceManager#remove} method. */ public void remove(Face face, int userId, FaceManager.RemovalCallback callback) { mFaceManager.remove(face, userId, new NotifyingRemovalCallback(mContext, callback)); } /** * Decorator of the {@link FaceManager.EnrollmentCallback} class that notifies other * interested parties that a face setting has changed. */ private static class NotifyingEnrollmentCallback extends FaceManager.EnrollmentCallback { private final Context mContext; private final FaceManager.EnrollmentCallback mCallback; NotifyingEnrollmentCallback(Context context, FaceManager.EnrollmentCallback callback) { mContext = context; mCallback = callback; } @Override public void onEnrollmentError(int errMsgId, CharSequence errString) { mCallback.onEnrollmentError(errMsgId, errString); } @Override public void onEnrollmentHelp(int helpMsgId, CharSequence helpString) { mCallback.onEnrollmentHelp(helpMsgId, helpString); } @Override public void onEnrollmentFrame(int helpCode, @Nullable CharSequence helpMessage, @Nullable FaceEnrollCell cell, int stage, float pan, float tilt, float distance) { mCallback.onEnrollmentFrame(helpCode, helpMessage, cell, stage, pan, tilt, distance); } @Override public void onEnrollmentProgress(int remaining) { mCallback.onEnrollmentProgress(remaining); if (remaining == 0) { BiometricsSafetySource.sendSafetyData(mContext); // biometrics data changed } } } /** * Decorator of the {@link FaceManager.RemovalCallback} class that notifies other * interested parties that a face setting has changed. */ private static class NotifyingRemovalCallback extends FaceManager.RemovalCallback { private final Context mContext; private final FaceManager.RemovalCallback mCallback; NotifyingRemovalCallback(Context context, FaceManager.RemovalCallback callback) { mContext = context; mCallback = callback; } @Override public void onRemovalError(Face fp, int errMsgId, CharSequence errString) { mCallback.onRemovalError(fp, errMsgId, errString); } @Override public void onRemovalSucceeded(@Nullable Face fp, int remaining) { mCallback.onRemovalSucceeded(fp, remaining); BiometricsSafetySource.sendSafetyData(mContext); // biometrics data changed } } }
src/com/android/settings/homepage/contextualcards/FaceReEnrollDialog.java +4 −1 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ import com.android.internal.app.AlertActivity; import com.android.internal.app.AlertController; import com.android.settings.R; import com.android.settings.Utils; import com.android.settings.biometrics.face.FaceUpdater; import com.android.settings.homepage.contextualcards.slices.FaceSetupSlice; /** Loading @@ -43,6 +44,7 @@ public class FaceReEnrollDialog extends AlertActivity implements private static final String BIOMETRIC_ENROLL_ACTION = "android.settings.BIOMETRIC_ENROLL"; private FaceManager mFaceManager; private FaceUpdater mFaceUpdater; /** * The type of re-enrollment that has been requested, * see {@link Settings.Secure#FACE_UNLOCK_RE_ENROLL} for more details. Loading @@ -67,6 +69,7 @@ public class FaceReEnrollDialog extends AlertActivity implements alertParams.mPositiveButtonListener = this; mFaceManager = Utils.getFaceManagerOrNull(getApplicationContext()); mFaceUpdater = new FaceUpdater(getApplicationContext(), mFaceManager); final Context context = getApplicationContext(); mReEnrollType = FaceSetupSlice.getReEnrollSetting(context, getUserId()); Loading Loading @@ -96,7 +99,7 @@ public class FaceReEnrollDialog extends AlertActivity implements if (mFaceManager == null || !mFaceManager.hasEnrolledTemplates(userId)) { finish(); } mFaceManager.remove(new Face("", 0, 0), userId, new FaceManager.RemovalCallback() { mFaceUpdater.remove(new Face("", 0, 0), userId, new FaceManager.RemovalCallback() { @Override public void onRemovalError(Face face, int errMsgId, CharSequence errString) { super.onRemovalError(face, errMsgId, errString); Loading
tests/unit/src/com/android/settings/biometrics/face/FaceUpdaterTest.java 0 → 100644 +277 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 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 org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.same; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import android.content.Context; import android.hardware.face.Face; import android.hardware.face.FaceEnrollCell; import android.hardware.face.FaceEnrollStages; import android.hardware.face.FaceManager; import android.os.CancellationSignal; import android.view.Surface; import androidx.annotation.Nullable; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.android.settings.safetycenter.SafetyCenterManagerWrapper; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @RunWith(AndroidJUnit4.class) public class FaceUpdaterTest { private static final byte[] HARDWARE_AUTH_TOKEN = new byte[] {0}; private static final CancellationSignal CANCELLATION_SIGNAL = new CancellationSignal(); private static final int USER_ID = 0; private static final int ERR_MSG_ID = 0; private static final int HELP_MSG_ID = 0; private static final String HELP_STRING = ""; private static final String ERR_STRING = ""; private static final Face FACE = new Face(/* name= */"", /* faceId */ 0, /* deviceId= */ 0L); private static final int[] DISABLED_FEATURES = new int[] {0}; private static final boolean DEBUG_CONSENT = false; private static final Surface PREVIEW_SURFACE = new Surface(); private static final int HELP_CODE = 0; private static final CharSequence HELP_MESSAGE = ""; private static final FaceEnrollCell CELL = new FaceEnrollCell(/* x= */ 0, /* y= */ 0, /* z= */ 0); private static final int STAGE = FaceEnrollStages.UNKNOWN; private static final float PAN = 0; private static final float TILT = 0; private static final float DISTANCE = 0; @Mock private FaceManager mFaceManager; @Mock private SafetyCenterManagerWrapper mSafetyCenterManagerWrapper; private FaceUpdater mFaceUpdater; private Context mContext; private FaceManager.EnrollmentCallback mEnrollmentCallback; private FaceManager.RemovalCallback mRemovalCallback; @Before public void setUp() { MockitoAnnotations.initMocks(this); mContext = ApplicationProvider.getApplicationContext(); mFaceUpdater = new FaceUpdater(mContext, mFaceManager); mEnrollmentCallback = spy(new TestEnrollmentCallback()); mRemovalCallback = spy(new TestRemovalCallback()); SafetyCenterManagerWrapper.sInstance = mSafetyCenterManagerWrapper; } @Test public void enroll_firstVersion_onEnrollmentCallbacks_triggerGivenCallback() { ArgumentCaptor<FaceManager.EnrollmentCallback> callbackCaptor = ArgumentCaptor.forClass(FaceManager.EnrollmentCallback.class); mFaceUpdater.enroll(USER_ID, HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, mEnrollmentCallback, DISABLED_FEATURES); verify(mFaceManager).enroll( eq(USER_ID), same(HARDWARE_AUTH_TOKEN), same(CANCELLATION_SIGNAL), callbackCaptor.capture(), same(DISABLED_FEATURES)); FaceManager.EnrollmentCallback callback = callbackCaptor.getValue(); callback.onEnrollmentError(ERR_MSG_ID, ERR_STRING); callback.onEnrollmentProgress(/* remaining= */ 2); callback.onEnrollmentHelp(HELP_MSG_ID, HELP_STRING); callback.onEnrollmentFrame(HELP_CODE, HELP_MESSAGE, CELL, STAGE, PAN, TILT, DISTANCE); verify(mEnrollmentCallback, atLeast(1)).onEnrollmentError(ERR_MSG_ID, ERR_STRING); verify(mEnrollmentCallback, atLeast(1)).onEnrollmentProgress(/* remaining= */ 2); verify(mEnrollmentCallback, atLeast(1)).onEnrollmentHelp(HELP_MSG_ID, HELP_STRING); verify(mEnrollmentCallback, atLeast(1)) .onEnrollmentFrame(HELP_CODE, HELP_MESSAGE, CELL, STAGE, PAN, TILT, DISTANCE); } @Test public void enroll_firstVersion_onEnrollmentSuccess_invokedInteractionWithSafetyCenter() { ArgumentCaptor<FaceManager.EnrollmentCallback> callbackCaptor = ArgumentCaptor.forClass(FaceManager.EnrollmentCallback.class); mFaceUpdater.enroll(USER_ID, HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, mEnrollmentCallback, DISABLED_FEATURES); verify(mFaceManager).enroll( eq(USER_ID), same(HARDWARE_AUTH_TOKEN), same(CANCELLATION_SIGNAL), callbackCaptor.capture(), same(DISABLED_FEATURES)); FaceManager.EnrollmentCallback callback = callbackCaptor.getValue(); callback.onEnrollmentProgress(/* remaining= */ 0); verify(mSafetyCenterManagerWrapper).isEnabled(mContext); } @Test public void enroll_firstVersion_onEnrollmentNotYetFinished_didntInvokeInteractionWithSafetyCenter() { ArgumentCaptor<FaceManager.EnrollmentCallback> callbackCaptor = ArgumentCaptor.forClass(FaceManager.EnrollmentCallback.class); mFaceUpdater.enroll(USER_ID, HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, mEnrollmentCallback, DISABLED_FEATURES); verify(mFaceManager).enroll( eq(USER_ID), same(HARDWARE_AUTH_TOKEN), same(CANCELLATION_SIGNAL), callbackCaptor.capture(), same(DISABLED_FEATURES)); FaceManager.EnrollmentCallback callback = callbackCaptor.getValue(); callback.onEnrollmentProgress(/* remaining= */ 1); verify(mSafetyCenterManagerWrapper, never()).isEnabled(any()); } @Test public void enroll_secondVersion_onEnrollmentCallbacks_triggerGivenCallback() { ArgumentCaptor<FaceManager.EnrollmentCallback> callbackCaptor = ArgumentCaptor.forClass(FaceManager.EnrollmentCallback.class); mFaceUpdater.enroll(USER_ID, HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, mEnrollmentCallback, DISABLED_FEATURES, PREVIEW_SURFACE, DEBUG_CONSENT); verify(mFaceManager).enroll( eq(USER_ID), same(HARDWARE_AUTH_TOKEN), same(CANCELLATION_SIGNAL), callbackCaptor.capture(), same(DISABLED_FEATURES), same(PREVIEW_SURFACE), eq(DEBUG_CONSENT)); FaceManager.EnrollmentCallback callback = callbackCaptor.getValue(); callback.onEnrollmentError(ERR_MSG_ID, ERR_STRING); callback.onEnrollmentProgress(/* remaining= */ 2); callback.onEnrollmentHelp(HELP_MSG_ID, HELP_STRING); callback.onEnrollmentFrame(HELP_CODE, HELP_MESSAGE, CELL, STAGE, PAN, TILT, DISTANCE); verify(mEnrollmentCallback, atLeast(1)).onEnrollmentError(ERR_MSG_ID, ERR_STRING); verify(mEnrollmentCallback, atLeast(1)).onEnrollmentProgress(/* remaining= */ 2); verify(mEnrollmentCallback, atLeast(1)).onEnrollmentHelp(HELP_MSG_ID, HELP_STRING); verify(mEnrollmentCallback, atLeast(1)) .onEnrollmentFrame(HELP_CODE, HELP_MESSAGE, CELL, STAGE, PAN, TILT, DISTANCE); } @Test public void enroll_secondVersion_onEnrollmentSuccess_invokedInteractionWithSafetyCenter() { ArgumentCaptor<FaceManager.EnrollmentCallback> callbackCaptor = ArgumentCaptor.forClass(FaceManager.EnrollmentCallback.class); mFaceUpdater.enroll(USER_ID, HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, mEnrollmentCallback, DISABLED_FEATURES, PREVIEW_SURFACE, DEBUG_CONSENT); verify(mFaceManager).enroll( eq(USER_ID), same(HARDWARE_AUTH_TOKEN), same(CANCELLATION_SIGNAL), callbackCaptor.capture(), same(DISABLED_FEATURES), same(PREVIEW_SURFACE), eq(DEBUG_CONSENT)); FaceManager.EnrollmentCallback callback = callbackCaptor.getValue(); callback.onEnrollmentProgress(/* remaining= */ 0); verify(mSafetyCenterManagerWrapper).isEnabled(mContext); } @Test public void enroll_secondVersion_onEnrollmentNotYetFinished_didntInvokeInteractionWithSafetyCenter() { ArgumentCaptor<FaceManager.EnrollmentCallback> callbackCaptor = ArgumentCaptor.forClass(FaceManager.EnrollmentCallback.class); mFaceUpdater.enroll(USER_ID, HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, mEnrollmentCallback, DISABLED_FEATURES, PREVIEW_SURFACE, DEBUG_CONSENT); verify(mFaceManager).enroll( eq(USER_ID), same(HARDWARE_AUTH_TOKEN), same(CANCELLATION_SIGNAL), callbackCaptor.capture(), same(DISABLED_FEATURES), same(PREVIEW_SURFACE), eq(DEBUG_CONSENT)); FaceManager.EnrollmentCallback callback = callbackCaptor.getValue(); callback.onEnrollmentProgress(/* remaining= */ 1); verify(mSafetyCenterManagerWrapper, never()).isEnabled(any()); } @Test public void remove_onRemovalCallbacks_triggerGivenCallback() { ArgumentCaptor<FaceManager.RemovalCallback> callbackCaptor = ArgumentCaptor.forClass(FaceManager.RemovalCallback.class); mFaceUpdater.remove(FACE, USER_ID, mRemovalCallback); verify(mFaceManager) .remove(same(FACE), eq(USER_ID), callbackCaptor.capture()); FaceManager.RemovalCallback callback = callbackCaptor.getValue(); callback.onRemovalSucceeded(FACE, /* remaining= */ 1); callback.onRemovalError(FACE, ERR_MSG_ID, ERR_STRING); verify(mRemovalCallback).onRemovalSucceeded(any(), eq(1)); verify(mRemovalCallback).onRemovalError(FACE, ERR_MSG_ID, ERR_STRING); } @Test public void remove_onRemovalSuccess_invokedInteractionWithSafetyCenter() { ArgumentCaptor<FaceManager.RemovalCallback> callbackCaptor = ArgumentCaptor.forClass(FaceManager.RemovalCallback.class); mFaceUpdater.remove(FACE, USER_ID, mRemovalCallback); verify(mFaceManager) .remove(same(FACE), eq(USER_ID), callbackCaptor.capture()); FaceManager.RemovalCallback callback = callbackCaptor.getValue(); callback.onRemovalSucceeded(FACE, /* remaining= */ 0); verify(mSafetyCenterManagerWrapper).isEnabled(mContext); } public static class TestEnrollmentCallback extends FaceManager.EnrollmentCallback { @Override public void onEnrollmentError(int errMsgId, CharSequence errString) {} @Override public void onEnrollmentHelp(int helpMsgId, CharSequence helpString) {} @Override public void onEnrollmentProgress(int remaining) {} @Override public void onEnrollmentFrame(int helpCode, @Nullable CharSequence helpMessage, @Nullable FaceEnrollCell cell, int stage, float pan, float tilt, float distance) {} } public static class TestRemovalCallback extends FaceManager.RemovalCallback { @Override public void onRemovalError(Face fp, int errMsgId, CharSequence errString) {} @Override public void onRemovalSucceeded(@Nullable Face fp, int remaining) {} } }