From 57e694427769ce41bd8eaf960b53146190188f80 Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 4 Feb 2022 17:27:15 +0000 Subject: [PATCH] Adding a timeout for active-unlock Adding a timer for active-unlock. This timer should interact smoothly with the timer for extend unlock. That means that if there is a timer for extend unlock that expires while the active unlock timer is running, it should set the phone back into a trustable state. If there is an extend unlock timer when the active-unlock timer expires it should prevent active-unlocking but allow extend unlock to continue. Test: manual Fixes: 215391978 Change-Id: Ia0ccf7ecc9dacf9f4a7a913c7fce8532e6a1ece1 Merged-In: Ia0ccf7ecc9dacf9f4a7a913c7fce8532e6a1ece1 (cherry picked from commit 2410cfc05e226d1c1f6f9364764d03c6f149b5b8) --- .../server/trust/TrustAgentWrapper.java | 8 +- .../server/trust/TrustManagerService.java | 274 +++++++++++++++--- 2 files changed, 239 insertions(+), 43 deletions(-) diff --git a/services/core/java/com/android/server/trust/TrustAgentWrapper.java b/services/core/java/com/android/server/trust/TrustAgentWrapper.java index 1dea3d7943d89..fa243c023a878 100644 --- a/services/core/java/com/android/server/trust/TrustAgentWrapper.java +++ b/services/core/java/com/android/server/trust/TrustAgentWrapper.java @@ -17,6 +17,7 @@ package com.android.server.trust; import static android.service.trust.TrustAgentService.FLAG_GRANT_TRUST_DISPLAY_MESSAGE; +import static android.service.trust.TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE; import android.annotation.TargetApi; import android.app.AlarmManager; @@ -158,7 +159,7 @@ public class TrustAgentWrapper { mMessage = (CharSequence) msg.obj; int flags = msg.arg1; mDisplayTrustGrantedMessage = (flags & FLAG_GRANT_TRUST_DISPLAY_MESSAGE) != 0; - if ((flags & TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) != 0) { + if ((flags & FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) != 0) { mWaitingForTrustableDowngrade = true; } else { mWaitingForTrustableDowngrade = false; @@ -638,6 +639,11 @@ public class TrustAgentWrapper { return mTrustable && mManagingTrust && !mTrustDisabledByDpm; } + /** Set the trustagent as not trustable */ + public void setUntrustable() { + mTrustable = false; + } + public boolean isManagingTrust() { return mManagingTrust && !mTrustDisabledByDpm; } diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java index b4c54f9beeb7e..bd4b8d15f453c 100644 --- a/services/core/java/com/android/server/trust/TrustManagerService.java +++ b/services/core/java/com/android/server/trust/TrustManagerService.java @@ -16,6 +16,8 @@ package com.android.server.trust; +import static android.service.trust.TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE; + import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; @@ -127,13 +129,16 @@ public class TrustManagerService extends SystemService { private static final int MSG_DISPATCH_UNLOCK_LOCKOUT = 13; private static final int MSG_REFRESH_DEVICE_LOCKED_FOR_USER = 14; private static final int MSG_SCHEDULE_TRUST_TIMEOUT = 15; - public static final int MSG_USER_REQUESTED_UNLOCK = 16; + private static final int MSG_USER_REQUESTED_UNLOCK = 16; + private static final int MSG_REFRESH_TRUSTABLE_TIMERS_AFTER_AUTH = 17; private static final String REFRESH_DEVICE_LOCKED_EXCEPT_USER = "except"; private static final int TRUST_USUALLY_MANAGED_FLUSH_DELAY = 2 * 60 * 1000; private static final String TRUST_TIMEOUT_ALARM_TAG = "TrustManagerService.trustTimeoutForUser"; private static final long TRUST_TIMEOUT_IN_MILLIS = 4 * 60 * 60 * 1000; + private static final long TRUSTABLE_IDLE_TIMEOUT_IN_MILLIS = 8 * 60 * 60 * 1000; + private static final long TRUSTABLE_TIMEOUT_IN_MILLIS = 24 * 60 * 60 * 1000; private static final String PRIV_NAMESPACE = "http://schemas.android.com/apk/prv/res/android"; @@ -203,9 +208,18 @@ public class TrustManagerService extends SystemService { @GuardedBy("mUsersUnlockedByBiometric") private final SparseBooleanArray mUsersUnlockedByBiometric = new SparseBooleanArray(); - private final ArrayMap mTrustTimeoutAlarmListenerForUser = + private enum TimeoutType { + TRUSTED, + TRUSTABLE + } + private final ArrayMap mTrustTimeoutAlarmListenerForUser = new ArrayMap<>(); + private final SparseArray mTrustableTimeoutAlarmListenerForUser = + new SparseArray<>(); + private final SparseArray + mIdleTrustableTimeoutAlarmListenerForUser = new SparseArray<>(); private AlarmManager mAlarmManager; + private final Object mAlarmLock = new Object(); private final SettingsObserver mSettingsObserver; private final StrongAuthTracker mStrongAuthTracker; @@ -258,7 +272,7 @@ public class TrustManagerService extends SystemService { private final boolean mIsAutomotive; private final ContentResolver mContentResolver; - private boolean mTrustAgentsExtendUnlock; + private boolean mTrustAgentsNonrenewableTrust; private boolean mLockWhenTrustLost; /** @@ -295,11 +309,11 @@ public class TrustManagerService extends SystemService { @Override public void onChange(boolean selfChange, Uri uri) { if (TRUST_AGENTS_EXTEND_UNLOCK.equals(uri)) { - // Smart lock should only extend unlock. The only exception is for automotive, - // where it can actively unlock the head unit. + // Smart lock should only grant non-renewable trust. The only exception is for + // automotive, where it can actively unlock the head unit. int defaultValue = mIsAutomotive ? 0 : 1; - mTrustAgentsExtendUnlock = + mTrustAgentsNonrenewableTrust = Settings.Secure.getIntForUser( mContentResolver, Settings.Secure.TRUST_AGENTS_EXTEND_UNLOCK, @@ -315,8 +329,8 @@ public class TrustManagerService extends SystemService { } } - boolean getTrustAgentsExtendUnlock() { - return mTrustAgentsExtendUnlock; + boolean getTrustAgentsNonrenewableTrust() { + return mTrustAgentsNonrenewableTrust; } boolean getLockWhenTrustLost() { @@ -339,36 +353,53 @@ public class TrustManagerService extends SystemService { // If active unlocking is not allowed, cancel any pending trust timeouts because the // screen is already locked. - TrustTimeoutAlarmListener alarm = mTrustTimeoutAlarmListenerForUser.get(userId); - if (alarm != null && mSettingsObserver.getTrustAgentsExtendUnlock()) { + TrustedTimeoutAlarmListener alarm = mTrustTimeoutAlarmListenerForUser.get(userId); + if (alarm != null && mSettingsObserver.getTrustAgentsNonrenewableTrust()) { mAlarmManager.cancel(alarm); alarm.setQueued(false /* isQueued */); } } } - private void scheduleTrustTimeout(int userId, boolean override) { + private void scheduleTrustTimeout(boolean override, boolean isTrustableTimeout) { int shouldOverride = override ? 1 : 0; - if (override) { - shouldOverride = 1; - } - mHandler.obtainMessage(MSG_SCHEDULE_TRUST_TIMEOUT, userId, shouldOverride).sendToTarget(); + int trustableTimeout = isTrustableTimeout ? 1 : 0; + mHandler.obtainMessage(MSG_SCHEDULE_TRUST_TIMEOUT, shouldOverride, + trustableTimeout).sendToTarget(); } - private void handleScheduleTrustTimeout(int userId, int shouldOverride) { + private void handleScheduleTrustTimeout(boolean shouldOverride, TimeoutType timeoutType) { + int userId = mCurrentUser; + if (timeoutType == TimeoutType.TRUSTABLE) { + // don't override the hard timeout unless biometric or knowledge factor authentication + // occurs which isn't where this is called from. Override the idle timeout what the + // calling function has determined. + handleScheduleTrustableTimeouts(userId, shouldOverride, + false /* overrideHardTimeout */); + } else { + handleScheduleTrustedTimeout(userId, shouldOverride); + } + } + + /* Override both the idle and hard trustable timeouts */ + private void refreshTrustableTimers(int userId) { + handleScheduleTrustableTimeouts(userId, true /* overrideIdleTimeout */, + true /* overrideHardTimeout */); + } + + private void handleScheduleTrustedTimeout(int userId, boolean shouldOverride) { long when = SystemClock.elapsedRealtime() + TRUST_TIMEOUT_IN_MILLIS; - userId = mCurrentUser; - TrustTimeoutAlarmListener alarm = mTrustTimeoutAlarmListenerForUser.get(userId); + TrustedTimeoutAlarmListener alarm = mTrustTimeoutAlarmListenerForUser.get(userId); // Cancel existing trust timeouts for this user if needed. if (alarm != null) { - if (shouldOverride == 0 && alarm.isQueued()) { + if (!shouldOverride && alarm.isQueued()) { if (DEBUG) Slog.d(TAG, "Found existing trust timeout alarm. Skipping."); return; } mAlarmManager.cancel(alarm); } else { - alarm = new TrustTimeoutAlarmListener(userId); + alarm = new TrustedTimeoutAlarmListener(userId); mTrustTimeoutAlarmListenerForUser.put(userId, alarm); } @@ -379,6 +410,59 @@ public class TrustManagerService extends SystemService { mHandler); } + private void handleScheduleTrustableTimeouts(int userId, boolean overrideIdleTimeout, + boolean overrideHardTimeout) { + setUpIdleTimeout(userId, overrideIdleTimeout); + setUpHardTimeout(userId, overrideHardTimeout); + } + + private void setUpIdleTimeout(int userId, boolean overrideIdleTimeout) { + long when = SystemClock.elapsedRealtime() + TRUSTABLE_IDLE_TIMEOUT_IN_MILLIS; + TrustableTimeoutAlarmListener alarm = mIdleTrustableTimeoutAlarmListenerForUser.get(userId); + mContext.enforceCallingOrSelfPermission(Manifest.permission.SCHEDULE_EXACT_ALARM, null); + + // Cancel existing trustable timeouts for this user if needed. + if (alarm != null) { + if (!overrideIdleTimeout && alarm.isQueued()) { + if (DEBUG) Slog.d(TAG, "Found existing trustable timeout alarm. Skipping."); + return; + } + mAlarmManager.cancel(alarm); + } else { + alarm = new TrustableTimeoutAlarmListener(userId); + mIdleTrustableTimeoutAlarmListenerForUser.put(userId, alarm); + } + + if (DEBUG) Slog.d(TAG, "\tSetting up trustable idle timeout alarm"); + alarm.setQueued(true /* isQueued */); + mAlarmManager.setExact( + AlarmManager.ELAPSED_REALTIME_WAKEUP, when, TRUST_TIMEOUT_ALARM_TAG, alarm, + mHandler); + } + + private void setUpHardTimeout(int userId, boolean overrideHardTimeout) { + mContext.enforceCallingOrSelfPermission(Manifest.permission.SCHEDULE_EXACT_ALARM, null); + TrustableTimeoutAlarmListener alarm = mTrustableTimeoutAlarmListenerForUser.get(userId); + + // if the alarm doesn't exist, or hasn't been queued, or needs to be overridden we need to + // set it + if (alarm == null || !alarm.isQueued() || overrideHardTimeout) { + // schedule hard limit on renewable trust use + long when = SystemClock.elapsedRealtime() + TRUSTABLE_TIMEOUT_IN_MILLIS; + if (alarm == null) { + alarm = new TrustableTimeoutAlarmListener(userId); + mTrustableTimeoutAlarmListenerForUser.put(userId, alarm); + } else if (overrideHardTimeout) { + mAlarmManager.cancel(alarm); + } + if (DEBUG) Slog.d(TAG, "\tSetting up trustable hard timeout alarm"); + alarm.setQueued(true /* isQueued */); + mAlarmManager.setExact( + AlarmManager.ELAPSED_REALTIME_WAKEUP, when, TRUST_TIMEOUT_ALARM_TAG, alarm, + mHandler); + } + } + // Agent management private static final class AgentInfo { @@ -419,11 +503,11 @@ public class TrustManagerService extends SystemService { if (ENABLE_ACTIVE_UNLOCK_FLAG) { updateTrustWithRenewableUnlock(userId, flags, isFromUnlock); } else { - updateTrustWithExtendUnlock(userId, flags, isFromUnlock); + updateTrustWithNonrenewableTrust(userId, flags, isFromUnlock); } } - private void updateTrustWithExtendUnlock(int userId, int flags, boolean isFromUnlock) { + private void updateTrustWithNonrenewableTrust(int userId, int flags, boolean isFromUnlock) { boolean managed = aggregateIsTrustManaged(userId); dispatchOnTrustManagedChanged(managed, userId); if (mStrongAuthTracker.isTrustAllowedForUser(userId) @@ -441,8 +525,8 @@ public class TrustManagerService extends SystemService { boolean changed; synchronized (mUserIsTrusted) { - if (mSettingsObserver.getTrustAgentsExtendUnlock()) { - // In extend unlock trust agents can only set the device to trusted if it already + if (mSettingsObserver.getTrustAgentsNonrenewableTrust()) { + // For non-renewable trust agents can only set the device to trusted if it already // trusted or the device is unlocked. Attempting to set the device as trusted // when the device is locked will be ignored. changed = mUserIsTrusted.get(userId) != trusted; @@ -464,7 +548,7 @@ public class TrustManagerService extends SystemService { if (!trusted) { maybeLockScreen(userId); } else { - scheduleTrustTimeout(userId, false /* override */); + scheduleTrustTimeout(false /* override */, false /* isTrustableTimeout*/); } } } @@ -522,7 +606,12 @@ public class TrustManagerService extends SystemService { if (!isNowTrusted) { maybeLockScreen(userId); } else { - scheduleTrustTimeout(userId, false /* override */); + boolean isTrustableTimeout = + (flags & FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) != 0; + // Every time we grant renewable trust we should override the idle trustable + // timeout. If this is for non-renewable trust, then we shouldn't override. + scheduleTrustTimeout(isTrustableTimeout /* override */, + isTrustableTimeout /* isTrustableTimeout */); } } } @@ -1102,8 +1191,9 @@ public class TrustManagerService extends SystemService { private void dispatchUnlockAttempt(boolean successful, int userId) { if (successful) { mStrongAuthTracker.allowTrustFromUnlock(userId); - // Allow the presence of trust on a successful unlock attempt to extend unlock. + // Allow the presence of trust on a successful unlock attempt to extend unlock updateTrust(userId, 0 /* flags */, true); + mHandler.obtainMessage(MSG_REFRESH_TRUSTABLE_TIMERS_AFTER_AUTH, userId).sendToTarget(); } for (int i = 0; i < mActiveAgents.size(); i++) { @@ -1520,11 +1610,12 @@ public class TrustManagerService extends SystemService { synchronized(mUsersUnlockedByBiometric) { mUsersUnlockedByBiometric.put(userId, true); } - // In extend unlock mode we need to refresh trust state here, which will call + // In non-renewable trust mode we need to refresh trust state here, which will call // refreshDeviceLockedForUser() - int updateTrustOnUnlock = mSettingsObserver.getTrustAgentsExtendUnlock() ? 1 : 0; + int updateTrustOnUnlock = mSettingsObserver.getTrustAgentsNonrenewableTrust() ? 1 : 0; mHandler.obtainMessage(MSG_REFRESH_DEVICE_LOCKED_FOR_USER, userId, updateTrustOnUnlock).sendToTarget(); + mHandler.obtainMessage(MSG_REFRESH_TRUSTABLE_TIMERS_AFTER_AUTH, userId).sendToTarget(); } @Override @@ -1643,7 +1734,13 @@ public class TrustManagerService extends SystemService { refreshDeviceLockedForUser(msg.arg1, unlockedUser); break; case MSG_SCHEDULE_TRUST_TIMEOUT: - handleScheduleTrustTimeout(msg.arg1, msg.arg2); + boolean shouldOverride = msg.arg1 == 1 ? true : false; + TimeoutType timeoutType = + msg.arg2 == 1 ? TimeoutType.TRUSTABLE : TimeoutType.TRUSTED; + handleScheduleTrustTimeout(shouldOverride, timeoutType); + break; + case MSG_REFRESH_TRUSTABLE_TIMERS_AFTER_AUTH: + refreshTrustableTimers(msg.arg1); break; } } @@ -1759,10 +1856,11 @@ public class TrustManagerService extends SystemService { // Cancel pending alarms if we require some auth anyway. if (!isTrustAllowedForUser(userId)) { TrustTimeoutAlarmListener alarm = mTrustTimeoutAlarmListenerForUser.get(userId); - if (alarm != null && alarm.isQueued()) { - alarm.setQueued(false /* isQueued */); - mAlarmManager.cancel(alarm); - } + cancelPendingAlarm(alarm); + alarm = mTrustableTimeoutAlarmListenerForUser.get(userId); + cancelPendingAlarm(alarm); + alarm = mIdleTrustableTimeoutAlarmListenerForUser.get(userId); + cancelPendingAlarm(alarm); } refreshAgentList(userId); @@ -1772,6 +1870,13 @@ public class TrustManagerService extends SystemService { updateTrust(userId, 0 /* flags */); } + private void cancelPendingAlarm(@Nullable TrustTimeoutAlarmListener alarm) { + if (alarm != null && alarm.isQueued()) { + alarm.setQueued(false /* isQueued */); + mAlarmManager.cancel(alarm); + } + } + boolean canAgentsRunForUser(int userId) { return mStartFromSuccessfulUnlock.get(userId) || super.isTrustAllowedForUser(userId); @@ -1804,9 +1909,9 @@ public class TrustManagerService extends SystemService { } } - private class TrustTimeoutAlarmListener implements OnAlarmListener { - private final int mUserId; - private boolean mIsQueued = false; + private abstract class TrustTimeoutAlarmListener implements OnAlarmListener { + protected final int mUserId; + protected boolean mIsQueued = false; TrustTimeoutAlarmListener(int userId) { mUserId = userId; @@ -1815,8 +1920,7 @@ public class TrustManagerService extends SystemService { @Override public void onAlarm() { mIsQueued = false; - int strongAuthState = mStrongAuthTracker.getStrongAuthForUser(mUserId); - + handleAlarm(); // Only fire if trust can unlock. if (mStrongAuthTracker.isTrustAllowedForUser(mUserId)) { if (DEBUG) Slog.d(TAG, "Revoking all trust because of trust timeout"); @@ -1826,12 +1930,98 @@ public class TrustManagerService extends SystemService { maybeLockScreen(mUserId); } - public void setQueued(boolean isQueued) { - mIsQueued = isQueued; - } + protected abstract void handleAlarm(); public boolean isQueued() { return mIsQueued; } + + public void setQueued(boolean isQueued) { + mIsQueued = isQueued; + } + } + + private class TrustedTimeoutAlarmListener extends TrustTimeoutAlarmListener { + + TrustedTimeoutAlarmListener(int userId) { + super(userId); + } + + @Override + public void handleAlarm() { + TrustableTimeoutAlarmListener otherAlarm; + boolean otherAlarmPresent; + if (ENABLE_ACTIVE_UNLOCK_FLAG) { + otherAlarm = mTrustableTimeoutAlarmListenerForUser.get(mUserId); + otherAlarmPresent = (otherAlarm != null) && otherAlarm.isQueued(); + if (otherAlarmPresent) { + synchronized (mAlarmLock) { + disableNonrenewableTrustWhileRenewableTrustIsPresent(); + } + return; + } + } + } + + private void disableNonrenewableTrustWhileRenewableTrustIsPresent() { + synchronized (mUserTrustState) { + if (mUserTrustState.get(mUserId) == TrustState.TRUSTED) { + // if we're trusted and we have a trustable alarm, we need to + // downgrade to trustable + mUserTrustState.put(mUserId, TrustState.TRUSTABLE); + updateTrust(mUserId, 0 /* flags */); + } + } + } + } + + private class TrustableTimeoutAlarmListener extends TrustTimeoutAlarmListener { + + TrustableTimeoutAlarmListener(int userId) { + super(userId); + } + + @Override + public void handleAlarm() { + TrustedTimeoutAlarmListener otherAlarm; + boolean otherAlarmPresent; + if (ENABLE_ACTIVE_UNLOCK_FLAG) { + cancelBothTrustableAlarms(); + otherAlarm = mTrustTimeoutAlarmListenerForUser.get(mUserId); + otherAlarmPresent = (otherAlarm != null) && otherAlarm.isQueued(); + if (otherAlarmPresent) { + synchronized (mAlarmLock) { + disableRenewableTrustWhileNonrenewableTrustIsPresent(); + } + return; + } + } + } + + private void cancelBothTrustableAlarms() { + TrustableTimeoutAlarmListener idleTimeout = + mIdleTrustableTimeoutAlarmListenerForUser.get( + mUserId); + TrustableTimeoutAlarmListener trustableTimeout = + mTrustableTimeoutAlarmListenerForUser.get( + mUserId); + if (idleTimeout != null && idleTimeout.isQueued()) { + idleTimeout.setQueued(false); + mAlarmManager.cancel(idleTimeout); + } + if (trustableTimeout != null && trustableTimeout.isQueued()) { + trustableTimeout.setQueued(false); + mAlarmManager.cancel(trustableTimeout); + } + } + + private void disableRenewableTrustWhileNonrenewableTrustIsPresent() { + // if non-renewable trust is running, we need to temporarily prevent + // renewable trust from being used + for (AgentInfo agentInfo : mActiveAgents) { + agentInfo.agent.setUntrustable(); + } + updateTrust(mUserId, 0 /* flags */); + } } }