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