Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6714f64f authored by Suprabh Shukla's avatar Suprabh Shukla
Browse files

Acquire wakelock before alarm dispatch

This is required to avoid stalls in the app if a suspend operation is
already under-way for some reason. The expectation is that the userspace
wakelock acquisition will either block or succeed, in both cases
guaranteeing that the app executes continously while processing the
alarm.

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

Flag: com.android.server.alarm.acquire_wakelock_before_send

Bug: 391413964
Change-Id: I56a0739a60aa58139d40e8d64523328d5430b6ec
parent 39d38ede
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -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
    }
}
+19 −5
Original line number Diff line number Diff line
@@ -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,15 +5411,17 @@ 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 (!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);
            mBroadcastRefCount++;
+26 −0
Original line number Diff line number Diff line
@@ -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.
        }
@@ -947,6 +951,28 @@ public final class AlarmManagerServiceTest {
        assertEquals(expectedTriggerTime, mTestTimer.getElapsed());
    }

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