原文:fingerprint-faceunlock连续使用72小时后,需要输入密码进行强认证解锁_face unlock remover-CSDN博客
fingerprint-faceunlock连续使用72小时后,需要输入密码进行强认证解锁流程探索
Google官方术语中,password, pin or pattern这三种类型的屏幕锁称为strong method authentication,强方法认证,而像指纹/面容解锁的方式则称作辅助的认证方式(weak authentication)。
为了安全起见,当上一次强认证解锁后达到一定的时间,例如72小时,则需要用户进行强制认证进行解锁设备,本文主要叙述timeout发生后要求强制解锁认证的流程。
设定强认证解锁超时机制是从解锁开始的,以password解锁为例,简单介绍下解锁的逻辑。
1.设备解锁流程
当我们在password界面输入了正确的密码,点击IME功能键或者Enter键,则会触发verifyPasswordAndUnlock。
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
// Check if this was the result of hitting the enter key
final boolean isSoftImeEvent = event == null
&& (actionId == EditorInfo.IME_NULL
|| actionId == EditorInfo.IME_ACTION_DONE
|| actionId == EditorInfo.IME_ACTION_NEXT);
final boolean isKeyboardEnterKey = event != null
&& KeyEvent.isConfirmKey(event.getKeyCode())
&& event.getAction() == KeyEvent.ACTION_DOWN;
if (isSoftImeEvent || isKeyboardEnterKey) {
verifyPasswordAndUnlock();
return true;
}
return false;
}
LockPatternChecker.checkPassword
--- LockPatternUtils.checkPassword
--- checkCredential
--- LockSettingsService::checkCredential
--- doVerifyCredential
--- spBasedDoVerifyCredential
--- getGateKeeperService().verifyChallenge(userId, challenge, storedHash.hash, credential); 去底层完成校验,略,后续深入
通过spBasedDoVerifyCredential方法得到一个response值,表示校验返回的结果。
public static final int RESPONSE_ERROR = -1;
public static final int RESPONSE_OK = 0;
public static final int RESPONSE_RETRY = 1;
当底层校验成功后,返回RESPONSE_OK,通知上层验证成功,完成解锁。
2. 设置Alarm超时进行强认证解锁
底层校验密码成功,则会进行report动作,调用reportSuccessfulStrongAuthUnlock:
if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
mStrongAuth.reportSuccessfulStrongAuthUnlock(userId); // LockSettingsStrongAuth
if (shouldReEnrollBaseZero) {
setLockCredentialInternal(credential, storedHash.type, credentialToVerify,
DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, userId, false,
/* isLockTiedToParent= */ false);
}
}
public void reportSuccessfulStrongAuthUnlock(int userId) {
final int argNotUsed = 0;
mHandler.obtainMessage(MSG_SCHEDULE_STRONG_AUTH_TIMEOUT, userId, argNotUsed).sendToTarget();
}
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_REGISTER_TRACKER:
handleAddStrongAuthTracker((IStrongAuthTracker) msg.obj);
break;
case MSG_UNREGISTER_TRACKER:
handleRemoveStrongAuthTracker((IStrongAuthTracker) msg.obj);
break;
case MSG_REQUIRE_STRONG_AUTH:
handleRequireStrongAuth(msg.arg1, msg.arg2);
break;
case MSG_REMOVE_USER:
handleRemoveUser(msg.arg1);
break;
case MSG_SCHEDULE_STRONG_AUTH_TIMEOUT:
handleScheduleStrongAuthTimeout(msg.arg1);
break;
}
}
};
private void handleScheduleStrongAuthTimeout(int userId) {
final DevicePolicyManager dpm =
(DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
long when = SystemClock.elapsedRealtime() + dpm.getRequiredStrongAuthTimeout(null, userId);
// cancel current alarm listener for the user (if there was one)
StrongAuthTimeoutAlarmListener alarm = mStrongAuthTimeoutAlarmListenerForUser.get(userId);
if (alarm != null) {
mAlarmManager.cancel(alarm);
} else {
alarm = new StrongAuthTimeoutAlarmListener(userId);
mStrongAuthTimeoutAlarmListenerForUser.put(userId, alarm);
}
// schedule a new alarm listener for the user
mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, when, STRONG_AUTH_TIMEOUT_ALARM_TAG,
alarm, mHandler);
}
通过handleScheduleStrongAuthTimeout方法,从当前时间戳开始,加上timeout超时的时间戳,即为下一次需要强制解锁认证的时间,设置定时Alarm来提醒用户进行强认证流程。
而超时的时间戳则通过getRequiredStrongAuthTimeout方法获取,此方法最终由DPMS(DevicePolicyManagerService)服务提供。
@RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
public long getRequiredStrongAuthTimeout(@Nullable ComponentName admin) {
return getRequiredStrongAuthTimeout(admin, myUserId());
}
/** @hide per-user version */
@UnsupportedAppUsage
@RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
public long getRequiredStrongAuthTimeout(@Nullable ComponentName admin, @UserIdInt int userId) {
if (mService != null) {
try {
return mService.getRequiredStrongAuthTimeout(admin, userId, mParentInstance);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
return DEFAULT_STRONG_AUTH_TIMEOUT_MS;
}
/**
* Return a single admin's strong auth unlock timeout or minimum value (strictest) of all
* admins if who is null.
* Returns 0 if not configured for the provided admin.
*/
@Override
public long getRequiredStrongAuthTimeout(ComponentName who, int userId, boolean parent) {
if (!mHasFeature) {
return DevicePolicyManager.DEFAULT_STRONG_AUTH_TIMEOUT_MS;
}
if (!mLockPatternUtils.hasSecureLockScreen()) {
// No strong auth timeout on devices not supporting the
// {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature
return 0;
}
enforceFullCrossUsersPermission(userId);
synchronized (getLockObject()) {
if (who != null) {
ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userId, parent);
return admin != null ? admin.strongAuthUnlockTimeout : 0;
}
// Return the strictest policy across all participating admins.
List<ActiveAdmin> admins = getActiveAdminsForLockscreenPoliciesLocked(userId, parent);
long strongAuthUnlockTimeout = DevicePolicyManager.DEFAULT_STRONG_AUTH_TIMEOUT_MS;
for (int i = 0; i < admins.size(); i++) {
final long timeout = admins.get(i).strongAuthUnlockTimeout;
if (timeout != 0) { // take only participating admins into account
strongAuthUnlockTimeout = Math.min(timeout, strongAuthUnlockTimeout);
}
}
return Math.max(strongAuthUnlockTimeout, getMinimumStrongAuthTimeoutMs());
}
}
这里面,我们的device owner以及profile owner类的应用是有权限去修改超时时间的,如果没有干预,则默认返回DEFAULT_STRONG_AUTH_TIMEOUT_MS:
/**
* Default and maximum timeout in milliseconds after which unlocking with weak auth times out,
* i.e. the user has to use a strong authentication method like password, PIN or pattern.
*
* @hide
*/
public static final long DEFAULT_STRONG_AUTH_TIMEOUT_MS = 72 * 60 * 60 * 1000; // 72h
3. Alarm触发引导用户进行强认证解锁
通过上面的分析,我们得知,每当使用强认证方式解锁一次,timeout将会重新计算,通过getRequiredStrongAuthTimeout方法得到超时时间。
而在我们持续使用设备期间,如果一直使用fingerprint/faceunlock方式解锁设备,知道设定的超时闹钟触发,就引导用户强制解锁认证流程,接下来我们跟进这一部分。
StrongAuthTimeoutAlarmListener alarm = mStrongAuthTimeoutAlarmListenerForUser.get(userId);
mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, when, STRONG_AUTH_TIMEOUT_ALARM_TAG,
alarm, mHandler);
alrm触发后,回调onAlarm方法,最终通过消息机制触发handler来处理:
@Override
public void onAlarm() {
requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_TIMEOUT, mUserId); // 注意这里传入的reason:STRONG_AUTH_REQUIRED_AFTER_TIMEOUT
}
public void requireStrongAuth(int strongAuthReason, int userId) {
if (userId == UserHandle.USER_ALL || userId >= UserHandle.USER_SYSTEM) {
mHandler.obtainMessage(MSG_REQUIRE_STRONG_AUTH, strongAuthReason,
userId).sendToTarget();
} else {
throw new IllegalArgumentException(
"userId must be an explicit user id or USER_ALL");
}
}
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_REQUIRE_STRONG_AUTH:
handleRequireStrongAuth(msg.arg1, msg.arg2);
break;
}
}
};
private void handleRequireStrongAuth(int strongAuthReason, int userId) {
if (userId == UserHandle.USER_ALL) {
for (int i = 0; i < mStrongAuthForUser.size(); i++) {
int key = mStrongAuthForUser.keyAt(i);
handleRequireStrongAuthOneUser(strongAuthReason, key);
}
} else {
handleRequireStrongAuthOneUser(strongAuthReason, userId);
}
}
private void handleRequireStrongAuthOneUser(int strongAuthReason, int userId) {
int oldValue = mStrongAuthForUser.get(userId, mDefaultStrongAuthFlags);
int newValue = strongAuthReason == STRONG_AUTH_NOT_REQUIRED
? STRONG_AUTH_NOT_REQUIRED
: (oldValue | strongAuthReason);
if (oldValue != newValue) {
mStrongAuthForUser.put(userId, newValue);
notifyStrongAuthTrackers(newValue, userId);
}
}
private void notifyStrongAuthTrackers(int strongAuthReason, int userId) {
int i = mTrackers.beginBroadcast();
try {
while (i > 0) {
i--;
try {
mTrackers.getBroadcastItem(i).onStrongAuthRequiredChanged(
strongAuthReason, userId);
} catch (RemoteException e) {
Slog.e(TAG, "Exception while notifying StrongAuthTracker.", e);
}
}
} finally {
mTrackers.finishBroadcast();
}
}
通过onStrongAuthRequiredChanged方法通知到SystemUI-Keyguard中,这里的mStrongAuthRequiredChangedCallback是构造StrongAuthTracker传入的,最终执行的是notifyStrongAuthStateChanged方法:
@Override
public void onStrongAuthRequiredChanged(int userId) {
mStrongAuthRequiredChangedCallback.accept(userId);
}
mStrongAuthTracker = new StrongAuthTracker(context, this::notifyStrongAuthStateChanged);
private void notifyStrongAuthStateChanged(int userId) {
checkIsHandlerThread();
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
cb.onStrongAuthStateChanged(userId);
}
}
}
KeyguardBouncer内部类mUpdateMonitorCallback实现了onStrongAuthStateChanged方法,此方法通过getBouncerPromptReason去取要求输密码的原因.
@Override
public int getBouncerPromptReason() {
int currentUser = ActivityManager.getCurrentUser();
boolean trust = mTrustManager.isTrustUsuallyManaged(currentUser);
boolean biometrics = mUpdateMonitor.isUnlockingWithBiometricsPossible(currentUser);
boolean any = trust || biometrics;
KeyguardUpdateMonitor.StrongAuthTracker strongAuthTracker =
mUpdateMonitor.getStrongAuthTracker();
int strongAuth = strongAuthTracker.getStrongAuthForUser(currentUser);
if (any && !strongAuthTracker.hasUserAuthenticatedSinceBoot()) {
return KeyguardSecurityView.PROMPT_REASON_RESTART;
} else if (any && (strongAuth & STRONG_AUTH_REQUIRED_AFTER_TIMEOUT) != 0) { // alarm触发后,设置的reason为:STRONG_AUTH_REQUIRED_AFTER_TIMEOUT,故最终匹配这里
return KeyguardSecurityView.PROMPT_REASON_TIMEOUT;
} else if (any && (strongAuth & STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW) != 0) {
return KeyguardSecurityView.PROMPT_REASON_DEVICE_ADMIN;
} else if (trust && (strongAuth & SOME_AUTH_REQUIRED_AFTER_USER_REQUEST) != 0) {
return KeyguardSecurityView.PROMPT_REASON_USER_REQUEST;
} else if (any && (strongAuth & STRONG_AUTH_REQUIRED_AFTER_LOCKOUT) != 0) {
return KeyguardSecurityView.PROMPT_REASON_AFTER_LOCKOUT;
}
return KeyguardSecurityView.PROMPT_REASON_NONE;
}
根据PromptReason,获取之前的锁屏类型,显示锁屏界面并通过相应的强认证解锁原因字符串,提示用户解锁设备,当password/pin/pattern解锁认证成功,又会进行新一轮的timeout设置。
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/huilin9960/article/details/115010309