diff options
author | Wink Saville <wink@google.com> | 2010-05-19 13:05:55 -0700 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2010-05-19 13:05:55 -0700 |
commit | 7e3b31d786f88792dc1506d8cc4d53bd56741913 (patch) | |
tree | be74175cdfa83ddb3e2d3f15cc08a16e4d580952 | |
parent | f5172c555f67a2a521be9d458c3afc69d9dfe18b (diff) | |
parent | a4f3bec29c85ef9e0d07fdd551fe3c50f28b9adc (diff) | |
download | frameworks_base-7e3b31d786f88792dc1506d8cc4d53bd56741913.zip frameworks_base-7e3b31d786f88792dc1506d8cc4d53bd56741913.tar.gz frameworks_base-7e3b31d786f88792dc1506d8cc4d53bd56741913.tar.bz2 |
Merge "Update docs, add HANDLED, NOT_HANDLED and getCurrentMessage." into kraken
-rw-r--r-- | core/java/com/android/internal/util/HierarchicalStateMachine.java | 244 | ||||
-rw-r--r-- | core/tests/coretests/src/android/os/HierarchicalStateMachineTest.java | 74 |
2 files changed, 165 insertions, 153 deletions
diff --git a/core/java/com/android/internal/util/HierarchicalStateMachine.java b/core/java/com/android/internal/util/HierarchicalStateMachine.java index 9911f48..c599d68 100644 --- a/core/java/com/android/internal/util/HierarchicalStateMachine.java +++ b/core/java/com/android/internal/util/HierarchicalStateMachine.java @@ -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> @@ -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> @@ -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 @@ -109,44 +109,19 @@ 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. + * mS1, mP1, mP0 assuming each processMessage indicates it can't handle this + * 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) { @@ -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(); @@ -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 @@ -184,45 +159,68 @@ void testHelloWorld() { mS2 mS1 </code> * - * documented as: + * Here is a description of this state machine using pseudo code. * - * Hsm1 { - * + mP1 { - * CMD_2 { - * $CMD_3 - * %CMD_2 - * >mS2 - * }. - * } - * ++ # 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}. - * } + * state mP1 { + * enter { log("mP1.enter"); } + * exit { log("mP1.exit"); } + * on msg { + * CMD_2 { + * send(CMD_3); + * defer(msg); + * transitonTo(mS2); + * 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. + * INITIAL + * state mS1 parent mP1 { + * enter { log("mS1.enter"); } + * exit { log("mS1.exit"); } + * on msg { + * CMD_1 { + * transitionTo(mS1); + * return HANDLED; + * } + * return NOT_HANDLED; + * } + * } * - * 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> @@ -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; @@ -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() { @@ -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; @@ -349,7 +347,7 @@ class Hsm1 extends HierarchicalStateMachine { transitionToHaltingState(); break; } - return true; + return HANDLED; } @Override public void exit() { Log.d(TAG, "mP2.exit"); @@ -357,7 +355,7 @@ class Hsm1 extends HierarchicalStateMachine { } @Override - protected void halting() { + void halting() { Log.d(TAG, "halting"); synchronized (this) { this.notifyAll(); @@ -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 */ @@ -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(); @@ -550,8 +533,7 @@ public class HierarchicalStateMachine { private class QuittingState extends HierarchicalState { @Override public boolean processMessage(Message msg) { - // Ignore - return false; + return NOT_HANDLED; } } @@ -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 */ @@ -679,6 +664,7 @@ public class HierarchicalStateMachine { * starting at the first entry. */ mIsConstructionCompleted = true; + mMsg = obtainMessage(HSM_INIT_CMD); invokeEnterMethods(0); /** @@ -855,6 +841,13 @@ public class HierarchicalStateMachine { } /** + * @return current message + */ + private final Message getCurrentMessage() { + return mMsg; + } + + /** * @return current state */ private final HierarchicalState getCurrentState() { @@ -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 */ @@ -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 diff --git a/core/tests/coretests/src/android/os/HierarchicalStateMachineTest.java b/core/tests/coretests/src/android/os/HierarchicalStateMachineTest.java index 89b3fb6..639372b2 100644 --- a/core/tests/coretests/src/android/os/HierarchicalStateMachineTest.java +++ b/core/tests/coretests/src/android/os/HierarchicalStateMachineTest.java @@ -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; } } } @@ -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"); } @@ -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); @@ -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; } } @@ -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 @@ -321,7 +333,7 @@ public class HierarchicalStateMachineTest extends TestCase { if (message.what == TEST_CMD_6) { transitionToHaltingState(); } - return true; + return HANDLED; } } @@ -415,7 +427,7 @@ public class HierarchicalStateMachineTest extends TestCase { assertEquals(1, mExitCount); transitionToHaltingState(); } - return true; + return HANDLED; } @Override protected void exit() { @@ -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() { @@ -523,7 +535,7 @@ public class HierarchicalStateMachineTest extends TestCase { if (message.what == TEST_CMD_2) { transitionToHaltingState(); } - return true; + return HANDLED; } } @@ -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; } } @@ -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; } } @@ -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; @@ -822,7 +834,7 @@ public class HierarchicalStateMachineTest extends TestCase { assertEquals(0, mChildState5ExitCount); transitionTo(mChildState2); - return true; + return HANDLED; } @Override protected void exit() { mChildState1ExitCount += 1; @@ -850,7 +862,7 @@ public class HierarchicalStateMachineTest extends TestCase { assertEquals(0, mChildState5ExitCount); transitionTo(mChildState5); - return true; + return HANDLED; } @Override protected void exit() { mChildState2ExitCount += 1; @@ -878,7 +890,7 @@ public class HierarchicalStateMachineTest extends TestCase { assertEquals(1, mChildState5ExitCount); transitionToHaltingState(); - return true; + return HANDLED; } @Override protected void exit() { mParentState2ExitCount += 1; @@ -906,7 +918,7 @@ public class HierarchicalStateMachineTest extends TestCase { assertEquals(1, mChildState5ExitCount); transitionTo(mChildState4); - return true; + return HANDLED; } @Override protected void exit() { mChildState3ExitCount += 1; @@ -934,7 +946,7 @@ public class HierarchicalStateMachineTest extends TestCase { assertEquals(1, mChildState5ExitCount); transitionTo(mParentState2); - return true; + return HANDLED; } @Override protected void exit() { mChildState4ExitCount += 1; @@ -962,7 +974,7 @@ public class HierarchicalStateMachineTest extends TestCase { assertEquals(0, mChildState5ExitCount); transitionTo(mChildState3); - return true; + return HANDLED; } @Override protected void exit() { mChildState5ExitCount += 1; @@ -1108,7 +1120,7 @@ public class HierarchicalStateMachineTest extends TestCase { mArrivalTimeMsg2 = SystemClock.elapsedRealtime(); transitionToHaltingState(); } - return true; + return HANDLED; } @Override protected void exit() { @@ -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); @@ -1216,7 +1228,7 @@ public class HierarchicalStateMachineTest extends TestCase { if (mMsgCount == 2) { transitionToHaltingState(); } - return true; + return HANDLED; } @Override protected void exit() { @@ -1300,7 +1312,7 @@ public class HierarchicalStateMachineTest extends TestCase { if (message.what == TEST_CMD_2) { transitionToHaltingState(); } - return false; + return NOT_HANDLED; } } @@ -1369,7 +1381,7 @@ public class HierarchicalStateMachineTest extends TestCase { if (message.what == TEST_CMD_4) { transitionToHaltingState(); } - return true; + return HANDLED; } } @@ -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() { @@ -1618,7 +1630,7 @@ class Hsm1 extends HierarchicalStateMachine { transitionToHaltingState(); break; } - return true; + return HANDLED; } @Override protected void exit() { Log.d(TAG, "P2.exit"); |