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

Commit e5bdecea authored by Wink Saville's avatar Wink Saville Committed by Android (Google) Code Review
Browse files

Merge "Allow transitionTo in enter/exit."

parents 42a9c537 e7be6a85
Loading
Loading
Loading
Loading
+5 −4
Original line number Diff line number Diff line
@@ -21,10 +21,9 @@ import android.os.Message;
/**
 * {@hide}
 *
 * The abstract class for implementing states in a
 * HierarchicalStateMachine and HandlerStateMachine.
 * The class for implementing states in a HierarchicalStateMachine
 */
public abstract class HierarchicalState {
public class HierarchicalState {

    /**
     * Constructor
@@ -54,7 +53,9 @@ public abstract class HierarchicalState {
     *         if the parent state's processMessage should
     *         be invoked.
     */
    abstract protected boolean processMessage(Message msg);
    protected boolean processMessage(Message msg) {
        return false;
    }

    /**
     * Called when a state is exited.
+43 −15
Original line number Diff line number Diff line
@@ -574,23 +574,41 @@ public class HierarchicalStateMachine {
            }

            /**
             * Process the message abiding by the hierarchical semantics.
             * Process the message abiding by the hierarchical semantics
             * and perform any requested transitions.
             */
            processMsg(msg);
            performTransitions();

            if (mDbg) Log.d(TAG, "handleMessage: X");
        }

        /**
         * Do any transitions
         */
        private void performTransitions() {
            /**
             * If transitionTo has been called, exit and then enter
             * the appropriate states.
             * the appropriate states. We loop on this to allow
             * enter and exit methods to use transitionTo.
             */
            if (mDestState != null) {
            HierarchicalState destState = null;
            while (mDestState != null) {
                if (mDbg) Log.d(TAG, "handleMessage: new destination call exit");

                /**
                 * Save mDestState locally and set to null
                 * to know if enter/exit use transitionTo.
                 */
                destState = mDestState;
                mDestState = null;

                /**
                 * Determine the states to exit and enter and return the
                 * common ancestor state of the enter/exit states. Then
                 * invoke the exit methods then the enter methods.
                 */
                StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(mDestState);
                StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState);
                invokeExitMethods(commonStateInfo);
                int stateStackEnteringIndex = moveTempStateStackToStateStack();
                invokeEnterMethods(stateStackEnteringIndex);
@@ -603,25 +621,31 @@ public class HierarchicalStateMachine {
                 * message queue.
                 */
                moveDeferredMessageAtFrontOfQueue();
            }

            /**
                 * Call halting() if we've transitioned to the halting
                 * state. All subsequent messages will be processed in
                 * in the halting state which invokes haltedProcessMessage(msg);
             * After processing all transitions check and
             * see if the last transition was to quit or halt.
             */
            if (destState != null) {
                if (destState == mQuittingState) {
                    /**
                     * We are quitting so ignore all messages.
                     */
                if (mDestState == mQuittingState) {
                    mHsm.quitting();
                    if (mHsm.mHsmThread != null) {
                        // If we made the thread then quit looper
                        getLooper().quit();
                    }
                } else if (mDestState == mHaltingState) {
                } else if (destState == mHaltingState) {
                    /**
                     * Call halting() if we've transitioned to the halting
                     * state. All subsequent messages will be processed in
                     * in the halting state which invokes haltedProcessMessage(msg);
                     */
                    mHsm.halting();
                }
                mDestState = null;
            }

            if (mDbg) Log.d(TAG, "handleMessage: X");
        }

        /**
@@ -657,6 +681,11 @@ public class HierarchicalStateMachine {
            mIsConstructionCompleted = true;
            invokeEnterMethods(0);

            /**
             * Perform any transitions requested by the enter methods
             */
            performTransitions();

            if (mDbg) Log.d(TAG, "completeConstruction: X");
        }

@@ -1167,7 +1196,6 @@ public class HierarchicalStateMachine {
        return Message.obtain(mHsmHandler, what, obj);
    }


    /**
     * Enqueue a message to this state machine.
     */
+149 −1
Original line number Diff line number Diff line
@@ -48,7 +48,7 @@ public class HierarchicalStateMachineTest extends TestCase {
    private static final int TEST_CMD_6 = 6;

    private static final boolean DBG = true;
    private static final boolean WAIT_FOR_DEBUGGER = false;
    private static final boolean WAIT_FOR_DEBUGGER = true;
    private static final String TAG = "HierarchicalStateMachineTest";

    /**
@@ -151,6 +151,154 @@ public class HierarchicalStateMachineTest extends TestCase {
        if (smQuitTest.isDbg()) Log.d(TAG, "testStateMachineQuitTest X");
    }

    /**
     * Test enter/exit can use transitionTo
     */
    class StateMachineEnterExitTransitionToTest extends HierarchicalStateMachine {
        StateMachineEnterExitTransitionToTest(String name) {
            super(name);
            mThisSm = this;
            setDbg(DBG);

            // Setup state machine with 1 state
            addState(mS1);
            addState(mS2);
            addState(mS3);
            addState(mS4);

            // Set the initial state
            setInitialState(mS1);
        }

        class S1 extends HierarchicalState {
            @Override protected void enter() {
                // Test that a transition in enter and the initial state works
                mS1EnterCount += 1;
                transitionTo(mS2);
                Log.d(TAG, "S1.enter");
            }
            @Override protected void exit() {
                mS1ExitCount += 1;
                Log.d(TAG, "S1.exit");
            }
        }

        class S2 extends HierarchicalState {
            @Override protected void enter() {
                mS2EnterCount += 1;
                Log.d(TAG, "S2.enter");
            }
            @Override protected void exit() {
                // Test transition in exit work
                mS2ExitCount += 1;
                transitionTo(mS4);
                Log.d(TAG, "S2.exit");
            }
            @Override protected boolean processMessage(Message message) {
                // Start a transition to S3 but it will be
                // changed to a transition to S4
                transitionTo(mS3);
                Log.d(TAG, "S2.processMessage");
                return true;
            }
        }

        class S3 extends HierarchicalState {
            @Override protected void enter() {
                // Test that we can do halting in an enter/exit
                transitionToHaltingState();
                mS3EnterCount += 1;
                Log.d(TAG, "S3.enter");
            }
            @Override protected void exit() {
                mS3ExitCount += 1;
                Log.d(TAG, "S3.exit");
            }
        }


        class S4 extends HierarchicalState {
            @Override protected void enter() {
                // Test that we can do halting in an enter/exit
                transitionToHaltingState();
                mS4EnterCount += 1;
                Log.d(TAG, "S4.enter");
            }
            @Override protected void exit() {
                mS4ExitCount += 1;
                Log.d(TAG, "S4.exit");
            }
        }

        @Override
        protected void halting() {
            synchronized (mThisSm) {
                mThisSm.notifyAll();
            }
        }

        private StateMachineEnterExitTransitionToTest mThisSm;
        private S1 mS1 = new S1();
        private S2 mS2 = new S2();
        private S3 mS3 = new S3();
        private S4 mS4 = new S4();
        private int mS1EnterCount = 0;
        private int mS1ExitCount = 0;
        private int mS2EnterCount = 0;
        private int mS2ExitCount = 0;
        private int mS3EnterCount = 0;
        private int mS3ExitCount = 0;
        private int mS4EnterCount = 0;
        private int mS4ExitCount = 0;
    }

    @SmallTest
    public void testStateMachineEnterExitTransitionToTest() throws Exception {
        //if (WAIT_FOR_DEBUGGER) Debug.waitForDebugger();

        StateMachineEnterExitTransitionToTest smEnterExitTranstionToTest =
            new StateMachineEnterExitTransitionToTest("smEnterExitTranstionToTest");
        smEnterExitTranstionToTest.start();
        if (smEnterExitTranstionToTest.isDbg()) {
            Log.d(TAG, "testStateMachineEnterExitTransitionToTest E");
        }

        synchronized (smEnterExitTranstionToTest) {
            smEnterExitTranstionToTest.sendMessage(1);

            try {
                // wait for the messages to be handled
                smEnterExitTranstionToTest.wait();
            } catch (InterruptedException e) {
                Log.e(TAG, "testStateMachineEnterExitTransitionToTest: exception while waiting "
                    + e.getMessage());
            }
        }

        assertTrue(smEnterExitTranstionToTest.getProcessedMessagesCount() == 1);

        ProcessedMessages.Info pmi;

        // Message should be handled by mS2.
        pmi = smEnterExitTranstionToTest.getProcessedMessage(0);
        assertEquals(TEST_CMD_1, pmi.getWhat());
        assertEquals(smEnterExitTranstionToTest.mS2, pmi.getState());
        assertEquals(smEnterExitTranstionToTest.mS2, pmi.getOriginalState());

        assertEquals(smEnterExitTranstionToTest.mS1EnterCount, 1);
        assertEquals(smEnterExitTranstionToTest.mS1ExitCount, 1);
        assertEquals(smEnterExitTranstionToTest.mS2EnterCount, 1);
        assertEquals(smEnterExitTranstionToTest.mS2ExitCount, 1);
        assertEquals(smEnterExitTranstionToTest.mS3EnterCount, 1);
        assertEquals(smEnterExitTranstionToTest.mS3ExitCount, 1);
        assertEquals(smEnterExitTranstionToTest.mS3EnterCount, 1);
        assertEquals(smEnterExitTranstionToTest.mS3ExitCount, 1);

        if (smEnterExitTranstionToTest.isDbg()) {
            Log.d(TAG, "testStateMachineEnterExitTransitionToTest X");
        }
    }

    /**
     * Tests that ProcessedMessage works as a circular buffer.
     */