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;
|
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
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
@@ -632,7 +649,7 @@ class AlarmManagerService extends SystemService {
|
|||||||
mTag = tag;
|
mTag = tag;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static final class BroadcastStats {
|
static final class BroadcastStats {
|
||||||
final int mUid;
|
final int mUid;
|
||||||
final String mPackageName;
|
final String mPackageName;
|
||||||
@@ -649,7 +666,7 @@ class AlarmManagerService extends SystemService {
|
|||||||
mPackageName = packageName;
|
mPackageName = packageName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final SparseArray<ArrayMap<String, BroadcastStats>> mBroadcastStats
|
final SparseArray<ArrayMap<String, BroadcastStats>> mBroadcastStats
|
||||||
= new SparseArray<ArrayMap<String, BroadcastStats>>();
|
= new SparseArray<ArrayMap<String, BroadcastStats>>();
|
||||||
|
|
||||||
@@ -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)) {
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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