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

Commit 5502b894 authored by Adrian Roos's avatar Adrian Roos
Browse files

AOD: Fix crash when AOD is stopped immediately after starting

Fixes an issue where the screen state was set after DozeService was already
destroyed, causing a crash.

Change-Id: I13ff590b62f905330ccb438692410e118b76c242
Fixes: 64907936
Test: runtest -x $ANDROID_BUILD_TOP/frameworks/base/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
parent dd09e994
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -36,10 +36,21 @@ public class DozeScreenState implements DozeMachine.Part {
    @Override
    public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) {
        int screenState = newState.screenState();

        if (newState == DozeMachine.State.FINISH) {
            // Make sure not to apply the screen state after DozeService was destroyed.
            mPendingScreenState = Display.STATE_UNKNOWN;
            mHandler.removeCallbacks(mApplyPendingScreenState);

            applyScreenState(screenState);
            return;
        }

        if (screenState == Display.STATE_UNKNOWN) {
            // We'll keep it in the existing state
            return;
        }

        boolean messagePending = mHandler.hasCallbacks(mApplyPendingScreenState);
        if (messagePending || oldState == DozeMachine.State.INITIALIZED) {
            // During initialization, we hide the navigation bar. That is however only applied after
+32 −26
Original line number Diff line number Diff line
@@ -20,23 +20,22 @@ import static com.android.systemui.doze.DozeMachine.State.DOZE;
import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD;
import static com.android.systemui.doze.DozeMachine.State.DOZE_PULSING;
import static com.android.systemui.doze.DozeMachine.State.DOZE_REQUEST_PULSE;
import static com.android.systemui.doze.DozeMachine.State.FINISH;
import static com.android.systemui.doze.DozeMachine.State.INITIALIZED;
import static com.android.systemui.doze.DozeMachine.State.UNINITIALIZED;
import static com.android.systemui.utils.os.FakeHandler.Mode.QUEUEING;

import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.view.Display;

import com.android.systemui.SysuiTestCase;
import com.android.systemui.utils.os.FakeHandler;

import org.junit.Before;
import org.junit.Test;
@@ -48,13 +47,13 @@ public class DozeScreenStateTest extends SysuiTestCase {

    DozeServiceFake mServiceFake;
    DozeScreenState mScreen;
    private ImmediateHandler mHandler;
    FakeHandler mHandlerFake;

    @Before
    public void setUp() throws Exception {
        mServiceFake = new DozeServiceFake();
        mHandler = spy(new ImmediateHandler(Looper.getMainLooper()));
        mScreen = new DozeScreenState(mServiceFake, mHandler);
        mHandlerFake = new FakeHandler(Looper.getMainLooper());
        mScreen = new DozeScreenState(mServiceFake, mHandlerFake);
    }

    @Test
@@ -105,27 +104,34 @@ public class DozeScreenStateTest extends SysuiTestCase {
    }

    @Test
    public void test_postedToHandler() {
    public void test_initialScreenStatePostedToHandler() {
        mHandlerFake.setMode(QUEUEING);

        mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
        mServiceFake.screenStateSet = false;
        mScreen.transitionTo(INITIALIZED, DOZE_AOD);

        verify(mHandler).sendMessageAtTime(any(), anyLong());
    }
        assertFalse(mServiceFake.screenStateSet);

    private static class ImmediateHandler extends Handler {
        mHandlerFake.dispatchQueuedMessages();

        public ImmediateHandler(Looper looper) {
            super(looper);
        assertTrue(mServiceFake.screenStateSet);
        assertEquals(Display.STATE_DOZE_SUSPEND, mServiceFake.screenState);
    }

        @Override
        public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
            Runnable callback = msg.getCallback();
            if (callback != null) {
                callback.run();
                return false;
            }
            return super.sendMessageAtTime(msg, uptimeMillis);
        }
    @Test
    public void test_noScreenStateSetAfterFinish() {
        mHandlerFake.setMode(QUEUEING);

        mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
        mScreen.transitionTo(INITIALIZED, DOZE_AOD);
        mScreen.transitionTo(DOZE_AOD, FINISH);

        mServiceFake.screenStateSet = false;

        mHandlerFake.dispatchQueuedMessages();

        assertFalse(mServiceFake.screenStateSet);
    }

}
 No newline at end of file
+9 −0
Original line number Diff line number Diff line
@@ -19,10 +19,16 @@ package com.android.systemui.doze;
import android.os.PowerManager;
import android.view.Display;

/**
 * Fake implementation of {@link DozeMachine.Service} for tests.
 *
 * Useful instead of mocking because it allows verifying state instead of interactions.
 */
public class DozeServiceFake implements DozeMachine.Service {

    public boolean finished;
    public int screenState;
    public boolean screenStateSet;
    public boolean requestedWakeup;
    public int screenBrightness;

@@ -38,6 +44,7 @@ public class DozeServiceFake implements DozeMachine.Service {
    @Override
    public void setDozeScreenState(int state) {
        screenState = state;
        screenStateSet = true;
    }

    @Override
@@ -53,6 +60,8 @@ public class DozeServiceFake implements DozeMachine.Service {
    public void reset() {
        finished = false;
        screenState = Display.STATE_UNKNOWN;
        screenStateSet = false;
        requestedWakeup = false;
        screenBrightness = PowerManager.BRIGHTNESS_DEFAULT;
    }
}
+71 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 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.systemui.utils.os;

import android.os.Handler;
import android.os.Looper;
import android.os.Message;

import java.util.ArrayList;

/**
 * A handler that allows control over when to dispatch messages and callbacks.
 *
 * WARNING: Because most Handler methods are final, the only thing this handler can intercept
 *          are sending messages and posting runnables, but *NOT* removing messages nor runnables.
 *          It also *CANNOT* intercept messages posted to the front of queue.
 */
public class FakeHandler extends Handler {

    private Mode mMode = Mode.IMMEDIATE;
    private ArrayList<Message> mQueuedMessages = new ArrayList<>();

    public FakeHandler(Looper looper) {
        super(looper);
    }

    @Override
    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        mQueuedMessages.add(msg);
        if (mMode == Mode.IMMEDIATE) {
            dispatchQueuedMessages();
        }
        return true;
    }

    public void setMode(Mode mode) {
        mMode = mode;
    }

    /**
     * Dispatch any messages that have been queued on the calling thread.
     */
    public void dispatchQueuedMessages() {
        ArrayList<Message> messages = new ArrayList<>(mQueuedMessages);
        mQueuedMessages.clear();
        for (Message msg : messages) {
            dispatchMessage(msg);
        }
    }

    public enum Mode {
        /** Messages are dispatched immediately on the calling thread. */
        IMMEDIATE,
        /** Messages are queued until {@link #dispatchQueuedMessages()} is called. */
        QUEUEING,
    }
}