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;
/**
* 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

View File

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

View File

@@ -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)) {

View File

@@ -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>

View File

@@ -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,

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