diff --git a/core/java/com/android/internal/widget/LockSettingsInternal.java b/core/java/com/android/internal/widget/LockSettingsInternal.java index 90a18ef87ee21..38588ea9239de 100644 --- a/core/java/com/android/internal/widget/LockSettingsInternal.java +++ b/core/java/com/android/internal/widget/LockSettingsInternal.java @@ -107,4 +107,10 @@ public abstract class LockSettingsInternal { * @return true if the arming worked */ public abstract boolean armRebootEscrow(); + + + /** + * Refreshes pending strong auth timeout with the latest admin requirement set by device policy. + */ + public abstract void refreshStrongAuthTimeout(int userId); } diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index 7972f247b46dd..93d45c882e1c1 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -3441,6 +3441,11 @@ public class LockSettingsService extends ILockSettings.Stub { public boolean armRebootEscrow() { return mRebootEscrowManager.armRebootEscrowIfNeeded(); } + + @Override + public void refreshStrongAuthTimeout(int userId) { + mStrongAuth.refreshStrongAuthTimeout(userId); + } } private class RebootEscrowCallbacks implements RebootEscrowManager.Callbacks { diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java b/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java index 848019738abe4..a102406cc1312 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java @@ -58,6 +58,7 @@ public class LockSettingsStrongAuth { private static final int MSG_SCHEDULE_NON_STRONG_BIOMETRIC_TIMEOUT = 7; private static final int MSG_STRONG_BIOMETRIC_UNLOCK = 8; private static final int MSG_SCHEDULE_NON_STRONG_BIOMETRIC_IDLE_TIMEOUT = 9; + private static final int MSG_REFRESH_STRONG_AUTH_TIMEOUT = 10; @VisibleForTesting protected static final String STRONG_AUTH_TIMEOUT_ALARM_TAG = @@ -143,6 +144,15 @@ public class LockSettingsStrongAuth { public long getNextAlarmTimeMs(long timeout) { return SystemClock.elapsedRealtime() + timeout; } + + /** + * Wraps around {@link SystemClock#elapsedRealtime}, which returns the number of + * milliseconds since boot, including time spent in sleep. + */ + @VisibleForTesting + public long getElapsedRealtimeMs() { + return SystemClock.elapsedRealtime(); + } } private void handleAddStrongAuthTracker(IStrongAuthTracker tracker) { @@ -231,22 +241,33 @@ public class LockSettingsStrongAuth { } } - private void handleScheduleStrongAuthTimeout(int userId) { + /** + * Re-schedule the strong auth timeout alarm with latest information on the most recent + * successful strong auth time and strong auth timeout from device policy. + */ + private void rescheduleStrongAuthTimeoutAlarm(long strongAuthTime, int userId) { final DevicePolicyManager dpm = (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE); - long nextAlarmTime = - mInjector.getNextAlarmTimeMs(dpm.getRequiredStrongAuthTimeout(null, userId)); // cancel current alarm listener for the user (if there was one) StrongAuthTimeoutAlarmListener alarm = mStrongAuthTimeoutAlarmListenerForUser.get(userId); if (alarm != null) { mAlarmManager.cancel(alarm); + alarm.setLatestStrongAuthTime(strongAuthTime); } else { - alarm = new StrongAuthTimeoutAlarmListener(userId); + alarm = new StrongAuthTimeoutAlarmListener(strongAuthTime, userId); mStrongAuthTimeoutAlarmListenerForUser.put(userId, alarm); } + // AlarmManager.set() correctly handles the case where nextAlarmTime has already been in + // the past (by firing the listener straight away), so nothing special for us to do here. + long nextAlarmTime = strongAuthTime + dpm.getRequiredStrongAuthTimeout(null, userId); + // schedule a new alarm listener for the user mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, nextAlarmTime, STRONG_AUTH_TIMEOUT_ALARM_TAG, alarm, mHandler); + } + + private void handleScheduleStrongAuthTimeout(int userId) { + rescheduleStrongAuthTimeoutAlarm(mInjector.getElapsedRealtimeMs(), userId); // cancel current non-strong biometric alarm listener for the user (if there was one) cancelNonStrongBiometricAlarmListener(userId); @@ -256,6 +277,13 @@ public class LockSettingsStrongAuth { setIsNonStrongBiometricAllowed(true, userId); } + private void handleRefreshStrongAuthTimeout(int userId) { + StrongAuthTimeoutAlarmListener alarm = mStrongAuthTimeoutAlarmListenerForUser.get(userId); + if (alarm != null) { + rescheduleStrongAuthTimeoutAlarm(alarm.getLatestStrongAuthTime(), userId); + } + } + private void handleScheduleNonStrongBiometricTimeout(int userId) { if (DEBUG) Slog.d(TAG, "handleScheduleNonStrongBiometricTimeout for userId=" + userId); long nextAlarmTime = mInjector.getNextAlarmTimeMs(DEFAULT_NON_STRONG_BIOMETRIC_TIMEOUT_MS); @@ -455,6 +483,13 @@ public class LockSettingsStrongAuth { mHandler.obtainMessage(MSG_SCHEDULE_STRONG_AUTH_TIMEOUT, userId, argNotUsed).sendToTarget(); } + /** + * Refreshes pending strong auth timeout with the latest admin requirement set by device policy. + */ + public void refreshStrongAuthTimeout(int userId) { + mHandler.obtainMessage(MSG_REFRESH_STRONG_AUTH_TIMEOUT, userId, 0).sendToTarget(); + } + /** * Report successful unlocking with biometric */ @@ -489,12 +524,30 @@ public class LockSettingsStrongAuth { @VisibleForTesting protected class StrongAuthTimeoutAlarmListener implements OnAlarmListener { + private long mLatestStrongAuthTime; private final int mUserId; - public StrongAuthTimeoutAlarmListener(int userId) { + public StrongAuthTimeoutAlarmListener(long latestStrongAuthTime, int userId) { + mLatestStrongAuthTime = latestStrongAuthTime; mUserId = userId; } + /** + * Sets the most recent time when a successful strong auth happened, in number of + * milliseconds. + */ + public void setLatestStrongAuthTime(long strongAuthTime) { + mLatestStrongAuthTime = strongAuthTime; + } + + /** + * Returns the most recent time when a successful strong auth happened, in number of + * milliseconds. + */ + public long getLatestStrongAuthTime() { + return mLatestStrongAuthTime; + } + @Override public void onAlarm() { requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_TIMEOUT, mUserId); @@ -558,6 +611,9 @@ public class LockSettingsStrongAuth { case MSG_SCHEDULE_STRONG_AUTH_TIMEOUT: handleScheduleStrongAuthTimeout(msg.arg1); break; + case MSG_REFRESH_STRONG_AUTH_TIMEOUT: + handleRefreshStrongAuthTimeout(msg.arg1); + break; case MSG_NO_LONGER_REQUIRE_STRONG_AUTH: handleNoLongerRequireStrongAuth(msg.arg1, msg.arg2); break; diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index a74706be89157..ee135042f6603 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -5904,12 +5904,22 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } final int userHandle = mInjector.userHandleGetCallingUserId(); + boolean changed = false; synchronized (getLockObject()) { ActiveAdmin ap = getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, parent); if (ap.strongAuthUnlockTimeout != timeoutMs) { ap.strongAuthUnlockTimeout = timeoutMs; saveSettingsLocked(userHandle); + changed = true; + } + } + if (changed) { + mLockSettingsInternal.refreshStrongAuthTimeout(userHandle); + // Refreshes the parent if profile has unified challenge, since the timeout would + // also affect the parent user in this case. + if (isManagedProfile(userHandle) && !isSeparateProfileChallengeEnabled(userHandle)) { + mLockSettingsInternal.refreshStrongAuthTimeout(getProfileParentId(userHandle)); } } } diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStrongAuthTest.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStrongAuthTest.java index c9dbdd2364cc0..acb20edfe8d85 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStrongAuthTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStrongAuthTest.java @@ -24,6 +24,7 @@ import static com.android.server.locksettings.LockSettingsStrongAuth.NON_STRONG_ import static com.android.server.locksettings.LockSettingsStrongAuth.NON_STRONG_BIOMETRIC_TIMEOUT_ALARM_TAG; import static com.android.server.locksettings.LockSettingsStrongAuth.STRONG_AUTH_TIMEOUT_ALARM_TAG; +import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertNull; @@ -163,9 +164,11 @@ public class LockSettingsStrongAuthTest { @Test public void testReportSuccessfulStrongAuthUnlock_schedulePrimaryAuthTimeout() { - final long nextAlarmTime = 1000; - when(mInjector.getNextAlarmTimeMs(mDPM.getRequiredStrongAuthTimeout(null, PRIMARY_USER_ID))) - .thenReturn(nextAlarmTime); + final long currentTime = 1000; + final long timeout = 1000; + final long nextAlarmTime = currentTime + timeout; + when(mInjector.getElapsedRealtimeMs()).thenReturn(currentTime); + when(mDPM.getRequiredStrongAuthTimeout(null, PRIMARY_USER_ID)).thenReturn(timeout); mStrongAuth.reportSuccessfulStrongAuthUnlock(PRIMARY_USER_ID); waitForIdle(); @@ -177,6 +180,29 @@ public class LockSettingsStrongAuthTest { verifyAlarm(nextAlarmTime, STRONG_AUTH_TIMEOUT_ALARM_TAG, alarm); } + @Test + public void testReportSuccessfulStrongAuthUnlock_testRefreshStrongAuthTimeout() { + final long currentTime = 1000; + final long oldTimeout = 5000; + final long nextAlarmTime = currentTime + oldTimeout; + when(mInjector.getElapsedRealtimeMs()).thenReturn(currentTime); + when(mDPM.getRequiredStrongAuthTimeout(null, PRIMARY_USER_ID)).thenReturn(oldTimeout); + mStrongAuth.reportSuccessfulStrongAuthUnlock(PRIMARY_USER_ID); + waitForIdle(); + + StrongAuthTimeoutAlarmListener alarm = + mStrongAuth.mStrongAuthTimeoutAlarmListenerForUser.get(PRIMARY_USER_ID); + assertEquals(currentTime, alarm.getLatestStrongAuthTime()); + verifyAlarm(nextAlarmTime, STRONG_AUTH_TIMEOUT_ALARM_TAG, alarm); + + final long newTimeout = 3000; + when(mDPM.getRequiredStrongAuthTimeout(null, PRIMARY_USER_ID)).thenReturn(newTimeout); + mStrongAuth.refreshStrongAuthTimeout(PRIMARY_USER_ID); + waitForIdle(); + verify(mAlarmManager).cancel(alarm); + verifyAlarm(currentTime + newTimeout, STRONG_AUTH_TIMEOUT_ALARM_TAG, alarm); + } + @Test public void testReportSuccessfulStrongAuthUnlock_cancelAlarmsAndAllowNonStrongBio() { setupAlarms(PRIMARY_USER_ID);