Improved trust error messaging (1/2)
Tracks why trust agents are disabled and shows
a generic message on the keyguard. Dedicated strings
in follow-up.
Bug: 22704995
Change-Id: Ibb4fd9c9386c4dc12f0734004502b9a9cc6ded79
diff --git a/core/java/android/app/trust/ITrustManager.aidl b/core/java/android/app/trust/ITrustManager.aidl
index a3fe6ab..d3d02e5 100644
--- a/core/java/android/app/trust/ITrustManager.aidl
+++ b/core/java/android/app/trust/ITrustManager.aidl
@@ -32,4 +32,5 @@
void setDeviceLockedForUser(int userId, boolean locked);
boolean isDeviceLocked(int userId);
boolean isDeviceSecure(int userId);
+ boolean isTrustUsuallyManaged(int userId);
}
diff --git a/core/java/android/app/trust/TrustManager.java b/core/java/android/app/trust/TrustManager.java
index 88ba874..2bbc54f 100644
--- a/core/java/android/app/trust/TrustManager.java
+++ b/core/java/android/app/trust/TrustManager.java
@@ -16,21 +16,17 @@
package android.app.trust;
+import com.android.internal.widget.LockPatternUtils;
+
import android.Manifest;
-import android.annotation.IntDef;
import android.annotation.RequiresPermission;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.Log;
-import android.util.SparseIntArray;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
/**
* See {@link com.android.server.trust.TrustManagerService}
@@ -158,6 +154,21 @@
}
}
+ /**
+ * @return whether {@userId} has enabled and configured trust agents. Ignores short-term
+ * unavailability of trust due to {@link LockPatternUtils.StrongAuthTracker}.
+ */
+ @RequiresPermission(android.Manifest.permission.TRUST_LISTENER)
+ public boolean isTrustUsuallyManaged(int userId) {
+ try {
+ return mService.isTrustUsuallyManaged(userId);
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+
+
private void onError(Exception e) {
Log.e(TAG, "Error while calling TrustManagerService", e);
}
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 4e8f19c..a1b18fe 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -136,6 +136,7 @@
private static final String LOCK_SCREEN_DEVICE_OWNER_INFO = "lockscreen.device_owner_info";
private static final String ENABLED_TRUST_AGENTS = "lockscreen.enabledtrustagents";
+ private static final String IS_TRUST_USUALLY_MANAGED = "lockscreen.istrustusuallymanaged";
private static final String SEPARATE_PROFILE_CHALLENGE_KEY = "lockscreen.profilechallenge";
@@ -149,6 +150,30 @@
private ILockSettings mLockSettingsService;
private UserManager mUserManager;
+ /**
+ * Use {@link TrustManager#isTrustUsuallyManaged(int)}.
+ *
+ * This returns the lazily-peristed value and should only be used by TrustManagerService.
+ */
+ public boolean isTrustUsuallyManaged(int userId) {
+ if (!(mLockSettingsService instanceof ILockSettings.Stub)) {
+ throw new IllegalStateException("May only be called by TrustManagerService. "
+ + "Use TrustManager.isTrustUsuallyManaged()");
+ }
+ try {
+ return getLockSettings().getBoolean(IS_TRUST_USUALLY_MANAGED, false, userId);
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ public void setTrustUsuallyManaged(boolean managed, int userId) {
+ try {
+ getLockSettings().setBoolean(IS_TRUST_USUALLY_MANAGED, managed, userId);
+ } catch (RemoteException e) {
+ // System dead.
+ }
+ }
public static final class RequestThrottledException extends Exception {
private int mTimeoutMs;
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java
index 3a7e6d0..63dec8b 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java
@@ -117,8 +117,10 @@
return R.string.kg_prompt_reason_restart_password;
case PROMPT_REASON_TIMEOUT:
return R.string.kg_prompt_reason_timeout_password;
- default:
+ case PROMPT_REASON_NONE:
return 0;
+ default:
+ return R.string.kg_prompt_reason_timeout_password;
}
}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
index 2a2f5a0..be2701d 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
@@ -339,7 +339,11 @@
mSecurityMessageDisplay.setMessage(R.string.kg_prompt_reason_timeout_pattern,
true /* important */);
break;
+ case PROMPT_REASON_NONE:
+ break;
default:
+ mSecurityMessageDisplay.setMessage(R.string.kg_prompt_reason_timeout_pattern,
+ true /* important */);
break;
}
}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPinBasedInputView.java
index f51e10f..cedd88d 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardPinBasedInputView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPinBasedInputView.java
@@ -103,8 +103,10 @@
return R.string.kg_prompt_reason_restart_pin;
case PROMPT_REASON_TIMEOUT:
return R.string.kg_prompt_reason_timeout_pin;
- default:
+ case PROMPT_REASON_NONE:
return 0;
+ default:
+ return R.string.kg_prompt_reason_timeout_pin;
}
}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityView.java
index 38302fb..aa74940 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityView.java
@@ -34,6 +34,26 @@
int PROMPT_REASON_TIMEOUT = 2;
/**
+ * Strong auth is required because a device admin requested it.
+ */
+ int PROMPT_REASON_DEVICE_ADMIN = 3;
+
+ /**
+ * Some auth is required because the user force locked.
+ */
+ int PROMPT_REASON_USER_REQUEST = 4;
+
+ /**
+ * Some auth is required because too many wrong credentials led to a lockout.
+ */
+ int PROMPT_REASON_AFTER_LOCKOUT = 5;
+
+ /**
+ * Some auth is required because a single wrong credential has been tried.
+ */
+ int PROMPT_REASON_WRONG_CREDENTIAL = 6;
+
+ /**
* Interface back to keyguard to tell it when security
* @param callback
*/
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 704de97..0475c72 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -84,6 +84,10 @@
import java.util.List;
import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_WRONG_CREDENTIAL;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
/**
* Mediates requests related to the keyguard. This includes queries about the
@@ -550,14 +554,28 @@
@Override
public int getBouncerPromptReason() {
int currentUser = ActivityManager.getCurrentUser();
- if ((mUpdateMonitor.getUserTrustIsManaged(currentUser)
- || mUpdateMonitor.isUnlockWithFingerprintPossible(currentUser))
- && !mUpdateMonitor.getStrongAuthTracker().hasUserAuthenticatedSinceBoot()) {
+ boolean trust = mTrustManager.isTrustUsuallyManaged(currentUser);
+ boolean fingerprint = mUpdateMonitor.isUnlockWithFingerprintPossible(currentUser);
+ boolean any = trust || fingerprint;
+ KeyguardUpdateMonitor.StrongAuthTracker strongAuthTracker =
+ mUpdateMonitor.getStrongAuthTracker();
+ int strongAuth = strongAuthTracker.getStrongAuthForUser(currentUser);
+
+ if (any && !strongAuthTracker.hasUserAuthenticatedSinceBoot()) {
return KeyguardSecurityView.PROMPT_REASON_RESTART;
- } else if (mUpdateMonitor.isUnlockWithFingerprintPossible(currentUser)
- && mUpdateMonitor.hasFingerprintUnlockTimedOut(currentUser)) {
+ } else if (fingerprint && mUpdateMonitor.hasFingerprintUnlockTimedOut(currentUser)) {
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;
+ } else if (trust && (strongAuth & SOME_AUTH_REQUIRED_AFTER_WRONG_CREDENTIAL) != 0) {
+ return KeyguardSecurityView.PROMPT_REASON_WRONG_CREDENTIAL;
}
+
+
return KeyguardSecurityView.PROMPT_REASON_NONE;
}
};
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 1d498e1..3452f41 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -60,7 +60,6 @@
import android.util.Log;
import android.util.Slog;
import android.util.SparseBooleanArray;
-import android.util.SparseIntArray;
import android.util.Xml;
import android.view.IWindowManager;
import android.view.WindowManagerGlobal;
@@ -103,6 +102,9 @@
private static final int MSG_CLEANUP_USER = 8;
private static final int MSG_SWITCH_USER = 9;
private static final int MSG_SET_DEVICE_LOCKED = 10;
+ private static final int MSG_FLUSH_TRUST_USUALLY_MANAGED = 11;
+
+ public static final int TRUST_USUALLY_MANAGED_FLUSH_DELAY = 2 * 60 * 1000;
private final ArraySet<AgentInfo> mActiveAgents = new ArraySet<>();
private final ArrayList<ITrustListener> mTrustListeners = new ArrayList<>();
@@ -120,6 +122,9 @@
@GuardedBy("mDeviceLockedForUser")
private final SparseBooleanArray mDeviceLockedForUser = new SparseBooleanArray();
+ @GuardedBy("mDeviceLockedForUser")
+ private final SparseBooleanArray mTrustUsuallyManagedForUser = new SparseBooleanArray();
+
private boolean mTrustAgentsCanRun = false;
private int mCurrentUser = UserHandle.USER_SYSTEM;
@@ -187,7 +192,12 @@
}
public void updateTrust(int userId, int flags) {
- dispatchOnTrustManagedChanged(aggregateIsTrustManaged(userId), userId);
+ boolean managed = aggregateIsTrustManaged(userId);
+ dispatchOnTrustManagedChanged(managed, userId);
+ if (mStrongAuthTracker.isTrustAllowedForUser(userId)
+ && isTrustUsuallyManagedInternal(userId) != managed) {
+ updateTrustUsuallyManaged(userId, managed);
+ }
boolean trusted = aggregateIsTrusted(userId);
boolean changed;
synchronized (mUserIsTrusted) {
@@ -200,6 +210,18 @@
}
}
+ private void updateTrustUsuallyManaged(int userId, boolean managed) {
+ synchronized (mTrustUsuallyManagedForUser) {
+ mTrustUsuallyManagedForUser.put(userId, managed);
+ }
+ // Wait a few minutes before committing to flash, in case the trust agent is transiently not
+ // managing trust (crashed, needs to acknowledge DPM restrictions, etc).
+ mHandler.removeMessages(MSG_FLUSH_TRUST_USUALLY_MANAGED);
+ mHandler.sendMessageDelayed(
+ mHandler.obtainMessage(MSG_FLUSH_TRUST_USUALLY_MANAGED),
+ TRUST_USUALLY_MANAGED_FLUSH_DELAY);
+ }
+
void refreshAgentList(int userId) {
if (DEBUG) Slog.d(TAG, "refreshAgentList()");
if (!mTrustAgentsCanRun) {
@@ -787,8 +809,37 @@
mHandler.obtainMessage(MSG_SET_DEVICE_LOCKED, value ? 1 : 0, userId)
.sendToTarget();
}
+
+ @Override
+ public boolean isTrustUsuallyManaged(int userId) {
+ mContext.enforceCallingPermission(Manifest.permission.TRUST_LISTENER,
+ "query trust state");
+ return isTrustUsuallyManagedInternal(userId);
+ }
};
+ private boolean isTrustUsuallyManagedInternal(int userId) {
+ synchronized (mTrustUsuallyManagedForUser) {
+ int i = mTrustUsuallyManagedForUser.indexOfKey(userId);
+ if (i >= 0) {
+ return mTrustUsuallyManagedForUser.valueAt(i);
+ }
+ }
+ // It's not in memory yet, get the value from persisted storage instead
+ boolean persistedValue = mLockPatternUtils.isTrustUsuallyManaged(userId);
+ synchronized (mTrustUsuallyManagedForUser) {
+ int i = mTrustUsuallyManagedForUser.indexOfKey(userId);
+ if (i >= 0) {
+ // Someone set the trust usually managed in the mean time. Better use that.
+ return mTrustUsuallyManagedForUser.valueAt(i);
+ } else {
+ // .. otherwise it's safe to cache the fetched value now.
+ mTrustUsuallyManagedForUser.put(userId, persistedValue);
+ return persistedValue;
+ }
+ }
+ }
+
private int resolveProfileParent(int userId) {
long identity = Binder.clearCallingIdentity();
try {
@@ -834,6 +885,19 @@
case MSG_SET_DEVICE_LOCKED:
setDeviceLockedForUser(msg.arg2, msg.arg1 != 0);
break;
+ case MSG_FLUSH_TRUST_USUALLY_MANAGED:
+ SparseBooleanArray usuallyManaged;
+ synchronized (mTrustUsuallyManagedForUser) {
+ usuallyManaged = mTrustUsuallyManagedForUser.clone();
+ }
+
+ for (int i = 0; i < usuallyManaged.size(); i++) {
+ int userId = usuallyManaged.keyAt(i);
+ boolean value = usuallyManaged.valueAt(i);
+ if (value != mLockPatternUtils.isTrustUsuallyManaged(userId)) {
+ mLockPatternUtils.setTrustUsuallyManaged(value, userId);
+ }
+ }
}
}
};