• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.Activity;
20 import android.app.PendingIntent;
21 import android.app.admin.DevicePolicyManager;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.IntentSender;
25 import android.os.storage.StorageManager;
26 import android.util.Log;
27 import android.view.Surface;
28 
29 import androidx.annotation.NonNull;
30 import androidx.annotation.Nullable;
31 import androidx.fragment.app.FragmentActivity;
32 
33 import com.android.internal.widget.LockPatternUtils;
34 import com.android.internal.widget.VerifyCredentialResponse;
35 import com.android.settings.SetupWizardUtils;
36 import com.android.settings.biometrics.face.FaceEnrollIntroduction;
37 import com.android.settings.biometrics.fingerprint.FingerprintEnrollFindSensor;
38 import com.android.settings.biometrics.fingerprint.FingerprintEnrollIntroduction;
39 import com.android.settings.biometrics.fingerprint.SetupFingerprintEnrollIntroduction;
40 import com.android.settings.password.ChooseLockGeneric;
41 import com.android.settings.password.ChooseLockSettingsHelper;
42 import com.android.settings.password.SetupChooseLockGeneric;
43 
44 import com.google.android.setupcompat.util.WizardManagerHelper;
45 
46 /**
47  * Common biometric utilities.
48  */
49 public class BiometricUtils {
50     private static final String TAG = "BiometricUtils";
51     /**
52      * Given the result from confirming or choosing a credential, request Gatekeeper to generate
53      * a HardwareAuthToken with the Gatekeeper Password together with a biometric challenge.
54      *
55      * @param context Caller's context
56      * @param result The onActivityResult intent from ChooseLock* or ConfirmLock*
57      * @param userId User ID that the credential/biometric operation applies to
58      * @param challenge Unique biometric challenge from FingerprintManager/FaceManager
59      * @return
60      */
requestGatekeeperHat(@onNull Context context, @NonNull Intent result, int userId, long challenge)61     public static byte[] requestGatekeeperHat(@NonNull Context context, @NonNull Intent result,
62             int userId, long challenge) {
63         if (!containsGatekeeperPasswordHandle(result)) {
64             throw new IllegalStateException("Gatekeeper Password is missing!!");
65         }
66         final long gatekeeperPasswordHandle = result.getLongExtra(
67                 ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, 0L);
68         return requestGatekeeperHat(context, gatekeeperPasswordHandle, userId, challenge);
69     }
70 
requestGatekeeperHat(@onNull Context context, long gkPwHandle, int userId, long challenge)71     public static byte[] requestGatekeeperHat(@NonNull Context context, long gkPwHandle, int userId,
72             long challenge) {
73         final LockPatternUtils utils = new LockPatternUtils(context);
74         final VerifyCredentialResponse response = utils.verifyGatekeeperPasswordHandle(gkPwHandle,
75                 challenge, userId);
76         if (!response.isMatched()) {
77             throw new IllegalStateException("Unable to request Gatekeeper HAT");
78         }
79         return response.getGatekeeperHAT();
80     }
81 
containsGatekeeperPasswordHandle(@ullable Intent data)82     public static boolean containsGatekeeperPasswordHandle(@Nullable Intent data) {
83         return data != null && data.hasExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE);
84     }
85 
getGatekeeperPasswordHandle(@onNull Intent data)86     public static long getGatekeeperPasswordHandle(@NonNull Intent data) {
87         return data.getLongExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, 0L);
88     }
89 
90     /**
91      * Requests {@link com.android.server.locksettings.LockSettingsService} to remove the
92      * gatekeeper password associated with a previous
93      * {@link ChooseLockSettingsHelper.Builder#setRequestGatekeeperPasswordHandle(boolean)}
94      *
95      * @param context Caller's context
96      * @param data The onActivityResult intent from ChooseLock* or ConfirmLock*
97      */
removeGatekeeperPasswordHandle(@onNull Context context, @Nullable Intent data)98     public static void removeGatekeeperPasswordHandle(@NonNull Context context,
99             @Nullable Intent data) {
100         if (data == null) {
101             return;
102         }
103         if (!containsGatekeeperPasswordHandle(data)) {
104             return;
105         }
106         removeGatekeeperPasswordHandle(context, getGatekeeperPasswordHandle(data));
107     }
108 
removeGatekeeperPasswordHandle(@onNull Context context, long handle)109     public static void removeGatekeeperPasswordHandle(@NonNull Context context, long handle) {
110         final LockPatternUtils utils = new LockPatternUtils(context);
111         utils.removeGatekeeperPasswordHandle(handle);
112         Log.d(TAG, "Removed handle");
113     }
114 
115     /**
116      * @param context caller's context
117      * @param activityIntent The intent that started the caller's activity
118      * @return Intent for starting ChooseLock*
119      */
getChooseLockIntent(@onNull Context context, @NonNull Intent activityIntent)120     public static Intent getChooseLockIntent(@NonNull Context context,
121             @NonNull Intent activityIntent) {
122         if (WizardManagerHelper.isAnySetupWizard(activityIntent)) {
123             // Default to PIN lock in setup wizard
124             Intent intent = new Intent(context, SetupChooseLockGeneric.class);
125             if (StorageManager.isFileEncryptedNativeOrEmulated()) {
126                 intent.putExtra(
127                         LockPatternUtils.PASSWORD_TYPE_KEY,
128                         DevicePolicyManager.PASSWORD_QUALITY_NUMERIC);
129                 intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment
130                         .EXTRA_SHOW_OPTIONS_BUTTON, true);
131             }
132             WizardManagerHelper.copyWizardManagerExtras(activityIntent, intent);
133             return intent;
134         } else {
135             return new Intent(context, ChooseLockGeneric.class);
136         }
137     }
138 
139     /**
140      * @param context caller's context
141      * @param activityIntent The intent that started the caller's activity
142      * @return Intent for starting FingerprintEnrollFindSensor
143      */
getFingerprintFindSensorIntent(@onNull Context context, @NonNull Intent activityIntent)144     public static Intent getFingerprintFindSensorIntent(@NonNull Context context,
145             @NonNull Intent activityIntent) {
146         Intent intent = new Intent(context, FingerprintEnrollFindSensor.class);
147         SetupWizardUtils.copySetupExtras(activityIntent, intent);
148         return intent;
149     }
150 
151     /**
152      * @param context caller's context
153      * @param activityIntent The intent that started the caller's activity
154      * @return Intent for starting FingerprintEnrollIntroduction
155      */
getFingerprintIntroIntent(@onNull Context context, @NonNull Intent activityIntent)156     public static Intent getFingerprintIntroIntent(@NonNull Context context,
157             @NonNull Intent activityIntent) {
158         if (WizardManagerHelper.isAnySetupWizard(activityIntent)) {
159             Intent intent = new Intent(context, SetupFingerprintEnrollIntroduction.class);
160             WizardManagerHelper.copyWizardManagerExtras(activityIntent, intent);
161             return intent;
162         } else {
163             return new Intent(context, FingerprintEnrollIntroduction.class);
164         }
165     }
166 
167     /**
168      * @param context caller's context
169      * @param activityIntent The intent that started the caller's activity
170      * @return Intent for starting FaceEnrollIntroduction
171      */
getFaceIntroIntent(@onNull Context context, @NonNull Intent activityIntent)172     public static Intent getFaceIntroIntent(@NonNull Context context,
173             @NonNull Intent activityIntent) {
174         final Intent intent = new Intent(context, FaceEnrollIntroduction.class);
175         WizardManagerHelper.copyWizardManagerExtras(activityIntent, intent);
176         return intent;
177     }
178 
179     /**
180      * Start an activity that prompts the user to hand the device to their parent or guardian.
181      * @param context caller's context
182      * @param activityIntent The intent that started the caller's activity
183      * @return Intent for starting BiometricHandoffActivity
184      */
getHandoffToParentIntent(@onNull Context context, @NonNull Intent activityIntent)185     public static Intent getHandoffToParentIntent(@NonNull Context context,
186             @NonNull Intent activityIntent) {
187         final Intent intent = new Intent(context, BiometricHandoffActivity.class);
188         WizardManagerHelper.copyWizardManagerExtras(activityIntent, intent);
189         return intent;
190     }
191 
192     /**
193      * @param activity Reference to the calling activity, used to startActivity
194      * @param intent Intent pointing to the enrollment activity
195      * @param requestCode If non-zero, will invoke startActivityForResult instead of startActivity
196      * @param hardwareAuthToken HardwareAuthToken from Gatekeeper
197      * @param userId User to request enrollment for
198      */
launchEnrollForResult(@onNull FragmentActivity activity, @NonNull Intent intent, int requestCode, @Nullable byte[] hardwareAuthToken, @Nullable Long gkPwHandle, int userId)199     public static void launchEnrollForResult(@NonNull FragmentActivity activity,
200             @NonNull Intent intent, int requestCode,
201             @Nullable byte[] hardwareAuthToken, @Nullable Long gkPwHandle, int userId) {
202         if (hardwareAuthToken != null) {
203             intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN,
204                     hardwareAuthToken);
205         }
206         if (gkPwHandle != null) {
207             intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, (long) gkPwHandle);
208         }
209 
210         if (activity instanceof BiometricEnrollActivity.InternalActivity) {
211             intent.putExtra(Intent.EXTRA_USER_ID, userId);
212         }
213 
214         if (requestCode != 0) {
215             activity.startActivityForResult(intent, requestCode);
216         } else {
217             activity.startActivity(intent);
218             activity.finish();
219         }
220     }
221 
222     /**
223      * @param activity Activity that we want to check
224      * @return True if the activity is going through a multi-biometric enrollment flow.
225      */
isMultiBiometricEnrollmentFlow(@onNull Activity activity)226     public static boolean isMultiBiometricEnrollmentFlow(@NonNull Activity activity) {
227         return activity.getIntent().hasExtra(MultiBiometricEnrollHelper.EXTRA_ENROLL_AFTER_FACE);
228     }
229 
copyMultiBiometricExtras(@onNull Intent fromIntent, @NonNull Intent toIntent)230     public static void copyMultiBiometricExtras(@NonNull Intent fromIntent,
231             @NonNull Intent toIntent) {
232         final PendingIntent pendingIntent = (PendingIntent) fromIntent.getExtra(
233                 MultiBiometricEnrollHelper.EXTRA_ENROLL_AFTER_FACE, null);
234         if (pendingIntent != null) {
235             toIntent.putExtra(MultiBiometricEnrollHelper.EXTRA_ENROLL_AFTER_FACE, pendingIntent);
236         }
237     }
238 
239     /**
240      * If the current biometric enrollment (e.g. face) should be followed by another one (e.g.
241      * fingerprint) (see {@link #isMultiBiometricEnrollmentFlow(Activity)}), retrieves the
242      * PendingIntent pointing to the next enrollment and starts it. The caller will receive the
243      * result in onActivityResult.
244      * @return true if the next enrollment was started
245      */
tryStartingNextBiometricEnroll(@onNull Activity activity, int requestCode)246     public static boolean tryStartingNextBiometricEnroll(@NonNull Activity activity,
247             int requestCode) {
248         final PendingIntent pendingIntent = (PendingIntent) activity.getIntent()
249                 .getExtra(MultiBiometricEnrollHelper.EXTRA_ENROLL_AFTER_FACE);
250         if (pendingIntent != null) {
251             try {
252                 Log.d(TAG, "Starting pendingIntent: " + pendingIntent);
253                 IntentSender intentSender = pendingIntent.getIntentSender();
254                 activity.startIntentSenderForResult(intentSender, requestCode,
255                         null /* fillInIntent */, 0 /* flagMask */, 0 /* flagValues */,
256                         0 /* extraFlags */);
257                 return true;
258             } catch (IntentSender.SendIntentException e) {
259                 Log.e(TAG, "Pending intent canceled: " + e);
260             }
261         }
262         return false;
263     }
264 
265     /**
266      * Returns {@code true} if the screen is going into a landscape mode and the angle is equal to
267      * 270.
268      * @param context Context that we use to get the display this context is associated with
269      * @return True if the angle of the rotation is equal to 270.
270      */
isReverseLandscape(@onNull Context context)271     public static boolean isReverseLandscape(@NonNull Context context) {
272         return context.getDisplay().getRotation() == Surface.ROTATION_270;
273     }
274 }
275