1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.settings.biometrics;
18 
19 import android.app.PendingIntent;
20 import android.content.Intent;
21 import android.hardware.face.FaceManager;
22 import android.hardware.fingerprint.FingerprintManager;
23 
24 import androidx.annotation.NonNull;
25 import androidx.fragment.app.FragmentActivity;
26 
27 import com.android.internal.annotations.VisibleForTesting;
28 import com.android.settings.password.ChooseLockSettingsHelper;
29 
30 import java.util.function.Function;
31 
32 /**
33  * Helper for {@link BiometricEnrollActivity} when multiple sensors exist on a device.
34  */
35 public class MultiBiometricEnrollHelper {
36 
37     private static final String TAG = "MultiBiometricEnrollHelper";
38 
39     private static final int REQUEST_FACE_ENROLL = 3000;
40     private static final int REQUEST_FINGERPRINT_ENROLL = 3001;
41 
42     public static final String EXTRA_ENROLL_AFTER_FACE = "enroll_after_face";
43     public static final String EXTRA_ENROLL_AFTER_FINGERPRINT = "enroll_after_finger";
44     public static final String EXTRA_SKIP_PENDING_ENROLL = "skip_pending_enroll";
45 
46     @NonNull private final FragmentActivity mActivity;
47     private final long mGkPwHandle;
48     private final int mUserId;
49     private final boolean mRequestEnrollFace;
50     private final boolean mRequestEnrollFingerprint;
51     private final FingerprintManager mFingerprintManager;
52     private final FaceManager mFaceManager;
53     private final Intent mFingerprintEnrollIntroductionIntent;
54     private final Intent mFaceEnrollIntroductionIntent;
55     private Function<Long, byte[]> mGatekeeperHatSupplier;
56 
57     @VisibleForTesting
MultiBiometricEnrollHelper(@onNull FragmentActivity activity, int userId, boolean enrollFace, boolean enrollFingerprint, long gkPwHandle, FingerprintManager fingerprintManager, FaceManager faceManager, Intent fingerprintEnrollIntroductionIntent, Intent faceEnrollIntroductionIntent, Function<Long, byte[]> gatekeeperHatSupplier)58     MultiBiometricEnrollHelper(@NonNull FragmentActivity activity, int userId,
59             boolean enrollFace, boolean enrollFingerprint, long gkPwHandle,
60             FingerprintManager fingerprintManager,
61             FaceManager faceManager, Intent fingerprintEnrollIntroductionIntent,
62             Intent faceEnrollIntroductionIntent, Function<Long, byte[]> gatekeeperHatSupplier) {
63         mActivity = activity;
64         mUserId = userId;
65         mGkPwHandle = gkPwHandle;
66         mRequestEnrollFace = enrollFace;
67         mRequestEnrollFingerprint = enrollFingerprint;
68         mFingerprintManager = fingerprintManager;
69         mFaceManager = faceManager;
70         mFingerprintEnrollIntroductionIntent = fingerprintEnrollIntroductionIntent;
71         mFaceEnrollIntroductionIntent = faceEnrollIntroductionIntent;
72         mGatekeeperHatSupplier = gatekeeperHatSupplier;
73     }
74 
MultiBiometricEnrollHelper(@onNull FragmentActivity activity, int userId, boolean enrollFace, boolean enrollFingerprint, long gkPwHandle)75     MultiBiometricEnrollHelper(@NonNull FragmentActivity activity, int userId,
76             boolean enrollFace, boolean enrollFingerprint, long gkPwHandle) {
77         this(activity, userId, enrollFace, enrollFingerprint, gkPwHandle,
78                 activity.getSystemService(FingerprintManager.class),
79                 activity.getSystemService(FaceManager.class),
80                 BiometricUtils.getFingerprintIntroIntent(activity, activity.getIntent()),
81                 BiometricUtils.getFaceIntroIntent(activity, activity.getIntent()),
82                 (challenge) ->  BiometricUtils.requestGatekeeperHat(activity, gkPwHandle,
83                         userId, challenge));
84     }
85 
startNextStep()86     void startNextStep() {
87         if (mRequestEnrollFingerprint) {
88             launchFingerprintEnroll();
89         } else if (mRequestEnrollFace) {
90             launchFaceEnroll();
91         } else {
92             mActivity.setResult(BiometricEnrollIntroduction.RESULT_SKIP);
93             mActivity.finish();
94         }
95     }
96 
launchFaceEnroll()97     private void launchFaceEnroll() {
98         mFaceManager.generateChallenge(mUserId, (sensorId, userId, challenge) -> {
99             final byte[] hardwareAuthToken = mGatekeeperHatSupplier.apply(challenge);
100             mFaceEnrollIntroductionIntent.putExtra(
101                     BiometricEnrollBase.EXTRA_KEY_SENSOR_ID, sensorId);
102             mFaceEnrollIntroductionIntent.putExtra(
103                     BiometricEnrollBase.EXTRA_KEY_CHALLENGE, challenge);
104             BiometricUtils.launchEnrollForResult(mActivity, mFaceEnrollIntroductionIntent,
105                     REQUEST_FACE_ENROLL, hardwareAuthToken, mGkPwHandle, mUserId);
106         });
107     }
108 
launchFingerprintEnroll()109     private void launchFingerprintEnroll() {
110         mFingerprintManager.generateChallenge(mUserId, ((sensorId, userId, challenge) -> {
111             final byte[] hardwareAuthToken = mGatekeeperHatSupplier.apply(challenge);
112             mFingerprintEnrollIntroductionIntent.putExtra(
113                     BiometricEnrollBase.EXTRA_KEY_SENSOR_ID, sensorId);
114             mFingerprintEnrollIntroductionIntent.putExtra(
115                     BiometricEnrollBase.EXTRA_KEY_CHALLENGE, challenge);
116             if (mRequestEnrollFace) {
117                 // Give FingerprintEnroll a pendingIntent pointing to face enrollment, so that it
118                 // can be started when user skips or finishes fingerprint enrollment.
119                 // FLAG_UPDATE_CURRENT ensures it is launched with the most recent values.
120                 mFaceEnrollIntroductionIntent.putExtra(Intent.EXTRA_USER_ID, mUserId);
121                 mFaceEnrollIntroductionIntent.putExtra(
122                         ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, mGkPwHandle);
123                 final PendingIntent faceAfterFp = PendingIntent.getActivity(mActivity,
124                         0 /* requestCode */, mFaceEnrollIntroductionIntent,
125                         PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);
126                 mFingerprintEnrollIntroductionIntent.putExtra(EXTRA_ENROLL_AFTER_FINGERPRINT,
127                         faceAfterFp);
128             }
129             BiometricUtils.launchEnrollForResult(mActivity, mFingerprintEnrollIntroductionIntent,
130                     REQUEST_FINGERPRINT_ENROLL, hardwareAuthToken, mGkPwHandle, mUserId);
131         }));
132     }
133 }
134