• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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.password;
18 
19 import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED;
20 
21 import android.app.settings.SettingsEnums;
22 import android.hardware.biometrics.BiometricManager;
23 import android.hardware.biometrics.BiometricPrompt;
24 import android.hardware.biometrics.BiometricPrompt.AuthenticationCallback;
25 import android.hardware.biometrics.BiometricPrompt.AuthenticationResult;
26 import android.hardware.biometrics.PromptInfo;
27 import android.multiuser.Flags;
28 import android.os.Bundle;
29 import android.os.CancellationSignal;
30 import android.text.TextUtils;
31 
32 import androidx.annotation.NonNull;
33 
34 import com.android.settings.core.InstrumentedFragment;
35 
36 import java.util.concurrent.Executor;
37 
38 /**
39  * A fragment that wraps the BiometricPrompt and manages its lifecycle.
40  */
41 public class BiometricFragment extends InstrumentedFragment {
42 
43     private static final String TAG = "ConfirmDeviceCredential/BiometricFragment";
44 
45     private static final String KEY_PROMPT_INFO = "prompt_info";
46 
47     // Re-set by the application. Should be done upon orientation changes, etc
48     private Executor mClientExecutor;
49     private AuthenticationCallback mClientCallback;
50 
51     // Re-settable by the application.
52     private int mUserId;
53 
54     // Created/Initialized once and retained
55     private BiometricPrompt mBiometricPrompt;
56     private CancellationSignal mCancellationSignal;
57 
58     private AuthenticationCallback mAuthenticationCallback =
59             new AuthenticationCallback() {
60         @Override
61         public void onAuthenticationError(int error, @NonNull CharSequence message) {
62             mClientExecutor.execute(() -> {
63                 mClientCallback.onAuthenticationError(error, message);
64             });
65             cleanup();
66         }
67 
68         @Override
69         public void onAuthenticationSucceeded(AuthenticationResult result) {
70             mClientExecutor.execute(() -> {
71                 mClientCallback.onAuthenticationSucceeded(result);
72             });
73             cleanup();
74         }
75 
76         @Override
77         public void onAuthenticationFailed() {
78             mClientExecutor.execute(() -> {
79                 mClientCallback.onAuthenticationFailed();
80             });
81         }
82 
83         @Override
84         public void onSystemEvent(int event) {
85             mClientExecutor.execute(() -> {
86                 mClientCallback.onSystemEvent(event);
87             });
88         }
89     };
90 
91     /**
92      * @param promptInfo
93      * @return
94      */
newInstance(PromptInfo promptInfo)95     public static BiometricFragment newInstance(PromptInfo promptInfo) {
96         BiometricFragment biometricFragment = new BiometricFragment();
97         final Bundle bundle = new Bundle();
98         bundle.putParcelable(KEY_PROMPT_INFO, promptInfo);
99         biometricFragment.setArguments(bundle);
100         return biometricFragment;
101     }
102 
setCallbacks(Executor executor, AuthenticationCallback callback)103     public void setCallbacks(Executor executor, AuthenticationCallback callback) {
104         mClientExecutor = executor;
105         mClientCallback = callback;
106     }
107 
setUser(int userId)108     public void setUser(int userId) {
109         mUserId = userId;
110     }
111 
cancel()112     public void cancel() {
113         if (mCancellationSignal != null) {
114             mCancellationSignal.cancel();
115         }
116         cleanup();
117     }
118 
cleanup()119     private void cleanup() {
120         if (getActivity() != null) {
121             getActivity().getSupportFragmentManager().beginTransaction().remove(this)
122                     .commitAllowingStateLoss();
123         }
124     }
125 
126     @Override
onCreate(Bundle savedInstanceState)127     public void onCreate(Bundle savedInstanceState) {
128         super.onCreate(savedInstanceState);
129         setRetainInstance(true);
130 
131         final Bundle bundle = getArguments();
132         final PromptInfo promptInfo = bundle.getParcelable(KEY_PROMPT_INFO);
133         BiometricPrompt.Builder promptBuilder = new BiometricPrompt.Builder(getContext())
134                 .setTitle(promptInfo.getTitle())
135                 .setUseDefaultTitle() // use default title if title is null/empty
136                 .setAllowedAuthenticators(promptInfo.getAuthenticators())
137                 .setSubtitle(promptInfo.getSubtitle())
138                 .setDescription(promptInfo.getDescription())
139                 .setTextForDeviceCredential(
140                         promptInfo.getDeviceCredentialTitle(),
141                         promptInfo.getDeviceCredentialSubtitle(),
142                         promptInfo.getDeviceCredentialDescription())
143                 .setConfirmationRequired(promptInfo.isConfirmationRequested())
144                 .setDisallowBiometricsIfPolicyExists(
145                         promptInfo.isDisallowBiometricsIfPolicyExists())
146                 .setShowEmergencyCallButton(promptInfo.isShowEmergencyCallButton())
147                 .setReceiveSystemEvents(true)
148                 .setRealCallerForConfirmDeviceCredentialActivity(
149                         promptInfo.getRealCallerForConfirmDeviceCredentialActivity());
150         if (promptInfo.getLogoRes() != 0){
151             promptBuilder.setLogoRes(promptInfo.getLogoRes());
152         }
153         String logoDescription = promptInfo.getLogoDescription();
154         if (!TextUtils.isEmpty(logoDescription)) {
155             promptBuilder.setLogoDescription(logoDescription);
156         }
157 
158         if (android.os.Flags.allowPrivateProfile() && Flags.enablePrivateSpaceFeatures()
159                 && Flags.enableBiometricsToUnlockPrivateSpace()) {
160             promptBuilder = promptBuilder.setAllowBackgroundAuthentication(true /* allow */,
161                     promptInfo.shouldUseParentProfileForDeviceCredential());
162         } else {
163             promptBuilder = promptBuilder.setAllowBackgroundAuthentication(true /* allow */);
164         }
165 
166         // Check if the default subtitle should be used if subtitle is null/empty
167         if (promptInfo.isUseDefaultSubtitle()) {
168             promptBuilder.setUseDefaultSubtitle();
169         }
170 
171         if ((promptInfo.getAuthenticators()
172                 & BiometricManager.Authenticators.DEVICE_CREDENTIAL) == 0) {
173             promptBuilder.setNegativeButton(promptInfo.getNegativeButtonText(),
174                     getContext().getMainExecutor(),
175                     (dialog, which) -> mAuthenticationCallback.onAuthenticationError(
176                             BIOMETRIC_ERROR_USER_CANCELED,
177                             null /* errString */));
178         }
179         mBiometricPrompt = promptBuilder.build();
180     }
181 
182     @Override
onResume()183     public void onResume() {
184         super.onResume();
185 
186         if (mCancellationSignal == null) {
187             mCancellationSignal = new CancellationSignal();
188             mBiometricPrompt.authenticateUser(mCancellationSignal, mClientExecutor,
189                     mAuthenticationCallback, mUserId);
190         }
191     }
192 
193     @Override
getMetricsCategory()194     public int getMetricsCategory() {
195         return SettingsEnums.BIOMETRIC_FRAGMENT;
196     }
197 }
198