Loading core/java/com/android/internal/util/HierarchicalState.java +5 −4 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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. Loading core/java/com/android/internal/util/HierarchicalStateMachine.java +43 −15 Original line number Diff line number Diff line Loading @@ -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); Loading @@ -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"); } /** Loading Loading @@ -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"); } Loading Loading @@ -1167,7 +1196,6 @@ public class HierarchicalStateMachine { return Message.obtain(mHsmHandler, what, obj); } /** * Enqueue a message to this state machine. */ Loading core/tests/coretests/src/android/os/HierarchicalStateMachineTest.java +149 −1 Original line number Diff line number Diff line Loading @@ -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"; /** Loading Loading @@ -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. */ Loading Loading
core/java/com/android/internal/util/HierarchicalState.java +5 −4 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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. Loading
core/java/com/android/internal/util/HierarchicalStateMachine.java +43 −15 Original line number Diff line number Diff line Loading @@ -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); Loading @@ -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"); } /** Loading Loading @@ -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"); } Loading Loading @@ -1167,7 +1196,6 @@ public class HierarchicalStateMachine { return Message.obtain(mHsmHandler, what, obj); } /** * Enqueue a message to this state machine. */ Loading
core/tests/coretests/src/android/os/HierarchicalStateMachineTest.java +149 −1 Original line number Diff line number Diff line Loading @@ -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"; /** Loading Loading @@ -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. */ Loading