- Add face unlock entry to the Settings->Security->Screen lock menu as shown in fig. 1 (a).
- Add face unlock view to Android screen lock as shown in fig. 1 (b).
Fig1. (a) screen lock settings | Fig. 1 (b) face unlock view |
- LG Nexus 4
- AOSP: android-4.4.2_r2
- Eclipse Luna with ADT plugin (Android API 19)
Extend screen unlock settings
Add biometric entry to the unlock list in file “packages/apps/Settings/res/xml/security_settings_picker.xml” below "unlock_set_password" preference screen declarationThe definitions for the plain strings are in the "packages/apps/Settings/res/values/strings.xml"file. We add the unlock_set_biometric_strong below the "unlock_set_unlock_password_summary" as:
So that's the interface part, we need also figure out how the inner method call works.Biometric Strong security, on trail
Fig. 2 Face unlock settings sequence diagram |
Fig 3. SecuritySettings Class |
public boolean isSecure() { long mode = getKeyguardStoredPasswordQuality(); final boolean isPattern = mode == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING; final boolean isPassword = mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC || mode == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX; final boolean isBiometricStrong = mode == DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_STRONG; final boolean secure = isPattern && isLockPatternEnabled() && savedPatternExists() || isPassword && savedPasswordExists() || isBiometricStrong; return secure; }We add the biometric authentication also to be secure.
After that, it calls the addPreferencesFromResource of the PreferenceActivity class which "Inflates the given XML resource and adds the preference hierarchy to the current preference hierarchy".
Then as the other UI components, it waits for user action and pass control to other component. For PreferenceActivity class or subclasses, the action handler function in response to user click is onPreferenceTreeClick. It determines which preference element is being clicked by checking the preference key for each UI element. But what's the key of preference? Where was it set? Remember in the createPreferenceHierarchy we loaded the preference by resource id? It's right in the xml files that define the corresponding UI interfaces that the IDs are declared.
Following is the security_settings_biometric_strong.xml file that defines the settings interface when the biometric unlock is selected. We can see that line "android:key="unlock_set_or_change"" is where the preference key is set.
By checking the key, it starts the ChooseLockGeneric fragment when the screen lock entry is clicked.
final String key = preference.getKey(); final LockPatternUtils lockPatternUtils = mChooseLockSettingsHelper.utils(); if (KEY_UNLOCK_SET_OR_CHANGE.equals(key)) { startFragment(this, "com.android.settings.ChooseLockGeneric$ChooseLockGenericFragment", SET_OR_CHANGE_LOCK_METHOD_REQUEST, null); } else if (KEY_BIOMETRIC_WEAK_IMPROVE_MATCHING.equals(key)) { ChooseLockSettingsHelper helper = new ChooseLockSettingsHelper(this.getActivity(), this); if (!helper.launchConfirmationActivity( CONFIRM_EXISTING_FOR_BIOMETRIC_WEAK_IMPROVE_REQUEST, null, null)) { // If this returns false, it means no password confirmation is required, so // go ahead and start improve. // Note: currently a backup is required for biometric_weak so this code path // can't be reached, but is here in case things change in the future startBiometricWeakImprove(); }And ChooseLockGenericFragment is also of the SettingsPreferenceFragment type. Take a look at the onCreate function of the fragment
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mDPM = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE); mKeyStore = KeyStore.getInstance(); mChooseLockSettingsHelper = new ChooseLockSettingsHelper(this.getActivity()); // Defaults to needing to confirm credentials final boolean confirmCredentials = getActivity().getIntent() .getBooleanExtra(CONFIRM_CREDENTIALS, true); if (getActivity() instanceof ChooseLockGeneric.InternalActivity) { mPasswordConfirmed = !confirmCredentials; } if (savedInstanceState != null) { mPasswordConfirmed = savedInstanceState.getBoolean(PASSWORD_CONFIRMED); mWaitingForConfirmation = savedInstanceState.getBoolean(WAITING_FOR_CONFIRMATION); mFinishPending = savedInstanceState.getBoolean(FINISH_PENDING); } if (mPasswordConfirmed) { updatePreferencesOrFinish(); } else if (!mWaitingForConfirmation) { ChooseLockSettingsHelper helper = new ChooseLockSettingsHelper(this.getActivity(), this); if (!helper.launchConfirmationActivity(CONFIRM_EXISTING_REQUEST, null, null)) { mPasswordConfirmed = true; // no password set, so no need to confirm updatePreferencesOrFinish(); } else { mWaitingForConfirmation = true; } } }the settings program doesn't show the unlock methods list to the user directly. It first checks if the current user has confirmed the unlock credential or not. If so, the undatePreferenceOrFinish function will be called.
private void updatePreferencesOrFinish() { Intent intent = getActivity().getIntent(); int quality = intent.getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY, -1); if (quality == -1) { // If caller didn't specify password quality, show UI and allow the user to choose. quality = intent.getIntExtra(MINIMUM_QUALITY_KEY, -1); MutableBoolean allowBiometric = new MutableBoolean(false); quality = upgradeQuality(quality, allowBiometric); final PreferenceScreen prefScreen = getPreferenceScreen(); if (prefScreen != null) { prefScreen.removeAll(); } addPreferencesFromResource(R.xml.security_settings_picker); disableUnusablePreferences(quality, allowBiometric); } else { updateUnlockMethodAndFinish(quality, false); } }In the caller doesn't already specify the password quality, it will load the security_settings_picker preference tree and let the user select unlock method. Otherwise, it goes directly to the updateUnlockMethodAndFinish function. If the control flow goes from the security_settings_picker and the user pick up the unlock method, it also goes to the updateUnlockMethodAndFinish. So let's check what happens in that method.
void updateUnlockMethodAndFinish(int quality, boolean disabled) { // Sanity check. We should never get here without confirming user's existing password. if (!mPasswordConfirmed) { throw new IllegalStateException("Tried to update password without confirming it"); } final boolean isFallback = getActivity().getIntent() .getBooleanExtra(LockPatternUtils.LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK, false); quality = upgradeQuality(quality, null); if (quality >= DevicePolicyManager.PASSWORD_QUALITY_NUMERIC) { int minLength = mDPM.getPasswordMinimumLength(null); if (minLength < MIN_PASSWORD_LENGTH) { minLength = MIN_PASSWORD_LENGTH; } final int maxLength = mDPM.getPasswordMaximumLength(quality); Intent intent = new Intent().setClass(getActivity(), ChooseLockPassword.class); intent.putExtra(LockPatternUtils.PASSWORD_TYPE_KEY, quality); intent.putExtra(ChooseLockPassword.PASSWORD_MIN_KEY, minLength); intent.putExtra(ChooseLockPassword.PASSWORD_MAX_KEY, maxLength); intent.putExtra(CONFIRM_CREDENTIALS, false); intent.putExtra(LockPatternUtils.LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK, isFallback); if (isFallback) { startActivityForResult(intent, FALLBACK_REQUEST); return; } else { mFinishPending = true; intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); startActivity(intent); } } else if (quality == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING) { Intent intent = new Intent(getActivity(), ChooseLockPattern.class); intent.putExtra("key_lock_method", "pattern"); intent.putExtra(CONFIRM_CREDENTIALS, false); intent.putExtra(LockPatternUtils.LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK, isFallback); if (isFallback) { startActivityForResult(intent, FALLBACK_REQUEST); return; } else { mFinishPending = true; intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); startActivity(intent); } } else if (quality == DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK) { Intent intent = getBiometricSensorIntent(); mFinishPending = true; startActivity(intent); } else if (quality == DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_STRONG) { Intent intent = new Intent(); intent.setClass(getActivity(), ChooseLockBiometric.class); //intent.setClassName("edu.temple.dulab.biounlock", "edu.temple.dulab.biounlock.FaceEnroll"); //intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); mFinishPending = true; startActivity(intent); } else if (quality == DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) { mChooseLockSettingsHelper.utils().clearLock(false); mChooseLockSettingsHelper.utils().setLockScreenDisabled(disabled); getActivity().setResult(Activity.RESULT_OK); finish(); } else { finish(); } }Basically it switches control to other activity according to the selected password quality. So here we add the "PASSWORD_QUALITY_BIOMETRIC_STRONG" branch to direct the program to the face enrollment activity (ChooseLockBiometric). It that activity, we include a button to start face enrollment in the view.
// FacerecActivity in com.philriesch.android.vflock; Intent intent = new Intent(); intent.setClassName("com.philriesch.android.vflock", "com.philriesch.android.vflock.FacerecActivity"); intent.putExtra(EXTRA_ENROLLMODE, true); startActivityForResult(intent, REQUEST_ENROLL_FACE);We start the FacerecActivity located in the "com.philriesch.android.vflock" package. Once the activity result is received and is OK, we set the selected PASSWORD_TYPE_KEY to be PASSWORD_QUALITY_BIOMETRIC_STRONG and set the password quality for DPM in the LockPatternUtils instance.
That's the face enrollment extension to Android screen settings. Next we'll talk about extend keyguard to support face unlock.
Very good post! We are linked to this great article on our website. Keep up the good writing. Find more information: How To Lock Files The Planet Using Just Your Blog.
ReplyDelete