Merge "Implement some control over ALLOW_WHILE_IDLE alarms." into mnc-dev

This commit is contained in:
Dianne Hackborn
2015-06-12 19:38:38 +00:00
committed by Android (Google) Code Review
6 changed files with 257 additions and 69 deletions

View File

@@ -141,6 +141,15 @@ public class AlarmManager {
*/ */
public static final int FLAG_ALLOW_WHILE_IDLE = 1<<2; 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 * 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. * mode. It may be moved by the alarm manager to match the first wake-from-idle alarm.
@@ -148,7 +157,7 @@ public class AlarmManager {
* avoids scheduling any further alarms until the marker alarm is executed. * avoids scheduling any further alarms until the marker alarm is executed.
* @hide * @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 IAlarmManager mService;
private final boolean mAlwaysExact; 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 * of the device when idle (and thus cause significant battery blame to the app scheduling
* them), so they should be used with care. * 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 * <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 * 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 * 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 * of the device when idle (and thus cause significant battery blame to the app scheduling
* them), so they should be used with care. * 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 * <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 * 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 * when the device is idle (since this alarm can go off while idle, when any other alarms

View File

@@ -246,41 +246,65 @@ public class TimeUtils {
public static final long NANOS_PER_MS = 1000000; public static final long NANOS_PER_MS = 1000000;
private static final Object sFormatSync = new Object(); private static final Object sFormatSync = new Object();
private static char[] sFormatStr = new char[HUNDRED_DAY_FIELD_LEN+5]; private static char[] sFormatStr = new char[HUNDRED_DAY_FIELD_LEN+10];
private static char[] sTmpFormatStr = new char[HUNDRED_DAY_FIELD_LEN+10];
private static final long LARGEST_DURATION = (1000 * DateUtils.DAY_IN_MILLIS) - 1;
static private int accumField(int amt, int suffix, boolean always, int zeropad) { static private int accumField(int amt, int suffix, boolean always, int zeropad) {
if (amt > 99 || (always && zeropad >= 3)) { if (amt > 999) {
return 3+suffix; int num = 0;
} while (amt != 0) {
if (amt > 9 || (always && zeropad >= 2)) { num++;
return 2+suffix; amt /= 10;
} }
if (always || amt > 0) { return num + suffix;
return 1+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; 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) { boolean always, int zeropad) {
if (always || amt > 0) { if (always || amt > 0) {
final int startPos = pos; final int startPos = pos;
if ((always && zeropad >= 3) || amt > 99) { if (amt > 999) {
int dig = amt/100; int tmp = 0;
formatStr[pos] = (char)(dig + '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++; 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++;
formatStr[pos] = suffix; formatStr[pos] = suffix;
pos++; pos++;
} }
@@ -312,10 +336,6 @@ public class TimeUtils {
duration = -duration; duration = -duration;
} }
if (duration > LARGEST_DURATION) {
duration = LARGEST_DURATION;
}
int millis = (int)(duration%1000); int millis = (int)(duration%1000);
int seconds = (int) Math.floor(duration / 1000); int seconds = (int) Math.floor(duration / 1000);
int days = 0, hours = 0, minutes = 0; int days = 0, hours = 0, minutes = 0;
@@ -353,11 +373,11 @@ public class TimeUtils {
int start = pos; int start = pos;
boolean zeropad = fieldLen != 0; boolean zeropad = fieldLen != 0;
pos = printField(formatStr, days, 'd', pos, false, 0); pos = printFieldLocked(formatStr, days, 'd', pos, false, 0);
pos = printField(formatStr, hours, 'h', pos, pos != start, zeropad ? 2 : 0); pos = printFieldLocked(formatStr, hours, 'h', pos, pos != start, zeropad ? 2 : 0);
pos = printField(formatStr, minutes, 'm', pos, pos != start, zeropad ? 2 : 0); pos = printFieldLocked(formatStr, minutes, 'm', pos, pos != start, zeropad ? 2 : 0);
pos = printField(formatStr, seconds, 's', pos, pos != start, zeropad ? 2 : 0); pos = printFieldLocked(formatStr, seconds, 's', pos, pos != start, zeropad ? 2 : 0);
pos = printField(formatStr, millis, 'm', pos, true, (zeropad && pos != start) ? 3 : 0); pos = printFieldLocked(formatStr, millis, 'm', pos, true, (zeropad && pos != start) ? 3 : 0);
formatStr[pos] = 's'; formatStr[pos] = 's';
return pos + 1; return pos + 1;
} }

View File

@@ -47,6 +47,7 @@ import android.util.Log;
import android.util.Slog; import android.util.Slog;
import android.util.SparseArray; import android.util.SparseArray;
import android.util.SparseBooleanArray; import android.util.SparseBooleanArray;
import android.util.SparseLongArray;
import android.util.TimeUtils; import android.util.TimeUtils;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
@@ -84,6 +85,12 @@ class AlarmManagerService extends SystemService {
// Minimum alarm recurrence interval // Minimum alarm recurrence interval
private static final long MIN_INTERVAL = 60 * 1000; // one minute, in millis 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_WAKEUP_MASK = 1 << RTC_WAKEUP;
private static final int RTC_MASK = 1 << RTC; private static final int RTC_MASK = 1 << RTC;
private static final int ELAPSED_REALTIME_WAKEUP_MASK = 1 << ELAPSED_REALTIME_WAKEUP; private static final int ELAPSED_REALTIME_WAKEUP_MASK = 1 << ELAPSED_REALTIME_WAKEUP;
@@ -123,8 +130,8 @@ class AlarmManagerService extends SystemService {
int mBroadcastRefCount = 0; int mBroadcastRefCount = 0;
PowerManager.WakeLock mWakeLock; PowerManager.WakeLock mWakeLock;
boolean mLastWakeLockUnimportantForLogging; boolean mLastWakeLockUnimportantForLogging;
ArrayList<Alarm> mPendingNonWakeupAlarms = new ArrayList<Alarm>(); ArrayList<Alarm> mPendingNonWakeupAlarms = new ArrayList<>();
ArrayList<InFlight> mInFlight = new ArrayList<InFlight>(); ArrayList<InFlight> mInFlight = new ArrayList<>();
final AlarmHandler mHandler = new AlarmHandler(); final AlarmHandler mHandler = new AlarmHandler();
ClockReceiver mClockReceiver; ClockReceiver mClockReceiver;
InteractiveStateReceiver mInteractiveStateReceiver; InteractiveStateReceiver mInteractiveStateReceiver;
@@ -141,8 +148,15 @@ class AlarmManagerService extends SystemService {
long mNextNonWakeupDeliveryTime; long mNextNonWakeupDeliveryTime;
long mLastTimeChangeClockTime; long mLastTimeChangeClockTime;
long mLastTimeChangeRealtime; long mLastTimeChangeRealtime;
long mAllowWhileIdleMinTime = ALLOW_WHILE_IDLE_SHORT_TIME;
int mNumTimeChanged; 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 = private final SparseArray<AlarmManager.AlarmClockInfo> mNextAlarmClockForUser =
new SparseArray<>(); new SparseArray<>();
private final SparseArray<AlarmManager.AlarmClockInfo> mTmpSparseAlarmClockArray = private final SparseArray<AlarmManager.AlarmClockInfo> mTmpSparseAlarmClockArray =
@@ -552,7 +566,7 @@ class AlarmManagerService extends SystemService {
a.when = a.origWhen; a.when = a.origWhen;
long whenElapsed = convertToElapsed(a.when, a.type); long whenElapsed = convertToElapsed(a.when, a.type);
final long maxElapsed; final long maxElapsed;
if (a.whenElapsed == a.maxWhenElapsed) { if (a.windowLength == AlarmManager.WINDOW_EXACT) {
// Exact // Exact
maxElapsed = whenElapsed; maxElapsed = whenElapsed;
} else { } 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. // Reschedule everything.
rescheduleKernelAlarmsLocked(); rescheduleKernelAlarmsLocked();
updateNextAlarmClockLocked(); updateNextAlarmClockLocked();
@@ -751,7 +768,7 @@ class AlarmManagerService extends SystemService {
void setImpl(int type, long triggerAtTime, long windowLength, long interval, void setImpl(int type, long triggerAtTime, long windowLength, long interval,
PendingIntent operation, int flags, WorkSource workSource, PendingIntent operation, int flags, WorkSource workSource,
AlarmManager.AlarmClockInfo alarmClock) { AlarmManager.AlarmClockInfo alarmClock, int callingUid) {
if (operation == null) { if (operation == null) {
Slog.w(TAG, "set/setRepeating ignored because there is no intent"); Slog.w(TAG, "set/setRepeating ignored because there is no intent");
return; return;
@@ -779,9 +796,8 @@ class AlarmManagerService extends SystemService {
} }
if (triggerAtTime < 0) { if (triggerAtTime < 0) {
final long who = Binder.getCallingUid();
final long what = Binder.getCallingPid(); 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); + " pid=" + what);
triggerAtTime = 0; triggerAtTime = 0;
} }
@@ -797,12 +813,12 @@ class AlarmManagerService extends SystemService {
maxElapsed = triggerElapsed; maxElapsed = triggerElapsed;
} else if (windowLength < 0) { } else if (windowLength < 0) {
maxElapsed = maxTriggerTime(nowElapsed, triggerElapsed, interval); maxElapsed = maxTriggerTime(nowElapsed, triggerElapsed, interval);
// Fix this window in place, so that as time approaches we don't collapse it.
windowLength = maxElapsed - triggerElapsed;
} else { } else {
maxElapsed = triggerElapsed + windowLength; maxElapsed = triggerElapsed + windowLength;
} }
final int userId = UserHandle.getCallingUserId();
synchronized (mLock) { synchronized (mLock) {
if (DEBUG_BATCH) { if (DEBUG_BATCH) {
Slog.v(TAG, "set(" + operation + ") : type=" + type Slog.v(TAG, "set(" + operation + ") : type=" + type
@@ -811,26 +827,20 @@ class AlarmManagerService extends SystemService {
+ " interval=" + interval + " flags=0x" + Integer.toHexString(flags)); + " interval=" + interval + " flags=0x" + Integer.toHexString(flags));
} }
setImplLocked(type, triggerAtTime, triggerElapsed, windowLength, maxElapsed, 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, private void setImplLocked(int type, long when, long whenElapsed, long windowLength,
long maxWhen, long interval, PendingIntent operation, int flags, long maxWhen, long interval, PendingIntent operation, int flags,
boolean doValidate, WorkSource workSource, AlarmManager.AlarmClockInfo alarmClock, boolean doValidate, WorkSource workSource, AlarmManager.AlarmClockInfo alarmClock,
int userId) { int uid) {
Alarm a = new Alarm(type, when, whenElapsed, windowLength, maxWhen, interval, Alarm a = new Alarm(type, when, whenElapsed, windowLength, maxWhen, interval,
operation, workSource, flags, alarmClock, userId); operation, workSource, flags, alarmClock, uid);
removeLocked(operation); removeLocked(operation);
setImplLocked(a, false, doValidate); setImplLocked(a, false, doValidate);
} }
private void updateNextWakeFromIdleFuzzLocked() {
if (mNextWakeFromIdle != null) {
}
}
private void setImplLocked(Alarm a, boolean rebatching, boolean doValidate) { private void setImplLocked(Alarm a, boolean rebatching, boolean doValidate) {
if ((a.flags&AlarmManager.FLAG_IDLE_UNTIL) != 0) { if ((a.flags&AlarmManager.FLAG_IDLE_UNTIL) != 0) {
// This is a special alarm that will put the system into idle until it goes off. // 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) { } else if (mPendingIdleUntil != null) {
// We currently have an idle until alarm scheduled; if the new alarm has // 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. // 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) { == 0) {
mPendingWhileIdleAlarms.add(a); mPendingWhileIdleAlarms.add(a);
return; return;
@@ -892,6 +904,7 @@ class AlarmManagerService extends SystemService {
if ((a.flags&AlarmManager.FLAG_IDLE_UNTIL) != 0) { if ((a.flags&AlarmManager.FLAG_IDLE_UNTIL) != 0) {
mPendingIdleUntil = a; mPendingIdleUntil = a;
mAllowWhileIdleMinTime = ALLOW_WHILE_IDLE_LONG_TIME;
needRebatch = true; needRebatch = true;
} else if ((a.flags&AlarmManager.FLAG_WAKE_FROM_IDLE) != 0) { } else if ((a.flags&AlarmManager.FLAG_WAKE_FROM_IDLE) != 0) {
if (mNextWakeFromIdle == null || mNextWakeFromIdle.whenElapsed > a.whenElapsed) { 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, public void set(int type, long triggerAtTime, long windowLength, long interval, int flags,
PendingIntent operation, WorkSource workSource, PendingIntent operation, WorkSource workSource,
AlarmManager.AlarmClockInfo alarmClock) { AlarmManager.AlarmClockInfo alarmClock) {
final int callingUid = Binder.getCallingUid();
if (workSource != null) { if (workSource != null) {
getContext().enforceCallingPermission( getContext().enforcePermission(
android.Manifest.permission.UPDATE_DEVICE_STATS, 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) { if (windowLength == AlarmManager.WINDOW_EXACT) {
flags |= AlarmManager.FLAG_STANDALONE; 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) { if (alarmClock != null) {
flags |= AlarmManager.FLAG_WAKE_FROM_IDLE | AlarmManager.FLAG_STANDALONE; 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, setImpl(type, triggerAtTime, windowLength, interval, operation,
flags, workSource, alarmClock); flags, workSource, alarmClock, callingUid);
} }
@Override @Override
@@ -1126,6 +1161,22 @@ class AlarmManagerService extends SystemService {
pw.print(" Broadcast ref count: "); pw.println(mBroadcastRefCount); pw.print(" Broadcast ref count: "); pw.println(mBroadcastRefCount);
pw.println(); 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", " ")) { if (mLog.dump(pw, " Recent problems", " ")) {
pw.println(); pw.println();
} }
@@ -1322,7 +1373,7 @@ class AlarmManagerService extends SystemService {
for (int j = 0; j < M; j++) { for (int j = 0; j < M; j++) {
Alarm a = alarms.get(j); Alarm a = alarms.get(j);
if (a.alarmClock != null) { if (a.alarmClock != null) {
final int userId = a.userId; final int userId = UserHandle.getUserId(a.uid);
if (DEBUG_ALARM_CLOCK) { if (DEBUG_ALARM_CLOCK) {
Log.v(TAG, "Found AlarmClockInfo at " + Log.v(TAG, "Found AlarmClockInfo at " +
@@ -1531,6 +1582,11 @@ class AlarmManagerService extends SystemService {
mPendingWhileIdleAlarms.remove(i); 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 (didRemove) {
if (DEBUG_BATCH) { if (DEBUG_BATCH) {
@@ -1666,6 +1722,25 @@ class AlarmManagerService extends SystemService {
final int N = batch.size(); final int N = batch.size();
for (int i = 0; i < N; i++) { for (int i = 0; i < N; i++) {
Alarm alarm = batch.get(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; alarm.count = 1;
triggerList.add(alarm); triggerList.add(alarm);
if ((alarm.flags&AlarmManager.FLAG_WAKE_FROM_IDLE) != 0) { 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, setImplLocked(alarm.type, alarm.when + delta, nextElapsed, alarm.windowLength,
maxTriggerTime(nowELAPSED, nextElapsed, alarm.repeatInterval), maxTriggerTime(nowELAPSED, nextElapsed, alarm.repeatInterval),
alarm.repeatInterval, alarm.operation, alarm.flags, true, alarm.repeatInterval, alarm.operation, alarm.flags, true,
alarm.workSource, alarm.alarmClock, alarm.userId); alarm.workSource, alarm.alarmClock, alarm.uid);
} }
if (alarm.wakeup) { if (alarm.wakeup) {
@@ -1749,19 +1824,19 @@ class AlarmManagerService extends SystemService {
public final String tag; public final String tag;
public final WorkSource workSource; public final WorkSource workSource;
public final int flags; public final int flags;
public final AlarmManager.AlarmClockInfo alarmClock;
public final int uid;
public int count; public int count;
public long when; public long when;
public long windowLength; public long windowLength;
public long whenElapsed; // 'when' in the elapsed time base public long whenElapsed; // 'when' in the elapsed time base
public long maxWhenElapsed; // also in the elapsed time base public long maxWhenElapsed; // also in the elapsed time base
public long repeatInterval; public long repeatInterval;
public final AlarmManager.AlarmClockInfo alarmClock;
public final int userId;
public PriorityClass priorityClass; public PriorityClass priorityClass;
public Alarm(int _type, long _when, long _whenElapsed, long _windowLength, long _maxWhen, public Alarm(int _type, long _when, long _whenElapsed, long _windowLength, long _maxWhen,
long _interval, PendingIntent _op, WorkSource _ws, int _flags, long _interval, PendingIntent _op, WorkSource _ws, int _flags,
AlarmManager.AlarmClockInfo _info, int _userId) { AlarmManager.AlarmClockInfo _info, int _uid) {
type = _type; type = _type;
origWhen = _when; origWhen = _when;
wakeup = _type == AlarmManager.ELAPSED_REALTIME_WAKEUP wakeup = _type == AlarmManager.ELAPSED_REALTIME_WAKEUP
@@ -1776,7 +1851,7 @@ class AlarmManagerService extends SystemService {
workSource = _ws; workSource = _ws;
flags = _flags; flags = _flags;
alarmClock = _info; alarmClock = _info;
userId = _userId; uid = _uid;
} }
public static String makeTag(PendingIntent pi, int type) { 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.print(" when="); TimeUtils.formatDuration(when, nowELAPSED, pw);
} }
pw.println(); 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(" repeatInterval="); pw.print(repeatInterval);
pw.print(" count="); pw.print(count); pw.print(" count="); pw.print(count);
pw.print(" flags=0x"); pw.println(Integer.toHexString(flags)); pw.print(" flags=0x"); pw.println(Integer.toHexString(flags));
@@ -1925,6 +2000,11 @@ class AlarmManagerService extends SystemService {
mInFlight.add(inflight); mInFlight.add(inflight);
mBroadcastRefCount++; 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; final BroadcastStats bs = inflight.mBroadcastStats;
bs.count++; bs.count++;
if (bs.nesting == 0) { if (bs.nesting == 0) {
@@ -2196,7 +2276,8 @@ class AlarmManagerService extends SystemService {
final WorkSource workSource = null; // Let system take blame for time tick events. final WorkSource workSource = null; // Let system take blame for time tick events.
setImpl(ELAPSED_REALTIME, SystemClock.elapsedRealtime() + tickEventDelay, 0, 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() { public void scheduleDateChangedEvent() {
@@ -2210,7 +2291,7 @@ class AlarmManagerService extends SystemService {
final WorkSource workSource = null; // Let system take blame for date change events. final WorkSource workSource = null; // Let system take blame for date change events.
setImpl(RTC, calendar.getTimeInMillis(), 0, 0, mDateChangeSender, 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(); IntentFilter sdFilter = new IntentFilter();
sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
sdFilter.addAction(Intent.ACTION_USER_STOPPED); sdFilter.addAction(Intent.ACTION_USER_STOPPED);
sdFilter.addAction(Intent.ACTION_UID_REMOVED);
getContext().registerReceiver(this, sdFilter); getContext().registerReceiver(this, sdFilter);
} }
@@ -2267,6 +2349,11 @@ class AlarmManagerService extends SystemService {
if (userHandle >= 0) { if (userHandle >= 0) {
removeUserLocked(userHandle); 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 { } else {
if (Intent.ACTION_PACKAGE_REMOVED.equals(action) if (Intent.ACTION_PACKAGE_REMOVED.equals(action)
&& intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { && intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {

View File

@@ -76,5 +76,6 @@
android:authorities="com.google.android.test.activity.single_user" android:authorities="com.google.android.test.activity.single_user"
android:singleUser="true" android:exported="true" /> android:singleUser="true" android:exported="true" />
<receiver android:name="TrackTimeReceiver" /> <receiver android:name="TrackTimeReceiver" />
<receiver android:name="AlarmSpamReceiver" />
</application> </application>
</manifest> </manifest>

View File

@@ -22,6 +22,7 @@ import java.util.List;
import android.app.Activity; import android.app.Activity;
import android.app.ActivityManager; import android.app.ActivityManager;
import android.app.ActivityOptions; import android.app.ActivityOptions;
import android.app.AlarmManager;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.ActivityNotFoundException; import android.content.ActivityNotFoundException;
@@ -36,6 +37,7 @@ import android.os.Handler;
import android.os.IBinder; import android.os.IBinder;
import android.os.Message; import android.os.Message;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle; import android.os.UserHandle;
import android.os.UserManager; import android.os.UserManager;
import android.graphics.Bitmap; import android.graphics.Bitmap;
@@ -58,6 +60,7 @@ public class ActivityTestMain extends Activity {
static final String KEY_CONFIGURATION = "configuration"; static final String KEY_CONFIGURATION = "configuration";
ActivityManager mAm; ActivityManager mAm;
AlarmManager mAlarm;
Configuration mOverrideConfig; Configuration mOverrideConfig;
int mSecondUser; int mSecondUser;
@@ -66,6 +69,7 @@ public class ActivityTestMain extends Activity {
ServiceConnection mIsolatedConnection; ServiceConnection mIsolatedConnection;
static final int MSG_SPAM = 1; static final int MSG_SPAM = 1;
static final int MSG_SPAM_ALARM = 2;
final Handler mHandler = new Handler() { final Handler mHandler = new Handler() {
@Override @Override
@@ -82,6 +86,15 @@ public class ActivityTestMain extends Activity {
startActivity(intent, options); startActivity(intent, options);
scheduleSpam(!fg); scheduleSpam(!fg);
} break; } 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); super.handleMessage(msg);
} }
@@ -146,6 +159,7 @@ public class ActivityTestMain extends Activity {
Log.i(TAG, "Referrer: " + getReferrer()); Log.i(TAG, "Referrer: " + getReferrer());
mAm = (ActivityManager)getSystemService(ACTIVITY_SERVICE); mAm = (ActivityManager)getSystemService(ACTIVITY_SERVICE);
mAlarm = (AlarmManager)getSystemService(ALARM_SERVICE);
if (savedInstanceState != null) { if (savedInstanceState != null) {
mOverrideConfig = savedInstanceState.getParcelable(KEY_CONFIGURATION); mOverrideConfig = savedInstanceState.getParcelable(KEY_CONFIGURATION);
if (mOverrideConfig != null) { if (mOverrideConfig != null) {
@@ -436,6 +450,13 @@ public class ActivityTestMain extends Activity {
return true; return true;
} }
}); });
menu.add("Spam idle alarm").setOnMenuItemClickListener(
new MenuItem.OnMenuItemClickListener() {
@Override public boolean onMenuItemClick(MenuItem item) {
scheduleSpamAlarm(0);
return true;
}
});
return true; return true;
} }
@@ -467,6 +488,7 @@ public class ActivityTestMain extends Activity {
@Override @Override
protected void onStop() { protected void onStop() {
super.onStop(); super.onStop();
mHandler.removeMessages(MSG_SPAM_ALARM);
for (ServiceConnection conn : mConnections) { for (ServiceConnection conn : mConnections) {
unbindService(conn); unbindService(conn);
} }
@@ -536,6 +558,12 @@ public class ActivityTestMain extends Activity {
mHandler.sendMessageDelayed(msg, 500); 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) { private View scrollWrap(View view) {
ScrollView scroller = new ScrollView(this); ScrollView scroller = new ScrollView(this);
scroller.addView(view, new ScrollView.LayoutParams(ScrollView.LayoutParams.MATCH_PARENT, scroller.addView(view, new ScrollView.LayoutParams(ScrollView.LayoutParams.MATCH_PARENT,

View File

@@ -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);
}
}