diff options
author | Dianne Hackborn <hackbod@google.com> | 2015-06-12 19:38:38 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2015-06-12 19:38:39 +0000 |
commit | e554cc96597d4b738f14a1514772e4d49e78c542 (patch) | |
tree | cdee6876faa1c56d2e0ec3dc0cfb51f2913372db | |
parent | 17de4b2a73996366ff3d7759793a6809654caebe (diff) | |
parent | 3d1933c45fe9ba2389ebd166d96abeceab1971d1 (diff) | |
download | frameworks_base-e554cc96597d4b738f14a1514772e4d49e78c542.zip frameworks_base-e554cc96597d4b738f14a1514772e4d49e78c542.tar.gz frameworks_base-e554cc96597d4b738f14a1514772e4d49e78c542.tar.bz2 |
Merge "Implement some control over ALLOW_WHILE_IDLE alarms." into mnc-dev
6 files changed, 257 insertions, 69 deletions
diff --git a/core/java/android/app/AlarmManager.java b/core/java/android/app/AlarmManager.java index 5e7bd0d..9ea1606 100644 --- a/core/java/android/app/AlarmManager.java +++ b/core/java/android/app/AlarmManager.java @@ -142,13 +142,22 @@ 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. * Scheduling an alarm with this flag puts the alarm manager in to idle mode, where it * 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; @@ -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 @@ -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 diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java index f7d2821..353388d 100644 --- a/core/java/android/util/TimeUtils.java +++ b/core/java/android/util/TimeUtils.java @@ -246,41 +246,65 @@ 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 > 99 || (always && zeropad >= 3)) { - return 3+suffix; - } - if (amt > 9 || (always && zeropad >= 2)) { - return 2+suffix; - } - if (always || amt > 0) { - return 1+suffix; + 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; + } + if (amt > 9 || (always && zeropad >= 2)) { + return 2+suffix; + } + 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 ((always && zeropad >= 3) || amt > 99) { - int dig = amt/100; - formatStr[pos] = (char)(dig + '0'); - pos++; - amt -= (dig*100); - } - if ((always && zeropad >= 2) || amt > 9 || startPos != pos) { - int dig = amt/10; - formatStr[pos] = (char)(dig + '0'); + 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'); + pos++; + amt -= (dig*100); + } + if ((always && zeropad >= 2) || amt > 9 || startPos != pos) { + int dig = amt/10; + formatStr[pos] = (char)(dig + '0'); + pos++; + amt -= (dig*10); + } + formatStr[pos] = (char)(amt + '0'); pos++; - amt -= (dig*10); } - formatStr[pos] = (char)(amt + '0'); - pos++; formatStr[pos] = suffix; pos++; } @@ -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; @@ -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; } diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java index 742f570..26ece72 100644 --- a/services/core/java/com/android/server/AlarmManagerService.java +++ b/services/core/java/com/android/server/AlarmManagerService.java @@ -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; @@ -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; @@ -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; @@ -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 = @@ -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 { @@ -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(); @@ -632,7 +649,7 @@ class AlarmManagerService extends SystemService { mTag = tag; } } - + static final class BroadcastStats { final int mUid; final String mPackageName; @@ -649,7 +666,7 @@ class AlarmManagerService extends SystemService { mPackageName = packageName; } } - + final SparseArray<ArrayMap<String, BroadcastStats>> mBroadcastStats = new SparseArray<ArrayMap<String, BroadcastStats>>(); @@ -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; @@ -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; } @@ -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 @@ -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. @@ -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; @@ -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) { @@ -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 @@ -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(); } @@ -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 " + @@ -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) { @@ -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) { @@ -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) { @@ -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 @@ -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) { @@ -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)); @@ -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) { @@ -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() { @@ -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()); } } @@ -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); } @@ -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)) { diff --git a/tests/ActivityTests/AndroidManifest.xml b/tests/ActivityTests/AndroidManifest.xml index c105491..dae7cc5 100644 --- a/tests/ActivityTests/AndroidManifest.xml +++ b/tests/ActivityTests/AndroidManifest.xml @@ -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> diff --git a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java index ddcfd9e..94cbabf 100644 --- a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java +++ b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java @@ -22,6 +22,7 @@ import java.util.List; import android.app.Activity; import android.app.ActivityManager; import android.app.ActivityOptions; +import android.app.AlarmManager; import android.app.AlertDialog; import android.app.PendingIntent; import android.content.ActivityNotFoundException; @@ -36,6 +37,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.RemoteException; +import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; import android.graphics.Bitmap; @@ -58,6 +60,7 @@ public class ActivityTestMain extends Activity { static final String KEY_CONFIGURATION = "configuration"; ActivityManager mAm; + AlarmManager mAlarm; Configuration mOverrideConfig; int mSecondUser; @@ -66,6 +69,7 @@ public class ActivityTestMain extends Activity { ServiceConnection mIsolatedConnection; static final int MSG_SPAM = 1; + static final int MSG_SPAM_ALARM = 2; final Handler mHandler = new Handler() { @Override @@ -82,6 +86,15 @@ public class ActivityTestMain extends Activity { startActivity(intent, options); scheduleSpam(!fg); } break; + case MSG_SPAM_ALARM: { + long when = SystemClock.elapsedRealtime(); + Intent intent = new Intent(ActivityTestMain.this, AlarmSpamReceiver.class); + intent.setAction("com.example.SPAM_ALARM=" + when); + PendingIntent pi = PendingIntent.getBroadcast(ActivityTestMain.this, + 0, intent, 0); + mAlarm.setAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME, when+(30*1000), pi); + scheduleSpamAlarm(30*1000); + } break; } super.handleMessage(msg); } @@ -146,6 +159,7 @@ public class ActivityTestMain extends Activity { Log.i(TAG, "Referrer: " + getReferrer()); mAm = (ActivityManager)getSystemService(ACTIVITY_SERVICE); + mAlarm = (AlarmManager)getSystemService(ALARM_SERVICE); if (savedInstanceState != null) { mOverrideConfig = savedInstanceState.getParcelable(KEY_CONFIGURATION); if (mOverrideConfig != null) { @@ -436,6 +450,13 @@ public class ActivityTestMain extends Activity { return true; } }); + menu.add("Spam idle alarm").setOnMenuItemClickListener( + new MenuItem.OnMenuItemClickListener() { + @Override public boolean onMenuItemClick(MenuItem item) { + scheduleSpamAlarm(0); + return true; + } + }); return true; } @@ -467,6 +488,7 @@ public class ActivityTestMain extends Activity { @Override protected void onStop() { super.onStop(); + mHandler.removeMessages(MSG_SPAM_ALARM); for (ServiceConnection conn : mConnections) { unbindService(conn); } @@ -536,6 +558,12 @@ public class ActivityTestMain extends Activity { mHandler.sendMessageDelayed(msg, 500); } + void scheduleSpamAlarm(long delay) { + mHandler.removeMessages(MSG_SPAM_ALARM); + Message msg = mHandler.obtainMessage(MSG_SPAM_ALARM); + mHandler.sendMessageDelayed(msg, delay); + } + private View scrollWrap(View view) { ScrollView scroller = new ScrollView(this); scroller.addView(view, new ScrollView.LayoutParams(ScrollView.LayoutParams.MATCH_PARENT, diff --git a/tests/ActivityTests/src/com/google/android/test/activity/AlarmSpamReceiver.java b/tests/ActivityTests/src/com/google/android/test/activity/AlarmSpamReceiver.java new file mode 100644 index 0000000..0cb1ffb --- /dev/null +++ b/tests/ActivityTests/src/com/google/android/test/activity/AlarmSpamReceiver.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.test.activity; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.os.UserHandle; +import android.util.Log; + +public class AlarmSpamReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + Log.i("AlarmSpamReceiver", "Received spam = " + intent); + } +} |