diff options
| -rw-r--r-- | api/current.txt | 13 | ||||
| -rw-r--r-- | core/java/android/app/AlarmManager.java | 69 | ||||
| -rw-r--r-- | core/java/android/app/ContextImpl.java | 6 | ||||
| -rw-r--r-- | core/java/android/app/IAlarmManager.aidl | 4 | ||||
| -rw-r--r-- | services/java/com/android/server/AlarmManagerService.java | 811 | ||||
| -rw-r--r-- | services/java/com/android/server/net/NetworkStatsService.java | 24 |
6 files changed, 574 insertions, 353 deletions
diff --git a/api/current.txt b/api/current.txt index 494a6ecaa992..8188988247e0 100644 --- a/api/current.txt +++ b/api/current.txt @@ -3052,17 +3052,18 @@ package android.app { public class AlarmManager { method public void cancel(android.app.PendingIntent); method public void set(int, long, android.app.PendingIntent); - method public void setInexactRepeating(int, long, long, android.app.PendingIntent); + method public void setExact(int, long, android.app.PendingIntent); + method public deprecated void setInexactRepeating(int, long, long, android.app.PendingIntent); method public void setRepeating(int, long, long, android.app.PendingIntent); method public void setTime(long); method public void setTimeZone(java.lang.String); field public static final int ELAPSED_REALTIME = 3; // 0x3 field public static final int ELAPSED_REALTIME_WAKEUP = 2; // 0x2 - field public static final long INTERVAL_DAY = 86400000L; // 0x5265c00L - field public static final long INTERVAL_FIFTEEN_MINUTES = 900000L; // 0xdbba0L - field public static final long INTERVAL_HALF_DAY = 43200000L; // 0x2932e00L - field public static final long INTERVAL_HALF_HOUR = 1800000L; // 0x1b7740L - field public static final long INTERVAL_HOUR = 3600000L; // 0x36ee80L + field public static final deprecated long INTERVAL_DAY = 86400000L; // 0x5265c00L + field public static final deprecated long INTERVAL_FIFTEEN_MINUTES = 900000L; // 0xdbba0L + field public static final deprecated long INTERVAL_HALF_DAY = 43200000L; // 0x2932e00L + field public static final deprecated long INTERVAL_HALF_HOUR = 1800000L; // 0x1b7740L + field public static final deprecated long INTERVAL_HOUR = 3600000L; // 0x36ee80L field public static final int RTC = 1; // 0x1 field public static final int RTC_WAKEUP = 0; // 0x0 } diff --git a/core/java/android/app/AlarmManager.java b/core/java/android/app/AlarmManager.java index 2fe682d1861f..dbccbebbd739 100644 --- a/core/java/android/app/AlarmManager.java +++ b/core/java/android/app/AlarmManager.java @@ -16,7 +16,9 @@ package android.app; +import android.content.Context; import android.content.Intent; +import android.os.Build; import android.os.RemoteException; /** @@ -52,6 +54,8 @@ import android.os.RemoteException; */ public class AlarmManager { + private static final String TAG = "AlarmManager"; + /** * Alarm time in {@link System#currentTimeMillis System.currentTimeMillis()} * (wall clock time in UTC), which will wake up the device when @@ -81,12 +85,16 @@ public class AlarmManager public static final int ELAPSED_REALTIME = 3; private final IAlarmManager mService; + private final boolean mAlwaysExact; /** * package private on purpose */ - AlarmManager(IAlarmManager service) { + AlarmManager(IAlarmManager service, Context ctx) { mService = service; + + final int sdkVersion = ctx.getApplicationInfo().targetSdkVersion; + mAlwaysExact = (sdkVersion < Build.VERSION_CODES.KEY_LIME_PIE); } /** @@ -133,10 +141,7 @@ public class AlarmManager * @see #RTC_WAKEUP */ public void set(int type, long triggerAtMillis, PendingIntent operation) { - try { - mService.set(type, triggerAtMillis, operation); - } catch (RemoteException ex) { - } + setImpl(type, triggerAtMillis, 0, operation, mAlwaysExact); } /** @@ -188,22 +193,60 @@ public class AlarmManager */ public void setRepeating(int type, long triggerAtMillis, long intervalMillis, PendingIntent operation) { + setImpl(type, triggerAtMillis, intervalMillis, operation, mAlwaysExact); + } + + /** + * TBW: new 'exact' alarm that must be delivered as nearly as possible + * to the precise time specified. + */ + public void setExact(int type, long triggerAtMillis, PendingIntent operation) { + setImpl(type, triggerAtMillis, 0, operation, true); + } + + private void setImpl(int type, long triggerAtMillis, long intervalMillis, + PendingIntent operation, boolean isExact) { try { - mService.setRepeating(type, triggerAtMillis, intervalMillis, operation); + mService.set(type, triggerAtMillis, intervalMillis, operation, isExact); } catch (RemoteException ex) { } } /** - * Available inexact recurrence intervals recognized by - * {@link #setInexactRepeating(int, long, long, PendingIntent)} + * @deprecated setInexactRepeating() is deprecated; as of API 19 all + * repeating alarms are inexact. */ + @Deprecated public static final long INTERVAL_FIFTEEN_MINUTES = 15 * 60 * 1000; + + /** + * @deprecated setInexactRepeating() is deprecated; as of API 19 all + * repeating alarms are inexact. + */ + @Deprecated public static final long INTERVAL_HALF_HOUR = 2*INTERVAL_FIFTEEN_MINUTES; + + /** + * @deprecated setInexactRepeating() is deprecated; as of API 19 all + * repeating alarms are inexact. + */ + @Deprecated public static final long INTERVAL_HOUR = 2*INTERVAL_HALF_HOUR; + + /** + * @deprecated setInexactRepeating() is deprecated; as of API 19 all + * repeating alarms are inexact. + */ + @Deprecated public static final long INTERVAL_HALF_DAY = 12*INTERVAL_HOUR; + + /** + * @deprecated setInexactRepeating() is deprecated; as of API 19 all + * repeating alarms are inexact. + */ + @Deprecated public static final long INTERVAL_DAY = 2*INTERVAL_HALF_DAY; - + /** * Schedule a repeating alarm that has inexact trigger time requirements; * for example, an alarm that repeats every hour, but not necessarily at @@ -236,6 +279,8 @@ public class AlarmManager * typically comes from {@link PendingIntent#getBroadcast * IntentSender.getBroadcast()}. * + * @deprecated As of API 19, all repeating alarms are inexact. + * * @see android.os.Handler * @see #set * @see #cancel @@ -252,12 +297,10 @@ public class AlarmManager * @see #INTERVAL_HALF_DAY * @see #INTERVAL_DAY */ + @Deprecated public void setInexactRepeating(int type, long triggerAtMillis, long intervalMillis, PendingIntent operation) { - try { - mService.setInexactRepeating(type, triggerAtMillis, intervalMillis, operation); - } catch (RemoteException ex) { - } + setRepeating(type, triggerAtMillis, intervalMillis, operation); } /** diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 6a0fbd59bee3..71804089f9a3 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -309,11 +309,11 @@ class ContextImpl extends Context { return new ActivityManager(ctx.getOuterContext(), ctx.mMainThread.getHandler()); }}); - registerService(ALARM_SERVICE, new StaticServiceFetcher() { - public Object createStaticService() { + registerService(ALARM_SERVICE, new ServiceFetcher() { + public Object createService(ContextImpl ctx) { IBinder b = ServiceManager.getService(ALARM_SERVICE); IAlarmManager service = IAlarmManager.Stub.asInterface(b); - return new AlarmManager(service); + return new AlarmManager(service, ctx); }}); registerService(AUDIO_SERVICE, new ServiceFetcher() { diff --git a/core/java/android/app/IAlarmManager.aidl b/core/java/android/app/IAlarmManager.aidl index edb40ed77f51..3a2b21518cfa 100644 --- a/core/java/android/app/IAlarmManager.aidl +++ b/core/java/android/app/IAlarmManager.aidl @@ -24,9 +24,7 @@ import android.app.PendingIntent; * {@hide} */ interface IAlarmManager { - void set(int type, long triggerAtTime, in PendingIntent operation); - void setRepeating(int type, long triggerAtTime, long interval, in PendingIntent operation); - void setInexactRepeating(int type, long triggerAtTime, long interval, in PendingIntent operation); + void set(int type, long triggerAtTime, long interval, in PendingIntent operation, boolean isExact); void setTime(long millis); void setTimeZone(String zone); void remove(in PendingIntent operation); diff --git a/services/java/com/android/server/AlarmManagerService.java b/services/java/com/android/server/AlarmManagerService.java index 0be9ca593315..b1489ad8d241 100644 --- a/services/java/com/android/server/AlarmManagerService.java +++ b/services/java/com/android/server/AlarmManagerService.java @@ -18,7 +18,6 @@ package com.android.server; import android.app.Activity; import android.app.ActivityManagerNative; -import android.app.AlarmManager; import android.app.IAlarmManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; @@ -38,7 +37,6 @@ import android.os.SystemProperties; import android.os.UserHandle; import android.os.WorkSource; import android.text.TextUtils; -import android.text.format.Time; import android.util.Pair; import android.util.Slog; import android.util.TimeUtils; @@ -57,31 +55,38 @@ import java.util.LinkedList; import java.util.Map; import java.util.TimeZone; +import static android.app.AlarmManager.RTC_WAKEUP; +import static android.app.AlarmManager.RTC; +import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP; +import static android.app.AlarmManager.ELAPSED_REALTIME; + import com.android.internal.util.LocalLog; class AlarmManagerService extends IAlarmManager.Stub { // The threshold for how long an alarm can be late before we print a // warning message. The time duration is in milliseconds. private static final long LATE_ALARM_THRESHOLD = 10 * 1000; - - private static final int RTC_WAKEUP_MASK = 1 << AlarmManager.RTC_WAKEUP; - private static final int RTC_MASK = 1 << AlarmManager.RTC; - private static final int ELAPSED_REALTIME_WAKEUP_MASK = 1 << AlarmManager.ELAPSED_REALTIME_WAKEUP; - private static final int ELAPSED_REALTIME_MASK = 1 << AlarmManager.ELAPSED_REALTIME; + + 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; + private static final int ELAPSED_REALTIME_MASK = 1 << ELAPSED_REALTIME; private static final int TIME_CHANGED_MASK = 1 << 16; private static final int IS_WAKEUP_MASK = RTC_WAKEUP_MASK|ELAPSED_REALTIME_WAKEUP_MASK; - // Alignment quantum for inexact repeating alarms - private static final long QUANTUM = AlarmManager.INTERVAL_FIFTEEN_MINUTES; + // Mask for testing whether a given alarm type is wakeup vs non-wakeup + private static final int TYPE_NONWAKEUP_MASK = 0x1; // low bit => non-wakeup private static final String TAG = "AlarmManager"; private static final String ClockReceiver_TAG = "ClockReceiver"; private static final boolean localLOGV = false; + private static final boolean DEBUG_BATCH = localLOGV || false; private static final int ALARM_EVENT = 1; private static final String TIMEZONE_PROPERTY = "persist.sys.timezone"; private static final Intent mBackgroundIntent = new Intent().addFlags(Intent.FLAG_FROM_BACKGROUND); + private static final IncreasingTimeOrder sIncreasingTimeOrder = new IncreasingTimeOrder(); private static final boolean WAKEUP_STATS = true; @@ -90,14 +95,10 @@ class AlarmManagerService extends IAlarmManager.Stub { private final LocalLog mLog = new LocalLog(TAG); private Object mLock = new Object(); - - private final ArrayList<Alarm> mRtcWakeupAlarms = new ArrayList<Alarm>(); - private final ArrayList<Alarm> mRtcAlarms = new ArrayList<Alarm>(); - private final ArrayList<Alarm> mElapsedRealtimeWakeupAlarms = new ArrayList<Alarm>(); - private final ArrayList<Alarm> mElapsedRealtimeAlarms = new ArrayList<Alarm>(); - private final IncreasingTimeOrder mIncreasingTimeOrder = new IncreasingTimeOrder(); - + private int mDescriptor; + private long mNextWakeup; + private long mNextNonWakeup; private int mBroadcastRefCount = 0; private PowerManager.WakeLock mWakeLock; private ArrayList<InFlight> mInFlight = new ArrayList<InFlight>(); @@ -124,6 +125,277 @@ class AlarmManagerService extends IAlarmManager.Stub { private final LinkedList<WakeupEvent> mRecentWakeups = new LinkedList<WakeupEvent>(); private final long RECENT_WAKEUP_PERIOD = 1000L * 60 * 60 * 24; // one day + static final class Batch { + long start; // These endpoints are always in ELAPSED + long end; + boolean standalone; // certain "batches" don't participate in coalescing + + final ArrayList<Alarm> alarms = new ArrayList<Alarm>(); + + Batch() { + start = 0; + end = Long.MAX_VALUE; + } + + Batch(Alarm seed) { + start = seed.whenElapsed; + end = seed.maxWhen; + alarms.add(seed); + } + + int size() { + return alarms.size(); + } + + Alarm get(int index) { + return alarms.get(index); + } + + boolean canHold(long whenElapsed, long maxWhen) { + return (end >= whenElapsed) && (start <= maxWhen); + } + + boolean add(Alarm alarm) { + boolean newStart = false; + // narrows the batch if necessary; presumes that canHold(alarm) is true + int index = Collections.binarySearch(alarms, alarm, sIncreasingTimeOrder); + if (index < 0) { + index = 0 - index - 1; + } + alarms.add(index, alarm); + if (DEBUG_BATCH) { + Slog.v(TAG, "Adding " + alarm + " to " + this); + } + if (alarm.whenElapsed > start) { + start = alarm.whenElapsed; + newStart = true; + } + if (alarm.maxWhen < end) { + end = alarm.maxWhen; + } + + if (DEBUG_BATCH) { + Slog.v(TAG, " => now " + this); + } + return newStart; + } + + boolean remove(final PendingIntent operation) { + boolean didRemove = false; + long newStart = 0; // recalculate endpoints as we go + long newEnd = Long.MAX_VALUE; + for (int i = 0; i < alarms.size(); i++) { + Alarm alarm = alarms.get(i); + if (alarm.operation.equals(operation)) { + alarms.remove(i); + didRemove = true; + } else { + if (alarm.whenElapsed > newStart) { + newStart = alarm.whenElapsed; + } + if (alarm.maxWhen < newEnd) { + newEnd = alarm.maxWhen; + } + i++; + } + } + if (didRemove) { + // commit the new batch bounds + start = newStart; + end = newEnd; + } + return didRemove; + } + + boolean remove(final String packageName) { + boolean didRemove = false; + long newStart = 0; // recalculate endpoints as we go + long newEnd = Long.MAX_VALUE; + for (int i = 0; i < alarms.size(); i++) { + Alarm alarm = alarms.get(i); + if (alarm.operation.getTargetPackage().equals(packageName)) { + alarms.remove(i); + didRemove = true; + } else { + if (alarm.whenElapsed > newStart) { + newStart = alarm.whenElapsed; + } + if (alarm.maxWhen < newEnd) { + newEnd = alarm.maxWhen; + } + i++; + } + } + if (didRemove) { + // commit the new batch bounds + start = newStart; + end = newEnd; + } + return didRemove; + } + + boolean remove(final int userHandle) { + boolean didRemove = false; + long newStart = 0; // recalculate endpoints as we go + long newEnd = Long.MAX_VALUE; + for (int i = 0; i < alarms.size(); i++) { + Alarm alarm = alarms.get(i); + if (UserHandle.getUserId(alarm.operation.getCreatorUid()) == userHandle) { + alarms.remove(i); + didRemove = true; + } else { + if (alarm.whenElapsed > newStart) { + newStart = alarm.whenElapsed; + } + if (alarm.maxWhen < newEnd) { + newEnd = alarm.maxWhen; + } + i++; + } + } + if (didRemove) { + // commit the new batch bounds + start = newStart; + end = newEnd; + } + return didRemove; + } + + boolean hasPackage(final String packageName) { + final int N = alarms.size(); + for (int i = 0; i < N; i++) { + Alarm a = alarms.get(i); + if (a.operation.getTargetPackage().equals(packageName)) { + return true; + } + } + return false; + } + + boolean hasWakeups() { + final int N = alarms.size(); + for (int i = 0; i < N; i++) { + Alarm a = alarms.get(i); + // non-wakeup alarms are types 1 and 3, i.e. have the low bit set + if ((a.type & TYPE_NONWAKEUP_MASK) == 0) { + return true; + } + } + return false; + } + + @Override + public String toString() { + StringBuilder b = new StringBuilder(40); + b.append("Batch{"); b.append(Integer.toHexString(this.hashCode())); + b.append(" num="); b.append(size()); + b.append(" start="); b.append(start); + b.append(" end="); b.append(end); + if (standalone) { + b.append(" STANDALONE"); + } + b.append('}'); + return b.toString(); + } + } + + static class BatchTimeOrder implements Comparator<Batch> { + public int compare(Batch b1, Batch b2) { + long when1 = b1.start; + long when2 = b2.start; + if (when1 - when2 > 0) { + return 1; + } + if (when1 - when2 < 0) { + return -1; + } + return 0; + } + } + + // minimum recurrence period or alarm futurity for us to be able to fuzz it + private static final long MAX_FUZZABLE_INTERVAL = 10000; + private static final BatchTimeOrder sBatchOrder = new BatchTimeOrder(); + private final ArrayList<Batch> mAlarmBatches = new ArrayList<Batch>(); + + static long convertToElapsed(long when, int type) { + final boolean isRtc = (type == RTC || type == RTC_WAKEUP); + if (isRtc) { + when -= System.currentTimeMillis() - SystemClock.elapsedRealtime(); + } + return when; + } + + // Apply a heuristic to { recurrence interval, futurity of the trigger time } to + // calculate the end of our nominal delivery window for the alarm. + static long maxTriggerTime(long now, long triggerAtTime, long interval) { + // Current heuristic: batchable window is 75% of either the recurrence interval + // [for a periodic alarm] or of the time from now to the desired delivery time, + // with a minimum delay/interval of 10 seconds, under which we will simply not + // defer the alarm. + long futurity = (interval == 0) + ? (triggerAtTime - now) + : interval; + if (futurity < MAX_FUZZABLE_INTERVAL) { + futurity = 0; + } + return triggerAtTime + (long)(.75 * futurity); + } + + // returns true if the batch was added at the head + static boolean addBatchLocked(ArrayList<Batch> list, Batch newBatch) { + int index = Collections.binarySearch(list, newBatch, sBatchOrder); + if (index < 0) { + index = 0 - index - 1; + } + list.add(index, newBatch); + return (index == 0); + } + + Batch attemptCoalesceLocked(long whenElapsed, long maxWhen) { + final int N = mAlarmBatches.size(); + for (int i = 0; i < N; i++) { + Batch b = mAlarmBatches.get(i); + if (!b.standalone && b.canHold(whenElapsed, maxWhen)) { + return b; + } + } + return null; + } + + // The RTC clock has moved arbitrarily, so we need to recalculate all the batching + void rebatchAllAlarms() { + if (DEBUG_BATCH) { + Slog.v(TAG, "RTC changed; rebatching"); + } + synchronized (mLock) { + ArrayList<Batch> oldSet = (ArrayList<Batch>) mAlarmBatches.clone(); + mAlarmBatches.clear(); + final long nowElapsed = SystemClock.elapsedRealtime(); + for (Batch batch : oldSet) { + final int N = batch.size(); + for (int i = 0; i < N; i++) { + Alarm a = batch.get(i); + long whenElapsed = convertToElapsed(a.when, a.type); + long maxElapsed = (a.whenElapsed == a.maxWhen) + ? whenElapsed + : maxTriggerTime(nowElapsed, whenElapsed, a.repeatInterval); + if (batch.standalone) { + // this will also be the only alarm in the batch + a = new Alarm(a.type, a.when, whenElapsed, maxElapsed, + a.repeatInterval, a.operation); + Batch newBatch = new Batch(a); + newBatch.standalone = true; + addBatchLocked(mAlarmBatches, newBatch); + } else { + setImplLocked(a.type, a.when, whenElapsed, maxElapsed, + a.repeatInterval, a.operation, false); + } + } + } + } + } + private static final class InFlight extends Intent { final PendingIntent mPendingIntent; final Pair<String, ComponentName> mTarget; @@ -184,6 +456,7 @@ class AlarmManagerService extends IAlarmManager.Stub { public AlarmManagerService(Context context) { mContext = context; mDescriptor = init(); + mNextWakeup = mNextNonWakeup = 0; // We have to set current TimeZone info to kernel // because kernel doesn't keep this after reboot @@ -224,77 +497,89 @@ class AlarmManagerService extends IAlarmManager.Stub { super.finalize(); } } - - public void set(int type, long triggerAtTime, PendingIntent operation) { - setRepeating(type, triggerAtTime, 0, operation); + + @Override + public void set(int type, long triggerAtTime, long interval, + PendingIntent operation, boolean isExact) { + set(type, triggerAtTime, interval, operation, isExact, false); } - - public void setRepeating(int type, long triggerAtTime, long interval, - PendingIntent operation) { + + public void set(int type, long triggerAtTime, long interval, + PendingIntent operation, boolean isExact, boolean isStandalone) { if (operation == null) { Slog.w(TAG, "set/setRepeating ignored because there is no intent"); return; } - synchronized (mLock) { - Alarm alarm = new Alarm(); - alarm.type = type; - alarm.when = triggerAtTime; - alarm.repeatInterval = interval; - alarm.operation = operation; - // Remove this alarm if already scheduled. - removeLocked(operation); + if (type < RTC_WAKEUP || type > ELAPSED_REALTIME) { + throw new IllegalArgumentException("Invalid alarm type " + type); + } - if (localLOGV) Slog.v(TAG, "set: " + alarm); + long nowElapsed = SystemClock.elapsedRealtime(); + long triggerElapsed = convertToElapsed(triggerAtTime, type); + long maxElapsed = (isExact) + ? triggerElapsed + : maxTriggerTime(nowElapsed, triggerElapsed, interval); - int index = addAlarmLocked(alarm); - if (index == 0) { - setLocked(alarm); + synchronized (mLock) { + if (DEBUG_BATCH) { + Slog.v(TAG, "set(" + operation + ") : type=" + type + + " triggerAtTime=" + triggerAtTime + + " tElapsed=" + triggerElapsed + " maxElapsed=" + maxElapsed + + " interval=" + interval + " standalone=" + isStandalone); } + setImplLocked(type, triggerAtTime, triggerElapsed, maxElapsed, + interval, operation, isStandalone); } } - - public void setInexactRepeating(int type, long triggerAtTime, long interval, - PendingIntent operation) { - if (operation == null) { - Slog.w(TAG, "setInexactRepeating ignored because there is no intent"); - return; - } - if (interval <= 0) { - Slog.w(TAG, "setInexactRepeating ignored because interval " + interval - + " is invalid"); - return; + private void setImplLocked(int type, long when, long whenElapsed, long maxWhen, long interval, + PendingIntent operation, boolean isStandalone) { + Alarm a = new Alarm(type, when, whenElapsed, maxWhen, interval, operation); + removeLocked(operation); + + final boolean reschedule; + Batch batch = (isStandalone) ? null : attemptCoalesceLocked(whenElapsed, maxWhen); + if (batch == null) { + batch = new Batch(a); + batch.standalone = isStandalone; + if (DEBUG_BATCH) { + Slog.v(TAG, "Starting new alarm batch " + batch); + } + reschedule = addBatchLocked(mAlarmBatches, batch); + } else { + reschedule = batch.add(a); } - // If the requested interval isn't a multiple of 15 minutes, just treat it as exact - if (interval % QUANTUM != 0) { - if (localLOGV) Slog.v(TAG, "Interval " + interval + " not a quantum multiple"); - setRepeating(type, triggerAtTime, interval, operation); - return; + if (reschedule) { + rescheduleKernelAlarmsLocked(); } + } - // Translate times into the ELAPSED timebase for alignment purposes so that - // alignment never tries to match against wall clock times. - final boolean isRtc = (type == AlarmManager.RTC || type == AlarmManager.RTC_WAKEUP); - final long skew = (isRtc) - ? System.currentTimeMillis() - SystemClock.elapsedRealtime() - : 0; - - // Slip forward to the next ELAPSED-timebase quantum after the stated time. If - // we're *at* a quantum point, leave it alone. - final long adjustedTriggerTime; - long offset = (triggerAtTime - skew) % QUANTUM; - if (offset != 0) { - adjustedTriggerTime = triggerAtTime - offset + QUANTUM; - } else { - adjustedTriggerTime = triggerAtTime; + private Batch findFirstWakeupBatchLocked() { + final int N = mAlarmBatches.size(); + for (int i = 0; i < N; i++) { + Batch b = mAlarmBatches.get(i); + if (b.hasWakeups()) { + return b; + } } + return null; + } - // Set the alarm based on the quantum-aligned start time - if (localLOGV) Slog.v(TAG, "setInexactRepeating: type=" + type + " interval=" + interval - + " trigger=" + adjustedTriggerTime + " orig=" + triggerAtTime); - setRepeating(type, adjustedTriggerTime, interval, operation); + private void rescheduleKernelAlarmsLocked() { + // Schedule the next upcoming wakeup alarm. If there is a deliverable batch + // prior to that which contains no wakeups, we schedule that as well. + final Batch firstWakeup = findFirstWakeupBatchLocked(); + final Batch firstBatch = mAlarmBatches.get(0); + if (firstWakeup != null && mNextWakeup != firstWakeup.start) { + mNextWakeup = firstWakeup.start; + setLocked(ELAPSED_REALTIME_WAKEUP, firstWakeup.start); + } + if (firstBatch != firstWakeup && mNextNonWakeup != firstBatch.start) { + mNextNonWakeup = firstBatch.start; + setLocked(ELAPSED_REALTIME, firstBatch.start); + } } public void setTime(long millis) { @@ -356,160 +641,61 @@ class AlarmManagerService extends IAlarmManager.Stub { } public void removeLocked(PendingIntent operation) { - removeLocked(mRtcWakeupAlarms, operation); - removeLocked(mRtcAlarms, operation); - removeLocked(mElapsedRealtimeWakeupAlarms, operation); - removeLocked(mElapsedRealtimeAlarms, operation); - } - - private void removeLocked(ArrayList<Alarm> alarmList, - PendingIntent operation) { - if (alarmList.size() <= 0) { - return; - } - - // iterator over the list removing any it where the intent match - for (int i=0; i<alarmList.size(); i++) { - Alarm alarm = alarmList.get(i); - if (alarm.operation.equals(operation)) { - alarmList.remove(i); - i--; + for (int i = mAlarmBatches.size() - 1; i >= 0; i--) { + Batch b = mAlarmBatches.get(i); + b.remove(operation); + if (b.size() == 0) { + mAlarmBatches.remove(i); } } } public void removeLocked(String packageName) { - removeLocked(mRtcWakeupAlarms, packageName); - removeLocked(mRtcAlarms, packageName); - removeLocked(mElapsedRealtimeWakeupAlarms, packageName); - removeLocked(mElapsedRealtimeAlarms, packageName); - } - - private void removeLocked(ArrayList<Alarm> alarmList, - String packageName) { - if (alarmList.size() <= 0) { - return; - } - - // iterator over the list removing any it where the intent match - for (int i=0; i<alarmList.size(); i++) { - Alarm alarm = alarmList.get(i); - if (alarm.operation.getTargetPackage().equals(packageName)) { - alarmList.remove(i); - i--; + for (int i = mAlarmBatches.size() - 1; i >= 0; i--) { + Batch b = mAlarmBatches.get(i); + b.remove(packageName); + if (b.size() == 0) { + mAlarmBatches.remove(i); } } } public void removeUserLocked(int userHandle) { - removeUserLocked(mRtcWakeupAlarms, userHandle); - removeUserLocked(mRtcAlarms, userHandle); - removeUserLocked(mElapsedRealtimeWakeupAlarms, userHandle); - removeUserLocked(mElapsedRealtimeAlarms, userHandle); - } - - private void removeUserLocked(ArrayList<Alarm> alarmList, int userHandle) { - if (alarmList.size() <= 0) { - return; - } - - // iterator over the list removing any it where the intent match - for (int i=0; i<alarmList.size(); i++) { - Alarm alarm = alarmList.get(i); - if (UserHandle.getUserId(alarm.operation.getCreatorUid()) == userHandle) { - alarmList.remove(i); - i--; + for (int i = mAlarmBatches.size() - 1; i >= 0; i--) { + Batch b = mAlarmBatches.get(i); + b.remove(userHandle); + if (b.size() == 0) { + mAlarmBatches.remove(i); } } } - - public boolean lookForPackageLocked(String packageName) { - return lookForPackageLocked(mRtcWakeupAlarms, packageName) - || lookForPackageLocked(mRtcAlarms, packageName) - || lookForPackageLocked(mElapsedRealtimeWakeupAlarms, packageName) - || lookForPackageLocked(mElapsedRealtimeAlarms, packageName); - } - private boolean lookForPackageLocked(ArrayList<Alarm> alarmList, String packageName) { - for (int i=alarmList.size()-1; i>=0; i--) { - if (alarmList.get(i).operation.getTargetPackage().equals(packageName)) { + public boolean lookForPackageLocked(String packageName) { + for (int i = 0; i < mAlarmBatches.size(); i++) { + Batch b = mAlarmBatches.get(i); + if (b.hasPackage(packageName)) { return true; } } return false; } - - private ArrayList<Alarm> getAlarmList(int type) { - switch (type) { - case AlarmManager.RTC_WAKEUP: return mRtcWakeupAlarms; - case AlarmManager.RTC: return mRtcAlarms; - case AlarmManager.ELAPSED_REALTIME_WAKEUP: return mElapsedRealtimeWakeupAlarms; - case AlarmManager.ELAPSED_REALTIME: return mElapsedRealtimeAlarms; - } - - return null; - } - - private int addAlarmLocked(Alarm alarm) { - ArrayList<Alarm> alarmList = getAlarmList(alarm.type); - - int index = Collections.binarySearch(alarmList, alarm, mIncreasingTimeOrder); - if (index < 0) { - index = 0 - index - 1; - } - if (localLOGV) Slog.v(TAG, "Adding alarm " + alarm + " at " + index); - alarmList.add(index, alarm); - - if (localLOGV) { - // Display the list of alarms for this alarm type - Slog.v(TAG, "alarms: " + alarmList.size() + " type: " + alarm.type); - int position = 0; - for (Alarm a : alarmList) { - Time time = new Time(); - time.set(a.when); - String timeStr = time.format("%b %d %I:%M:%S %p"); - Slog.v(TAG, position + ": " + timeStr - + " " + a.operation.getTargetPackage()); - position += 1; - } - } - - return index; - } - - public long timeToNextAlarm() { - long nextAlarm = Long.MAX_VALUE; - synchronized (mLock) { - for (int i=AlarmManager.RTC_WAKEUP; - i<=AlarmManager.ELAPSED_REALTIME; i++) { - ArrayList<Alarm> alarmList = getAlarmList(i); - if (alarmList.size() > 0) { - Alarm a = alarmList.get(0); - if (a.when < nextAlarm) { - nextAlarm = a.when; - } - } - } - } - return nextAlarm; - } - - private void setLocked(Alarm alarm) + + private void setLocked(int type, long when) { if (mDescriptor != -1) { // The kernel never triggers alarms with negative wakeup times // so we ensure they are positive. long alarmSeconds, alarmNanoseconds; - if (alarm.when < 0) { + if (when < 0) { alarmSeconds = 0; alarmNanoseconds = 0; } else { - alarmSeconds = alarm.when / 1000; - alarmNanoseconds = (alarm.when % 1000) * 1000 * 1000; + alarmSeconds = when / 1000; + alarmNanoseconds = (when % 1000) * 1000 * 1000; } - set(mDescriptor, alarm.type, alarmSeconds, alarmNanoseconds); + set(mDescriptor, type, alarmSeconds, alarmNanoseconds); } else { @@ -517,7 +703,7 @@ class AlarmManagerService extends IAlarmManager.Stub { msg.what = ALARM_EVENT; mHandler.removeMessages(ALARM_EVENT); - mHandler.sendMessageAtTime(msg, alarm.when); + mHandler.sendMessageAtTime(msg, when); } } @@ -533,29 +719,28 @@ class AlarmManagerService extends IAlarmManager.Stub { synchronized (mLock) { pw.println("Current Alarm Manager state:"); - if (mRtcWakeupAlarms.size() > 0 || mRtcAlarms.size() > 0) { - final long now = System.currentTimeMillis(); - SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - pw.println(" "); - pw.print(" Realtime wakeup (now="); - pw.print(sdf.format(new Date(now))); pw.println("):"); - if (mRtcWakeupAlarms.size() > 0) { - dumpAlarmList(pw, mRtcWakeupAlarms, " ", "RTC_WAKEUP", now); - } - if (mRtcAlarms.size() > 0) { - dumpAlarmList(pw, mRtcAlarms, " ", "RTC", now); - } - } - if (mElapsedRealtimeWakeupAlarms.size() > 0 || mElapsedRealtimeAlarms.size() > 0) { - final long now = SystemClock.elapsedRealtime(); - pw.println(" "); - pw.print(" Elapsed realtime wakeup (now="); - TimeUtils.formatDuration(now, pw); pw.println("):"); - if (mElapsedRealtimeWakeupAlarms.size() > 0) { - dumpAlarmList(pw, mElapsedRealtimeWakeupAlarms, " ", "ELAPSED_WAKEUP", now); - } - if (mElapsedRealtimeAlarms.size() > 0) { - dumpAlarmList(pw, mElapsedRealtimeAlarms, " ", "ELAPSED", now); + final long nowRTC = System.currentTimeMillis(); + final long nowELAPSED = SystemClock.elapsedRealtime(); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + + pw.print("nowRTC="); pw.print(nowRTC); + pw.print("="); pw.print(sdf.format(new Date(nowRTC))); + pw.print(" nowELAPSED="); pw.println(nowELAPSED); + + long nextWakeupRTC = mNextWakeup + (nowRTC - nowELAPSED); + long nextNonWakeupRTC = mNextNonWakeup + (nowRTC - nowELAPSED); + pw.print("Next alarm: "); pw.print(mNextNonWakeup); + pw.print(" = "); pw.println(sdf.format(new Date(nextNonWakeupRTC))); + pw.print("Next wakeup: "); pw.print(mNextWakeup); + pw.print(" = "); pw.println(sdf.format(new Date(nextWakeupRTC))); + + if (mAlarmBatches.size() > 0) { + pw.println(); + pw.print("Pending alarm batches: "); + pw.println(mAlarmBatches.size()); + for (Batch b : mAlarmBatches) { + pw.print(b); pw.println(':'); + dumpAlarmList(pw, b.alarms, " ", nowELAPSED, nowRTC); } } @@ -662,7 +847,6 @@ class AlarmManagerService extends IAlarmManager.Stub { if (WAKEUP_STATS) { pw.println(); pw.println(" Recent Wakeup History:"); - final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd-HH:mm:ss.SSS"); long last = -1; for (WakeupEvent event : mRecentWakeups) { pw.print(" "); pw.print(sdf.format(new Date(event.when))); @@ -691,77 +875,78 @@ class AlarmManagerService extends IAlarmManager.Stub { a.dump(pw, prefix + " ", now); } } - + + private static final String labelForType(int type) { + switch (type) { + case RTC: return "RTC"; + case RTC_WAKEUP : return "RTC_WAKEUP"; + case ELAPSED_REALTIME : return "ELAPSED"; + case ELAPSED_REALTIME_WAKEUP: return "ELAPSED_WAKEUP"; + default: + break; + } + return "--unknown--"; + } + + private static final void dumpAlarmList(PrintWriter pw, ArrayList<Alarm> list, + String prefix, long nowELAPSED, long nowRTC) { + for (int i=list.size()-1; i>=0; i--) { + Alarm a = list.get(i); + final String label = labelForType(a.type); + long now = (a.type <= RTC) ? nowRTC : nowELAPSED; + pw.print(prefix); pw.print(label); pw.print(" #"); pw.print(i); + pw.print(": "); pw.println(a); + a.dump(pw, prefix + " ", now); + } + } + private native int init(); private native void close(int fd); private native void set(int fd, int type, long seconds, long nanoseconds); private native int waitForAlarm(int fd); private native int setKernelTimezone(int fd, int minuteswest); - private void triggerAlarmsLocked(ArrayList<Alarm> alarmList, - ArrayList<Alarm> triggerList, - long now) - { - ArrayList<Alarm> repeats = null; - - for (int i=0; i<alarmList.size(); i++) { - Alarm alarm = alarmList.get(i); - - if (localLOGV) Slog.v(TAG, "Checking active alarm when=" + alarm.when + " " + alarm); + private void triggerAlarmsLocked(ArrayList<Alarm> triggerList, long nowELAPSED, long nowRTC) { + Batch batch; - if (alarm.when > now) { - // don't fire alarms in the future + // batches are temporally sorted, so we need only pull from the + // start of the list until we either empty it or hit a batch + // that is not yet deliverable + while ((batch = mAlarmBatches.get(0)) != null) { + if (batch.start > nowELAPSED) { + // Everything else is scheduled for the future break; } - - // If the alarm is late, then print a warning message. - // Note that this can happen if the user creates a new event on - // the Calendar app with a reminder that is in the past. In that - // case, the reminder alarm will fire immediately. - if (localLOGV && now - alarm.when > LATE_ALARM_THRESHOLD) { - Slog.v(TAG, "alarm is late! alarm time: " + alarm.when - + " now: " + now + " delay (in seconds): " - + (now - alarm.when) / 1000); - } - // Recurring alarms may have passed several alarm intervals while the - // phone was asleep or off, so pass a trigger count when sending them. - if (localLOGV) Slog.v(TAG, "Alarm triggering: " + alarm); - alarm.count = 1; - if (alarm.repeatInterval > 0) { - // this adjustment will be zero if we're late by - // less than one full repeat interval - alarm.count += (now - alarm.when) / alarm.repeatInterval; - } - triggerList.add(alarm); - - // remove the alarm from the list - alarmList.remove(i); - i--; - - // if it repeats queue it up to be read-added to the list - if (alarm.repeatInterval > 0) { - if (repeats == null) { - repeats = new ArrayList<Alarm>(); + // We will (re)schedule some alarms now; don't let that interfere + // with delivery of this current batch + mAlarmBatches.remove(0); + + final int N = batch.size(); + for (int i = 0; i < N; i++) { + Alarm alarm = batch.get(i); + alarm.count = 1; + triggerList.add(alarm); + + // Recurring alarms may have passed several alarm intervals while the + // phone was asleep or off, so pass a trigger count when sending them. + if (alarm.repeatInterval > 0) { + // this adjustment will be zero if we're late by + // less than one full repeat interval + alarm.count += (nowELAPSED - alarm.whenElapsed) / alarm.repeatInterval; + + // Also schedule its next recurrence + final long delta = alarm.count * alarm.repeatInterval; + final long nextElapsed = alarm.whenElapsed + delta; + setImplLocked(alarm.type, alarm.when + delta, nextElapsed, + maxTriggerTime(nowELAPSED, nextElapsed, alarm.repeatInterval), + alarm.repeatInterval, alarm.operation, batch.standalone); } - repeats.add(alarm); - } - } - // reset any repeating alarms. - if (repeats != null) { - for (int i=0; i<repeats.size(); i++) { - Alarm alarm = repeats.get(i); - alarm.when += alarm.count * alarm.repeatInterval; - addAlarmLocked(alarm); } } - - if (alarmList.size() > 0) { - setLocked(alarmList.get(0)); - } } - + /** * This Comparator sorts Alarms into increasing time order. */ @@ -783,15 +968,21 @@ class AlarmManagerService extends IAlarmManager.Stub { public int type; public int count; public long when; + public long whenElapsed; // 'when' in the elapsed time base + public long maxWhen; // also in the elapsed time base public long repeatInterval; public PendingIntent operation; - public Alarm() { - when = 0; - repeatInterval = 0; - operation = null; + public Alarm(int _type, long _when, long _whenElapsed, long _maxWhen, + long _interval, PendingIntent _op) { + type = _type; + when = _when; + whenElapsed = _whenElapsed; + maxWhen = _maxWhen; + repeatInterval = _interval; + operation = _op; } - + @Override public String toString() { @@ -808,6 +999,7 @@ class AlarmManagerService extends IAlarmManager.Stub { public void dump(PrintWriter pw, String prefix, long now) { pw.print(prefix); pw.print("type="); pw.print(type); + pw.print(" whenElapsed="); pw.print(whenElapsed); pw.print(" when="); TimeUtils.formatDuration(when, now, pw); pw.print(" repeatInterval="); pw.print(repeatInterval); pw.print(" count="); pw.println(count); @@ -815,16 +1007,22 @@ class AlarmManagerService extends IAlarmManager.Stub { } } - void recordWakeupAlarms(ArrayList<Alarm> alarms, long now, long skewToRTC) { - for (Alarm a : alarms) { - if (a.when > now) { + void recordWakeupAlarms(ArrayList<Batch> batches, long nowELAPSED, long nowRTC) { + final int numBatches = batches.size(); + for (int i = 0; i < numBatches; i++) { + Batch b = batches.get(i); + if (b.start > nowELAPSED) { break; } - WakeupEvent e = new WakeupEvent(now + skewToRTC, - a.operation.getCreatorUid(), - a.operation.getIntent().getAction()); - mRecentWakeups.add(e); + final int numAlarms = b.alarms.size(); + for (int j = 0; j < numAlarms; j++) { + Alarm a = b.alarms.get(i); + WakeupEvent e = new WakeupEvent(nowRTC, + a.operation.getCreatorUid(), + a.operation.getIntent().getAction()); + mRecentWakeups.add(e); + } } } @@ -847,6 +1045,7 @@ class AlarmManagerService extends IAlarmManager.Stub { if ((result & TIME_CHANGED_MASK) != 0) { remove(mTimeTickSender); + rebatchAllAlarms(); mClockReceiver.scheduleTimeTickEvent(); Intent intent = new Intent(Intent.ACTION_TIME_CHANGED); intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING @@ -873,28 +1072,14 @@ class AlarmManagerService extends IAlarmManager.Stub { mRecentWakeups.remove(); } - recordWakeupAlarms(mRtcWakeupAlarms, - nowRTC, - 0); - recordWakeupAlarms(mElapsedRealtimeWakeupAlarms, - nowELAPSED, - nowRTC - nowELAPSED); + recordWakeupAlarms(mAlarmBatches, nowELAPSED, nowRTC); } } - if ((result & RTC_WAKEUP_MASK) != 0) - triggerAlarmsLocked(mRtcWakeupAlarms, triggerList, nowRTC); - - if ((result & RTC_MASK) != 0) - triggerAlarmsLocked(mRtcAlarms, triggerList, nowRTC); - - if ((result & ELAPSED_REALTIME_WAKEUP_MASK) != 0) - triggerAlarmsLocked(mElapsedRealtimeWakeupAlarms, triggerList, nowELAPSED); - - if ((result & ELAPSED_REALTIME_MASK) != 0) - triggerAlarmsLocked(mElapsedRealtimeAlarms, triggerList, nowELAPSED); - - // now trigger the alarms + triggerAlarmsLocked(triggerList, nowELAPSED, nowRTC); + rescheduleKernelAlarmsLocked(); + + // now deliver the alarm intents for (int i=0; i<triggerList.size(); i++) { Alarm alarm = triggerList.get(i); try { @@ -930,8 +1115,8 @@ class AlarmManagerService extends IAlarmManager.Stub { } else { fs.nesting++; } - if (alarm.type == AlarmManager.ELAPSED_REALTIME_WAKEUP - || alarm.type == AlarmManager.RTC_WAKEUP) { + if (alarm.type == ELAPSED_REALTIME_WAKEUP + || alarm.type == RTC_WAKEUP) { bs.numWakeup++; fs.numWakeup++; ActivityManagerNative.noteWakeupAlarm( @@ -980,10 +1165,8 @@ class AlarmManagerService extends IAlarmManager.Stub { ArrayList<Alarm> triggerList = new ArrayList<Alarm>(); synchronized (mLock) { final long nowRTC = System.currentTimeMillis(); - triggerAlarmsLocked(mRtcWakeupAlarms, triggerList, nowRTC); - triggerAlarmsLocked(mRtcAlarms, triggerList, nowRTC); - triggerAlarmsLocked(mElapsedRealtimeWakeupAlarms, triggerList, nowRTC); - triggerAlarmsLocked(mElapsedRealtimeAlarms, triggerList, nowRTC); + final long nowELAPSED = SystemClock.elapsedRealtime(); + triggerAlarmsLocked(triggerList, nowELAPSED, nowRTC); } // now trigger the alarms without the lock held @@ -1035,8 +1218,8 @@ class AlarmManagerService extends IAlarmManager.Stub { // the top of the next minute. final long tickEventDelay = nextTime - currentTime; - set(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + tickEventDelay, - mTimeTickSender); + set(ELAPSED_REALTIME, SystemClock.elapsedRealtime() + tickEventDelay, + 0, mTimeTickSender, true, true); } public void scheduleDateChangedEvent() { @@ -1048,7 +1231,7 @@ class AlarmManagerService extends IAlarmManager.Stub { calendar.set(Calendar.MILLISECOND, 0); calendar.add(Calendar.DAY_OF_MONTH, 1); - set(AlarmManager.RTC, calendar.getTimeInMillis(), mDateChangeSender); + set(RTC, calendar.getTimeInMillis(), 0, mDateChangeSender, true, true); } } diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java index 5074409694ad..05eeb36195ee 100644 --- a/services/java/com/android/server/net/NetworkStatsService.java +++ b/services/java/com/android/server/net/NetworkStatsService.java @@ -154,7 +154,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { private final Context mContext; private final INetworkManagementService mNetworkManager; - private final IAlarmManager mAlarmManager; + private final AlarmManager mAlarmManager; private final TrustedTime mTime; private final TelephonyManager mTeleManager; private final NetworkStatsSettings mSettings; @@ -261,10 +261,10 @@ public class NetworkStatsService extends INetworkStatsService.Stub { NetworkStatsSettings settings) { mContext = checkNotNull(context, "missing Context"); mNetworkManager = checkNotNull(networkManager, "missing INetworkManagementService"); - mAlarmManager = checkNotNull(alarmManager, "missing IAlarmManager"); mTime = checkNotNull(time, "missing TrustedTime"); mTeleManager = checkNotNull(TelephonyManager.getDefault(), "missing TelephonyManager"); mSettings = checkNotNull(settings, "missing NetworkStatsSettings"); + mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); final PowerManager powerManager = (PowerManager) context.getSystemService( Context.POWER_SERVICE); @@ -420,20 +420,16 @@ public class NetworkStatsService extends INetworkStatsService.Stub { * reschedule based on current {@link NetworkStatsSettings#getPollInterval()}. */ private void registerPollAlarmLocked() { - try { - if (mPollIntent != null) { - mAlarmManager.remove(mPollIntent); - } + if (mPollIntent != null) { + mAlarmManager.cancel(mPollIntent); + } - mPollIntent = PendingIntent.getBroadcast( - mContext, 0, new Intent(ACTION_NETWORK_STATS_POLL), 0); + mPollIntent = PendingIntent.getBroadcast( + mContext, 0, new Intent(ACTION_NETWORK_STATS_POLL), 0); - final long currentRealtime = SystemClock.elapsedRealtime(); - mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME, currentRealtime, - mSettings.getPollInterval(), mPollIntent); - } catch (RemoteException e) { - // ignored; service lives in system_server - } + final long currentRealtime = SystemClock.elapsedRealtime(); + mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME, currentRealtime, + mSettings.getPollInterval(), mPollIntent); } /** |