diff options
3 files changed, 55 insertions, 5 deletions
diff --git a/apex/jobscheduler/service/aconfig/alarm.aconfig b/apex/jobscheduler/service/aconfig/alarm.aconfig index a6e980726a9a..9181ef0c532a 100644 --- a/apex/jobscheduler/service/aconfig/alarm.aconfig +++ b/apex/jobscheduler/service/aconfig/alarm.aconfig @@ -7,3 +7,13 @@ flag { description: "Persist list of users with alarms scheduled and wakeup stopped users before alarms are due" bug: "314907186" } + +flag { + name: "acquire_wakelock_before_send" + namespace: "backstage_power" + description: "Acquire the userspace alarm wakelock before sending the alarm" + bug: "391413964" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java index 829442aed6ac..f89b13dce307 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java @@ -5334,6 +5334,18 @@ public class AlarmManagerService extends SystemService { public void deliverLocked(Alarm alarm, long nowELAPSED) { final long workSourceToken = ThreadLocalWorkSource.setUid( getAlarmAttributionUid(alarm)); + + if (Flags.acquireWakelockBeforeSend()) { + // Acquire the wakelock before starting the app. This needs to be done to avoid + // random stalls in the receiving app in case a suspend attempt is already in + // progress. See b/391413964 for an incident where this was found to happen. + if (mBroadcastRefCount == 0) { + setWakelockWorkSource(alarm.workSource, alarm.creatorUid, alarm.statsTag, true); + mWakeLock.acquire(); + mHandler.obtainMessage(AlarmHandler.REPORT_ALARMS_ACTIVE, 1, 0).sendToTarget(); + } + } + try { if (alarm.operation != null) { // PendingIntent alarm @@ -5399,14 +5411,16 @@ public class AlarmManagerService extends SystemService { ThreadLocalWorkSource.restore(workSourceToken); } - // The alarm is now in flight; now arrange wakelock and stats tracking if (DEBUG_WAKELOCK) { Slog.d(TAG, "mBroadcastRefCount -> " + (mBroadcastRefCount + 1)); } - if (mBroadcastRefCount == 0) { - setWakelockWorkSource(alarm.workSource, alarm.creatorUid, alarm.statsTag, true); - mWakeLock.acquire(); - mHandler.obtainMessage(AlarmHandler.REPORT_ALARMS_ACTIVE, 1, 0).sendToTarget(); + if (!Flags.acquireWakelockBeforeSend()) { + // The alarm is now in flight; now arrange wakelock and stats tracking + if (mBroadcastRefCount == 0) { + setWakelockWorkSource(alarm.workSource, alarm.creatorUid, alarm.statsTag, true); + mWakeLock.acquire(); + mHandler.obtainMessage(AlarmHandler.REPORT_ALARMS_ACTIVE, 1, 0).sendToTarget(); + } } final InFlight inflight = new InFlight(AlarmManagerService.this, alarm, nowELAPSED); mInFlight.add(inflight); diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java index 8dc8c14f8948..cb52f1849b5b 100644 --- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java @@ -151,6 +151,7 @@ import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.Presubmit; import android.platform.test.flag.junit.SetFlagsRule; import android.platform.test.flag.util.FlagSetException; @@ -192,7 +193,9 @@ import org.junit.runner.RunWith; import org.mockito.Answers; import org.mockito.ArgumentCaptor; import org.mockito.ArgumentMatchers; +import org.mockito.InOrder; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.quality.Strictness; import org.mockito.stubbing.Answer; @@ -435,6 +438,7 @@ public final class AlarmManagerServiceTest { private void disableFlagsNotSetByAnnotation() { try { mSetFlagsRule.disableFlags(Flags.FLAG_START_USER_BEFORE_SCHEDULED_ALARMS); + mSetFlagsRule.disableFlags(Flags.FLAG_ACQUIRE_WAKELOCK_BEFORE_SEND); } catch (FlagSetException fse) { // Expected if the test about to be run requires this enabled. } @@ -948,6 +952,28 @@ public final class AlarmManagerServiceTest { } @Test + @EnableFlags(Flags.FLAG_ACQUIRE_WAKELOCK_BEFORE_SEND) + public void testWakelockOrdering() throws Exception { + final long triggerTime = mNowElapsedTest + 5000; + final PendingIntent alarmPi = getNewMockPendingIntent(); + setTestAlarm(ELAPSED_REALTIME_WAKEUP, triggerTime, alarmPi); + + mNowElapsedTest = mTestTimer.getElapsed(); + mTestTimer.expire(); + + final InOrder inOrder = Mockito.inOrder(alarmPi, mWakeLock); + inOrder.verify(mWakeLock).acquire(); + + final ArgumentCaptor<PendingIntent.OnFinished> onFinishedCaptor = + ArgumentCaptor.forClass(PendingIntent.OnFinished.class); + inOrder.verify(alarmPi).send(eq(mMockContext), eq(0), any(Intent.class), + onFinishedCaptor.capture(), any(Handler.class), isNull(), any()); + onFinishedCaptor.getValue().onSendFinished(alarmPi, null, 0, null, null); + + inOrder.verify(mWakeLock).release(); + } + + @Test public void testMinFuturityCoreUid() { setDeviceConfigLong(KEY_MIN_FUTURITY, 10L); assertEquals(10, mService.mConstants.MIN_FUTURITY); |