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.biometrics; 18 19 import android.app.admin.DevicePolicyManager; 20 import android.content.Intent; 21 import android.os.Bundle; 22 import android.os.UserHandle; 23 import android.os.UserManager; 24 import android.os.storage.StorageManager; 25 import android.view.View; 26 import android.widget.TextView; 27 28 import com.android.internal.widget.LockPatternUtils; 29 import com.android.settings.R; 30 import com.android.settings.SetupWizardUtils; 31 import com.android.settings.password.ChooseLockGeneric; 32 import com.android.settings.password.ChooseLockGeneric.ChooseLockGenericFragment; 33 import com.android.settings.password.ChooseLockSettingsHelper; 34 import com.android.settings.password.SetupChooseLockGeneric; 35 36 import com.google.android.setupcompat.template.FooterButton; 37 import com.google.android.setupcompat.util.WizardManagerHelper; 38 import com.google.android.setupdesign.span.LinkSpan; 39 40 /** 41 * Abstract base class for the intro onboarding activity for biometric enrollment. 42 */ 43 public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase 44 implements LinkSpan.OnClickListener { 45 46 private UserManager mUserManager; 47 private boolean mHasPassword; 48 private boolean mBiometricUnlockDisabledByAdmin; 49 private TextView mErrorText; 50 51 /** 52 * @return true if the biometric is disabled by a device administrator 53 */ isDisabledByAdmin()54 protected abstract boolean isDisabledByAdmin(); 55 56 /** 57 * @return the layout resource 58 */ getLayoutResource()59 protected abstract int getLayoutResource(); 60 61 /** 62 * @return the header resource for if the biometric has been disabled by a device administrator 63 */ getHeaderResDisabledByAdmin()64 protected abstract int getHeaderResDisabledByAdmin(); 65 66 /** 67 * @return the default header resource 68 */ getHeaderResDefault()69 protected abstract int getHeaderResDefault(); 70 71 /** 72 * @return the description resource for if the biometric has been disabled by a device admin 73 */ getDescriptionResDisabledByAdmin()74 protected abstract int getDescriptionResDisabledByAdmin(); 75 76 /** 77 * @return the cancel button 78 */ getCancelButton()79 protected abstract FooterButton getCancelButton(); 80 81 /** 82 * @return the next button 83 */ getNextButton()84 protected abstract FooterButton getNextButton(); 85 86 /** 87 * @return the error TextView 88 */ getErrorTextView()89 protected abstract TextView getErrorTextView(); 90 91 /** 92 * @return 0 if there are no errors, otherwise returns the resource ID for the error string 93 * to be displayed. 94 */ checkMaxEnrolled()95 protected abstract int checkMaxEnrolled(); 96 97 /** 98 * @return the challenge generated by the biometric hardware 99 */ getChallenge()100 protected abstract long getChallenge(); 101 102 /** 103 * @return one of the ChooseLockSettingsHelper#EXTRA_KEY_FOR_* constants 104 */ getExtraKeyForBiometric()105 protected abstract String getExtraKeyForBiometric(); 106 107 /** 108 * @return the intent for proceeding to the next step of enrollment. For Fingerprint, this 109 * should lead to the "Find Sensor" activity. For Face, this should lead to the "Enrolling" 110 * activity. 111 */ getEnrollingIntent()112 protected abstract Intent getEnrollingIntent(); 113 114 /** 115 * @return the title to be shown on the ConfirmLock screen. 116 */ getConfirmLockTitleResId()117 protected abstract int getConfirmLockTitleResId(); 118 119 /** 120 * @param span 121 */ onClick(LinkSpan span)122 public abstract void onClick(LinkSpan span); 123 124 @Override onCreate(Bundle savedInstanceState)125 protected void onCreate(Bundle savedInstanceState) { 126 super.onCreate(savedInstanceState); 127 128 Intent intent = getIntent(); 129 if (intent.getStringExtra(WizardManagerHelper.EXTRA_THEME) == null) { 130 // Put the theme in the intent so it gets propagated to other activities in the flow 131 intent.putExtra( 132 WizardManagerHelper.EXTRA_THEME, 133 SetupWizardUtils.getThemeString(intent)); 134 } 135 136 mBiometricUnlockDisabledByAdmin = isDisabledByAdmin(); 137 138 setContentView(getLayoutResource()); 139 if (mBiometricUnlockDisabledByAdmin) { 140 setHeaderText(getHeaderResDisabledByAdmin()); 141 } else { 142 setHeaderText(getHeaderResDefault()); 143 } 144 145 mErrorText = getErrorTextView(); 146 147 mUserManager = UserManager.get(this); 148 updatePasswordQuality(); 149 150 if (!mHasPassword) { 151 // No password registered, launch into enrollment wizard. 152 launchChooseLock(); 153 } else if (mToken == null) { 154 // It's possible to have a token but mLaunchedConfirmLock == false, since 155 // ChooseLockGeneric can pass us a token. 156 launchConfirmLock(getConfirmLockTitleResId(), getChallenge()); 157 } 158 } 159 160 @Override onResume()161 protected void onResume() { 162 super.onResume(); 163 164 final int errorMsg = checkMaxEnrolled(); 165 if (errorMsg == 0) { 166 mErrorText.setText(null); 167 mErrorText.setVisibility(View.GONE); 168 getNextButton().setVisibility(View.VISIBLE); 169 } else { 170 mErrorText.setText(errorMsg); 171 mErrorText.setVisibility(View.VISIBLE); 172 getNextButton().setText(getResources().getString(R.string.done)); 173 getNextButton().setVisibility(View.VISIBLE); 174 } 175 } 176 updatePasswordQuality()177 private void updatePasswordQuality() { 178 final int passwordQuality = new ChooseLockSettingsHelper(this).utils() 179 .getActivePasswordQuality(mUserManager.getCredentialOwnerProfile(mUserId)); 180 mHasPassword = passwordQuality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; 181 } 182 183 @Override onNextButtonClick(View view)184 protected void onNextButtonClick(View view) { 185 if (checkMaxEnrolled() == 0) { 186 // Lock thingy is already set up, launch directly to the next page 187 launchNextEnrollingActivity(mToken); 188 } else { 189 setResult(RESULT_FINISHED); 190 finish(); 191 } 192 } 193 launchChooseLock()194 private void launchChooseLock() { 195 Intent intent = getChooseLockIntent(); 196 long challenge = getChallenge(); 197 intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.MINIMUM_QUALITY_KEY, 198 DevicePolicyManager.PASSWORD_QUALITY_SOMETHING); 199 intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.HIDE_DISABLED_PREFS, true); 200 intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, true); 201 intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, challenge); 202 intent.putExtra(getExtraKeyForBiometric(), true); 203 if (mUserId != UserHandle.USER_NULL) { 204 intent.putExtra(Intent.EXTRA_USER_ID, mUserId); 205 } 206 startActivityForResult(intent, CHOOSE_LOCK_GENERIC_REQUEST); 207 } 208 launchNextEnrollingActivity(byte[] token)209 private void launchNextEnrollingActivity(byte[] token) { 210 Intent intent = getEnrollingIntent(); 211 if (token != null) { 212 intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, token); 213 } 214 if (mUserId != UserHandle.USER_NULL) { 215 intent.putExtra(Intent.EXTRA_USER_ID, mUserId); 216 } 217 intent.putExtra(EXTRA_FROM_SETTINGS_SUMMARY, mFromSettingsSummary); 218 startActivityForResult(intent, BIOMETRIC_FIND_SENSOR_REQUEST); 219 } 220 getChooseLockIntent()221 protected Intent getChooseLockIntent() { 222 if (WizardManagerHelper.isAnySetupWizard(getIntent())) { 223 // Default to PIN lock in setup wizard 224 Intent intent = new Intent(this, SetupChooseLockGeneric.class); 225 if (StorageManager.isFileEncryptedNativeOrEmulated()) { 226 intent.putExtra( 227 LockPatternUtils.PASSWORD_TYPE_KEY, 228 DevicePolicyManager.PASSWORD_QUALITY_NUMERIC); 229 intent.putExtra(ChooseLockGenericFragment.EXTRA_SHOW_OPTIONS_BUTTON, true); 230 } 231 WizardManagerHelper.copyWizardManagerExtras(getIntent(), intent); 232 return intent; 233 } else { 234 return new Intent(this, ChooseLockGeneric.class); 235 } 236 } 237 238 @Override onActivityResult(int requestCode, int resultCode, Intent data)239 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 240 if (requestCode == BIOMETRIC_FIND_SENSOR_REQUEST) { 241 if (resultCode == RESULT_FINISHED || resultCode == RESULT_SKIP) { 242 setResult(resultCode, data); 243 finish(); 244 return; 245 } 246 } else if (requestCode == CHOOSE_LOCK_GENERIC_REQUEST) { 247 if (resultCode == RESULT_FINISHED) { 248 updatePasswordQuality(); 249 mToken = data.getByteArrayExtra( 250 ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN); 251 overridePendingTransition(R.anim.sud_slide_next_in, R.anim.sud_slide_next_out); 252 return; 253 } else { 254 setResult(resultCode, data); 255 finish(); 256 } 257 } else if (requestCode == CONFIRM_REQUEST) { 258 if (resultCode == RESULT_OK && data != null) { 259 mToken = data.getByteArrayExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN); 260 overridePendingTransition(R.anim.sud_slide_next_in, R.anim.sud_slide_next_out); 261 } else { 262 setResult(resultCode, data); 263 finish(); 264 } 265 } else if (requestCode == LEARN_MORE_REQUEST) { 266 overridePendingTransition(R.anim.sud_slide_back_in, R.anim.sud_slide_back_out); 267 } 268 super.onActivityResult(requestCode, resultCode, data); 269 } 270 onCancelButtonClick(View view)271 protected void onCancelButtonClick(View view) { 272 finish(); 273 } 274 onSkipButtonClick(View view)275 protected void onSkipButtonClick(View view) { 276 setResult(RESULT_SKIP); 277 finish(); 278 } 279 280 @Override initViews()281 protected void initViews() { 282 super.initViews(); 283 284 TextView description = (TextView) findViewById(R.id.sud_layout_description); 285 if (mBiometricUnlockDisabledByAdmin) { 286 description.setText(getDescriptionResDisabledByAdmin()); 287 } 288 } 289 } 290