Loading core/java/android/app/AlarmManager.java +22 −1 Original line number Diff line number Diff line Loading @@ -141,6 +141,15 @@ public class AlarmManager { */ public static final int FLAG_ALLOW_WHILE_IDLE = 1<<2; /** * Flag for alarms: same as {@link #FLAG_ALLOW_WHILE_IDLE}, but doesn't have restrictions * on how frequently it can be scheduled. Only available (and automatically applied) to * system alarms. * * @hide */ public static final int FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED = 1<<3; /** * Flag for alarms: this alarm marks the point where we would like to come out of idle * mode. It may be moved by the alarm manager to match the first wake-from-idle alarm. Loading @@ -148,7 +157,7 @@ public class AlarmManager { * avoids scheduling any further alarms until the marker alarm is executed. * @hide */ public static final int FLAG_IDLE_UNTIL = 1<<3; public static final int FLAG_IDLE_UNTIL = 1<<4; private final IAlarmManager mService; private final boolean mAlwaysExact; Loading Loading @@ -565,6 +574,12 @@ public class AlarmManager { * of the device when idle (and thus cause significant battery blame to the app scheduling * them), so they should be used with care. * * <p>To reduce abuse, there are restrictions on how frequently these alarms will go off * for a particular application. Under normal system operation, it will not dispatch these * alarms more than about every minute (at which point every such pending alarm is * dispatched); when in low-power idle modes this duration may be significantly longer, * such as 15 minutes.</p> * * <p>Unlike other alarms, the system is free to reschedule this type of alarm to happen * out of order with any other alarms, even those from the same app. This will clearly happen * when the device is idle (since this alarm can go off while idle, when any other alarms Loading Loading @@ -608,6 +623,12 @@ public class AlarmManager { * of the device when idle (and thus cause significant battery blame to the app scheduling * them), so they should be used with care. * * <p>To reduce abuse, there are restrictions on how frequently these alarms will go off * for a particular application. Under normal system operation, it will not dispatch these * alarms more than about every minute (at which point every such pending alarm is * dispatched); when in low-power idle modes this duration may be significantly longer, * such as 15 minutes.</p> * * <p>Unlike other alarms, the system is free to reschedule this type of alarm to happen * out of order with any other alarms, even those from the same app. This will clearly happen * when the device is idle (since this alarm can go off while idle, when any other alarms Loading core/java/android/util/TimeUtils.java +53 −33 Original line number Diff line number Diff line Loading @@ -246,11 +246,18 @@ public class TimeUtils { public static final long NANOS_PER_MS = 1000000; private static final Object sFormatSync = new Object(); private static char[] sFormatStr = new char[HUNDRED_DAY_FIELD_LEN+5]; private static final long LARGEST_DURATION = (1000 * DateUtils.DAY_IN_MILLIS) - 1; private static char[] sFormatStr = new char[HUNDRED_DAY_FIELD_LEN+10]; private static char[] sTmpFormatStr = new char[HUNDRED_DAY_FIELD_LEN+10]; static private int accumField(int amt, int suffix, boolean always, int zeropad) { if (amt > 999) { int num = 0; while (amt != 0) { num++; amt /= 10; } return num + suffix; } else { if (amt > 99 || (always && zeropad >= 3)) { return 3+suffix; } Loading @@ -260,13 +267,29 @@ public class TimeUtils { if (always || amt > 0) { return 1+suffix; } } return 0; } static private int printField(char[] formatStr, int amt, char suffix, int pos, static private int printFieldLocked(char[] formatStr, int amt, char suffix, int pos, boolean always, int zeropad) { if (always || amt > 0) { final int startPos = pos; if (amt > 999) { int tmp = 0; while (amt != 0 && tmp < sTmpFormatStr.length) { int dig = amt % 10; sTmpFormatStr[tmp] = (char)(dig + '0'); tmp++; amt /= 10; } tmp--; while (tmp >= 0) { formatStr[pos] = sTmpFormatStr[tmp]; pos++; tmp--; } } else { if ((always && zeropad >= 3) || amt > 99) { int dig = amt/100; formatStr[pos] = (char)(dig + '0'); Loading @@ -281,6 +304,7 @@ public class TimeUtils { } formatStr[pos] = (char)(amt + '0'); pos++; } formatStr[pos] = suffix; pos++; } Loading Loading @@ -312,10 +336,6 @@ public class TimeUtils { duration = -duration; } if (duration > LARGEST_DURATION) { duration = LARGEST_DURATION; } int millis = (int)(duration%1000); int seconds = (int) Math.floor(duration / 1000); int days = 0, hours = 0, minutes = 0; Loading Loading @@ -353,11 +373,11 @@ public class TimeUtils { int start = pos; boolean zeropad = fieldLen != 0; pos = printField(formatStr, days, 'd', pos, false, 0); pos = printField(formatStr, hours, 'h', pos, pos != start, zeropad ? 2 : 0); pos = printField(formatStr, minutes, 'm', pos, pos != start, zeropad ? 2 : 0); pos = printField(formatStr, seconds, 's', pos, pos != start, zeropad ? 2 : 0); pos = printField(formatStr, millis, 'm', pos, true, (zeropad && pos != start) ? 3 : 0); pos = printFieldLocked(formatStr, days, 'd', pos, false, 0); pos = printFieldLocked(formatStr, hours, 'h', pos, pos != start, zeropad ? 2 : 0); pos = printFieldLocked(formatStr, minutes, 'm', pos, pos != start, zeropad ? 2 : 0); pos = printFieldLocked(formatStr, seconds, 's', pos, pos != start, zeropad ? 2 : 0); pos = printFieldLocked(formatStr, millis, 'm', pos, true, (zeropad && pos != start) ? 3 : 0); formatStr[pos] = 's'; return pos + 1; } Loading services/core/java/com/android/server/AlarmManagerService.java +122 −35 Original line number Diff line number Diff line Loading @@ -47,6 +47,7 @@ import android.util.Log; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; import android.util.SparseLongArray; import android.util.TimeUtils; import java.io.ByteArrayOutputStream; Loading Loading @@ -84,6 +85,12 @@ class AlarmManagerService extends SystemService { // Minimum alarm recurrence interval private static final long MIN_INTERVAL = 60 * 1000; // one minute, in millis // Minimum time between ALLOW_WHILE_IDLE alarms when system is not idle. private static final long ALLOW_WHILE_IDLE_SHORT_TIME = 60*1000; // Minimum time between ALLOW_WHILE_IDLE alarms when system is idling. private static final long ALLOW_WHILE_IDLE_LONG_TIME = 15*60*1000; private static final int RTC_WAKEUP_MASK = 1 << RTC_WAKEUP; private static final int RTC_MASK = 1 << RTC; private static final int ELAPSED_REALTIME_WAKEUP_MASK = 1 << ELAPSED_REALTIME_WAKEUP; Loading Loading @@ -123,8 +130,8 @@ class AlarmManagerService extends SystemService { int mBroadcastRefCount = 0; PowerManager.WakeLock mWakeLock; boolean mLastWakeLockUnimportantForLogging; ArrayList<Alarm> mPendingNonWakeupAlarms = new ArrayList<Alarm>(); ArrayList<InFlight> mInFlight = new ArrayList<InFlight>(); ArrayList<Alarm> mPendingNonWakeupAlarms = new ArrayList<>(); ArrayList<InFlight> mInFlight = new ArrayList<>(); final AlarmHandler mHandler = new AlarmHandler(); ClockReceiver mClockReceiver; InteractiveStateReceiver mInteractiveStateReceiver; Loading @@ -141,8 +148,15 @@ class AlarmManagerService extends SystemService { long mNextNonWakeupDeliveryTime; long mLastTimeChangeClockTime; long mLastTimeChangeRealtime; long mAllowWhileIdleMinTime = ALLOW_WHILE_IDLE_SHORT_TIME; int mNumTimeChanged; /** * For each uid, this is the last time we dispatched an "allow while idle" alarm, * used to determine the earliest we can dispatch the next such alarm. */ final SparseLongArray mLastAllowWhileIdleDispatch = new SparseLongArray(); private final SparseArray<AlarmManager.AlarmClockInfo> mNextAlarmClockForUser = new SparseArray<>(); private final SparseArray<AlarmManager.AlarmClockInfo> mTmpSparseAlarmClockArray = Loading Loading @@ -552,7 +566,7 @@ class AlarmManagerService extends SystemService { a.when = a.origWhen; long whenElapsed = convertToElapsed(a.when, a.type); final long maxElapsed; if (a.whenElapsed == a.maxWhenElapsed) { if (a.windowLength == AlarmManager.WINDOW_EXACT) { // Exact maxElapsed = whenElapsed; } else { Loading Loading @@ -580,6 +594,9 @@ class AlarmManagerService extends SystemService { } } // Make sure we are using the correct ALLOW_WHILE_IDLE min time. mAllowWhileIdleMinTime = ALLOW_WHILE_IDLE_SHORT_TIME; // Reschedule everything. rescheduleKernelAlarmsLocked(); updateNextAlarmClockLocked(); Loading Loading @@ -751,7 +768,7 @@ class AlarmManagerService extends SystemService { void setImpl(int type, long triggerAtTime, long windowLength, long interval, PendingIntent operation, int flags, WorkSource workSource, AlarmManager.AlarmClockInfo alarmClock) { AlarmManager.AlarmClockInfo alarmClock, int callingUid) { if (operation == null) { Slog.w(TAG, "set/setRepeating ignored because there is no intent"); return; Loading Loading @@ -779,9 +796,8 @@ class AlarmManagerService extends SystemService { } if (triggerAtTime < 0) { final long who = Binder.getCallingUid(); final long what = Binder.getCallingPid(); Slog.w(TAG, "Invalid alarm trigger time! " + triggerAtTime + " from uid=" + who Slog.w(TAG, "Invalid alarm trigger time! " + triggerAtTime + " from uid=" + callingUid + " pid=" + what); triggerAtTime = 0; } Loading @@ -797,12 +813,12 @@ class AlarmManagerService extends SystemService { maxElapsed = triggerElapsed; } else if (windowLength < 0) { maxElapsed = maxTriggerTime(nowElapsed, triggerElapsed, interval); // Fix this window in place, so that as time approaches we don't collapse it. windowLength = maxElapsed - triggerElapsed; } else { maxElapsed = triggerElapsed + windowLength; } final int userId = UserHandle.getCallingUserId(); synchronized (mLock) { if (DEBUG_BATCH) { Slog.v(TAG, "set(" + operation + ") : type=" + type Loading @@ -811,26 +827,20 @@ class AlarmManagerService extends SystemService { + " interval=" + interval + " flags=0x" + Integer.toHexString(flags)); } setImplLocked(type, triggerAtTime, triggerElapsed, windowLength, maxElapsed, interval, operation, flags, true, workSource, alarmClock, userId); interval, operation, flags, true, workSource, alarmClock, callingUid); } } private void setImplLocked(int type, long when, long whenElapsed, long windowLength, long maxWhen, long interval, PendingIntent operation, int flags, boolean doValidate, WorkSource workSource, AlarmManager.AlarmClockInfo alarmClock, int userId) { int uid) { Alarm a = new Alarm(type, when, whenElapsed, windowLength, maxWhen, interval, operation, workSource, flags, alarmClock, userId); operation, workSource, flags, alarmClock, uid); removeLocked(operation); setImplLocked(a, false, doValidate); } private void updateNextWakeFromIdleFuzzLocked() { if (mNextWakeFromIdle != null) { } } private void setImplLocked(Alarm a, boolean rebatching, boolean doValidate) { if ((a.flags&AlarmManager.FLAG_IDLE_UNTIL) != 0) { // This is a special alarm that will put the system into idle until it goes off. Loading Loading @@ -862,7 +872,9 @@ class AlarmManagerService extends SystemService { } else if (mPendingIdleUntil != null) { // We currently have an idle until alarm scheduled; if the new alarm has // not explicitly stated it wants to run while idle, then put it on hold. if ((a.flags&(AlarmManager.FLAG_ALLOW_WHILE_IDLE|AlarmManager.FLAG_WAKE_FROM_IDLE)) if ((a.flags&(AlarmManager.FLAG_ALLOW_WHILE_IDLE | AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED | AlarmManager.FLAG_WAKE_FROM_IDLE)) == 0) { mPendingWhileIdleAlarms.add(a); return; Loading Loading @@ -892,6 +904,7 @@ class AlarmManagerService extends SystemService { if ((a.flags&AlarmManager.FLAG_IDLE_UNTIL) != 0) { mPendingIdleUntil = a; mAllowWhileIdleMinTime = ALLOW_WHILE_IDLE_LONG_TIME; needRebatch = true; } else if ((a.flags&AlarmManager.FLAG_WAKE_FROM_IDLE) != 0) { if (mNextWakeFromIdle == null || mNextWakeFromIdle.whenElapsed > a.whenElapsed) { Loading Loading @@ -933,23 +946,45 @@ class AlarmManagerService extends SystemService { public void set(int type, long triggerAtTime, long windowLength, long interval, int flags, PendingIntent operation, WorkSource workSource, AlarmManager.AlarmClockInfo alarmClock) { final int callingUid = Binder.getCallingUid(); if (workSource != null) { getContext().enforceCallingPermission( getContext().enforcePermission( android.Manifest.permission.UPDATE_DEVICE_STATS, "AlarmManager.set"); Binder.getCallingPid(), callingUid, "AlarmManager.set"); } // No incoming callers can request either WAKE_FROM_IDLE or // ALLOW_WHILE_IDLE_UNRESTRICTED -- we will apply those later as appropriate. flags &= ~(AlarmManager.FLAG_WAKE_FROM_IDLE | AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED); // Only the system can use FLAG_IDLE_UNTIL -- this is used to tell the alarm // manager when to come out of idle mode, which is only for DeviceIdleController. if (callingUid != Process.SYSTEM_UID) { flags &= ~AlarmManager.FLAG_IDLE_UNTIL; } // If the caller is a core system component, and not calling to do work on behalf // of someone else, then always set ALLOW_WHILE_IDLE_UNRESTRICTED. This means we // will allow these alarms to go off as normal even while idle, with no timing // restrictions. if (callingUid < Process.FIRST_APPLICATION_UID && workSource == null) { flags |= AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED; } // If this is an exact time alarm, then it can't be batched with other alarms. if (windowLength == AlarmManager.WINDOW_EXACT) { flags |= AlarmManager.FLAG_STANDALONE; } // If this alarm is for an alarm clock, then it must be standalone and we will // use it to wake early from idle if needed. if (alarmClock != null) { flags |= AlarmManager.FLAG_WAKE_FROM_IDLE | AlarmManager.FLAG_STANDALONE; } if (Binder.getCallingUid() < Process.FIRST_APPLICATION_UID) { flags |= AlarmManager.FLAG_ALLOW_WHILE_IDLE; } setImpl(type, triggerAtTime, windowLength, interval, operation, flags, workSource, alarmClock); flags, workSource, alarmClock, callingUid); } @Override Loading Loading @@ -1126,6 +1161,22 @@ class AlarmManagerService extends SystemService { pw.print(" Broadcast ref count: "); pw.println(mBroadcastRefCount); pw.println(); pw.print("mAllowWhileIdleMinTime="); TimeUtils.formatDuration(mAllowWhileIdleMinTime, pw); pw.println(); if (mLastAllowWhileIdleDispatch.size() > 0) { pw.println("Last allow while idle dispatch times:"); for (int i=0; i<mLastAllowWhileIdleDispatch.size(); i++) { pw.print(" UID "); UserHandle.formatUid(pw, mLastAllowWhileIdleDispatch.keyAt(i)); pw.print(": "); TimeUtils.formatDuration(mLastAllowWhileIdleDispatch.valueAt(i), nowELAPSED, pw); pw.println(); } } pw.println(); if (mLog.dump(pw, " Recent problems", " ")) { pw.println(); } Loading Loading @@ -1322,7 +1373,7 @@ class AlarmManagerService extends SystemService { for (int j = 0; j < M; j++) { Alarm a = alarms.get(j); if (a.alarmClock != null) { final int userId = a.userId; final int userId = UserHandle.getUserId(a.uid); if (DEBUG_ALARM_CLOCK) { Log.v(TAG, "Found AlarmClockInfo at " + Loading Loading @@ -1531,6 +1582,11 @@ class AlarmManagerService extends SystemService { mPendingWhileIdleAlarms.remove(i); } } for (int i = mLastAllowWhileIdleDispatch.size() - 1; i >= 0; i--) { if (UserHandle.getUserId(mLastAllowWhileIdleDispatch.keyAt(i)) == userHandle) { mLastAllowWhileIdleDispatch.removeAt(i); } } if (didRemove) { if (DEBUG_BATCH) { Loading Loading @@ -1666,6 +1722,25 @@ class AlarmManagerService extends SystemService { final int N = batch.size(); for (int i = 0; i < N; i++) { Alarm alarm = batch.get(i); if ((alarm.flags&AlarmManager.FLAG_ALLOW_WHILE_IDLE) != 0) { // If this is an ALLOW_WHILE_IDLE alarm, we constrain how frequently the app can // schedule such alarms. long lastTime = mLastAllowWhileIdleDispatch.get(alarm.uid, 0); long minTime = lastTime + mAllowWhileIdleMinTime; if (nowELAPSED < minTime) { // Whoops, it hasn't been long enough since the last ALLOW_WHILE_IDLE // alarm went off for this app. Reschedule the alarm to be in the // correct time period. alarm.whenElapsed = minTime; if (alarm.maxWhenElapsed < minTime) { alarm.maxWhenElapsed = minTime; } setImplLocked(alarm, true, false); continue; } } alarm.count = 1; triggerList.add(alarm); if ((alarm.flags&AlarmManager.FLAG_WAKE_FROM_IDLE) != 0) { Loading Loading @@ -1695,7 +1770,7 @@ class AlarmManagerService extends SystemService { setImplLocked(alarm.type, alarm.when + delta, nextElapsed, alarm.windowLength, maxTriggerTime(nowELAPSED, nextElapsed, alarm.repeatInterval), alarm.repeatInterval, alarm.operation, alarm.flags, true, alarm.workSource, alarm.alarmClock, alarm.userId); alarm.workSource, alarm.alarmClock, alarm.uid); } if (alarm.wakeup) { Loading Loading @@ -1749,19 +1824,19 @@ class AlarmManagerService extends SystemService { public final String tag; public final WorkSource workSource; public final int flags; public final AlarmManager.AlarmClockInfo alarmClock; public final int uid; public int count; public long when; public long windowLength; public long whenElapsed; // 'when' in the elapsed time base public long maxWhenElapsed; // also in the elapsed time base public long repeatInterval; public final AlarmManager.AlarmClockInfo alarmClock; public final int userId; public PriorityClass priorityClass; public Alarm(int _type, long _when, long _whenElapsed, long _windowLength, long _maxWhen, long _interval, PendingIntent _op, WorkSource _ws, int _flags, AlarmManager.AlarmClockInfo _info, int _userId) { AlarmManager.AlarmClockInfo _info, int _uid) { type = _type; origWhen = _when; wakeup = _type == AlarmManager.ELAPSED_REALTIME_WAKEUP Loading @@ -1776,7 +1851,7 @@ class AlarmManagerService extends SystemService { workSource = _ws; flags = _flags; alarmClock = _info; userId = _userId; uid = _uid; } public static String makeTag(PendingIntent pi, int type) { Loading Loading @@ -1812,7 +1887,7 @@ class AlarmManagerService extends SystemService { pw.print(" when="); TimeUtils.formatDuration(when, nowELAPSED, pw); } pw.println(); pw.print(prefix); pw.print("window="); pw.print(windowLength); pw.print(prefix); pw.print("window="); TimeUtils.formatDuration(windowLength, pw); pw.print(" repeatInterval="); pw.print(repeatInterval); pw.print(" count="); pw.print(count); pw.print(" flags=0x"); pw.println(Integer.toHexString(flags)); Loading Loading @@ -1925,6 +2000,11 @@ class AlarmManagerService extends SystemService { mInFlight.add(inflight); mBroadcastRefCount++; if ((alarm.flags&AlarmManager.FLAG_ALLOW_WHILE_IDLE) != 0) { // Record the last time this uid handled an ALLOW_WHILE_IDLE alarm. mLastAllowWhileIdleDispatch.put(alarm.uid, nowELAPSED); } final BroadcastStats bs = inflight.mBroadcastStats; bs.count++; if (bs.nesting == 0) { Loading Loading @@ -2196,7 +2276,8 @@ class AlarmManagerService extends SystemService { final WorkSource workSource = null; // Let system take blame for time tick events. setImpl(ELAPSED_REALTIME, SystemClock.elapsedRealtime() + tickEventDelay, 0, 0, mTimeTickSender, AlarmManager.FLAG_STANDALONE, workSource, null); 0, mTimeTickSender, AlarmManager.FLAG_STANDALONE, workSource, null, Process.myUid()); } public void scheduleDateChangedEvent() { Loading @@ -2210,7 +2291,7 @@ class AlarmManagerService extends SystemService { final WorkSource workSource = null; // Let system take blame for date change events. setImpl(RTC, calendar.getTimeInMillis(), 0, 0, mDateChangeSender, AlarmManager.FLAG_STANDALONE, workSource, null); AlarmManager.FLAG_STANDALONE, workSource, null, Process.myUid()); } } Loading Loading @@ -2243,6 +2324,7 @@ class AlarmManagerService extends SystemService { IntentFilter sdFilter = new IntentFilter(); sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); sdFilter.addAction(Intent.ACTION_USER_STOPPED); sdFilter.addAction(Intent.ACTION_UID_REMOVED); getContext().registerReceiver(this, sdFilter); } Loading @@ -2267,6 +2349,11 @@ class AlarmManagerService extends SystemService { if (userHandle >= 0) { removeUserLocked(userHandle); } } else if (Intent.ACTION_UID_REMOVED.equals(action)) { int uid = intent.getIntExtra(Intent.EXTRA_UID, -1); if (uid >= 0) { mLastAllowWhileIdleDispatch.delete(uid); } } else { if (Intent.ACTION_PACKAGE_REMOVED.equals(action) && intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { Loading tests/ActivityTests/AndroidManifest.xml +1 −0 Original line number Diff line number Diff line Loading @@ -76,5 +76,6 @@ android:authorities="com.google.android.test.activity.single_user" android:singleUser="true" android:exported="true" /> <receiver android:name="TrackTimeReceiver" /> <receiver android:name="AlarmSpamReceiver" /> </application> </manifest> tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java +28 −0 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
core/java/android/app/AlarmManager.java +22 −1 Original line number Diff line number Diff line Loading @@ -141,6 +141,15 @@ public class AlarmManager { */ public static final int FLAG_ALLOW_WHILE_IDLE = 1<<2; /** * Flag for alarms: same as {@link #FLAG_ALLOW_WHILE_IDLE}, but doesn't have restrictions * on how frequently it can be scheduled. Only available (and automatically applied) to * system alarms. * * @hide */ public static final int FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED = 1<<3; /** * Flag for alarms: this alarm marks the point where we would like to come out of idle * mode. It may be moved by the alarm manager to match the first wake-from-idle alarm. Loading @@ -148,7 +157,7 @@ public class AlarmManager { * avoids scheduling any further alarms until the marker alarm is executed. * @hide */ public static final int FLAG_IDLE_UNTIL = 1<<3; public static final int FLAG_IDLE_UNTIL = 1<<4; private final IAlarmManager mService; private final boolean mAlwaysExact; Loading Loading @@ -565,6 +574,12 @@ public class AlarmManager { * of the device when idle (and thus cause significant battery blame to the app scheduling * them), so they should be used with care. * * <p>To reduce abuse, there are restrictions on how frequently these alarms will go off * for a particular application. Under normal system operation, it will not dispatch these * alarms more than about every minute (at which point every such pending alarm is * dispatched); when in low-power idle modes this duration may be significantly longer, * such as 15 minutes.</p> * * <p>Unlike other alarms, the system is free to reschedule this type of alarm to happen * out of order with any other alarms, even those from the same app. This will clearly happen * when the device is idle (since this alarm can go off while idle, when any other alarms Loading Loading @@ -608,6 +623,12 @@ public class AlarmManager { * of the device when idle (and thus cause significant battery blame to the app scheduling * them), so they should be used with care. * * <p>To reduce abuse, there are restrictions on how frequently these alarms will go off * for a particular application. Under normal system operation, it will not dispatch these * alarms more than about every minute (at which point every such pending alarm is * dispatched); when in low-power idle modes this duration may be significantly longer, * such as 15 minutes.</p> * * <p>Unlike other alarms, the system is free to reschedule this type of alarm to happen * out of order with any other alarms, even those from the same app. This will clearly happen * when the device is idle (since this alarm can go off while idle, when any other alarms Loading
core/java/android/util/TimeUtils.java +53 −33 Original line number Diff line number Diff line Loading @@ -246,11 +246,18 @@ public class TimeUtils { public static final long NANOS_PER_MS = 1000000; private static final Object sFormatSync = new Object(); private static char[] sFormatStr = new char[HUNDRED_DAY_FIELD_LEN+5]; private static final long LARGEST_DURATION = (1000 * DateUtils.DAY_IN_MILLIS) - 1; private static char[] sFormatStr = new char[HUNDRED_DAY_FIELD_LEN+10]; private static char[] sTmpFormatStr = new char[HUNDRED_DAY_FIELD_LEN+10]; static private int accumField(int amt, int suffix, boolean always, int zeropad) { if (amt > 999) { int num = 0; while (amt != 0) { num++; amt /= 10; } return num + suffix; } else { if (amt > 99 || (always && zeropad >= 3)) { return 3+suffix; } Loading @@ -260,13 +267,29 @@ public class TimeUtils { if (always || amt > 0) { return 1+suffix; } } return 0; } static private int printField(char[] formatStr, int amt, char suffix, int pos, static private int printFieldLocked(char[] formatStr, int amt, char suffix, int pos, boolean always, int zeropad) { if (always || amt > 0) { final int startPos = pos; if (amt > 999) { int tmp = 0; while (amt != 0 && tmp < sTmpFormatStr.length) { int dig = amt % 10; sTmpFormatStr[tmp] = (char)(dig + '0'); tmp++; amt /= 10; } tmp--; while (tmp >= 0) { formatStr[pos] = sTmpFormatStr[tmp]; pos++; tmp--; } } else { if ((always && zeropad >= 3) || amt > 99) { int dig = amt/100; formatStr[pos] = (char)(dig + '0'); Loading @@ -281,6 +304,7 @@ public class TimeUtils { } formatStr[pos] = (char)(amt + '0'); pos++; } formatStr[pos] = suffix; pos++; } Loading Loading @@ -312,10 +336,6 @@ public class TimeUtils { duration = -duration; } if (duration > LARGEST_DURATION) { duration = LARGEST_DURATION; } int millis = (int)(duration%1000); int seconds = (int) Math.floor(duration / 1000); int days = 0, hours = 0, minutes = 0; Loading Loading @@ -353,11 +373,11 @@ public class TimeUtils { int start = pos; boolean zeropad = fieldLen != 0; pos = printField(formatStr, days, 'd', pos, false, 0); pos = printField(formatStr, hours, 'h', pos, pos != start, zeropad ? 2 : 0); pos = printField(formatStr, minutes, 'm', pos, pos != start, zeropad ? 2 : 0); pos = printField(formatStr, seconds, 's', pos, pos != start, zeropad ? 2 : 0); pos = printField(formatStr, millis, 'm', pos, true, (zeropad && pos != start) ? 3 : 0); pos = printFieldLocked(formatStr, days, 'd', pos, false, 0); pos = printFieldLocked(formatStr, hours, 'h', pos, pos != start, zeropad ? 2 : 0); pos = printFieldLocked(formatStr, minutes, 'm', pos, pos != start, zeropad ? 2 : 0); pos = printFieldLocked(formatStr, seconds, 's', pos, pos != start, zeropad ? 2 : 0); pos = printFieldLocked(formatStr, millis, 'm', pos, true, (zeropad && pos != start) ? 3 : 0); formatStr[pos] = 's'; return pos + 1; } Loading
services/core/java/com/android/server/AlarmManagerService.java +122 −35 Original line number Diff line number Diff line Loading @@ -47,6 +47,7 @@ import android.util.Log; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; import android.util.SparseLongArray; import android.util.TimeUtils; import java.io.ByteArrayOutputStream; Loading Loading @@ -84,6 +85,12 @@ class AlarmManagerService extends SystemService { // Minimum alarm recurrence interval private static final long MIN_INTERVAL = 60 * 1000; // one minute, in millis // Minimum time between ALLOW_WHILE_IDLE alarms when system is not idle. private static final long ALLOW_WHILE_IDLE_SHORT_TIME = 60*1000; // Minimum time between ALLOW_WHILE_IDLE alarms when system is idling. private static final long ALLOW_WHILE_IDLE_LONG_TIME = 15*60*1000; private static final int RTC_WAKEUP_MASK = 1 << RTC_WAKEUP; private static final int RTC_MASK = 1 << RTC; private static final int ELAPSED_REALTIME_WAKEUP_MASK = 1 << ELAPSED_REALTIME_WAKEUP; Loading Loading @@ -123,8 +130,8 @@ class AlarmManagerService extends SystemService { int mBroadcastRefCount = 0; PowerManager.WakeLock mWakeLock; boolean mLastWakeLockUnimportantForLogging; ArrayList<Alarm> mPendingNonWakeupAlarms = new ArrayList<Alarm>(); ArrayList<InFlight> mInFlight = new ArrayList<InFlight>(); ArrayList<Alarm> mPendingNonWakeupAlarms = new ArrayList<>(); ArrayList<InFlight> mInFlight = new ArrayList<>(); final AlarmHandler mHandler = new AlarmHandler(); ClockReceiver mClockReceiver; InteractiveStateReceiver mInteractiveStateReceiver; Loading @@ -141,8 +148,15 @@ class AlarmManagerService extends SystemService { long mNextNonWakeupDeliveryTime; long mLastTimeChangeClockTime; long mLastTimeChangeRealtime; long mAllowWhileIdleMinTime = ALLOW_WHILE_IDLE_SHORT_TIME; int mNumTimeChanged; /** * For each uid, this is the last time we dispatched an "allow while idle" alarm, * used to determine the earliest we can dispatch the next such alarm. */ final SparseLongArray mLastAllowWhileIdleDispatch = new SparseLongArray(); private final SparseArray<AlarmManager.AlarmClockInfo> mNextAlarmClockForUser = new SparseArray<>(); private final SparseArray<AlarmManager.AlarmClockInfo> mTmpSparseAlarmClockArray = Loading Loading @@ -552,7 +566,7 @@ class AlarmManagerService extends SystemService { a.when = a.origWhen; long whenElapsed = convertToElapsed(a.when, a.type); final long maxElapsed; if (a.whenElapsed == a.maxWhenElapsed) { if (a.windowLength == AlarmManager.WINDOW_EXACT) { // Exact maxElapsed = whenElapsed; } else { Loading Loading @@ -580,6 +594,9 @@ class AlarmManagerService extends SystemService { } } // Make sure we are using the correct ALLOW_WHILE_IDLE min time. mAllowWhileIdleMinTime = ALLOW_WHILE_IDLE_SHORT_TIME; // Reschedule everything. rescheduleKernelAlarmsLocked(); updateNextAlarmClockLocked(); Loading Loading @@ -751,7 +768,7 @@ class AlarmManagerService extends SystemService { void setImpl(int type, long triggerAtTime, long windowLength, long interval, PendingIntent operation, int flags, WorkSource workSource, AlarmManager.AlarmClockInfo alarmClock) { AlarmManager.AlarmClockInfo alarmClock, int callingUid) { if (operation == null) { Slog.w(TAG, "set/setRepeating ignored because there is no intent"); return; Loading Loading @@ -779,9 +796,8 @@ class AlarmManagerService extends SystemService { } if (triggerAtTime < 0) { final long who = Binder.getCallingUid(); final long what = Binder.getCallingPid(); Slog.w(TAG, "Invalid alarm trigger time! " + triggerAtTime + " from uid=" + who Slog.w(TAG, "Invalid alarm trigger time! " + triggerAtTime + " from uid=" + callingUid + " pid=" + what); triggerAtTime = 0; } Loading @@ -797,12 +813,12 @@ class AlarmManagerService extends SystemService { maxElapsed = triggerElapsed; } else if (windowLength < 0) { maxElapsed = maxTriggerTime(nowElapsed, triggerElapsed, interval); // Fix this window in place, so that as time approaches we don't collapse it. windowLength = maxElapsed - triggerElapsed; } else { maxElapsed = triggerElapsed + windowLength; } final int userId = UserHandle.getCallingUserId(); synchronized (mLock) { if (DEBUG_BATCH) { Slog.v(TAG, "set(" + operation + ") : type=" + type Loading @@ -811,26 +827,20 @@ class AlarmManagerService extends SystemService { + " interval=" + interval + " flags=0x" + Integer.toHexString(flags)); } setImplLocked(type, triggerAtTime, triggerElapsed, windowLength, maxElapsed, interval, operation, flags, true, workSource, alarmClock, userId); interval, operation, flags, true, workSource, alarmClock, callingUid); } } private void setImplLocked(int type, long when, long whenElapsed, long windowLength, long maxWhen, long interval, PendingIntent operation, int flags, boolean doValidate, WorkSource workSource, AlarmManager.AlarmClockInfo alarmClock, int userId) { int uid) { Alarm a = new Alarm(type, when, whenElapsed, windowLength, maxWhen, interval, operation, workSource, flags, alarmClock, userId); operation, workSource, flags, alarmClock, uid); removeLocked(operation); setImplLocked(a, false, doValidate); } private void updateNextWakeFromIdleFuzzLocked() { if (mNextWakeFromIdle != null) { } } private void setImplLocked(Alarm a, boolean rebatching, boolean doValidate) { if ((a.flags&AlarmManager.FLAG_IDLE_UNTIL) != 0) { // This is a special alarm that will put the system into idle until it goes off. Loading Loading @@ -862,7 +872,9 @@ class AlarmManagerService extends SystemService { } else if (mPendingIdleUntil != null) { // We currently have an idle until alarm scheduled; if the new alarm has // not explicitly stated it wants to run while idle, then put it on hold. if ((a.flags&(AlarmManager.FLAG_ALLOW_WHILE_IDLE|AlarmManager.FLAG_WAKE_FROM_IDLE)) if ((a.flags&(AlarmManager.FLAG_ALLOW_WHILE_IDLE | AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED | AlarmManager.FLAG_WAKE_FROM_IDLE)) == 0) { mPendingWhileIdleAlarms.add(a); return; Loading Loading @@ -892,6 +904,7 @@ class AlarmManagerService extends SystemService { if ((a.flags&AlarmManager.FLAG_IDLE_UNTIL) != 0) { mPendingIdleUntil = a; mAllowWhileIdleMinTime = ALLOW_WHILE_IDLE_LONG_TIME; needRebatch = true; } else if ((a.flags&AlarmManager.FLAG_WAKE_FROM_IDLE) != 0) { if (mNextWakeFromIdle == null || mNextWakeFromIdle.whenElapsed > a.whenElapsed) { Loading Loading @@ -933,23 +946,45 @@ class AlarmManagerService extends SystemService { public void set(int type, long triggerAtTime, long windowLength, long interval, int flags, PendingIntent operation, WorkSource workSource, AlarmManager.AlarmClockInfo alarmClock) { final int callingUid = Binder.getCallingUid(); if (workSource != null) { getContext().enforceCallingPermission( getContext().enforcePermission( android.Manifest.permission.UPDATE_DEVICE_STATS, "AlarmManager.set"); Binder.getCallingPid(), callingUid, "AlarmManager.set"); } // No incoming callers can request either WAKE_FROM_IDLE or // ALLOW_WHILE_IDLE_UNRESTRICTED -- we will apply those later as appropriate. flags &= ~(AlarmManager.FLAG_WAKE_FROM_IDLE | AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED); // Only the system can use FLAG_IDLE_UNTIL -- this is used to tell the alarm // manager when to come out of idle mode, which is only for DeviceIdleController. if (callingUid != Process.SYSTEM_UID) { flags &= ~AlarmManager.FLAG_IDLE_UNTIL; } // If the caller is a core system component, and not calling to do work on behalf // of someone else, then always set ALLOW_WHILE_IDLE_UNRESTRICTED. This means we // will allow these alarms to go off as normal even while idle, with no timing // restrictions. if (callingUid < Process.FIRST_APPLICATION_UID && workSource == null) { flags |= AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED; } // If this is an exact time alarm, then it can't be batched with other alarms. if (windowLength == AlarmManager.WINDOW_EXACT) { flags |= AlarmManager.FLAG_STANDALONE; } // If this alarm is for an alarm clock, then it must be standalone and we will // use it to wake early from idle if needed. if (alarmClock != null) { flags |= AlarmManager.FLAG_WAKE_FROM_IDLE | AlarmManager.FLAG_STANDALONE; } if (Binder.getCallingUid() < Process.FIRST_APPLICATION_UID) { flags |= AlarmManager.FLAG_ALLOW_WHILE_IDLE; } setImpl(type, triggerAtTime, windowLength, interval, operation, flags, workSource, alarmClock); flags, workSource, alarmClock, callingUid); } @Override Loading Loading @@ -1126,6 +1161,22 @@ class AlarmManagerService extends SystemService { pw.print(" Broadcast ref count: "); pw.println(mBroadcastRefCount); pw.println(); pw.print("mAllowWhileIdleMinTime="); TimeUtils.formatDuration(mAllowWhileIdleMinTime, pw); pw.println(); if (mLastAllowWhileIdleDispatch.size() > 0) { pw.println("Last allow while idle dispatch times:"); for (int i=0; i<mLastAllowWhileIdleDispatch.size(); i++) { pw.print(" UID "); UserHandle.formatUid(pw, mLastAllowWhileIdleDispatch.keyAt(i)); pw.print(": "); TimeUtils.formatDuration(mLastAllowWhileIdleDispatch.valueAt(i), nowELAPSED, pw); pw.println(); } } pw.println(); if (mLog.dump(pw, " Recent problems", " ")) { pw.println(); } Loading Loading @@ -1322,7 +1373,7 @@ class AlarmManagerService extends SystemService { for (int j = 0; j < M; j++) { Alarm a = alarms.get(j); if (a.alarmClock != null) { final int userId = a.userId; final int userId = UserHandle.getUserId(a.uid); if (DEBUG_ALARM_CLOCK) { Log.v(TAG, "Found AlarmClockInfo at " + Loading Loading @@ -1531,6 +1582,11 @@ class AlarmManagerService extends SystemService { mPendingWhileIdleAlarms.remove(i); } } for (int i = mLastAllowWhileIdleDispatch.size() - 1; i >= 0; i--) { if (UserHandle.getUserId(mLastAllowWhileIdleDispatch.keyAt(i)) == userHandle) { mLastAllowWhileIdleDispatch.removeAt(i); } } if (didRemove) { if (DEBUG_BATCH) { Loading Loading @@ -1666,6 +1722,25 @@ class AlarmManagerService extends SystemService { final int N = batch.size(); for (int i = 0; i < N; i++) { Alarm alarm = batch.get(i); if ((alarm.flags&AlarmManager.FLAG_ALLOW_WHILE_IDLE) != 0) { // If this is an ALLOW_WHILE_IDLE alarm, we constrain how frequently the app can // schedule such alarms. long lastTime = mLastAllowWhileIdleDispatch.get(alarm.uid, 0); long minTime = lastTime + mAllowWhileIdleMinTime; if (nowELAPSED < minTime) { // Whoops, it hasn't been long enough since the last ALLOW_WHILE_IDLE // alarm went off for this app. Reschedule the alarm to be in the // correct time period. alarm.whenElapsed = minTime; if (alarm.maxWhenElapsed < minTime) { alarm.maxWhenElapsed = minTime; } setImplLocked(alarm, true, false); continue; } } alarm.count = 1; triggerList.add(alarm); if ((alarm.flags&AlarmManager.FLAG_WAKE_FROM_IDLE) != 0) { Loading Loading @@ -1695,7 +1770,7 @@ class AlarmManagerService extends SystemService { setImplLocked(alarm.type, alarm.when + delta, nextElapsed, alarm.windowLength, maxTriggerTime(nowELAPSED, nextElapsed, alarm.repeatInterval), alarm.repeatInterval, alarm.operation, alarm.flags, true, alarm.workSource, alarm.alarmClock, alarm.userId); alarm.workSource, alarm.alarmClock, alarm.uid); } if (alarm.wakeup) { Loading Loading @@ -1749,19 +1824,19 @@ class AlarmManagerService extends SystemService { public final String tag; public final WorkSource workSource; public final int flags; public final AlarmManager.AlarmClockInfo alarmClock; public final int uid; public int count; public long when; public long windowLength; public long whenElapsed; // 'when' in the elapsed time base public long maxWhenElapsed; // also in the elapsed time base public long repeatInterval; public final AlarmManager.AlarmClockInfo alarmClock; public final int userId; public PriorityClass priorityClass; public Alarm(int _type, long _when, long _whenElapsed, long _windowLength, long _maxWhen, long _interval, PendingIntent _op, WorkSource _ws, int _flags, AlarmManager.AlarmClockInfo _info, int _userId) { AlarmManager.AlarmClockInfo _info, int _uid) { type = _type; origWhen = _when; wakeup = _type == AlarmManager.ELAPSED_REALTIME_WAKEUP Loading @@ -1776,7 +1851,7 @@ class AlarmManagerService extends SystemService { workSource = _ws; flags = _flags; alarmClock = _info; userId = _userId; uid = _uid; } public static String makeTag(PendingIntent pi, int type) { Loading Loading @@ -1812,7 +1887,7 @@ class AlarmManagerService extends SystemService { pw.print(" when="); TimeUtils.formatDuration(when, nowELAPSED, pw); } pw.println(); pw.print(prefix); pw.print("window="); pw.print(windowLength); pw.print(prefix); pw.print("window="); TimeUtils.formatDuration(windowLength, pw); pw.print(" repeatInterval="); pw.print(repeatInterval); pw.print(" count="); pw.print(count); pw.print(" flags=0x"); pw.println(Integer.toHexString(flags)); Loading Loading @@ -1925,6 +2000,11 @@ class AlarmManagerService extends SystemService { mInFlight.add(inflight); mBroadcastRefCount++; if ((alarm.flags&AlarmManager.FLAG_ALLOW_WHILE_IDLE) != 0) { // Record the last time this uid handled an ALLOW_WHILE_IDLE alarm. mLastAllowWhileIdleDispatch.put(alarm.uid, nowELAPSED); } final BroadcastStats bs = inflight.mBroadcastStats; bs.count++; if (bs.nesting == 0) { Loading Loading @@ -2196,7 +2276,8 @@ class AlarmManagerService extends SystemService { final WorkSource workSource = null; // Let system take blame for time tick events. setImpl(ELAPSED_REALTIME, SystemClock.elapsedRealtime() + tickEventDelay, 0, 0, mTimeTickSender, AlarmManager.FLAG_STANDALONE, workSource, null); 0, mTimeTickSender, AlarmManager.FLAG_STANDALONE, workSource, null, Process.myUid()); } public void scheduleDateChangedEvent() { Loading @@ -2210,7 +2291,7 @@ class AlarmManagerService extends SystemService { final WorkSource workSource = null; // Let system take blame for date change events. setImpl(RTC, calendar.getTimeInMillis(), 0, 0, mDateChangeSender, AlarmManager.FLAG_STANDALONE, workSource, null); AlarmManager.FLAG_STANDALONE, workSource, null, Process.myUid()); } } Loading Loading @@ -2243,6 +2324,7 @@ class AlarmManagerService extends SystemService { IntentFilter sdFilter = new IntentFilter(); sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); sdFilter.addAction(Intent.ACTION_USER_STOPPED); sdFilter.addAction(Intent.ACTION_UID_REMOVED); getContext().registerReceiver(this, sdFilter); } Loading @@ -2267,6 +2349,11 @@ class AlarmManagerService extends SystemService { if (userHandle >= 0) { removeUserLocked(userHandle); } } else if (Intent.ACTION_UID_REMOVED.equals(action)) { int uid = intent.getIntExtra(Intent.EXTRA_UID, -1); if (uid >= 0) { mLastAllowWhileIdleDispatch.delete(uid); } } else { if (Intent.ACTION_PACKAGE_REMOVED.equals(action) && intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { Loading
tests/ActivityTests/AndroidManifest.xml +1 −0 Original line number Diff line number Diff line Loading @@ -76,5 +76,6 @@ android:authorities="com.google.android.test.activity.single_user" android:singleUser="true" android:exported="true" /> <receiver android:name="TrackTimeReceiver" /> <receiver android:name="AlarmSpamReceiver" /> </application> </manifest>
tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java +28 −0 File changed.Preview size limit exceeded, changes collapsed. Show changes