Loading apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java +1 −27 Original line number Diff line number Diff line Loading @@ -673,9 +673,6 @@ public class AlarmManagerService extends SystemService { private static final String KEY_APP_STANDBY_RESTRICTED_WINDOW = "app_standby_restricted_window"; @VisibleForTesting static final String KEY_LAZY_BATCHING = "lazy_batching"; private static final String KEY_TIME_TICK_ALLOWED_WHILE_IDLE = "time_tick_allowed_while_idle"; Loading Loading @@ -725,7 +722,6 @@ public class AlarmManagerService extends SystemService { private static final int DEFAULT_APP_STANDBY_RESTRICTED_QUOTA = 1; private static final long DEFAULT_APP_STANDBY_RESTRICTED_WINDOW = INTERVAL_DAY; private static final boolean DEFAULT_LAZY_BATCHING = true; private static final boolean DEFAULT_TIME_TICK_ALLOWED_WHILE_IDLE = true; /** Loading Loading @@ -772,7 +768,6 @@ public class AlarmManagerService extends SystemService { public int APP_STANDBY_RESTRICTED_QUOTA = DEFAULT_APP_STANDBY_RESTRICTED_QUOTA; public long APP_STANDBY_RESTRICTED_WINDOW = DEFAULT_APP_STANDBY_RESTRICTED_WINDOW; public boolean LAZY_BATCHING = DEFAULT_LAZY_BATCHING; public boolean TIME_TICK_ALLOWED_WHILE_IDLE = DEFAULT_TIME_TICK_ALLOWED_WHILE_IDLE; public int ALLOW_WHILE_IDLE_QUOTA = DEFAULT_ALLOW_WHILE_IDLE_QUOTA; Loading Loading @@ -976,14 +971,6 @@ public class AlarmManagerService extends SystemService { case KEY_APP_STANDBY_RESTRICTED_WINDOW: updateStandbyWindowsLocked(); break; case KEY_LAZY_BATCHING: final boolean oldLazyBatching = LAZY_BATCHING; LAZY_BATCHING = properties.getBoolean( KEY_LAZY_BATCHING, DEFAULT_LAZY_BATCHING); if (oldLazyBatching != LAZY_BATCHING) { migrateAlarmsToNewStoreLocked(); } break; case KEY_TIME_TICK_ALLOWED_WHILE_IDLE: TIME_TICK_ALLOWED_WHILE_IDLE = properties.getBoolean( KEY_TIME_TICK_ALLOWED_WHILE_IDLE, Loading Loading @@ -1094,15 +1081,6 @@ public class AlarmManagerService extends SystemService { } } private void migrateAlarmsToNewStoreLocked() { final AlarmStore newStore = LAZY_BATCHING ? new LazyAlarmStore() : new BatchingAlarmStore(); final ArrayList<Alarm> allAlarms = mAlarmStore.remove((unused) -> true); newStore.addAll(allAlarms); mAlarmStore = newStore; mAlarmStore.setAlarmClockRemovalListener(mAlarmClockUpdater); } private void updateDeviceIdleFuzzBoundaries() { final DeviceConfig.Properties properties = DeviceConfig.getProperties( DeviceConfig.NAMESPACE_ALARM_MANAGER, Loading Loading @@ -1240,9 +1218,6 @@ public class AlarmManagerService extends SystemService { TimeUtils.formatDuration(APP_STANDBY_RESTRICTED_WINDOW, pw); pw.println(); pw.print(KEY_LAZY_BATCHING, LAZY_BATCHING); pw.println(); pw.print(KEY_TIME_TICK_ALLOWED_WHILE_IDLE, TIME_TICK_ALLOWED_WHILE_IDLE); pw.println(); Loading Loading @@ -1878,8 +1853,7 @@ public class AlarmManagerService extends SystemService { mHandler = new AlarmHandler(); mConstants = new Constants(mHandler); mAlarmStore = mConstants.LAZY_BATCHING ? new LazyAlarmStore() : new BatchingAlarmStore(); mAlarmStore = new LazyAlarmStore(); mAlarmStore.setAlarmClockRemovalListener(mAlarmClockUpdater); mAppWakeupHistory = new AppWakeupHistory(Constants.DEFAULT_APP_STANDBY_WINDOW); Loading apex/jobscheduler/service/java/com/android/server/alarm/BatchingAlarmStore.javadeleted 100644 → 0 +0 −408 Original line number Diff line number Diff line /* * Copyright (C) 2020 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.android.server.alarm; import static com.android.server.alarm.AlarmManagerService.DEBUG_BATCH; import static com.android.server.alarm.AlarmManagerService.clampPositive; import static com.android.server.alarm.AlarmManagerService.dumpAlarmList; import static com.android.server.alarm.AlarmManagerService.isTimeTickAlarm; import android.app.AlarmManager; import android.util.IndentingPrintWriter; import android.util.Slog; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.StatLogger; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.function.Predicate; /** * Batching implementation of an Alarm Store. * This keeps the alarms in batches, which are sorted on the start time of their delivery window. */ public class BatchingAlarmStore implements AlarmStore { @VisibleForTesting static final String TAG = BatchingAlarmStore.class.getSimpleName(); private final ArrayList<Batch> mAlarmBatches = new ArrayList<>(); private int mSize; private Runnable mOnAlarmClockRemoved; interface Stats { int REBATCH_ALL_ALARMS = 0; int GET_COUNT = 1; } final StatLogger mStatLogger = new StatLogger(TAG + " stats", new String[]{ "REBATCH_ALL_ALARMS", "GET_COUNT", }); private static final Comparator<Batch> sBatchOrder = Comparator.comparingLong(b -> b.mStart); private static final Comparator<Alarm> sIncreasingTimeOrder = Comparator.comparingLong( Alarm::getWhenElapsed); @Override public void add(Alarm a) { insertAndBatchAlarm(a); mSize++; } @Override public void addAll(ArrayList<Alarm> alarms) { if (alarms == null) { return; } for (final Alarm a : alarms) { add(a); } } @Override public ArrayList<Alarm> remove(Predicate<Alarm> whichAlarms) { final ArrayList<Alarm> removed = new ArrayList<>(); for (int i = mAlarmBatches.size() - 1; i >= 0; i--) { final Batch b = mAlarmBatches.get(i); removed.addAll(b.remove(whichAlarms)); if (b.size() == 0) { mAlarmBatches.remove(i); } } if (!removed.isEmpty()) { mSize -= removed.size(); // Not needed if only whole batches were removed, but keeping existing behavior. rebatchAllAlarms(); } return removed; } @Override public void setAlarmClockRemovalListener(Runnable listener) { mOnAlarmClockRemoved = listener; } @Override public Alarm getNextWakeFromIdleAlarm() { for (final Batch batch : mAlarmBatches) { if ((batch.mFlags & AlarmManager.FLAG_WAKE_FROM_IDLE) == 0) { continue; } for (int i = 0; i < batch.size(); i++) { final Alarm a = batch.get(i); if ((a.flags & AlarmManager.FLAG_WAKE_FROM_IDLE) != 0) { return a; } } } return null; } private void rebatchAllAlarms() { final long start = mStatLogger.getTime(); final ArrayList<Batch> oldBatches = (ArrayList<Batch>) mAlarmBatches.clone(); mAlarmBatches.clear(); for (final Batch batch : oldBatches) { for (int i = 0; i < batch.size(); i++) { insertAndBatchAlarm(batch.get(i)); } } mStatLogger.logDurationStat(Stats.REBATCH_ALL_ALARMS, start); } @Override public int size() { return mSize; } @Override public long getNextWakeupDeliveryTime() { for (Batch b : mAlarmBatches) { if (b.hasWakeups()) { return b.mStart; } } return 0; } @Override public long getNextDeliveryTime() { if (mAlarmBatches.size() > 0) { return mAlarmBatches.get(0).mStart; } return 0; } @Override public ArrayList<Alarm> removePendingAlarms(long nowElapsed) { final ArrayList<Alarm> removedAlarms = new ArrayList<>(); while (mAlarmBatches.size() > 0) { final Batch batch = mAlarmBatches.get(0); if (batch.mStart > nowElapsed) { break; } mAlarmBatches.remove(0); for (int i = 0; i < batch.size(); i++) { removedAlarms.add(batch.get(i)); } } mSize -= removedAlarms.size(); return removedAlarms; } @Override public boolean updateAlarmDeliveries(AlarmDeliveryCalculator deliveryCalculator) { boolean changed = false; for (final Batch b : mAlarmBatches) { for (int i = 0; i < b.size(); i++) { changed |= deliveryCalculator.updateAlarmDelivery(b.get(i)); } } if (changed) { rebatchAllAlarms(); } return changed; } @Override public ArrayList<Alarm> asList() { final ArrayList<Alarm> allAlarms = new ArrayList<>(); for (final Batch batch : mAlarmBatches) { for (int i = 0; i < batch.size(); i++) { allAlarms.add(batch.get(i)); } } return allAlarms; } @Override public void dump(IndentingPrintWriter ipw, long nowElapsed, SimpleDateFormat sdf) { ipw.print("Pending alarm batches: "); ipw.println(mAlarmBatches.size()); for (Batch b : mAlarmBatches) { ipw.print(b); ipw.println(':'); ipw.increaseIndent(); dumpAlarmList(ipw, b.mAlarms, nowElapsed, sdf); ipw.decreaseIndent(); } mStatLogger.dump(ipw); } @Override public void dumpProto(ProtoOutputStream pos, long nowElapsed) { for (Batch b : mAlarmBatches) { b.dumpDebug(pos, AlarmManagerServiceDumpProto.PENDING_ALARM_BATCHES, nowElapsed); } } @Override public String getName() { return TAG; } @Override public int getCount(Predicate<Alarm> condition) { long start = mStatLogger.getTime(); int count = 0; for (Batch b : mAlarmBatches) { for (int i = 0; i < b.size(); i++) { if (condition.test(b.get(i))) { count++; } } } mStatLogger.logDurationStat(Stats.GET_COUNT, start); return count; } private void insertAndBatchAlarm(Alarm alarm) { final int whichBatch = ((alarm.flags & AlarmManager.FLAG_STANDALONE) != 0) ? -1 : attemptCoalesce(alarm.getWhenElapsed(), alarm.getMaxWhenElapsed()); if (whichBatch < 0) { addBatch(mAlarmBatches, new Batch(alarm)); } else { final Batch batch = mAlarmBatches.get(whichBatch); if (batch.add(alarm)) { // The start time of this batch advanced, so batch ordering may // have just been broken. Move it to where it now belongs. mAlarmBatches.remove(whichBatch); addBatch(mAlarmBatches, batch); } } } static void addBatch(ArrayList<Batch> list, Batch newBatch) { int index = Collections.binarySearch(list, newBatch, sBatchOrder); if (index < 0) { index = 0 - index - 1; } list.add(index, newBatch); } // Return the index of the matching batch, or -1 if none found. private int attemptCoalesce(long whenElapsed, long maxWhen) { final int n = mAlarmBatches.size(); for (int i = 0; i < n; i++) { Batch b = mAlarmBatches.get(i); if ((b.mFlags & AlarmManager.FLAG_STANDALONE) == 0 && b.canHold(whenElapsed, maxWhen)) { return i; } } return -1; } final class Batch { long mStart; // These endpoints are always in ELAPSED long mEnd; int mFlags; // Flags for alarms, such as FLAG_STANDALONE. final ArrayList<Alarm> mAlarms = new ArrayList<>(); Batch(Alarm seed) { mStart = seed.getWhenElapsed(); mEnd = clampPositive(seed.getMaxWhenElapsed()); mFlags = seed.flags; mAlarms.add(seed); } int size() { return mAlarms.size(); } Alarm get(int index) { return mAlarms.get(index); } boolean canHold(long whenElapsed, long maxWhen) { return (mEnd >= whenElapsed) && (mStart <= maxWhen); } boolean add(Alarm alarm) { boolean newStart = false; // narrows the batch if necessary; presumes that canHold(alarm) is true int index = Collections.binarySearch(mAlarms, alarm, sIncreasingTimeOrder); if (index < 0) { index = 0 - index - 1; } mAlarms.add(index, alarm); if (DEBUG_BATCH) { Slog.v(TAG, "Adding " + alarm + " to " + this); } if (alarm.getWhenElapsed() > mStart) { mStart = alarm.getWhenElapsed(); newStart = true; } if (alarm.getMaxWhenElapsed() < mEnd) { mEnd = alarm.getMaxWhenElapsed(); } mFlags |= alarm.flags; if (DEBUG_BATCH) { Slog.v(TAG, " => now " + this); } return newStart; } ArrayList<Alarm> remove(Predicate<Alarm> predicate) { final ArrayList<Alarm> removed = new ArrayList<>(); long newStart = 0; // recalculate endpoints as we go long newEnd = Long.MAX_VALUE; int newFlags = 0; for (int i = 0; i < mAlarms.size(); ) { Alarm alarm = mAlarms.get(i); if (predicate.test(alarm)) { removed.add(mAlarms.remove(i)); if (alarm.alarmClock != null && mOnAlarmClockRemoved != null) { mOnAlarmClockRemoved.run(); } if (isTimeTickAlarm(alarm)) { // This code path is not invoked when delivering alarms, only when removing // alarms due to the caller cancelling it or getting uninstalled, etc. Slog.wtf(TAG, "Removed TIME_TICK alarm"); } } else { if (alarm.getWhenElapsed() > newStart) { newStart = alarm.getWhenElapsed(); } if (alarm.getMaxWhenElapsed() < newEnd) { newEnd = alarm.getMaxWhenElapsed(); } newFlags |= alarm.flags; i++; } } if (!removed.isEmpty()) { // commit the new batch bounds mStart = newStart; mEnd = newEnd; mFlags = newFlags; } return removed; } boolean hasWakeups() { final int n = mAlarms.size(); for (int i = 0; i < n; i++) { Alarm a = mAlarms.get(i); if (a.wakeup) { 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(mStart); b.append(" end="); b.append(mEnd); if (mFlags != 0) { b.append(" flgs=0x"); b.append(Integer.toHexString(mFlags)); } b.append('}'); return b.toString(); } public void dumpDebug(ProtoOutputStream proto, long fieldId, long nowElapsed) { final long token = proto.start(fieldId); proto.write(BatchProto.START_REALTIME, mStart); proto.write(BatchProto.END_REALTIME, mEnd); proto.write(BatchProto.FLAGS, mFlags); for (Alarm a : mAlarms) { a.dumpDebug(proto, BatchProto.ALARMS, nowElapsed); } proto.end(token); } } } services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java +0 −32 Original line number Diff line number Diff line Loading @@ -74,7 +74,6 @@ import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_W import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION; import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_WINDOW; import static com.android.server.alarm.AlarmManagerService.Constants.KEY_EXACT_ALARM_DENY_LIST; import static com.android.server.alarm.AlarmManagerService.Constants.KEY_LAZY_BATCHING; import static com.android.server.alarm.AlarmManagerService.Constants.KEY_LISTENER_TIMEOUT; import static com.android.server.alarm.AlarmManagerService.Constants.KEY_MAX_DEVICE_IDLE_FUZZ; import static com.android.server.alarm.AlarmManagerService.Constants.KEY_MAX_INTERVAL; Loading Loading @@ -3093,37 +3092,6 @@ public class AlarmManagerServiceTest { optionsSentOnExpiration(false, null); } @Test public void alarmStoreMigration() { setDeviceConfigBoolean(KEY_LAZY_BATCHING, false); final int numAlarms = 10; final PendingIntent[] pis = new PendingIntent[numAlarms]; for (int i = 0; i < numAlarms; i++) { pis[i] = getNewMockPendingIntent(); setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + i + 1, pis[i]); } final ArrayList<Alarm> alarmsBefore = mService.mAlarmStore.asList(); assertEquals(numAlarms, alarmsBefore.size()); for (int i = 0; i < numAlarms; i++) { final PendingIntent pi = pis[i]; assertTrue(i + "th PendingIntent missing: ", alarmsBefore.removeIf(a -> a.matches(pi, null))); } assertEquals(BatchingAlarmStore.TAG, mService.mAlarmStore.getName()); setDeviceConfigBoolean(KEY_LAZY_BATCHING, true); final ArrayList<Alarm> alarmsAfter = mService.mAlarmStore.asList(); assertEquals(numAlarms, alarmsAfter.size()); for (int i = 0; i < numAlarms; i++) { final PendingIntent pi = pis[i]; assertTrue(i + "th PendingIntent missing: ", alarmsAfter.removeIf(a -> a.matches(pi, null))); } assertEquals(LazyAlarmStore.TAG, mService.mAlarmStore.getName()); } private void registerAppIds(String[] packages, Integer[] ids) { assertEquals(packages.length, ids.length); Loading services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java +0 −1 Original line number Diff line number Diff line Loading @@ -56,7 +56,6 @@ public class AlarmStoreTest { public static Object[] stores() { return new AlarmStore[]{ new LazyAlarmStore(), new BatchingAlarmStore(), }; } Loading Loading
apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java +1 −27 Original line number Diff line number Diff line Loading @@ -673,9 +673,6 @@ public class AlarmManagerService extends SystemService { private static final String KEY_APP_STANDBY_RESTRICTED_WINDOW = "app_standby_restricted_window"; @VisibleForTesting static final String KEY_LAZY_BATCHING = "lazy_batching"; private static final String KEY_TIME_TICK_ALLOWED_WHILE_IDLE = "time_tick_allowed_while_idle"; Loading Loading @@ -725,7 +722,6 @@ public class AlarmManagerService extends SystemService { private static final int DEFAULT_APP_STANDBY_RESTRICTED_QUOTA = 1; private static final long DEFAULT_APP_STANDBY_RESTRICTED_WINDOW = INTERVAL_DAY; private static final boolean DEFAULT_LAZY_BATCHING = true; private static final boolean DEFAULT_TIME_TICK_ALLOWED_WHILE_IDLE = true; /** Loading Loading @@ -772,7 +768,6 @@ public class AlarmManagerService extends SystemService { public int APP_STANDBY_RESTRICTED_QUOTA = DEFAULT_APP_STANDBY_RESTRICTED_QUOTA; public long APP_STANDBY_RESTRICTED_WINDOW = DEFAULT_APP_STANDBY_RESTRICTED_WINDOW; public boolean LAZY_BATCHING = DEFAULT_LAZY_BATCHING; public boolean TIME_TICK_ALLOWED_WHILE_IDLE = DEFAULT_TIME_TICK_ALLOWED_WHILE_IDLE; public int ALLOW_WHILE_IDLE_QUOTA = DEFAULT_ALLOW_WHILE_IDLE_QUOTA; Loading Loading @@ -976,14 +971,6 @@ public class AlarmManagerService extends SystemService { case KEY_APP_STANDBY_RESTRICTED_WINDOW: updateStandbyWindowsLocked(); break; case KEY_LAZY_BATCHING: final boolean oldLazyBatching = LAZY_BATCHING; LAZY_BATCHING = properties.getBoolean( KEY_LAZY_BATCHING, DEFAULT_LAZY_BATCHING); if (oldLazyBatching != LAZY_BATCHING) { migrateAlarmsToNewStoreLocked(); } break; case KEY_TIME_TICK_ALLOWED_WHILE_IDLE: TIME_TICK_ALLOWED_WHILE_IDLE = properties.getBoolean( KEY_TIME_TICK_ALLOWED_WHILE_IDLE, Loading Loading @@ -1094,15 +1081,6 @@ public class AlarmManagerService extends SystemService { } } private void migrateAlarmsToNewStoreLocked() { final AlarmStore newStore = LAZY_BATCHING ? new LazyAlarmStore() : new BatchingAlarmStore(); final ArrayList<Alarm> allAlarms = mAlarmStore.remove((unused) -> true); newStore.addAll(allAlarms); mAlarmStore = newStore; mAlarmStore.setAlarmClockRemovalListener(mAlarmClockUpdater); } private void updateDeviceIdleFuzzBoundaries() { final DeviceConfig.Properties properties = DeviceConfig.getProperties( DeviceConfig.NAMESPACE_ALARM_MANAGER, Loading Loading @@ -1240,9 +1218,6 @@ public class AlarmManagerService extends SystemService { TimeUtils.formatDuration(APP_STANDBY_RESTRICTED_WINDOW, pw); pw.println(); pw.print(KEY_LAZY_BATCHING, LAZY_BATCHING); pw.println(); pw.print(KEY_TIME_TICK_ALLOWED_WHILE_IDLE, TIME_TICK_ALLOWED_WHILE_IDLE); pw.println(); Loading Loading @@ -1878,8 +1853,7 @@ public class AlarmManagerService extends SystemService { mHandler = new AlarmHandler(); mConstants = new Constants(mHandler); mAlarmStore = mConstants.LAZY_BATCHING ? new LazyAlarmStore() : new BatchingAlarmStore(); mAlarmStore = new LazyAlarmStore(); mAlarmStore.setAlarmClockRemovalListener(mAlarmClockUpdater); mAppWakeupHistory = new AppWakeupHistory(Constants.DEFAULT_APP_STANDBY_WINDOW); Loading
apex/jobscheduler/service/java/com/android/server/alarm/BatchingAlarmStore.javadeleted 100644 → 0 +0 −408 Original line number Diff line number Diff line /* * Copyright (C) 2020 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.android.server.alarm; import static com.android.server.alarm.AlarmManagerService.DEBUG_BATCH; import static com.android.server.alarm.AlarmManagerService.clampPositive; import static com.android.server.alarm.AlarmManagerService.dumpAlarmList; import static com.android.server.alarm.AlarmManagerService.isTimeTickAlarm; import android.app.AlarmManager; import android.util.IndentingPrintWriter; import android.util.Slog; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.StatLogger; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.function.Predicate; /** * Batching implementation of an Alarm Store. * This keeps the alarms in batches, which are sorted on the start time of their delivery window. */ public class BatchingAlarmStore implements AlarmStore { @VisibleForTesting static final String TAG = BatchingAlarmStore.class.getSimpleName(); private final ArrayList<Batch> mAlarmBatches = new ArrayList<>(); private int mSize; private Runnable mOnAlarmClockRemoved; interface Stats { int REBATCH_ALL_ALARMS = 0; int GET_COUNT = 1; } final StatLogger mStatLogger = new StatLogger(TAG + " stats", new String[]{ "REBATCH_ALL_ALARMS", "GET_COUNT", }); private static final Comparator<Batch> sBatchOrder = Comparator.comparingLong(b -> b.mStart); private static final Comparator<Alarm> sIncreasingTimeOrder = Comparator.comparingLong( Alarm::getWhenElapsed); @Override public void add(Alarm a) { insertAndBatchAlarm(a); mSize++; } @Override public void addAll(ArrayList<Alarm> alarms) { if (alarms == null) { return; } for (final Alarm a : alarms) { add(a); } } @Override public ArrayList<Alarm> remove(Predicate<Alarm> whichAlarms) { final ArrayList<Alarm> removed = new ArrayList<>(); for (int i = mAlarmBatches.size() - 1; i >= 0; i--) { final Batch b = mAlarmBatches.get(i); removed.addAll(b.remove(whichAlarms)); if (b.size() == 0) { mAlarmBatches.remove(i); } } if (!removed.isEmpty()) { mSize -= removed.size(); // Not needed if only whole batches were removed, but keeping existing behavior. rebatchAllAlarms(); } return removed; } @Override public void setAlarmClockRemovalListener(Runnable listener) { mOnAlarmClockRemoved = listener; } @Override public Alarm getNextWakeFromIdleAlarm() { for (final Batch batch : mAlarmBatches) { if ((batch.mFlags & AlarmManager.FLAG_WAKE_FROM_IDLE) == 0) { continue; } for (int i = 0; i < batch.size(); i++) { final Alarm a = batch.get(i); if ((a.flags & AlarmManager.FLAG_WAKE_FROM_IDLE) != 0) { return a; } } } return null; } private void rebatchAllAlarms() { final long start = mStatLogger.getTime(); final ArrayList<Batch> oldBatches = (ArrayList<Batch>) mAlarmBatches.clone(); mAlarmBatches.clear(); for (final Batch batch : oldBatches) { for (int i = 0; i < batch.size(); i++) { insertAndBatchAlarm(batch.get(i)); } } mStatLogger.logDurationStat(Stats.REBATCH_ALL_ALARMS, start); } @Override public int size() { return mSize; } @Override public long getNextWakeupDeliveryTime() { for (Batch b : mAlarmBatches) { if (b.hasWakeups()) { return b.mStart; } } return 0; } @Override public long getNextDeliveryTime() { if (mAlarmBatches.size() > 0) { return mAlarmBatches.get(0).mStart; } return 0; } @Override public ArrayList<Alarm> removePendingAlarms(long nowElapsed) { final ArrayList<Alarm> removedAlarms = new ArrayList<>(); while (mAlarmBatches.size() > 0) { final Batch batch = mAlarmBatches.get(0); if (batch.mStart > nowElapsed) { break; } mAlarmBatches.remove(0); for (int i = 0; i < batch.size(); i++) { removedAlarms.add(batch.get(i)); } } mSize -= removedAlarms.size(); return removedAlarms; } @Override public boolean updateAlarmDeliveries(AlarmDeliveryCalculator deliveryCalculator) { boolean changed = false; for (final Batch b : mAlarmBatches) { for (int i = 0; i < b.size(); i++) { changed |= deliveryCalculator.updateAlarmDelivery(b.get(i)); } } if (changed) { rebatchAllAlarms(); } return changed; } @Override public ArrayList<Alarm> asList() { final ArrayList<Alarm> allAlarms = new ArrayList<>(); for (final Batch batch : mAlarmBatches) { for (int i = 0; i < batch.size(); i++) { allAlarms.add(batch.get(i)); } } return allAlarms; } @Override public void dump(IndentingPrintWriter ipw, long nowElapsed, SimpleDateFormat sdf) { ipw.print("Pending alarm batches: "); ipw.println(mAlarmBatches.size()); for (Batch b : mAlarmBatches) { ipw.print(b); ipw.println(':'); ipw.increaseIndent(); dumpAlarmList(ipw, b.mAlarms, nowElapsed, sdf); ipw.decreaseIndent(); } mStatLogger.dump(ipw); } @Override public void dumpProto(ProtoOutputStream pos, long nowElapsed) { for (Batch b : mAlarmBatches) { b.dumpDebug(pos, AlarmManagerServiceDumpProto.PENDING_ALARM_BATCHES, nowElapsed); } } @Override public String getName() { return TAG; } @Override public int getCount(Predicate<Alarm> condition) { long start = mStatLogger.getTime(); int count = 0; for (Batch b : mAlarmBatches) { for (int i = 0; i < b.size(); i++) { if (condition.test(b.get(i))) { count++; } } } mStatLogger.logDurationStat(Stats.GET_COUNT, start); return count; } private void insertAndBatchAlarm(Alarm alarm) { final int whichBatch = ((alarm.flags & AlarmManager.FLAG_STANDALONE) != 0) ? -1 : attemptCoalesce(alarm.getWhenElapsed(), alarm.getMaxWhenElapsed()); if (whichBatch < 0) { addBatch(mAlarmBatches, new Batch(alarm)); } else { final Batch batch = mAlarmBatches.get(whichBatch); if (batch.add(alarm)) { // The start time of this batch advanced, so batch ordering may // have just been broken. Move it to where it now belongs. mAlarmBatches.remove(whichBatch); addBatch(mAlarmBatches, batch); } } } static void addBatch(ArrayList<Batch> list, Batch newBatch) { int index = Collections.binarySearch(list, newBatch, sBatchOrder); if (index < 0) { index = 0 - index - 1; } list.add(index, newBatch); } // Return the index of the matching batch, or -1 if none found. private int attemptCoalesce(long whenElapsed, long maxWhen) { final int n = mAlarmBatches.size(); for (int i = 0; i < n; i++) { Batch b = mAlarmBatches.get(i); if ((b.mFlags & AlarmManager.FLAG_STANDALONE) == 0 && b.canHold(whenElapsed, maxWhen)) { return i; } } return -1; } final class Batch { long mStart; // These endpoints are always in ELAPSED long mEnd; int mFlags; // Flags for alarms, such as FLAG_STANDALONE. final ArrayList<Alarm> mAlarms = new ArrayList<>(); Batch(Alarm seed) { mStart = seed.getWhenElapsed(); mEnd = clampPositive(seed.getMaxWhenElapsed()); mFlags = seed.flags; mAlarms.add(seed); } int size() { return mAlarms.size(); } Alarm get(int index) { return mAlarms.get(index); } boolean canHold(long whenElapsed, long maxWhen) { return (mEnd >= whenElapsed) && (mStart <= maxWhen); } boolean add(Alarm alarm) { boolean newStart = false; // narrows the batch if necessary; presumes that canHold(alarm) is true int index = Collections.binarySearch(mAlarms, alarm, sIncreasingTimeOrder); if (index < 0) { index = 0 - index - 1; } mAlarms.add(index, alarm); if (DEBUG_BATCH) { Slog.v(TAG, "Adding " + alarm + " to " + this); } if (alarm.getWhenElapsed() > mStart) { mStart = alarm.getWhenElapsed(); newStart = true; } if (alarm.getMaxWhenElapsed() < mEnd) { mEnd = alarm.getMaxWhenElapsed(); } mFlags |= alarm.flags; if (DEBUG_BATCH) { Slog.v(TAG, " => now " + this); } return newStart; } ArrayList<Alarm> remove(Predicate<Alarm> predicate) { final ArrayList<Alarm> removed = new ArrayList<>(); long newStart = 0; // recalculate endpoints as we go long newEnd = Long.MAX_VALUE; int newFlags = 0; for (int i = 0; i < mAlarms.size(); ) { Alarm alarm = mAlarms.get(i); if (predicate.test(alarm)) { removed.add(mAlarms.remove(i)); if (alarm.alarmClock != null && mOnAlarmClockRemoved != null) { mOnAlarmClockRemoved.run(); } if (isTimeTickAlarm(alarm)) { // This code path is not invoked when delivering alarms, only when removing // alarms due to the caller cancelling it or getting uninstalled, etc. Slog.wtf(TAG, "Removed TIME_TICK alarm"); } } else { if (alarm.getWhenElapsed() > newStart) { newStart = alarm.getWhenElapsed(); } if (alarm.getMaxWhenElapsed() < newEnd) { newEnd = alarm.getMaxWhenElapsed(); } newFlags |= alarm.flags; i++; } } if (!removed.isEmpty()) { // commit the new batch bounds mStart = newStart; mEnd = newEnd; mFlags = newFlags; } return removed; } boolean hasWakeups() { final int n = mAlarms.size(); for (int i = 0; i < n; i++) { Alarm a = mAlarms.get(i); if (a.wakeup) { 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(mStart); b.append(" end="); b.append(mEnd); if (mFlags != 0) { b.append(" flgs=0x"); b.append(Integer.toHexString(mFlags)); } b.append('}'); return b.toString(); } public void dumpDebug(ProtoOutputStream proto, long fieldId, long nowElapsed) { final long token = proto.start(fieldId); proto.write(BatchProto.START_REALTIME, mStart); proto.write(BatchProto.END_REALTIME, mEnd); proto.write(BatchProto.FLAGS, mFlags); for (Alarm a : mAlarms) { a.dumpDebug(proto, BatchProto.ALARMS, nowElapsed); } proto.end(token); } } }
services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java +0 −32 Original line number Diff line number Diff line Loading @@ -74,7 +74,6 @@ import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_W import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION; import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_WINDOW; import static com.android.server.alarm.AlarmManagerService.Constants.KEY_EXACT_ALARM_DENY_LIST; import static com.android.server.alarm.AlarmManagerService.Constants.KEY_LAZY_BATCHING; import static com.android.server.alarm.AlarmManagerService.Constants.KEY_LISTENER_TIMEOUT; import static com.android.server.alarm.AlarmManagerService.Constants.KEY_MAX_DEVICE_IDLE_FUZZ; import static com.android.server.alarm.AlarmManagerService.Constants.KEY_MAX_INTERVAL; Loading Loading @@ -3093,37 +3092,6 @@ public class AlarmManagerServiceTest { optionsSentOnExpiration(false, null); } @Test public void alarmStoreMigration() { setDeviceConfigBoolean(KEY_LAZY_BATCHING, false); final int numAlarms = 10; final PendingIntent[] pis = new PendingIntent[numAlarms]; for (int i = 0; i < numAlarms; i++) { pis[i] = getNewMockPendingIntent(); setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + i + 1, pis[i]); } final ArrayList<Alarm> alarmsBefore = mService.mAlarmStore.asList(); assertEquals(numAlarms, alarmsBefore.size()); for (int i = 0; i < numAlarms; i++) { final PendingIntent pi = pis[i]; assertTrue(i + "th PendingIntent missing: ", alarmsBefore.removeIf(a -> a.matches(pi, null))); } assertEquals(BatchingAlarmStore.TAG, mService.mAlarmStore.getName()); setDeviceConfigBoolean(KEY_LAZY_BATCHING, true); final ArrayList<Alarm> alarmsAfter = mService.mAlarmStore.asList(); assertEquals(numAlarms, alarmsAfter.size()); for (int i = 0; i < numAlarms; i++) { final PendingIntent pi = pis[i]; assertTrue(i + "th PendingIntent missing: ", alarmsAfter.removeIf(a -> a.matches(pi, null))); } assertEquals(LazyAlarmStore.TAG, mService.mAlarmStore.getName()); } private void registerAppIds(String[] packages, Integer[] ids) { assertEquals(packages.length, ids.length); Loading
services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java +0 −1 Original line number Diff line number Diff line Loading @@ -56,7 +56,6 @@ public class AlarmStoreTest { public static Object[] stores() { return new AlarmStore[]{ new LazyAlarmStore(), new BatchingAlarmStore(), }; } Loading