diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollSidecar.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollSidecar.java index 40348d47a71..d8ecd206bd3 100644 --- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollSidecar.java +++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollSidecar.java @@ -22,7 +22,6 @@ import android.hardware.fingerprint.FingerprintManager; import android.util.Log; import com.android.settings.R; -import com.android.settings.Utils; import com.android.settings.biometrics.BiometricEnrollSidecar; /** @@ -31,13 +30,13 @@ import com.android.settings.biometrics.BiometricEnrollSidecar; public class FingerprintEnrollSidecar extends BiometricEnrollSidecar { private static final String TAG = "FingerprintEnrollSidecar"; - private FingerprintManager mFingerprintManager; + private FingerprintUpdater mFingerprintUpdater; private @FingerprintManager.EnrollReason int mEnrollReason; @Override public void onAttach(Activity activity) { super.onAttach(activity); - mFingerprintManager = Utils.getFingerprintManagerOrNull(activity); + mFingerprintUpdater = new FingerprintUpdater(activity); } @Override @@ -51,7 +50,7 @@ public class FingerprintEnrollSidecar extends BiometricEnrollSidecar { return; } - mFingerprintManager.enroll(mToken, mEnrollmentCancel, mUserId, mEnrollmentCallback, + mFingerprintUpdater.enroll(mToken, mEnrollmentCancel, mUserId, mEnrollmentCallback, mEnrollReason); } diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintRemoveSidecar.java b/src/com/android/settings/biometrics/fingerprint/FingerprintRemoveSidecar.java index b356103f83f..134462d10c1 100644 --- a/src/com/android/settings/biometrics/fingerprint/FingerprintRemoveSidecar.java +++ b/src/com/android/settings/biometrics/fingerprint/FingerprintRemoveSidecar.java @@ -21,7 +21,6 @@ import android.app.settings.SettingsEnums; import android.hardware.fingerprint.Fingerprint; import android.hardware.fingerprint.FingerprintManager; import android.os.Bundle; -import android.os.UserHandle; import android.util.Log; import com.android.settings.core.InstrumentedFragment; @@ -38,7 +37,7 @@ public class FingerprintRemoveSidecar extends InstrumentedFragment { private Listener mListener; private Fingerprint mFingerprintRemoving; private Queue mFingerprintsRemoved; - FingerprintManager mFingerprintManager; + private FingerprintUpdater mFingerprintUpdater; private class RemovalError { Fingerprint fingerprint; @@ -80,15 +79,15 @@ public class FingerprintRemoveSidecar extends InstrumentedFragment { return; } mFingerprintRemoving = fingerprint; - mFingerprintManager.remove(fingerprint, userId, mRemoveCallback);; + mFingerprintUpdater.remove(fingerprint, userId, mRemoveCallback); } public FingerprintRemoveSidecar() { mFingerprintsRemoved = new LinkedList<>(); } - public void setFingerprintManager(FingerprintManager fingerprintManager) { - mFingerprintManager = fingerprintManager; + public void setFingerprintUpdater(FingerprintUpdater fingerprintUpdater) { + mFingerprintUpdater = fingerprintUpdater; } @Override diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java index 50e17809bd9..abc6d53a63b 100644 --- a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java +++ b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java @@ -17,9 +17,9 @@ package com.android.settings.biometrics.fingerprint; -import static android.app.admin.DevicePolicyResources.Strings.UNDEFINED; import static android.app.admin.DevicePolicyResources.Strings.Settings.FINGERPRINT_UNLOCK_DISABLED_EXPLANATION; import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_FINGERPRINT_LAST_DELETE_MESSAGE; +import static android.app.admin.DevicePolicyResources.Strings.UNDEFINED; import static com.android.settings.Utils.SETTINGS_PACKAGE_NAME; @@ -137,6 +137,7 @@ public class FingerprintSettings extends SubSettings { protected static final boolean DEBUG = false; private FingerprintManager mFingerprintManager; + private FingerprintUpdater mFingerprintUpdater; private List mSensorProperties; private boolean mInFingerprintLockout; private byte[] mToken; @@ -299,6 +300,7 @@ public class FingerprintSettings extends SubSettings { Activity activity = getActivity(); mFingerprintManager = Utils.getFingerprintManagerOrNull(activity); + mFingerprintUpdater = new FingerprintUpdater(activity, mFingerprintManager); mSensorProperties = mFingerprintManager.getSensorPropertiesInternal(); mToken = getIntent().getByteArrayExtra( @@ -322,7 +324,7 @@ public class FingerprintSettings extends SubSettings { getFragmentManager().beginTransaction() .add(mRemovalSidecar, TAG_REMOVAL_SIDECAR).commit(); } - mRemovalSidecar.setFingerprintManager(mFingerprintManager); + mRemovalSidecar.setFingerprintUpdater(mFingerprintUpdater); mRemovalSidecar.setListener(mRemovalListener); RenameDialog renameDialog = (RenameDialog) getFragmentManager(). diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintUpdater.java b/src/com/android/settings/biometrics/fingerprint/FingerprintUpdater.java new file mode 100644 index 00000000000..75d8f7b80b2 --- /dev/null +++ b/src/com/android/settings/biometrics/fingerprint/FingerprintUpdater.java @@ -0,0 +1,121 @@ +/* + * 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.fingerprint; + +import android.content.Context; +import android.hardware.fingerprint.Fingerprint; +import android.hardware.fingerprint.FingerprintManager; +import android.os.CancellationSignal; + +import androidx.annotation.Nullable; + +import com.android.settings.Utils; +import com.android.settings.safetycenter.BiometricsSafetySource; + +/** + * Responsible for making {@link FingerprintManager#enroll} and {@link FingerprintManager#remove} + * calls and thus updating the fingerprint setting. + */ +public class FingerprintUpdater { + + private final Context mContext; + private final FingerprintManager mFingerprintManager; + + public FingerprintUpdater(Context context) { + mContext = context; + mFingerprintManager = Utils.getFingerprintManagerOrNull(context); + } + + public FingerprintUpdater(Context context, FingerprintManager fingerprintManager) { + mContext = context; + mFingerprintManager = fingerprintManager; + } + + /** Wrapper around the {@link FingerprintManager#enroll} method. */ + public void enroll(byte [] hardwareAuthToken, CancellationSignal cancel, int userId, + FingerprintManager.EnrollmentCallback callback, + @FingerprintManager.EnrollReason int enrollReason) { + mFingerprintManager.enroll(hardwareAuthToken, cancel, userId, + new NotifyingEnrollmentCallback(mContext, callback), enrollReason); + } + + /** Wrapper around the {@link FingerprintManager#remove} method. */ + public void remove(Fingerprint fp, int userId, FingerprintManager.RemovalCallback callback) { + mFingerprintManager.remove(fp, userId, new NotifyingRemovalCallback(mContext, callback)); + } + + /** + * Decorator of the {@link FingerprintManager.EnrollmentCallback} class that notifies other + * interested parties that a fingerprint setting has changed. + */ + private static class NotifyingEnrollmentCallback + extends FingerprintManager.EnrollmentCallback { + + private final Context mContext; + private final FingerprintManager.EnrollmentCallback mCallback; + + NotifyingEnrollmentCallback(Context context, + FingerprintManager.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 onEnrollmentProgress(int remaining) { + mCallback.onEnrollmentProgress(remaining); + if (remaining == 0) { + BiometricsSafetySource.sendSafetyData(mContext); // biometrics data changed + } + } + } + + /** + * Decorator of the {@link FingerprintManager.RemovalCallback} class that notifies other + * interested parties that a fingerprint setting has changed. + */ + private static class NotifyingRemovalCallback extends FingerprintManager.RemovalCallback { + + private final Context mContext; + private final FingerprintManager.RemovalCallback mCallback; + + NotifyingRemovalCallback(Context context, FingerprintManager.RemovalCallback callback) { + mContext = context; + mCallback = callback; + } + + @Override + public void onRemovalError(Fingerprint fp, int errMsgId, CharSequence errString) { + mCallback.onRemovalError(fp, errMsgId, errString); + } + + @Override + public void onRemovalSucceeded(@Nullable Fingerprint fp, int remaining) { + mCallback.onRemovalSucceeded(fp, remaining); + BiometricsSafetySource.sendSafetyData(mContext); // biometrics data changed + } + } +} diff --git a/tests/unit/src/com/android/settings/biometrics/fingerprint/FingerprintUpdaterTest.java b/tests/unit/src/com/android/settings/biometrics/fingerprint/FingerprintUpdaterTest.java new file mode 100644 index 00000000000..62435b4f8b2 --- /dev/null +++ b/tests/unit/src/com/android/settings/biometrics/fingerprint/FingerprintUpdaterTest.java @@ -0,0 +1,185 @@ +/* + * 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.fingerprint; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.same; +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.fingerprint.Fingerprint; +import android.hardware.fingerprint.FingerprintManager; +import android.os.CancellationSignal; + +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 FingerprintUpdaterTest { + + 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 ENROLL_REASON = 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 Fingerprint FINGERPRINT = + new Fingerprint(/* name= */"", /* fingerId */ 0, /* deviceId= */ 0L); + + @Mock private FingerprintManager mFingerprintManager; + @Mock private SafetyCenterManagerWrapper mSafetyCenterManagerWrapper; + + private FingerprintUpdater mFingerprintUpdater; + private Context mContext; + private FingerprintManager.EnrollmentCallback mEnrollmentCallback; + private FingerprintManager.RemovalCallback mRemovalCallback; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext = ApplicationProvider.getApplicationContext(); + mFingerprintUpdater = new FingerprintUpdater(mContext, mFingerprintManager); + mEnrollmentCallback = spy(new TestEntrollmentCallback()); + mRemovalCallback = spy(new TestRemovalCallback()); + SafetyCenterManagerWrapper.sInstance = mSafetyCenterManagerWrapper; + } + + @Test + public void enroll_onEnrollmentCallbacks_triggerGivenCallback() { + ArgumentCaptor callbackCaptor = + ArgumentCaptor.forClass(FingerprintManager.EnrollmentCallback.class); + mFingerprintUpdater.enroll(HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, USER_ID, + mEnrollmentCallback, ENROLL_REASON); + verify(mFingerprintManager).enroll( + same(HARDWARE_AUTH_TOKEN), + same(CANCELLATION_SIGNAL), + eq(USER_ID), + callbackCaptor.capture(), + eq(ENROLL_REASON)); + FingerprintManager.EnrollmentCallback callback = callbackCaptor.getValue(); + + callback.onEnrollmentError(ERR_MSG_ID, ERR_STRING); + callback.onEnrollmentProgress(/* remaining= */ 2); + callback.onEnrollmentHelp(HELP_MSG_ID, HELP_STRING); + + verify(mEnrollmentCallback).onEnrollmentError(ERR_MSG_ID, ERR_STRING); + verify(mEnrollmentCallback).onEnrollmentProgress(/* remaining= */ 2); + verify(mEnrollmentCallback).onEnrollmentHelp(HELP_MSG_ID, HELP_STRING); + } + + @Test + public void enroll_onEnrollmentSuccess_invokedInteractionWithSafetyCenter() { + ArgumentCaptor callbackCaptor = + ArgumentCaptor.forClass(FingerprintManager.EnrollmentCallback.class); + mFingerprintUpdater.enroll(HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, USER_ID, + mEnrollmentCallback, ENROLL_REASON); + verify(mFingerprintManager).enroll( + same(HARDWARE_AUTH_TOKEN), + same(CANCELLATION_SIGNAL), + eq(USER_ID), + callbackCaptor.capture(), + eq(ENROLL_REASON)); + FingerprintManager.EnrollmentCallback callback = callbackCaptor.getValue(); + + callback.onEnrollmentProgress(/* remaining= */ 0); + + verify(mSafetyCenterManagerWrapper).isEnabled(mContext); + } + + @Test + public void enroll_onEnrollmentNotYetFinished_didntInvokeInteractionWithSafetyCenter() { + ArgumentCaptor callbackCaptor = + ArgumentCaptor.forClass(FingerprintManager.EnrollmentCallback.class); + mFingerprintUpdater.enroll(HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, USER_ID, + mEnrollmentCallback, ENROLL_REASON); + verify(mFingerprintManager).enroll( + same(HARDWARE_AUTH_TOKEN), + same(CANCELLATION_SIGNAL), + eq(USER_ID), + callbackCaptor.capture(), + eq(ENROLL_REASON)); + FingerprintManager.EnrollmentCallback callback = callbackCaptor.getValue(); + + callback.onEnrollmentProgress(/* remaining= */ 1); + + verify(mSafetyCenterManagerWrapper, never()).isEnabled(any()); + } + + @Test + public void remove_onRemovalCallbacks_triggerGivenCallback() { + ArgumentCaptor callbackCaptor = + ArgumentCaptor.forClass(FingerprintManager.RemovalCallback.class); + mFingerprintUpdater.remove(FINGERPRINT, USER_ID, mRemovalCallback); + verify(mFingerprintManager) + .remove(same(FINGERPRINT), eq(USER_ID), callbackCaptor.capture()); + FingerprintManager.RemovalCallback callback = callbackCaptor.getValue(); + + callback.onRemovalSucceeded(FINGERPRINT, /* remaining= */ 1); + callback.onRemovalError(FINGERPRINT, ERR_MSG_ID, ERR_STRING); + + verify(mRemovalCallback).onRemovalSucceeded(any(), eq(1)); + verify(mRemovalCallback).onRemovalError(FINGERPRINT, ERR_MSG_ID, ERR_STRING); + } + + @Test + public void remove_onRemovalSuccess_invokedInteractionWithSafetyCenter() { + ArgumentCaptor callbackCaptor = + ArgumentCaptor.forClass(FingerprintManager.RemovalCallback.class); + mFingerprintUpdater.remove(FINGERPRINT, USER_ID, mRemovalCallback); + verify(mFingerprintManager) + .remove(same(FINGERPRINT), eq(USER_ID), callbackCaptor.capture()); + FingerprintManager.RemovalCallback callback = callbackCaptor.getValue(); + + callback.onRemovalSucceeded(FINGERPRINT, /* remaining= */ 0); + + verify(mSafetyCenterManagerWrapper).isEnabled(mContext); + } + + public static class TestEntrollmentCallback extends FingerprintManager.EnrollmentCallback { + @Override + public void onEnrollmentError(int errMsgId, CharSequence errString) {} + + @Override + public void onEnrollmentHelp(int helpMsgId, CharSequence helpString) {} + + @Override + public void onEnrollmentProgress(int remaining) {} + } + + public static class TestRemovalCallback extends FingerprintManager.RemovalCallback { + @Override + public void onRemovalError(Fingerprint fp, int errMsgId, CharSequence errString) {} + + @Override + public void onRemovalSucceeded(@Nullable Fingerprint fp, int remaining) {} + } +}