Update device password expiration/alarm behavior
* Change alarm math to snap to multiples of 24h before expiration
* Stop recurring alarm when no expirations upcoming
* Fix small bug in update logic when device password is updated
Change-Id: I31ce147e4f8c766245fae3e286fc50eaee4cfa22
diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java
index 53a19f5..0dead1c 100644
--- a/services/java/com/android/server/DevicePolicyManagerService.java
+++ b/services/java/com/android/server/DevicePolicyManagerService.java
@@ -407,17 +407,29 @@
context.registerReceiver(mReceiver, filter);
}
+ /**
+ * Set an alarm for an upcoming event - expiration warning, expiration, or post-expiration
+ * reminders. Clears alarm if no expirations are configured.
+ */
protected void setExpirationAlarmCheckLocked(Context context) {
final long expiration = getPasswordExpirationLocked(null);
final long now = System.currentTimeMillis();
final long timeToExpire = expiration - now;
final long alarmTime;
- if (timeToExpire > 0L && timeToExpire < MS_PER_DAY) {
- // Next expiration is less than a day, set alarm for exact expiration time
- alarmTime = now + timeToExpire;
- } else {
- // Check again in 24 hours...
+ if (expiration == 0) {
+ // No expirations are currently configured: Cancel alarm.
+ alarmTime = 0;
+ } else if (timeToExpire <= 0) {
+ // The password has already expired: Repeat every 24 hours.
alarmTime = now + MS_PER_DAY;
+ } else {
+ // Selecting the next alarm time: Roll forward to the next 24 hour multiple before
+ // the expiration time.
+ long alarmInterval = timeToExpire % MS_PER_DAY;
+ if (alarmInterval == 0) {
+ alarmInterval = MS_PER_DAY;
+ }
+ alarmTime = now + alarmInterval;
}
long token = Binder.clearCallingIdentity();
@@ -427,7 +439,9 @@
new Intent(ACTION_EXPIRED_PASSWORD_NOTIFICATION),
PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT);
am.cancel(pi);
- am.set(AlarmManager.RTC, alarmTime, pi);
+ if (alarmTime != 0) {
+ am.set(AlarmManager.RTC, alarmTime, pi);
+ }
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -794,7 +808,7 @@
if (admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD)
&& admin.passwordExpirationTimeout > 0L
&& admin.passwordExpirationDate > 0L
- && now > admin.passwordExpirationDate - EXPIRATION_GRACE_PERIOD_MS) {
+ && now >= admin.passwordExpirationDate - EXPIRATION_GRACE_PERIOD_MS) {
sendAdminCommandLocked(admin, DeviceAdminReceiver.ACTION_PASSWORD_EXPIRING);
}
}
@@ -1007,14 +1021,18 @@
}
}
+ /**
+ * Return a single admin's expiration cycle time, or the min of all cycle times.
+ * Returns 0 if not configured.
+ */
public long getPasswordExpirationTimeout(ComponentName who) {
synchronized (this) {
- long timeout = 0L;
if (who != null) {
ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
- return admin != null ? admin.passwordExpirationTimeout : timeout;
+ return admin != null ? admin.passwordExpirationTimeout : 0L;
}
+ long timeout = 0L;
final int N = mAdminList.size();
for (int i = 0; i < N; i++) {
ActiveAdmin admin = mAdminList.get(i);
@@ -1027,13 +1045,17 @@
}
}
+ /**
+ * Return a single admin's expiration date/time, or the min (soonest) for all admins.
+ * Returns 0 if not configured.
+ */
private long getPasswordExpirationLocked(ComponentName who) {
- long timeout = 0L;
if (who != null) {
ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
- return admin != null ? admin.passwordExpirationDate : timeout;
+ return admin != null ? admin.passwordExpirationDate : 0L;
}
+ long timeout = 0L;
final int N = mAdminList.size();
for (int i = 0; i < N; i++) {
ActiveAdmin admin = mAdminList.get(i);
@@ -1606,6 +1628,7 @@
mFailedPasswordAttempts = 0;
saveSettingsLocked();
updatePasswordExpirationsLocked();
+ setExpirationAlarmCheckLocked(mContext);
sendAdminCommandLocked(DeviceAdminReceiver.ACTION_PASSWORD_CHANGED,
DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
} finally {
@@ -1615,14 +1638,18 @@
}
}
+ /**
+ * Called any time the device password is updated. Resets all password expiration clocks.
+ */
private void updatePasswordExpirationsLocked() {
final int N = mAdminList.size();
if (N > 0) {
for (int i=0; i<N; i++) {
ActiveAdmin admin = mAdminList.get(i);
if (admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD)) {
- admin.passwordExpirationDate = System.currentTimeMillis()
- + admin.passwordExpirationTimeout;
+ long timeout = admin.passwordExpirationTimeout;
+ long expiration = timeout > 0L ? (timeout + System.currentTimeMillis()) : 0L;
+ admin.passwordExpirationDate = expiration;
}
}
saveSettingsLocked();