在系統服務裏面添加方法解決鎖屏失敗信息不同步問題

問題:

在設置裏面解鎖多次失敗後,系統提示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進程間通訊的方式:

  1. AIDL等Binder在內的方式,包括message
  2. Bundle/Intent,通過intent傳過去
  3. socket
  4. ContentProvider
  5. 廣播

廣播有延遲,這裏不能用,ContentProvider需要新建ContentProvider,很麻煩,socket也麻煩。使用binder是最好的方式,系統服務也是binder,於是使用LockSetting這個系統服務,LockSetting同一個管理解鎖服務,那麼增加一個管理解鎖失敗的最後時間也是沒有不妥的。

具體實現

  1. 首先修改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);
 }

  1. 在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就應用設置的。

  1. 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獲取時間,這樣無論在哪解鎖失敗了,其他應用使用這個解鎖都會知道。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章