Allow transitionTo in enter/exit.

bug: 2435366
Change-Id: Id15c5e2cca49ced5ebbda24887f8c490e717f101
This commit is contained in:
Wink Saville
2010-03-18 17:03:30 -07:00
parent b50ebe3a15
commit e7be6a85da
3 changed files with 197 additions and 20 deletions

View File

@@ -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.

View File

@@ -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);
*/
if (mDestState == mQuittingState) {
/**
* 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.
*/
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.
*/

View File

@@ -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.
*/