• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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 package com.android.settings.biometrics.combination;
17 
18 import static android.app.Activity.RESULT_OK;
19 
20 import static com.android.settings.password.ChooseLockPattern.RESULT_FINISHED;
21 
22 import android.content.Context;
23 import android.content.Intent;
24 import android.hardware.face.FaceManager;
25 import android.hardware.fingerprint.FingerprintManager;
26 import android.os.Bundle;
27 import android.os.UserHandle;
28 import android.util.Log;
29 
30 import androidx.annotation.Nullable;
31 import androidx.preference.Preference;
32 
33 import com.android.settings.R;
34 import com.android.settings.Utils;
35 import com.android.settings.biometrics.BiometricEnrollBase;
36 import com.android.settings.biometrics.BiometricUtils;
37 import com.android.settings.core.SettingsBaseActivity;
38 import com.android.settings.dashboard.DashboardFragment;
39 import com.android.settings.password.ChooseLockGeneric;
40 import com.android.settings.password.ChooseLockSettingsHelper;
41 import com.android.settingslib.transition.SettingsTransitionHelper;
42 
43 /**
44  * Base fragment with the confirming credential functionality for combined biometrics settings.
45  */
46 public abstract class BiometricsSettingsBase extends DashboardFragment {
47 
48     private static final int CONFIRM_REQUEST = 2001;
49     private static final int CHOOSE_LOCK_REQUEST = 2002;
50 
51     private static final String SAVE_STATE_CONFIRM_CREDETIAL = "confirm_credential";
52     private static final String DO_NOT_FINISH_ACTIVITY = "do_not_finish_activity";
53 
54     protected int mUserId;
55     protected long mGkPwHandle;
56     private boolean mConfirmCredential;
57     @Nullable private FaceManager mFaceManager;
58     @Nullable private FingerprintManager mFingerprintManager;
59     // Do not finish() if choosing/confirming credential, or showing fp/face settings
60     private boolean mDoNotFinishActivity;
61 
62     @Override
onAttach(Context context)63     public void onAttach(Context context) {
64         super.onAttach(context);
65         mUserId = getActivity().getIntent().getIntExtra(Intent.EXTRA_USER_ID,
66                 UserHandle.myUserId());
67     }
68 
69     @Override
onCreate(Bundle savedInstanceState)70     public void onCreate(Bundle savedInstanceState) {
71         super.onCreate(savedInstanceState);
72         mFaceManager = Utils.getFaceManagerOrNull(getActivity());
73         mFingerprintManager = Utils.getFingerprintManagerOrNull(getActivity());
74 
75         if (BiometricUtils.containsGatekeeperPasswordHandle(getIntent())) {
76             mGkPwHandle = BiometricUtils.getGatekeeperPasswordHandle(getIntent());
77         }
78 
79         if (savedInstanceState != null) {
80             mConfirmCredential = savedInstanceState.getBoolean(SAVE_STATE_CONFIRM_CREDETIAL);
81             mDoNotFinishActivity = savedInstanceState.getBoolean(DO_NOT_FINISH_ACTIVITY);
82             if (savedInstanceState.containsKey(
83                     ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_GK_PW_HANDLE)) {
84                 mGkPwHandle = savedInstanceState.getLong(
85                         ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_GK_PW_HANDLE);
86             }
87         }
88 
89         if (mGkPwHandle == 0L && !mConfirmCredential) {
90             mConfirmCredential = true;
91             launchChooseOrConfirmLock();
92         }
93     }
94 
95     @Override
onResume()96     public void onResume() {
97         super.onResume();
98         if (!mConfirmCredential) {
99             mDoNotFinishActivity = false;
100         }
101     }
102 
103     @Override
onStop()104     public void onStop() {
105         super.onStop();
106         if (!getActivity().isChangingConfigurations() && !mDoNotFinishActivity) {
107             BiometricUtils.removeGatekeeperPasswordHandle(getActivity(), mGkPwHandle);
108             getActivity().finish();
109         }
110     }
111 
112     @Override
onPreferenceTreeClick(Preference preference)113     public boolean onPreferenceTreeClick(Preference preference) {
114         final String key = preference.getKey();
115 
116         // Generate challenge (and request LSS to create a HAT) each time the preference is clicked,
117         // since FingerprintSettings and FaceSettings revoke the challenge when finishing.
118         if (getFacePreferenceKey().equals(key)) {
119             mDoNotFinishActivity = true;
120             mFaceManager.generateChallenge(mUserId, (sensorId, userId, challenge) -> {
121                 final byte[] token = BiometricUtils.requestGatekeeperHat(getActivity(), mGkPwHandle,
122                         mUserId, challenge);
123                 final Bundle extras = preference.getExtras();
124                 extras.putByteArray(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, token);
125                 extras.putInt(BiometricEnrollBase.EXTRA_KEY_SENSOR_ID, sensorId);
126                 extras.putLong(BiometricEnrollBase.EXTRA_KEY_CHALLENGE, challenge);
127                 super.onPreferenceTreeClick(preference);
128             });
129 
130             return true;
131         } else if (getFingerprintPreferenceKey().equals(key)) {
132             mDoNotFinishActivity = true;
133             mFingerprintManager.generateChallenge(mUserId, (sensorId, userId, challenge) -> {
134                 final byte[] token = BiometricUtils.requestGatekeeperHat(getActivity(), mGkPwHandle,
135                         mUserId, challenge);
136                 final Bundle extras = preference.getExtras();
137                 extras.putByteArray(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, token);
138                 extras.putLong(BiometricEnrollBase.EXTRA_KEY_CHALLENGE, challenge);
139                 super.onPreferenceTreeClick(preference);
140             });
141 
142             return true;
143         }
144 
145         return super.onPreferenceTreeClick(preference);
146     }
147 
148     @Override
onSaveInstanceState(Bundle outState)149     public void onSaveInstanceState(Bundle outState) {
150         super.onSaveInstanceState(outState);
151         outState.putBoolean(SAVE_STATE_CONFIRM_CREDETIAL, mConfirmCredential);
152         outState.putBoolean(DO_NOT_FINISH_ACTIVITY, mDoNotFinishActivity);
153         if (mGkPwHandle != 0L) {
154             outState.putLong(ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_GK_PW_HANDLE, mGkPwHandle);
155         }
156     }
157 
158     @Override
onActivityResult(int requestCode, int resultCode, @Nullable Intent data)159     public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
160         super.onActivityResult(requestCode, resultCode, data);
161         if (requestCode == CONFIRM_REQUEST || requestCode == CHOOSE_LOCK_REQUEST) {
162             mConfirmCredential = false;
163             mDoNotFinishActivity = false;
164             if (resultCode == RESULT_FINISHED || resultCode == RESULT_OK) {
165                 if (BiometricUtils.containsGatekeeperPasswordHandle(data)) {
166                     mGkPwHandle = BiometricUtils.getGatekeeperPasswordHandle(data);
167                 } else {
168                     Log.d(getLogTag(), "Data null or GK PW missing.");
169                     finish();
170                 }
171             } else {
172                 Log.d(getLogTag(), "Password not confirmed.");
173                 finish();
174             }
175         }
176     }
177 
178     /**
179      * Get the preference key of face for passing through credential data to face settings.
180      */
getFacePreferenceKey()181     public abstract String getFacePreferenceKey();
182 
183     /**
184      * Get the preference key of face for passing through credential data to face settings.
185      */
getFingerprintPreferenceKey()186     public abstract String getFingerprintPreferenceKey();
187 
launchChooseOrConfirmLock()188     private void launchChooseOrConfirmLock() {
189         final ChooseLockSettingsHelper.Builder builder =
190                 new ChooseLockSettingsHelper.Builder(getActivity(), this)
191                         .setRequestCode(CONFIRM_REQUEST)
192                         .setTitle(getString(R.string.security_settings_biometric_preference_title))
193                         .setRequestGatekeeperPasswordHandle(true)
194                         .setForegroundOnly(true)
195                         .setReturnCredentials(true);
196         if (mUserId != UserHandle.USER_NULL) {
197             builder.setUserId(mUserId);
198         }
199         mDoNotFinishActivity = true;
200         final boolean launched = builder.show();
201 
202         if (!launched) {
203             Intent intent = BiometricUtils.getChooseLockIntent(getActivity(), getIntent());
204             intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.HIDE_INSECURE_OPTIONS,
205                     true);
206             intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_GK_PW_HANDLE, true);
207             intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_BIOMETRICS, true);
208             intent.putExtra(SettingsBaseActivity.EXTRA_PAGE_TRANSITION_TYPE,
209                     SettingsTransitionHelper.TransitionType.TRANSITION_SLIDE);
210 
211             if (mUserId != UserHandle.USER_NULL) {
212                 intent.putExtra(Intent.EXTRA_USER_ID, mUserId);
213             }
214             startActivityForResult(intent, CHOOSE_LOCK_REQUEST);
215         }
216     }
217 }
218