Loading core/java/com/android/internal/util/HierarchicalStateMachine.java +122 −122 Original line number Diff line number Diff line Loading @@ -51,7 +51,7 @@ import java.util.HashMap; mS2 mS1 ----> initial state </code> * After the state machine is created and started, messages are sent to a state * machine using <code>sendMessage</code and the messages are created using * machine using <code>sendMessage</code> and the messages are created using * <code>obtainMessage</code>. When the state machine receives a message the * current state's <code>processMessage</code> is invoked. In the above example * mS1.processMessage will be invoked first. The state may use <code>transitionTo</code> Loading @@ -59,9 +59,9 @@ import java.util.HashMap; * * Each state in the state machine may have a zero or one parent states and if * a child state is unable to handle a message it may have the message processed * by its parent by returning false. If a message is never processed <code>unhandledMessage</code> * will be invoked to give one last chance for the state machine to process * the message. * by its parent by returning false or NOT_HANDLED. If a message is never processed * <code>unhandledMessage</code> will be invoked to give one last chance for the state machine * to process the message. * * When all processing is completed a state machine may choose to call * <code>transitionToHaltingState</code>. When the current <code>processingMessage</code> Loading Loading @@ -95,7 +95,7 @@ import java.util.HashMap; * any other messages that are on the queue or might be added later. Both of * these are protected and may only be invoked from within a state machine. * * To illustrate some of these properties we'll use state machine with 8 * To illustrate some of these properties we'll use state machine with an 8 * state hierarchy: <code> mP0 Loading @@ -110,43 +110,18 @@ import java.util.HashMap; * After starting mS5 the list of active states is mP0, mP1, mS1 and mS5. * So the order of calling processMessage when a message is received is mS5, * mS1, mP1, mP0 assuming each processMessage indicates it can't handle this * message by returning false. * message by returning false or NOT_HANDLED. * * Now assume mS5.processMessage receives a message it can handle, and during * the handling determines the machine should changes states. It would call * transitionTo(mS4) and return true. Immediately after returning from * the handling determines the machine should change states. It could call * transitionTo(mS4) and return true or HANDLED. Immediately after returning from * processMessage the state machine runtime will find the common parent, * which is mP1. It will then call mS5.exit, mS1.exit, mS2.enter and then * mS4.enter. The new list of active states is mP0, mP1, mS2 and mS4. So * when the next message is received mS4.processMessage will be invoked. * * To assist in describing an HSM a simple grammar has been created which * is informally defined here and a formal EBNF description is at the end * of the class comment. * * An HSM starts with the name and includes a set of hierarchical states. * A state is preceeded by one or more plus signs (+), to indicate its * depth and a hash (#) if its the initial state. Child states follow their * parents and have one more plus sign then their parent. Inside a state * are a series of messages, the actions they perform and if the processing * is complete ends with a period (.). If processing isn't complete and * the parent should process the message it ends with a caret (^). The * actions include send a message ($MESSAGE), defer a message (%MESSAGE), * transition to a new state (>MESSAGE) and an if statement * (if ( expression ) { list of actions }.) * * The Hsm HelloWorld could documented as: * * HelloWorld { * + # mState1. * } * * and interpreted as HSM HelloWorld: * * mState1 a root state (single +) and initial state (#) which * processes all messages completely, the period (.). * * The implementation is: * Now for some concrete examples, here is the canonical HelloWorld as an HSM. * It responds with "Hello World" being printed to the log for every message. <code> class HelloWorld extends HierarchicalStateMachine { Hsm1(String name) { Loading @@ -164,7 +139,7 @@ class HelloWorld extends HierarchicalStateMachine { class State1 extends HierarchicalState { @Override public boolean processMessage(Message message) { Log.d(TAG, "Hello World"); return true; return HANDLED; } } State1 mState1 = new State1(); Loading @@ -176,7 +151,7 @@ void testHelloWorld() { } </code> * * A more interesting state machine is one of four states * A more interesting state machine is one with four states * with two independent parent states. <code> mP1 mP2 Loading @@ -184,45 +159,68 @@ void testHelloWorld() { mS2 mS1 </code> * * documented as: * Here is a description of this state machine using pseudo code. * * * Hsm1 { * + mP1 { * state mP1 { * enter { log("mP1.enter"); } * exit { log("mP1.exit"); } * on msg { * CMD_2 { * $CMD_3 * %CMD_2 * >mS2 * }. * send(CMD_3); * defer(msg); * transitonTo(mS2); * return HANDLED; * } * return NOT_HANDLED; * } * ++ # mS1 { CMD_1{ >mS1 }^ } * ++ mS2 { * CMD_2{$CMD_4}. * CMD_3{%CMD_3 ; >mP2}. * } * * + mP2 e($CMD_5) { * CMD_3, CMD_4. * CMD_5{>HALT}. * INITIAL * state mS1 parent mP1 { * enter { log("mS1.enter"); } * exit { log("mS1.exit"); } * on msg { * CMD_1 { * transitionTo(mS1); * return HANDLED; * } * return NOT_HANDLED; * } * } * * and interpreted as HierarchicalStateMachine Hsm1: * * mP1 a root state. * processes message CMD_2 which sends CMD_3, defers CMD_2, and transitions to mS2 * * mS1 a child of mP1 is the initial state: * processes message CMD_1 which transitions to itself and returns false to let mP1 handle it. * * mS2 a child of mP1: * processes message CMD_2 which send CMD_4 * processes message CMD_3 which defers CMD_3 and transitions to mP2 * state mS2 parent mP1 { * enter { log("mS2.enter"); } * exit { log("mS2.exit"); } * on msg { * CMD_2 { * send(CMD_4); * return HANDLED; * } * CMD_3 { * defer(msg); * transitionTo(mP2); * return HANDLED; * } * return NOT_HANDLED; * } * } * * mP2 a root state. * on enter it sends CMD_5 * processes message CMD_3 * processes message CMD_4 * processes message CMD_5 which transitions to halt state * state mP2 { * enter { * log("mP2.enter"); * send(CMD_5); * } * exit { log("mP2.exit"); } * on msg { * CMD_3, CMD_4 { return HANDLED; } * CMD_5 { * transitionTo(HaltingState); * return HANDLED; * } * return NOT_HANDLED; * } * } * * The implementation is below and also in HierarchicalStateMachineTest: <code> Loading Loading @@ -271,11 +269,11 @@ class Hsm1 extends HierarchicalStateMachine { sendMessage(obtainMessage(CMD_3)); deferMessage(message); transitionTo(mS2); retVal = true; retVal = HANDLED; break; default: // Any message we don't understand in this state invokes unhandledMessage retVal = false; retVal = NOT_HANDLED; break; } return retVal; Loading @@ -294,10 +292,10 @@ class Hsm1 extends HierarchicalStateMachine { if (message.what == CMD_1) { // Transition to ourself to show that enter/exit is called transitionTo(mS1); return true; return HANDLED; } else { // Let parent process all other messages return false; return NOT_HANDLED; } } @Override public void exit() { Loading @@ -315,15 +313,15 @@ class Hsm1 extends HierarchicalStateMachine { switch(message.what) { case(CMD_2): sendMessage(obtainMessage(CMD_4)); retVal = true; retVal = HANDLED; break; case(CMD_3): deferMessage(message); transitionTo(mP2); retVal = true; retVal = HANDLED; break; default: retVal = false; retVal = NOT_HANDLED; break; } return retVal; Loading @@ -349,7 +347,7 @@ class Hsm1 extends HierarchicalStateMachine { transitionToHaltingState(); break; } return true; return HANDLED; } @Override public void exit() { Log.d(TAG, "mP2.exit"); Loading @@ -357,7 +355,7 @@ class Hsm1 extends HierarchicalStateMachine { } @Override protected void halting() { void halting() { Log.d(TAG, "halting"); synchronized (this) { this.notifyAll(); Loading Loading @@ -413,53 +411,32 @@ class Hsm1 extends HierarchicalStateMachine { * D/hsm1 ( 1999): mP2.exit * D/hsm1 ( 1999): halting * * Here is the HSM a BNF grammar, this is a first stab at creating an * HSM description language, suggestions corrections or alternatives * would be much appreciated. * * Legend: * {} ::= zero or more * {}+ ::= one or more * [] ::= zero or one * () ::= define a group with "or" semantics. * * HSM EBNF: * HSM = HSM_NAME "{" { STATE }+ "}" ; * HSM_NAME = alpha_numeric_name ; * STATE = INTRODUCE_STATE [ ENTER | [ ENTER EXIT ] "{" [ MESSAGES ] "}" [ EXIT ] ; * INTRODUCE_STATE = { STATE_DEPTH }+ [ INITIAL_STATE_INDICATOR ] STATE_NAME ; * STATE_DEPTH = "+" ; * INITIAL_STATE_INDICATOR = "#" * ENTER = "e(" SEND_ACTION | TRANSITION_ACTION | HALT_ACTION ")" ; * MESSAGES = { MSG_LIST MESSAGE_ACTIONS } ; * MSG_LIST = { MSG_NAME { "," MSG_NAME } }; * EXIT = "x(" SEND_ACTION | TRANSITION_ACTION | HALT_ACTION ")" ; * PROCESS_COMPLETION = PROCESS_IN_PARENT_OR_COMPLETE | PROCESS_COMPLETE ; * SEND_ACTION = "$" MSG_NAME ; * DEFER_ACTION = "%" MSG_NAME ; * TRANSITION_ACTION = ">" STATE_NAME ; * HALT_ACTION = ">" HALT ; * MESSAGE_ACTIONS = { "{" ACTION_LIST "}" } [ PROCESS_COMPLETION ] ; * ACTION_LIST = ACTION { (";" | "\n") ACTION } ; * ACTION = IF_ACTION | SEND_ACTION | DEFER_ACTION | TRANSITION_ACTION | HALT_ACTION ; * IF_ACTION = "if(" boolean_expression ")" "{" ACTION_LIST "}" * PROCESS_IN_PARENT_OR_COMPLETE = "^" ; * PROCESS_COMPLETE = "." ; * STATE_NAME = alpha_numeric_name ; * MSG_NAME = alpha_numeric_name | ALL_OTHER_MESSAGES ; * ALL_OTHER_MESSAGES = "*" ; * EXP = boolean_expression ; * * Idioms: * * { %* }. ::= All other messages will be deferred. */ public class HierarchicalStateMachine { private static final String TAG = "HierarchicalStateMachine"; private String mName; /** Message.what value when quitting */ public static final int HSM_QUIT_CMD = -1; /** Message.what value when initializing */ public static final int HSM_INIT_CMD = -1; /** * Convenience constant that maybe returned by processMessage * to indicate the the message was processed and is not to be * processed by parent states */ public static final boolean HANDLED = true; /** * Convenience constant that maybe returned by processMessage * to indicate the the message was NOT processed and is to be * processed by parent states */ public static final boolean NOT_HANDLED = false; private static class HsmHandler extends Handler { /** The debug flag */ Loading @@ -468,6 +445,12 @@ public class HierarchicalStateMachine { /** The quit object */ private static final Object mQuitObj = new Object(); /** The initialization message */ private static final Message mInitMsg = null; /** The current message */ private Message mMsg; /** A list of messages that this state machine has processed */ private ProcessedMessages mProcessedMessages = new ProcessedMessages(); Loading Loading @@ -550,8 +533,7 @@ public class HierarchicalStateMachine { private class QuittingState extends HierarchicalState { @Override public boolean processMessage(Message msg) { // Ignore return false; return NOT_HANDLED; } } Loading @@ -565,6 +547,9 @@ public class HierarchicalStateMachine { public final void handleMessage(Message msg) { if (mDbg) Log.d(TAG, "handleMessage: E msg.what=" + msg.what); /** Save the current message */ mMsg = msg; /** * Check that construction was completed */ Loading Loading @@ -679,6 +664,7 @@ public class HierarchicalStateMachine { * starting at the first entry. */ mIsConstructionCompleted = true; mMsg = obtainMessage(HSM_INIT_CMD); invokeEnterMethods(0); /** Loading Loading @@ -854,6 +840,13 @@ public class HierarchicalStateMachine { moveTempStateStackToStateStack(); } /** * @return current message */ private final Message getCurrentMessage() { return mMsg; } /** * @return current state */ Loading Loading @@ -1025,6 +1018,14 @@ public class HierarchicalStateMachine { protected final void addState(HierarchicalState state, HierarchicalState parent) { mHsmHandler.addState(state, parent); } /** * @return current message */ protected final Message getCurrentMessage() { return mHsmHandler.getCurrentMessage(); } /** * @return current state */ Loading @@ -1032,7 +1033,6 @@ public class HierarchicalStateMachine { return mHsmHandler.getCurrentState(); } /** * Add a new state to the state machine, parent will be null * @param state to add Loading core/tests/coretests/src/android/os/HierarchicalStateMachineTest.java +43 −31 Original line number Diff line number Diff line Loading @@ -74,15 +74,15 @@ public class HierarchicalStateMachineTest extends TestCase { if (isQuit(message)) { mQuitCount += 1; if (mQuitCount > 2) { // Returning false to actually quit return false; // Returning NOT_HANDLED to actually quit return NOT_HANDLED; } else { // Do NOT quit return true; return HANDLED; } } else { // All other message are handled return true; return HANDLED; } } } Loading Loading @@ -172,12 +172,18 @@ public class HierarchicalStateMachineTest extends TestCase { class S1 extends HierarchicalState { @Override protected void enter() { // Test that message is HSM_INIT_CMD assertEquals(HSM_INIT_CMD, getCurrentMessage().what); // Test that a transition in enter and the initial state works mS1EnterCount += 1; transitionTo(mS2); Log.d(TAG, "S1.enter"); } @Override protected void exit() { // Test that message is HSM_INIT_CMD assertEquals(HSM_INIT_CMD, getCurrentMessage().what); mS1ExitCount += 1; Log.d(TAG, "S1.exit"); } Loading @@ -185,10 +191,16 @@ public class HierarchicalStateMachineTest extends TestCase { class S2 extends HierarchicalState { @Override protected void enter() { // Test that message is HSM_INIT_CMD assertEquals(HSM_INIT_CMD, getCurrentMessage().what); mS2EnterCount += 1; Log.d(TAG, "S2.enter"); } @Override protected void exit() { // Test that message is TEST_CMD_1 assertEquals(TEST_CMD_1, getCurrentMessage().what); // Test transition in exit work mS2ExitCount += 1; transitionTo(mS4); Loading @@ -196,10 +208,10 @@ public class HierarchicalStateMachineTest extends TestCase { } @Override protected boolean processMessage(Message message) { // Start a transition to S3 but it will be // changed to a transition to S4 // changed to a transition to S4 in exit transitionTo(mS3); Log.d(TAG, "S2.processMessage"); return true; return HANDLED; } } Loading Loading @@ -264,7 +276,7 @@ public class HierarchicalStateMachineTest extends TestCase { } synchronized (smEnterExitTranstionToTest) { smEnterExitTranstionToTest.sendMessage(1); smEnterExitTranstionToTest.sendMessage(TEST_CMD_1); try { // wait for the messages to be handled Loading Loading @@ -321,7 +333,7 @@ public class HierarchicalStateMachineTest extends TestCase { if (message.what == TEST_CMD_6) { transitionToHaltingState(); } return true; return HANDLED; } } Loading Loading @@ -415,7 +427,7 @@ public class HierarchicalStateMachineTest extends TestCase { assertEquals(1, mExitCount); transitionToHaltingState(); } return true; return HANDLED; } @Override protected void exit() { Loading Loading @@ -510,7 +522,7 @@ public class HierarchicalStateMachineTest extends TestCase { if (message.what == TEST_CMD_2) { transitionTo(mS2); } return true; return HANDLED; } @Override protected void exit() { Loading @@ -523,7 +535,7 @@ public class HierarchicalStateMachineTest extends TestCase { if (message.what == TEST_CMD_2) { transitionToHaltingState(); } return true; return HANDLED; } } Loading Loading @@ -612,13 +624,13 @@ public class HierarchicalStateMachineTest extends TestCase { if (message.what == TEST_CMD_2) { transitionToHaltingState(); } return true; return HANDLED; } } class ChildState extends HierarchicalState { @Override protected boolean processMessage(Message message) { return false; return NOT_HANDLED; } } Loading Loading @@ -697,20 +709,20 @@ public class HierarchicalStateMachineTest extends TestCase { if (message.what == TEST_CMD_2) { transitionToHaltingState(); } return true; return HANDLED; } } class ChildState1 extends HierarchicalState { @Override protected boolean processMessage(Message message) { transitionTo(mChildState2); return true; return HANDLED; } } class ChildState2 extends HierarchicalState { @Override protected boolean processMessage(Message message) { return false; return NOT_HANDLED; } } Loading Loading @@ -794,7 +806,7 @@ public class HierarchicalStateMachineTest extends TestCase { mParentState1EnterCount += 1; } @Override protected boolean processMessage(Message message) { return true; return HANDLED; } @Override protected void exit() { mParentState1ExitCount += 1; Loading Loading @@ -822,7 +834,7 @@ public class HierarchicalStateMachineTest extends TestCase { assertEquals(0, mChildState5ExitCount); transitionTo(mChildState2); return true; return HANDLED; } @Override protected void exit() { mChildState1ExitCount += 1; Loading Loading @@ -850,7 +862,7 @@ public class HierarchicalStateMachineTest extends TestCase { assertEquals(0, mChildState5ExitCount); transitionTo(mChildState5); return true; return HANDLED; } @Override protected void exit() { mChildState2ExitCount += 1; Loading Loading @@ -878,7 +890,7 @@ public class HierarchicalStateMachineTest extends TestCase { assertEquals(1, mChildState5ExitCount); transitionToHaltingState(); return true; return HANDLED; } @Override protected void exit() { mParentState2ExitCount += 1; Loading Loading @@ -906,7 +918,7 @@ public class HierarchicalStateMachineTest extends TestCase { assertEquals(1, mChildState5ExitCount); transitionTo(mChildState4); return true; return HANDLED; } @Override protected void exit() { mChildState3ExitCount += 1; Loading Loading @@ -934,7 +946,7 @@ public class HierarchicalStateMachineTest extends TestCase { assertEquals(1, mChildState5ExitCount); transitionTo(mParentState2); return true; return HANDLED; } @Override protected void exit() { mChildState4ExitCount += 1; Loading Loading @@ -962,7 +974,7 @@ public class HierarchicalStateMachineTest extends TestCase { assertEquals(0, mChildState5ExitCount); transitionTo(mChildState3); return true; return HANDLED; } @Override protected void exit() { mChildState5ExitCount += 1; Loading Loading @@ -1108,7 +1120,7 @@ public class HierarchicalStateMachineTest extends TestCase { mArrivalTimeMsg2 = SystemClock.elapsedRealtime(); transitionToHaltingState(); } return true; return HANDLED; } @Override protected void exit() { Loading Loading @@ -1190,7 +1202,7 @@ public class HierarchicalStateMachineTest extends TestCase { class S1 extends HierarchicalState { @Override protected boolean processMessage(Message message) { transitionTo(mS2); return true; return HANDLED; } @Override protected void exit() { sendMessage(TEST_CMD_2); Loading @@ -1216,7 +1228,7 @@ public class HierarchicalStateMachineTest extends TestCase { if (mMsgCount == 2) { transitionToHaltingState(); } return true; return HANDLED; } @Override protected void exit() { Loading Loading @@ -1300,7 +1312,7 @@ public class HierarchicalStateMachineTest extends TestCase { if (message.what == TEST_CMD_2) { transitionToHaltingState(); } return false; return NOT_HANDLED; } } Loading Loading @@ -1369,7 +1381,7 @@ public class HierarchicalStateMachineTest extends TestCase { if (message.what == TEST_CMD_4) { transitionToHaltingState(); } return true; return HANDLED; } } Loading Loading @@ -1563,10 +1575,10 @@ class Hsm1 extends HierarchicalStateMachine { if (message.what == CMD_1) { // Transition to ourself to show that enter/exit is called transitionTo(mS1); return true; return HANDLED; } else { // Let parent process all other messages return false; return NOT_HANDLED; } } @Override protected void exit() { Loading Loading @@ -1618,7 +1630,7 @@ class Hsm1 extends HierarchicalStateMachine { transitionToHaltingState(); break; } return true; return HANDLED; } @Override protected void exit() { Log.d(TAG, "P2.exit"); Loading Loading
core/java/com/android/internal/util/HierarchicalStateMachine.java +122 −122 Original line number Diff line number Diff line Loading @@ -51,7 +51,7 @@ import java.util.HashMap; mS2 mS1 ----> initial state </code> * After the state machine is created and started, messages are sent to a state * machine using <code>sendMessage</code and the messages are created using * machine using <code>sendMessage</code> and the messages are created using * <code>obtainMessage</code>. When the state machine receives a message the * current state's <code>processMessage</code> is invoked. In the above example * mS1.processMessage will be invoked first. The state may use <code>transitionTo</code> Loading @@ -59,9 +59,9 @@ import java.util.HashMap; * * Each state in the state machine may have a zero or one parent states and if * a child state is unable to handle a message it may have the message processed * by its parent by returning false. If a message is never processed <code>unhandledMessage</code> * will be invoked to give one last chance for the state machine to process * the message. * by its parent by returning false or NOT_HANDLED. If a message is never processed * <code>unhandledMessage</code> will be invoked to give one last chance for the state machine * to process the message. * * When all processing is completed a state machine may choose to call * <code>transitionToHaltingState</code>. When the current <code>processingMessage</code> Loading Loading @@ -95,7 +95,7 @@ import java.util.HashMap; * any other messages that are on the queue or might be added later. Both of * these are protected and may only be invoked from within a state machine. * * To illustrate some of these properties we'll use state machine with 8 * To illustrate some of these properties we'll use state machine with an 8 * state hierarchy: <code> mP0 Loading @@ -110,43 +110,18 @@ import java.util.HashMap; * After starting mS5 the list of active states is mP0, mP1, mS1 and mS5. * So the order of calling processMessage when a message is received is mS5, * mS1, mP1, mP0 assuming each processMessage indicates it can't handle this * message by returning false. * message by returning false or NOT_HANDLED. * * Now assume mS5.processMessage receives a message it can handle, and during * the handling determines the machine should changes states. It would call * transitionTo(mS4) and return true. Immediately after returning from * the handling determines the machine should change states. It could call * transitionTo(mS4) and return true or HANDLED. Immediately after returning from * processMessage the state machine runtime will find the common parent, * which is mP1. It will then call mS5.exit, mS1.exit, mS2.enter and then * mS4.enter. The new list of active states is mP0, mP1, mS2 and mS4. So * when the next message is received mS4.processMessage will be invoked. * * To assist in describing an HSM a simple grammar has been created which * is informally defined here and a formal EBNF description is at the end * of the class comment. * * An HSM starts with the name and includes a set of hierarchical states. * A state is preceeded by one or more plus signs (+), to indicate its * depth and a hash (#) if its the initial state. Child states follow their * parents and have one more plus sign then their parent. Inside a state * are a series of messages, the actions they perform and if the processing * is complete ends with a period (.). If processing isn't complete and * the parent should process the message it ends with a caret (^). The * actions include send a message ($MESSAGE), defer a message (%MESSAGE), * transition to a new state (>MESSAGE) and an if statement * (if ( expression ) { list of actions }.) * * The Hsm HelloWorld could documented as: * * HelloWorld { * + # mState1. * } * * and interpreted as HSM HelloWorld: * * mState1 a root state (single +) and initial state (#) which * processes all messages completely, the period (.). * * The implementation is: * Now for some concrete examples, here is the canonical HelloWorld as an HSM. * It responds with "Hello World" being printed to the log for every message. <code> class HelloWorld extends HierarchicalStateMachine { Hsm1(String name) { Loading @@ -164,7 +139,7 @@ class HelloWorld extends HierarchicalStateMachine { class State1 extends HierarchicalState { @Override public boolean processMessage(Message message) { Log.d(TAG, "Hello World"); return true; return HANDLED; } } State1 mState1 = new State1(); Loading @@ -176,7 +151,7 @@ void testHelloWorld() { } </code> * * A more interesting state machine is one of four states * A more interesting state machine is one with four states * with two independent parent states. <code> mP1 mP2 Loading @@ -184,45 +159,68 @@ void testHelloWorld() { mS2 mS1 </code> * * documented as: * Here is a description of this state machine using pseudo code. * * * Hsm1 { * + mP1 { * state mP1 { * enter { log("mP1.enter"); } * exit { log("mP1.exit"); } * on msg { * CMD_2 { * $CMD_3 * %CMD_2 * >mS2 * }. * send(CMD_3); * defer(msg); * transitonTo(mS2); * return HANDLED; * } * return NOT_HANDLED; * } * ++ # mS1 { CMD_1{ >mS1 }^ } * ++ mS2 { * CMD_2{$CMD_4}. * CMD_3{%CMD_3 ; >mP2}. * } * * + mP2 e($CMD_5) { * CMD_3, CMD_4. * CMD_5{>HALT}. * INITIAL * state mS1 parent mP1 { * enter { log("mS1.enter"); } * exit { log("mS1.exit"); } * on msg { * CMD_1 { * transitionTo(mS1); * return HANDLED; * } * return NOT_HANDLED; * } * } * * and interpreted as HierarchicalStateMachine Hsm1: * * mP1 a root state. * processes message CMD_2 which sends CMD_3, defers CMD_2, and transitions to mS2 * * mS1 a child of mP1 is the initial state: * processes message CMD_1 which transitions to itself and returns false to let mP1 handle it. * * mS2 a child of mP1: * processes message CMD_2 which send CMD_4 * processes message CMD_3 which defers CMD_3 and transitions to mP2 * state mS2 parent mP1 { * enter { log("mS2.enter"); } * exit { log("mS2.exit"); } * on msg { * CMD_2 { * send(CMD_4); * return HANDLED; * } * CMD_3 { * defer(msg); * transitionTo(mP2); * return HANDLED; * } * return NOT_HANDLED; * } * } * * mP2 a root state. * on enter it sends CMD_5 * processes message CMD_3 * processes message CMD_4 * processes message CMD_5 which transitions to halt state * state mP2 { * enter { * log("mP2.enter"); * send(CMD_5); * } * exit { log("mP2.exit"); } * on msg { * CMD_3, CMD_4 { return HANDLED; } * CMD_5 { * transitionTo(HaltingState); * return HANDLED; * } * return NOT_HANDLED; * } * } * * The implementation is below and also in HierarchicalStateMachineTest: <code> Loading Loading @@ -271,11 +269,11 @@ class Hsm1 extends HierarchicalStateMachine { sendMessage(obtainMessage(CMD_3)); deferMessage(message); transitionTo(mS2); retVal = true; retVal = HANDLED; break; default: // Any message we don't understand in this state invokes unhandledMessage retVal = false; retVal = NOT_HANDLED; break; } return retVal; Loading @@ -294,10 +292,10 @@ class Hsm1 extends HierarchicalStateMachine { if (message.what == CMD_1) { // Transition to ourself to show that enter/exit is called transitionTo(mS1); return true; return HANDLED; } else { // Let parent process all other messages return false; return NOT_HANDLED; } } @Override public void exit() { Loading @@ -315,15 +313,15 @@ class Hsm1 extends HierarchicalStateMachine { switch(message.what) { case(CMD_2): sendMessage(obtainMessage(CMD_4)); retVal = true; retVal = HANDLED; break; case(CMD_3): deferMessage(message); transitionTo(mP2); retVal = true; retVal = HANDLED; break; default: retVal = false; retVal = NOT_HANDLED; break; } return retVal; Loading @@ -349,7 +347,7 @@ class Hsm1 extends HierarchicalStateMachine { transitionToHaltingState(); break; } return true; return HANDLED; } @Override public void exit() { Log.d(TAG, "mP2.exit"); Loading @@ -357,7 +355,7 @@ class Hsm1 extends HierarchicalStateMachine { } @Override protected void halting() { void halting() { Log.d(TAG, "halting"); synchronized (this) { this.notifyAll(); Loading Loading @@ -413,53 +411,32 @@ class Hsm1 extends HierarchicalStateMachine { * D/hsm1 ( 1999): mP2.exit * D/hsm1 ( 1999): halting * * Here is the HSM a BNF grammar, this is a first stab at creating an * HSM description language, suggestions corrections or alternatives * would be much appreciated. * * Legend: * {} ::= zero or more * {}+ ::= one or more * [] ::= zero or one * () ::= define a group with "or" semantics. * * HSM EBNF: * HSM = HSM_NAME "{" { STATE }+ "}" ; * HSM_NAME = alpha_numeric_name ; * STATE = INTRODUCE_STATE [ ENTER | [ ENTER EXIT ] "{" [ MESSAGES ] "}" [ EXIT ] ; * INTRODUCE_STATE = { STATE_DEPTH }+ [ INITIAL_STATE_INDICATOR ] STATE_NAME ; * STATE_DEPTH = "+" ; * INITIAL_STATE_INDICATOR = "#" * ENTER = "e(" SEND_ACTION | TRANSITION_ACTION | HALT_ACTION ")" ; * MESSAGES = { MSG_LIST MESSAGE_ACTIONS } ; * MSG_LIST = { MSG_NAME { "," MSG_NAME } }; * EXIT = "x(" SEND_ACTION | TRANSITION_ACTION | HALT_ACTION ")" ; * PROCESS_COMPLETION = PROCESS_IN_PARENT_OR_COMPLETE | PROCESS_COMPLETE ; * SEND_ACTION = "$" MSG_NAME ; * DEFER_ACTION = "%" MSG_NAME ; * TRANSITION_ACTION = ">" STATE_NAME ; * HALT_ACTION = ">" HALT ; * MESSAGE_ACTIONS = { "{" ACTION_LIST "}" } [ PROCESS_COMPLETION ] ; * ACTION_LIST = ACTION { (";" | "\n") ACTION } ; * ACTION = IF_ACTION | SEND_ACTION | DEFER_ACTION | TRANSITION_ACTION | HALT_ACTION ; * IF_ACTION = "if(" boolean_expression ")" "{" ACTION_LIST "}" * PROCESS_IN_PARENT_OR_COMPLETE = "^" ; * PROCESS_COMPLETE = "." ; * STATE_NAME = alpha_numeric_name ; * MSG_NAME = alpha_numeric_name | ALL_OTHER_MESSAGES ; * ALL_OTHER_MESSAGES = "*" ; * EXP = boolean_expression ; * * Idioms: * * { %* }. ::= All other messages will be deferred. */ public class HierarchicalStateMachine { private static final String TAG = "HierarchicalStateMachine"; private String mName; /** Message.what value when quitting */ public static final int HSM_QUIT_CMD = -1; /** Message.what value when initializing */ public static final int HSM_INIT_CMD = -1; /** * Convenience constant that maybe returned by processMessage * to indicate the the message was processed and is not to be * processed by parent states */ public static final boolean HANDLED = true; /** * Convenience constant that maybe returned by processMessage * to indicate the the message was NOT processed and is to be * processed by parent states */ public static final boolean NOT_HANDLED = false; private static class HsmHandler extends Handler { /** The debug flag */ Loading @@ -468,6 +445,12 @@ public class HierarchicalStateMachine { /** The quit object */ private static final Object mQuitObj = new Object(); /** The initialization message */ private static final Message mInitMsg = null; /** The current message */ private Message mMsg; /** A list of messages that this state machine has processed */ private ProcessedMessages mProcessedMessages = new ProcessedMessages(); Loading Loading @@ -550,8 +533,7 @@ public class HierarchicalStateMachine { private class QuittingState extends HierarchicalState { @Override public boolean processMessage(Message msg) { // Ignore return false; return NOT_HANDLED; } } Loading @@ -565,6 +547,9 @@ public class HierarchicalStateMachine { public final void handleMessage(Message msg) { if (mDbg) Log.d(TAG, "handleMessage: E msg.what=" + msg.what); /** Save the current message */ mMsg = msg; /** * Check that construction was completed */ Loading Loading @@ -679,6 +664,7 @@ public class HierarchicalStateMachine { * starting at the first entry. */ mIsConstructionCompleted = true; mMsg = obtainMessage(HSM_INIT_CMD); invokeEnterMethods(0); /** Loading Loading @@ -854,6 +840,13 @@ public class HierarchicalStateMachine { moveTempStateStackToStateStack(); } /** * @return current message */ private final Message getCurrentMessage() { return mMsg; } /** * @return current state */ Loading Loading @@ -1025,6 +1018,14 @@ public class HierarchicalStateMachine { protected final void addState(HierarchicalState state, HierarchicalState parent) { mHsmHandler.addState(state, parent); } /** * @return current message */ protected final Message getCurrentMessage() { return mHsmHandler.getCurrentMessage(); } /** * @return current state */ Loading @@ -1032,7 +1033,6 @@ public class HierarchicalStateMachine { return mHsmHandler.getCurrentState(); } /** * Add a new state to the state machine, parent will be null * @param state to add Loading
core/tests/coretests/src/android/os/HierarchicalStateMachineTest.java +43 −31 Original line number Diff line number Diff line Loading @@ -74,15 +74,15 @@ public class HierarchicalStateMachineTest extends TestCase { if (isQuit(message)) { mQuitCount += 1; if (mQuitCount > 2) { // Returning false to actually quit return false; // Returning NOT_HANDLED to actually quit return NOT_HANDLED; } else { // Do NOT quit return true; return HANDLED; } } else { // All other message are handled return true; return HANDLED; } } } Loading Loading @@ -172,12 +172,18 @@ public class HierarchicalStateMachineTest extends TestCase { class S1 extends HierarchicalState { @Override protected void enter() { // Test that message is HSM_INIT_CMD assertEquals(HSM_INIT_CMD, getCurrentMessage().what); // Test that a transition in enter and the initial state works mS1EnterCount += 1; transitionTo(mS2); Log.d(TAG, "S1.enter"); } @Override protected void exit() { // Test that message is HSM_INIT_CMD assertEquals(HSM_INIT_CMD, getCurrentMessage().what); mS1ExitCount += 1; Log.d(TAG, "S1.exit"); } Loading @@ -185,10 +191,16 @@ public class HierarchicalStateMachineTest extends TestCase { class S2 extends HierarchicalState { @Override protected void enter() { // Test that message is HSM_INIT_CMD assertEquals(HSM_INIT_CMD, getCurrentMessage().what); mS2EnterCount += 1; Log.d(TAG, "S2.enter"); } @Override protected void exit() { // Test that message is TEST_CMD_1 assertEquals(TEST_CMD_1, getCurrentMessage().what); // Test transition in exit work mS2ExitCount += 1; transitionTo(mS4); Loading @@ -196,10 +208,10 @@ public class HierarchicalStateMachineTest extends TestCase { } @Override protected boolean processMessage(Message message) { // Start a transition to S3 but it will be // changed to a transition to S4 // changed to a transition to S4 in exit transitionTo(mS3); Log.d(TAG, "S2.processMessage"); return true; return HANDLED; } } Loading Loading @@ -264,7 +276,7 @@ public class HierarchicalStateMachineTest extends TestCase { } synchronized (smEnterExitTranstionToTest) { smEnterExitTranstionToTest.sendMessage(1); smEnterExitTranstionToTest.sendMessage(TEST_CMD_1); try { // wait for the messages to be handled Loading Loading @@ -321,7 +333,7 @@ public class HierarchicalStateMachineTest extends TestCase { if (message.what == TEST_CMD_6) { transitionToHaltingState(); } return true; return HANDLED; } } Loading Loading @@ -415,7 +427,7 @@ public class HierarchicalStateMachineTest extends TestCase { assertEquals(1, mExitCount); transitionToHaltingState(); } return true; return HANDLED; } @Override protected void exit() { Loading Loading @@ -510,7 +522,7 @@ public class HierarchicalStateMachineTest extends TestCase { if (message.what == TEST_CMD_2) { transitionTo(mS2); } return true; return HANDLED; } @Override protected void exit() { Loading @@ -523,7 +535,7 @@ public class HierarchicalStateMachineTest extends TestCase { if (message.what == TEST_CMD_2) { transitionToHaltingState(); } return true; return HANDLED; } } Loading Loading @@ -612,13 +624,13 @@ public class HierarchicalStateMachineTest extends TestCase { if (message.what == TEST_CMD_2) { transitionToHaltingState(); } return true; return HANDLED; } } class ChildState extends HierarchicalState { @Override protected boolean processMessage(Message message) { return false; return NOT_HANDLED; } } Loading Loading @@ -697,20 +709,20 @@ public class HierarchicalStateMachineTest extends TestCase { if (message.what == TEST_CMD_2) { transitionToHaltingState(); } return true; return HANDLED; } } class ChildState1 extends HierarchicalState { @Override protected boolean processMessage(Message message) { transitionTo(mChildState2); return true; return HANDLED; } } class ChildState2 extends HierarchicalState { @Override protected boolean processMessage(Message message) { return false; return NOT_HANDLED; } } Loading Loading @@ -794,7 +806,7 @@ public class HierarchicalStateMachineTest extends TestCase { mParentState1EnterCount += 1; } @Override protected boolean processMessage(Message message) { return true; return HANDLED; } @Override protected void exit() { mParentState1ExitCount += 1; Loading Loading @@ -822,7 +834,7 @@ public class HierarchicalStateMachineTest extends TestCase { assertEquals(0, mChildState5ExitCount); transitionTo(mChildState2); return true; return HANDLED; } @Override protected void exit() { mChildState1ExitCount += 1; Loading Loading @@ -850,7 +862,7 @@ public class HierarchicalStateMachineTest extends TestCase { assertEquals(0, mChildState5ExitCount); transitionTo(mChildState5); return true; return HANDLED; } @Override protected void exit() { mChildState2ExitCount += 1; Loading Loading @@ -878,7 +890,7 @@ public class HierarchicalStateMachineTest extends TestCase { assertEquals(1, mChildState5ExitCount); transitionToHaltingState(); return true; return HANDLED; } @Override protected void exit() { mParentState2ExitCount += 1; Loading Loading @@ -906,7 +918,7 @@ public class HierarchicalStateMachineTest extends TestCase { assertEquals(1, mChildState5ExitCount); transitionTo(mChildState4); return true; return HANDLED; } @Override protected void exit() { mChildState3ExitCount += 1; Loading Loading @@ -934,7 +946,7 @@ public class HierarchicalStateMachineTest extends TestCase { assertEquals(1, mChildState5ExitCount); transitionTo(mParentState2); return true; return HANDLED; } @Override protected void exit() { mChildState4ExitCount += 1; Loading Loading @@ -962,7 +974,7 @@ public class HierarchicalStateMachineTest extends TestCase { assertEquals(0, mChildState5ExitCount); transitionTo(mChildState3); return true; return HANDLED; } @Override protected void exit() { mChildState5ExitCount += 1; Loading Loading @@ -1108,7 +1120,7 @@ public class HierarchicalStateMachineTest extends TestCase { mArrivalTimeMsg2 = SystemClock.elapsedRealtime(); transitionToHaltingState(); } return true; return HANDLED; } @Override protected void exit() { Loading Loading @@ -1190,7 +1202,7 @@ public class HierarchicalStateMachineTest extends TestCase { class S1 extends HierarchicalState { @Override protected boolean processMessage(Message message) { transitionTo(mS2); return true; return HANDLED; } @Override protected void exit() { sendMessage(TEST_CMD_2); Loading @@ -1216,7 +1228,7 @@ public class HierarchicalStateMachineTest extends TestCase { if (mMsgCount == 2) { transitionToHaltingState(); } return true; return HANDLED; } @Override protected void exit() { Loading Loading @@ -1300,7 +1312,7 @@ public class HierarchicalStateMachineTest extends TestCase { if (message.what == TEST_CMD_2) { transitionToHaltingState(); } return false; return NOT_HANDLED; } } Loading Loading @@ -1369,7 +1381,7 @@ public class HierarchicalStateMachineTest extends TestCase { if (message.what == TEST_CMD_4) { transitionToHaltingState(); } return true; return HANDLED; } } Loading Loading @@ -1563,10 +1575,10 @@ class Hsm1 extends HierarchicalStateMachine { if (message.what == CMD_1) { // Transition to ourself to show that enter/exit is called transitionTo(mS1); return true; return HANDLED; } else { // Let parent process all other messages return false; return NOT_HANDLED; } } @Override protected void exit() { Loading Loading @@ -1618,7 +1630,7 @@ class Hsm1 extends HierarchicalStateMachine { transitionToHaltingState(); break; } return true; return HANDLED; } @Override protected void exit() { Log.d(TAG, "P2.exit"); Loading