• 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 android.app.Activity;
20 import android.app.admin.DevicePolicyManager;
21 import android.app.settings.SettingsEnums;
22 import android.content.DialogInterface;
23 import android.hardware.biometrics.BiometricConstants;
24 import android.hardware.biometrics.BiometricPrompt;
25 import android.hardware.biometrics.BiometricPrompt.AuthenticationCallback;
26 import android.hardware.biometrics.BiometricPrompt.AuthenticationResult;
27 import android.hardware.biometrics.IBiometricConfirmDeviceCredentialCallback;
28 import android.os.Bundle;
29 import android.os.CancellationSignal;
30 import android.os.Handler;
31 import android.os.Looper;
32 import android.util.Log;
33 
34 import androidx.annotation.NonNull;
35 
36 import com.android.internal.widget.LockPatternUtils;
37 import com.android.settings.R;
38 import com.android.settings.core.InstrumentedFragment;
39 import com.android.settings.overlay.FeatureFactory;
40 
41 import java.util.concurrent.Executor;
42 
43 /**
44  * A fragment that wraps the BiometricPrompt and manages its lifecycle.
45  */
46 public class BiometricFragment extends InstrumentedFragment {
47 
48     private static final String TAG = "ConfirmDeviceCredential/BiometricFragment";
49 
50     // Re-set by the application. Should be done upon orientation changes, etc
51     private Executor mClientExecutor;
52     private AuthenticationCallback mClientCallback;
53 
54     // Re-settable by the application.
55     private int mUserId;
56 
57     // Created/Initialized once and retained
58     private Bundle mBundle;
59     private BiometricPrompt mBiometricPrompt;
60     private CancellationSignal mCancellationSignal;
61     private boolean mAuthenticating;
62 
63     private AuthenticationCallback mAuthenticationCallback =
64             new AuthenticationCallback() {
65         @Override
66         public void onAuthenticationError(int error, @NonNull CharSequence message) {
67             mAuthenticating = false;
68             mClientExecutor.execute(() -> {
69                 mClientCallback.onAuthenticationError(error, message);
70             });
71             cleanup();
72         }
73 
74         @Override
75         public void onAuthenticationSucceeded(AuthenticationResult result) {
76             mAuthenticating = false;
77             mClientExecutor.execute(() -> {
78                 mClientCallback.onAuthenticationSucceeded(result);
79             });
80             cleanup();
81         }
82     };
83 
84     private final DialogInterface.OnClickListener mNegativeButtonListener =
85             new DialogInterface.OnClickListener() {
86         @Override
87         public void onClick(DialogInterface dialog, int which) {
88             mAuthenticationCallback.onAuthenticationError(
89                     BiometricConstants.BIOMETRIC_ERROR_NEGATIVE_BUTTON,
90                     mBundle.getString(BiometricPrompt.KEY_NEGATIVE_TEXT));
91         }
92     };
93 
94     // TODO(b/123378871): Remove when moved.
95     private final IBiometricConfirmDeviceCredentialCallback mCancelCallback
96         = new IBiometricConfirmDeviceCredentialCallback.Stub() {
97         @Override
98         public void cancel() {
99             final Activity activity = getActivity();
100             if (activity != null) {
101                 activity.finish();
102             } else {
103                 Log.e(TAG, "Activity null!");
104             }
105         }
106     };
107 
108     /**
109      * @param bundle Bundle passed from {@link BiometricPrompt.Builder#buildIntent()}
110      * @return
111      */
newInstance(Bundle bundle)112     public static BiometricFragment newInstance(Bundle bundle) {
113         BiometricFragment biometricFragment = new BiometricFragment();
114         biometricFragment.setArguments(bundle);
115         return biometricFragment;
116     }
117 
setCallbacks(Executor executor, AuthenticationCallback callback)118     public void setCallbacks(Executor executor, AuthenticationCallback callback) {
119         mClientExecutor = executor;
120         mClientCallback = callback;
121     }
122 
setUser(int userId)123     public void setUser(int userId) {
124         mUserId = userId;
125     }
126 
cancel()127     public void cancel() {
128         if (mCancellationSignal != null) {
129             mCancellationSignal.cancel();
130         }
131         cleanup();
132     }
133 
cleanup()134     private void cleanup() {
135         if (getActivity() != null) {
136             getActivity().getSupportFragmentManager().beginTransaction().remove(this).commit();
137         }
138     }
139 
isAuthenticating()140     boolean isAuthenticating() {
141         return mAuthenticating;
142     }
143 
144     @Override
onCreate(Bundle savedInstanceState)145     public void onCreate(Bundle savedInstanceState) {
146         super.onCreate(savedInstanceState);
147         setRetainInstance(true);
148 
149         mBundle = getArguments();
150         final BiometricPrompt.Builder builder = new BiometricPrompt.Builder(getContext())
151                 .setTitle(mBundle.getString(BiometricPrompt.KEY_TITLE))
152                 .setUseDefaultTitle() // use default title if title is null/empty
153                 .setFromConfirmDeviceCredential()
154                 .setSubtitle(mBundle.getString(BiometricPrompt.KEY_SUBTITLE))
155                 .setDescription(mBundle.getString(BiometricPrompt.KEY_DESCRIPTION))
156                 .setConfirmationRequired(
157                         mBundle.getBoolean(BiometricPrompt.KEY_REQUIRE_CONFIRMATION, true));
158 
159         final LockPatternUtils lockPatternUtils = FeatureFactory.getFactory(
160                 getContext())
161                 .getSecurityFeatureProvider()
162                 .getLockPatternUtils(getContext());
163 
164         switch (lockPatternUtils.getKeyguardStoredPasswordQuality(mUserId)) {
165             case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
166                 builder.setNegativeButton(getResources().getString(
167                         R.string.confirm_device_credential_pattern),
168                         mClientExecutor, mNegativeButtonListener);
169                 break;
170             case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
171             case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX:
172                 builder.setNegativeButton(getResources().getString(
173                         R.string.confirm_device_credential_pin),
174                         mClientExecutor, mNegativeButtonListener);
175                 break;
176             case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
177             case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
178             case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
179             case DevicePolicyManager.PASSWORD_QUALITY_MANAGED:
180                 builder.setNegativeButton(getResources().getString(
181                         R.string.confirm_device_credential_password),
182                         mClientExecutor, mNegativeButtonListener);
183                 break;
184         }
185 
186         mBiometricPrompt = builder.build();
187         mCancellationSignal = new CancellationSignal();
188 
189         // TODO: CC doesn't use crypto for now
190         mAuthenticating = true;
191         mBiometricPrompt.authenticateUser(mCancellationSignal, mClientExecutor,
192                 mAuthenticationCallback, mUserId, mCancelCallback);
193     }
194 
195     @Override
getMetricsCategory()196     public int getMetricsCategory() {
197         return SettingsEnums.BIOMETRIC_FRAGMENT;
198     }
199 }
200 
201