Copy the list before passing to deliverAlarms

Downstream code from deliverAlarmsLocked can cause removeLocked or
removeImpl to be called which changes the size of the list.

Test: atest FrameworksMockingServicesTests:com.android.server.alarm

Bug: 175701084
Change-Id: I5228c323bb9698864c467e9e4c400459ca404b3c
Merged-In: I5228c323bb9698864c467e9e4c400459ca404b3c
This commit is contained in:
Suprabh Shukla
2020-12-22 13:14:12 -08:00
parent 2e3b8fefa2
commit 01b2e17cbd
2 changed files with 35 additions and 1 deletions

View File

@@ -3369,7 +3369,8 @@ class AlarmManagerService extends SystemService {
if (mMaxDelayTime < thisDelayTime) {
mMaxDelayTime = thisDelayTime;
}
deliverAlarmsLocked(mPendingNonWakeupAlarms, nowELAPSED);
final ArrayList<Alarm> triggerList = new ArrayList<>(mPendingNonWakeupAlarms);
deliverAlarmsLocked(triggerList, nowELAPSED);
mPendingNonWakeupAlarms.clear();
}
if (mNonInteractiveStartTime > 0) {

View File

@@ -100,6 +100,7 @@ import org.mockito.MockitoSession;
import org.mockito.quality.Strictness;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicInteger;
@Presubmit
@RunWith(AndroidJUnit4.class)
@@ -1077,6 +1078,38 @@ public class AlarmManagerServiceTest {
}
}
/**
* This tests that all non wakeup alarms are sent even when the mPendingNonWakeupAlarms gets
* modified before the send is complete. This avoids bugs like b/175701084.
*/
@Test
public void allNonWakeupAlarmsSentAtomically() throws Exception {
final int numAlarms = 5;
final AtomicInteger alarmsFired = new AtomicInteger(0);
for (int i = 0; i < numAlarms; i++) {
final IAlarmListener listener = new IAlarmListener.Stub() {
@Override
public void doAlarm(IAlarmCompleteListener callback) throws RemoteException {
alarmsFired.incrementAndGet();
mService.mPendingNonWakeupAlarms.clear();
}
};
setTestAlarmWithListener(ELAPSED_REALTIME, mNowElapsedTest + i + 5, listener);
}
doReturn(true).when(mService).checkAllowNonWakeupDelayLocked(anyLong());
// Advance time past all expirations.
mNowElapsedTest += numAlarms + 5;
mTestTimer.expire();
assertEquals(numAlarms, mService.mPendingNonWakeupAlarms.size());
// All of these alarms should be sent on interactive state change to true
mService.interactiveStateChangedLocked(false);
mService.interactiveStateChangedLocked(true);
assertEquals(numAlarms, alarmsFired.get());
assertEquals(0, mService.mPendingNonWakeupAlarms.size());
}
@Test
public void alarmCountOnPendingNonWakeupAlarmsRemoved() throws Exception {
final int numAlarms = 10;