问题:
在设置里面解锁多次失败后,系统提示30秒之后才能继续做解锁操作。如果此时到锁屏界面,锁屏界面能够做解锁操作。这是不合理的,应该在上次解锁失败30秒之后才能做解锁操作。
问题分析:
在Setting和SystemUI中,在解锁失败之后会把解锁失败的时间点保存在LockPatternUtils对象中,然后应用判断当前时间与这个之间差是否小于30秒,小于就冻结锁屏的view使之无法解锁,然后倒计时提示用户。但是该对象在SystemUI和Setting中没有任何关联,使得在Setting解锁失败,SystemUI是无法失败。
LockPatternUtils.java
public long setLockoutAttemptDeadline(int userId, int timeoutMs) {
final long deadline = SystemClock.elapsedRealtime() + timeoutMs;
if (userId == USER_FRP) {
// For secure password storage (that is required for FRP), the underlying storage also
// enforces the deadline. Since we cannot store settings for the FRP user, don't.
return deadline;
}
mLockoutDeadlines.put(userId, deadline);
return deadline;
}
解决思路
如果要使得在Setting LockPatternUtils对象保存的信息要SystemUI一致,就需要让解锁失败的信息的传过来,由于这个是在不同的进程中,所以需要跨进程传递。
Android进程间通讯的方式:
- AIDL等Binder在内的方式,包括message
- Bundle/Intent,通过intent传过去
- socket
- ContentProvider
- 广播
广播有延迟,这里不能用,ContentProvider需要新建ContentProvider,很麻烦,socket也麻烦。使用binder是最好的方式,系统服务也是binder,于是使用LockSetting这个系统服务,LockSetting同一个管理解锁服务,那么增加一个管理解锁失败的最后时间也是没有不妥的。
具体实现
- 首先修改LockSetting;作为一个系统服务,他是通过binder方式与应用交互的,找到base/core/java/com/android/internal/widget/ILockSettings.aidl
增加获取和设置解锁失败时间方法
ILockSettings.aidl
@@ -71,19 +71,21 @@ interface ILockSettings {
void closeSession(in String sessionId);
+ long setLockoutAttemptDeadline(int userId,int timeoutMs);
+ long getLockoutAttemptDeadline(int userId);
}
- 在LockSettings实现其API
LockSettingsService.java
@Override
public VerifyCredentialResponse checkCredential(String credential, int type, int userId,
ICheckCredentialProgressCallback progressCallback) throws RemoteException {
checkPasswordReadPermission(userId);
- return doVerifyCredential(credential, type, false, 0, userId, progressCallback);
+ VerifyCredentialResponse retResponse = doVerifyCredential(credential, type, false, 0, userId, progressCallback);
+ if(retResponse.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) {
+ setLockoutAttemptDeadline(userId,retResponse.getTimeout());
+ }
+ return retResponse;
+ }
+
+ public long setLockoutAttemptDeadline(int userId,int timeoutMs) {
+ final long deadline = SystemClock.elapsedRealtime() + timeoutMs;
+ //Special user id for triggering the FRP verification flow.
+ if (userId == android.os.UserHandle.USER_NULL + 1) {
+ // For secure password storage (that is required for FRP), the underlying storage also
+ // enforces the deadline. Since we cannot store settings for the FRP user, don't.
+ return deadline;
+ }
+ mLockoutDeadlines.put(userId, deadline);
+ return deadline;
}
+ public long getLockoutAttemptDeadline(int userId) {
+ final long deadline = mLockoutDeadlines.get(userId, 0L);
+ final long now = SystemClock.elapsedRealtime();
+ if (deadline < now && deadline != 0) {
+ // timeout expired
+ mLockoutDeadlines.put(userId, 0);
+ return 0L;
+ }
+ return deadline;
+ }
+
+
setLockoutAttemptDeadline设置锁屏失败的时间
getLockoutAttemptDeadline获取锁屏失败的时间
之所以要在checkCredentialdia设置锁屏失败的时间,是为了防止应用那边忘了写,正常情况在检查
,即checkCredentialdia后,如果返回RESPONSE_RETRY就应用设置的。
- client端
Setting和SystemUI应用都是通过LockPatternUtils这个类设置锁屏失败时间的;
LockPatternUtils.java
public long setLockoutAttemptDeadline(int userId, int timeoutMs) {
+ try {
+ Log.d(TAG,"setLockoutAttemptDeadline uid = "+ userId + " timeoutMs = " + timeoutMs);
+ getLockSettings().setLockoutAttemptDeadline(userId,timeoutMs);
+ } catch (RemoteException re) {
+ Log.e(TAG,"setLockoutAttemptDeadline RemoteException error");
+ }
final long deadline = SystemClock.elapsedRealtime() + timeoutMs;
if (userId == USER_FRP) {
// For secure password storage (that is required for FRP), the underlying storage also
// enforces the deadline. Since we cannot store settings for the FRP user, don't.
return deadline;
}
mLockoutDeadlines.put(userId, deadline);
return deadline;
}
/**
* @return The elapsed time in millis in the future when the user is allowed to
* attempt to enter his/her lock pattern, or 0 if the user is welcome to
* enter a pattern.
*/
public long getLockoutAttemptDeadline(int userId) {
+ try {
+ Log.e(TAG,"getLockoutAttemptDeadline");
+ return getLockSettings().getLockoutAttemptDeadline(userId);
+ } catch (RemoteException re) {
+ Log.e(TAG,"getLockoutAttemptDeadline RemoteException error");
+ }
+
final long deadline = mLockoutDeadlines.get(userId, 0L);
final long now = SystemClock.elapsedRealtime();
if (deadline < now && deadline != 0) {
// timeout expired
mLockoutDeadlines.put(userId, 0);
return 0L;
}
return deadline;
}
getLockSettings()获取LocakSettingSerivice的代理类,调用增加方法setLockoutAttemptDeadline设置时间,调用getLockoutAttemptDeadline获取时间,这样无论在哪解锁失败了,其他应用使用这个解锁都会知道。