問題:
在設置裏面解鎖多次失敗後,系統提示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獲取時間,這樣無論在哪解鎖失敗了,其他應用使用這個解鎖都會知道。