Merge "Implement some control over ALLOW_WHILE_IDLE alarms." into mnc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
e554cc9659
@@ -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.
|
||||
@@ -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;
|
||||
@@ -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
|
||||
|
||||
@@ -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');
|
||||
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*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;
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user