Home | History | Annotate | Download | only in util
      1 /**
      2  * Copyright (C) 2009 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.internal.util;
     18 
     19 import android.os.Handler;
     20 import android.os.HandlerThread;
     21 import android.os.Looper;
     22 import android.os.Message;
     23 import android.util.Log;
     24 
     25 import java.util.ArrayList;
     26 import java.util.HashMap;
     27 
     28 /**
     29  * {@hide}
     30  *
     31  * A hierarchical state machine is a state machine which processes messages
     32  * and can have states arranged hierarchically. A state is a <code>HierarchicalState</code>
     33  * object and must implement <code>processMessage</code> and optionally <code>enter/exit/getName</code>.
     34  * The enter/exit methods are equivalent to the construction and destruction
     35  * in Object Oriented programming and are used to perform initialization and
     36  * cleanup of the state respectively. The <code>getName</code> method returns the
     37  * name of the state the default implementation returns the class name it may be
     38  * desirable to have this return the name of the state instance name instead.
     39  * In particular if a particular state class has multiple instances.
     40  *
     41  * When a state machine is created <code>addState</code> is used to build the
     42  * hierarchy and <code>setInitialState</code> is used to identify which of these
     43  * is the initial state. After construction the programmer calls <code>start</code>
     44  * which initializes the state machine and calls <code>enter</code> for all of the initial
     45  * state's hierarchy, starting at its eldest parent. For example given the simple
     46  * state machine below after start is called mP1.enter will have been called and
     47  * then mS1.enter.
     48 <code>
     49         mP1
     50        /   \
     51       mS2   mS1 ----> initial state
     52 </code>
     53  * After the state machine is created and started, messages are sent to a state
     54  * machine using <code>sendMessage</code> and the messages are created using
     55  * <code>obtainMessage</code>. When the state machine receives a message the
     56  * current state's <code>processMessage</code> is invoked. In the above example
     57  * mS1.processMessage will be invoked first. The state may use <code>transitionTo</code>
     58  * to change the current state to a new state
     59  *
     60  * Each state in the state machine may have a zero or one parent states and if
     61  * a child state is unable to handle a message it may have the message processed
     62  * by its parent by returning false or NOT_HANDLED. If a message is never processed
     63  * <code>unhandledMessage</code> will be invoked to give one last chance for the state machine
     64  * to process the message.
     65  *
     66  * When all processing is completed a state machine may choose to call
     67  * <code>transitionToHaltingState</code>. When the current <code>processingMessage</code>
     68  * returns the state machine will transfer to an internal <code>HaltingState</code>
     69  * and invoke <code>halting</code>. Any message subsequently received by the state
     70  * machine will cause <code>haltedProcessMessage</code> to be invoked.
     71  *
     72  * If it is desirable to completely stop the state machine call <code>quit</code>. This
     73  * will exit the current state and its parent and then exit from the controlling thread
     74  * and no further messages will be processed.
     75  *
     76  * In addition to <code>processMessage</code> each <code>HierarchicalState</code> has
     77  * an <code>enter</code> method and <code>exit</exit> method which may be overridden.
     78  *
     79  * Since the states are arranged in a hierarchy transitioning to a new state
     80  * causes current states to be exited and new states to be entered. To determine
     81  * the list of states to be entered/exited the common parent closest to
     82  * the current state is found. We then exit from the current state and its
     83  * parent's up to but not including the common parent state and then enter all
     84  * of the new states below the common parent down to the destination state.
     85  * If there is no common parent all states are exited and then the new states
     86  * are entered.
     87  *
     88  * Two other methods that states can use are <code>deferMessage</code> and
     89  * <code>sendMessageAtFrontOfQueue</code>. The <code>sendMessageAtFrontOfQueue</code> sends
     90  * a message but places it on the front of the queue rather than the back. The
     91  * <code>deferMessage</code> causes the message to be saved on a list until a
     92  * transition is made to a new state. At which time all of the deferred messages
     93  * will be put on the front of the state machine queue with the oldest message
     94  * at the front. These will then be processed by the new current state before
     95  * any other messages that are on the queue or might be added later. Both of
     96  * these are protected and may only be invoked from within a state machine.
     97  *
     98  * To illustrate some of these properties we'll use state machine with an 8
     99  * state hierarchy:
    100 <code>
    101           mP0
    102          /   \
    103         mP1   mS0
    104        /   \
    105       mS2   mS1
    106      /  \    \
    107     mS3  mS4  mS5  ---> initial state
    108 </code>
    109  *
    110  * After starting mS5 the list of active states is mP0, mP1, mS1 and mS5.
    111  * So the order of calling processMessage when a message is received is mS5,
    112  * mS1, mP1, mP0 assuming each processMessage indicates it can't handle this
    113  * message by returning false or NOT_HANDLED.
    114  *
    115  * Now assume mS5.processMessage receives a message it can handle, and during
    116  * the handling determines the machine should change states. It could call
    117  * transitionTo(mS4) and return true or HANDLED. Immediately after returning from
    118  * processMessage the state machine runtime will find the common parent,
    119  * which is mP1. It will then call mS5.exit, mS1.exit, mS2.enter and then
    120  * mS4.enter. The new list of active states is mP0, mP1, mS2 and mS4. So
    121  * when the next message is received mS4.processMessage will be invoked.
    122  *
    123  * Now for some concrete examples, here is the canonical HelloWorld as an HSM.
    124  * It responds with "Hello World" being printed to the log for every message.
    125 <code>
    126 class HelloWorld extends HierarchicalStateMachine {
    127     Hsm1(String name) {
    128         super(name);
    129         addState(mState1);
    130         setInitialState(mState1);
    131     }
    132 
    133     public static HelloWorld makeHelloWorld() {
    134         HelloWorld hw = new HelloWorld("hw");
    135         hw.start();
    136         return hw;
    137     }
    138 
    139     class State1 extends HierarchicalState {
    140         &#64;Override public boolean processMessage(Message message) {
    141             Log.d(TAG, "Hello World");
    142             return HANDLED;
    143         }
    144     }
    145     State1 mState1 = new State1();
    146 }
    147 
    148 void testHelloWorld() {
    149     HelloWorld hw = makeHelloWorld();
    150     hw.sendMessage(hw.obtainMessage());
    151 }
    152 </code>
    153  *
    154  * A more interesting state machine is one with four states
    155  * with two independent parent states.
    156 <code>
    157         mP1      mP2
    158        /   \
    159       mS2   mS1
    160 </code>
    161  *
    162  * Here is a description of this state machine using pseudo code.
    163  *
    164  *
    165  * state mP1 {
    166  *      enter { log("mP1.enter"); }
    167  *      exit { log("mP1.exit");  }
    168  *      on msg {
    169  *          CMD_2 {
    170  *              send(CMD_3);
    171  *              defer(msg);
    172  *              transitonTo(mS2);
    173  *              return HANDLED;
    174  *          }
    175  *          return NOT_HANDLED;
    176  *      }
    177  * }
    178  *
    179  * INITIAL
    180  * state mS1 parent mP1 {
    181  *      enter { log("mS1.enter"); }
    182  *      exit  { log("mS1.exit");  }
    183  *      on msg {
    184  *          CMD_1 {
    185  *              transitionTo(mS1);
    186  *              return HANDLED;
    187  *          }
    188  *          return NOT_HANDLED;
    189  *      }
    190  * }
    191  *
    192  * state mS2 parent mP1 {
    193  *      enter { log("mS2.enter"); }
    194  *      exit  { log("mS2.exit");  }
    195  *      on msg {
    196  *          CMD_2 {
    197  *              send(CMD_4);
    198  *              return HANDLED;
    199  *          }
    200  *          CMD_3 {
    201  *              defer(msg);
    202  *              transitionTo(mP2);
    203  *              return HANDLED;
    204  *          }
    205  *          return NOT_HANDLED;
    206  *      }
    207  * }
    208  *
    209  * state mP2 {
    210  *      enter {
    211  *          log("mP2.enter");
    212  *          send(CMD_5);
    213  *      }
    214  *      exit { log("mP2.exit"); }
    215  *      on msg {
    216  *          CMD_3, CMD_4 { return HANDLED; }
    217  *          CMD_5 {
    218  *              transitionTo(HaltingState);
    219  *              return HANDLED;
    220  *          }
    221  *          return NOT_HANDLED;
    222  *      }
    223  * }
    224  *
    225  * The implementation is below and also in HierarchicalStateMachineTest:
    226 <code>
    227 class Hsm1 extends HierarchicalStateMachine {
    228     private static final String TAG = "hsm1";
    229 
    230     public static final int CMD_1 = 1;
    231     public static final int CMD_2 = 2;
    232     public static final int CMD_3 = 3;
    233     public static final int CMD_4 = 4;
    234     public static final int CMD_5 = 5;
    235 
    236     public static Hsm1 makeHsm1() {
    237         Log.d(TAG, "makeHsm1 E");
    238         Hsm1 sm = new Hsm1("hsm1");
    239         sm.start();
    240         Log.d(TAG, "makeHsm1 X");
    241         return sm;
    242     }
    243 
    244     Hsm1(String name) {
    245         super(name);
    246         Log.d(TAG, "ctor E");
    247 
    248         // Add states, use indentation to show hierarchy
    249         addState(mP1);
    250             addState(mS1, mP1);
    251             addState(mS2, mP1);
    252         addState(mP2);
    253 
    254         // Set the initial state
    255         setInitialState(mS1);
    256         Log.d(TAG, "ctor X");
    257     }
    258 
    259     class P1 extends HierarchicalState {
    260         &#64;Override public void enter() {
    261             Log.d(TAG, "mP1.enter");
    262         }
    263         &#64;Override public boolean processMessage(Message message) {
    264             boolean retVal;
    265             Log.d(TAG, "mP1.processMessage what=" + message.what);
    266             switch(message.what) {
    267             case CMD_2:
    268                 // CMD_2 will arrive in mS2 before CMD_3
    269                 sendMessage(obtainMessage(CMD_3));
    270                 deferMessage(message);
    271                 transitionTo(mS2);
    272                 retVal = HANDLED;
    273                 break;
    274             default:
    275                 // Any message we don't understand in this state invokes unhandledMessage
    276                 retVal = NOT_HANDLED;
    277                 break;
    278             }
    279             return retVal;
    280         }
    281         &#64;Override public void exit() {
    282             Log.d(TAG, "mP1.exit");
    283         }
    284     }
    285 
    286     class S1 extends HierarchicalState {
    287         &#64;Override public void enter() {
    288             Log.d(TAG, "mS1.enter");
    289         }
    290         &#64;Override public boolean processMessage(Message message) {
    291             Log.d(TAG, "S1.processMessage what=" + message.what);
    292             if (message.what == CMD_1) {
    293                 // Transition to ourself to show that enter/exit is called
    294                 transitionTo(mS1);
    295                 return HANDLED;
    296             } else {
    297                 // Let parent process all other messages
    298                 return NOT_HANDLED;
    299             }
    300         }
    301         &#64;Override public void exit() {
    302             Log.d(TAG, "mS1.exit");
    303         }
    304     }
    305 
    306     class S2 extends HierarchicalState {
    307         &#64;Override public void enter() {
    308             Log.d(TAG, "mS2.enter");
    309         }
    310         &#64;Override public boolean processMessage(Message message) {
    311             boolean retVal;
    312             Log.d(TAG, "mS2.processMessage what=" + message.what);
    313             switch(message.what) {
    314             case(CMD_2):
    315                 sendMessage(obtainMessage(CMD_4));
    316                 retVal = HANDLED;
    317                 break;
    318             case(CMD_3):
    319                 deferMessage(message);
    320                 transitionTo(mP2);
    321                 retVal = HANDLED;
    322                 break;
    323             default:
    324                 retVal = NOT_HANDLED;
    325                 break;
    326             }
    327             return retVal;
    328         }
    329         &#64;Override public void exit() {
    330             Log.d(TAG, "mS2.exit");
    331         }
    332     }
    333 
    334     class P2 extends HierarchicalState {
    335         &#64;Override public void enter() {
    336             Log.d(TAG, "mP2.enter");
    337             sendMessage(obtainMessage(CMD_5));
    338         }
    339         &#64;Override public boolean processMessage(Message message) {
    340             Log.d(TAG, "P2.processMessage what=" + message.what);
    341             switch(message.what) {
    342             case(CMD_3):
    343                 break;
    344             case(CMD_4):
    345                 break;
    346             case(CMD_5):
    347                 transitionToHaltingState();
    348                 break;
    349             }
    350             return HANDLED;
    351         }
    352         &#64;Override public void exit() {
    353             Log.d(TAG, "mP2.exit");
    354         }
    355     }
    356 
    357     &#64;Override
    358     void halting() {
    359         Log.d(TAG, "halting");
    360         synchronized (this) {
    361             this.notifyAll();
    362         }
    363     }
    364 
    365     P1 mP1 = new P1();
    366     S1 mS1 = new S1();
    367     S2 mS2 = new S2();
    368     P2 mP2 = new P2();
    369 }
    370 </code>
    371  *
    372  * If this is executed by sending two messages CMD_1 and CMD_2
    373  * (Note the synchronize is only needed because we use hsm.wait())
    374  *
    375  * Hsm1 hsm = makeHsm1();
    376  * synchronize(hsm) {
    377  *      hsm.sendMessage(obtainMessage(hsm.CMD_1));
    378  *      hsm.sendMessage(obtainMessage(hsm.CMD_2));
    379  *      try {
    380  *           // wait for the messages to be handled
    381  *           hsm.wait();
    382  *      } catch (InterruptedException e) {
    383  *           Log.e(TAG, "exception while waiting " + e.getMessage());
    384  *      }
    385  * }
    386  *
    387  *
    388  * The output is:
    389  *
    390  * D/hsm1    ( 1999): makeHsm1 E
    391  * D/hsm1    ( 1999): ctor E
    392  * D/hsm1    ( 1999): ctor X
    393  * D/hsm1    ( 1999): mP1.enter
    394  * D/hsm1    ( 1999): mS1.enter
    395  * D/hsm1    ( 1999): makeHsm1 X
    396  * D/hsm1    ( 1999): mS1.processMessage what=1
    397  * D/hsm1    ( 1999): mS1.exit
    398  * D/hsm1    ( 1999): mS1.enter
    399  * D/hsm1    ( 1999): mS1.processMessage what=2
    400  * D/hsm1    ( 1999): mP1.processMessage what=2
    401  * D/hsm1    ( 1999): mS1.exit
    402  * D/hsm1    ( 1999): mS2.enter
    403  * D/hsm1    ( 1999): mS2.processMessage what=2
    404  * D/hsm1    ( 1999): mS2.processMessage what=3
    405  * D/hsm1    ( 1999): mS2.exit
    406  * D/hsm1    ( 1999): mP1.exit
    407  * D/hsm1    ( 1999): mP2.enter
    408  * D/hsm1    ( 1999): mP2.processMessage what=3
    409  * D/hsm1    ( 1999): mP2.processMessage what=4
    410  * D/hsm1    ( 1999): mP2.processMessage what=5
    411  * D/hsm1    ( 1999): mP2.exit
    412  * D/hsm1    ( 1999): halting
    413  *
    414  */
    415 public class HierarchicalStateMachine {
    416 
    417     private static final String TAG = "HierarchicalStateMachine";
    418     private String mName;
    419 
    420     /** Message.what value when quitting */
    421     public static final int HSM_QUIT_CMD = -1;
    422 
    423     /** Message.what value when initializing */
    424     public static final int HSM_INIT_CMD = -1;
    425 
    426     /**
    427      * Convenience constant that maybe returned by processMessage
    428      * to indicate the the message was processed and is not to be
    429      * processed by parent states
    430      */
    431     public static final boolean HANDLED = true;
    432 
    433     /**
    434      * Convenience constant that maybe returned by processMessage
    435      * to indicate the the message was NOT processed and is to be
    436      * processed by parent states
    437      */
    438     public static final boolean NOT_HANDLED = false;
    439 
    440     private static class HsmHandler extends Handler {
    441 
    442         /** The debug flag */
    443         private boolean mDbg = false;
    444 
    445         /** The quit object */
    446         private static final Object mQuitObj = new Object();
    447 
    448         /** The initialization message */
    449         private static final Message mInitMsg = null;
    450 
    451         /** The current message */
    452         private Message mMsg;
    453 
    454         /** A list of messages that this state machine has processed */
    455         private ProcessedMessages mProcessedMessages = new ProcessedMessages();
    456 
    457         /** true if construction of the state machine has not been completed */
    458         private boolean mIsConstructionCompleted;
    459 
    460         /** Stack used to manage the current hierarchy of states */
    461         private StateInfo mStateStack[];
    462 
    463         /** Top of mStateStack */
    464         private int mStateStackTopIndex = -1;
    465 
    466         /** A temporary stack used to manage the state stack */
    467         private StateInfo mTempStateStack[];
    468 
    469         /** The top of the mTempStateStack */
    470         private int mTempStateStackCount;
    471 
    472         /** State used when state machine is halted */
    473         private HaltingState mHaltingState = new HaltingState();
    474 
    475         /** State used when state machine is quitting */
    476         private QuittingState mQuittingState = new QuittingState();
    477 
    478         /** Reference to the HierarchicalStateMachine */
    479         private HierarchicalStateMachine mHsm;
    480 
    481         /**
    482          * Information about a state.
    483          * Used to maintain the hierarchy.
    484          */
    485         private class StateInfo {
    486             /** The state */
    487             HierarchicalState state;
    488 
    489             /** The parent of this state, null if there is no parent */
    490             StateInfo parentStateInfo;
    491 
    492             /** True when the state has been entered and on the stack */
    493             boolean active;
    494 
    495             /**
    496              * Convert StateInfo to string
    497              */
    498             @Override
    499             public String toString() {
    500                 return "state=" + state.getName() + ",active=" + active
    501                         + ",parent=" + ((parentStateInfo == null) ?
    502                                         "null" : parentStateInfo.state.getName());
    503             }
    504         }
    505 
    506         /** The map of all of the states in the state machine */
    507         private HashMap<HierarchicalState, StateInfo> mStateInfo =
    508             new HashMap<HierarchicalState, StateInfo>();
    509 
    510         /** The initial state that will process the first message */
    511         private HierarchicalState mInitialState;
    512 
    513         /** The destination state when transitionTo has been invoked */
    514         private HierarchicalState mDestState;
    515 
    516         /** The list of deferred messages */
    517         private ArrayList<Message> mDeferredMessages = new ArrayList<Message>();
    518 
    519         /**
    520          * State entered when transitionToHaltingState is called.
    521          */
    522         private class HaltingState extends HierarchicalState {
    523             @Override
    524             public boolean processMessage(Message msg) {
    525                 mHsm.haltedProcessMessage(msg);
    526                 return true;
    527             }
    528         }
    529 
    530         /**
    531          * State entered when a valid quit message is handled.
    532          */
    533         private class QuittingState extends HierarchicalState {
    534             @Override
    535             public boolean processMessage(Message msg) {
    536                 return NOT_HANDLED;
    537             }
    538         }
    539 
    540         /**
    541          * Handle messages sent to the state machine by calling
    542          * the current state's processMessage. It also handles
    543          * the enter/exit calls and placing any deferred messages
    544          * back onto the queue when transitioning to a new state.
    545          */
    546         @Override
    547         public final void handleMessage(Message msg) {
    548             if (mDbg) Log.d(TAG, "handleMessage: E msg.what=" + msg.what);
    549 
    550             /** Save the current message */
    551             mMsg = msg;
    552 
    553             /**
    554              * Check that construction was completed
    555              */
    556             if (!mIsConstructionCompleted) {
    557                 Log.e(TAG, "The start method not called, ignore msg: " + msg);
    558                 return;
    559             }
    560 
    561             /**
    562              * Process the message abiding by the hierarchical semantics
    563              * and perform any requested transitions.
    564              */
    565             processMsg(msg);
    566             performTransitions();
    567 
    568             if (mDbg) Log.d(TAG, "handleMessage: X");
    569         }
    570 
    571         /**
    572          * Do any transitions
    573          */
    574         private void performTransitions() {
    575             /**
    576              * If transitionTo has been called, exit and then enter
    577              * the appropriate states. We loop on this to allow
    578              * enter and exit methods to use transitionTo.
    579              */
    580             HierarchicalState destState = null;
    581             while (mDestState != null) {
    582                 if (mDbg) Log.d(TAG, "handleMessage: new destination call exit");
    583 
    584                 /**
    585                  * Save mDestState locally and set to null
    586                  * to know if enter/exit use transitionTo.
    587                  */
    588                 destState = mDestState;
    589                 mDestState = null;
    590 
    591                 /**
    592                  * Determine the states to exit and enter and return the
    593                  * common ancestor state of the enter/exit states. Then
    594                  * invoke the exit methods then the enter methods.
    595                  */
    596                 StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState);
    597                 invokeExitMethods(commonStateInfo);
    598                 int stateStackEnteringIndex = moveTempStateStackToStateStack();
    599                 invokeEnterMethods(stateStackEnteringIndex);
    600 
    601 
    602                 /**
    603                  * Since we have transitioned to a new state we need to have
    604                  * any deferred messages moved to the front of the message queue
    605                  * so they will be processed before any other messages in the
    606                  * message queue.
    607                  */
    608                 moveDeferredMessageAtFrontOfQueue();
    609             }
    610 
    611             /**
    612              * After processing all transitions check and
    613              * see if the last transition was to quit or halt.
    614              */
    615             if (destState != null) {
    616                 if (destState == mQuittingState) {
    617                     /**
    618                      * We are quitting so ignore all messages.
    619                      */
    620                     mHsm.quitting();
    621                     if (mHsm.mHsmThread != null) {
    622                         // If we made the thread then quit looper
    623                         getLooper().quit();
    624                     }
    625                 } else if (destState == mHaltingState) {
    626                     /**
    627                      * Call halting() if we've transitioned to the halting
    628                      * state. All subsequent messages will be processed in
    629                      * in the halting state which invokes haltedProcessMessage(msg);
    630                      */
    631                     mHsm.halting();
    632                 }
    633             }
    634         }
    635 
    636         /**
    637          * Complete the construction of the state machine.
    638          */
    639         private final void completeConstruction() {
    640             if (mDbg) Log.d(TAG, "completeConstruction: E");
    641 
    642             /**
    643              * Determine the maximum depth of the state hierarchy
    644              * so we can allocate the state stacks.
    645              */
    646             int maxDepth = 0;
    647             for (StateInfo si : mStateInfo.values()) {
    648                 int depth = 0;
    649                 for (StateInfo i = si; i != null; depth++) {
    650                     i = i.parentStateInfo;
    651                 }
    652                 if (maxDepth < depth) {
    653                     maxDepth = depth;
    654                 }
    655             }
    656             if (mDbg) Log.d(TAG, "completeConstruction: maxDepth=" + maxDepth);
    657 
    658             mStateStack = new StateInfo[maxDepth];
    659             mTempStateStack = new StateInfo[maxDepth];
    660             setupInitialStateStack();
    661 
    662             /**
    663              * Construction is complete call all enter methods
    664              * starting at the first entry.
    665              */
    666             mIsConstructionCompleted = true;
    667             mMsg = obtainMessage(HSM_INIT_CMD);
    668             invokeEnterMethods(0);
    669 
    670             /**
    671              * Perform any transitions requested by the enter methods
    672              */
    673             performTransitions();
    674 
    675             if (mDbg) Log.d(TAG, "completeConstruction: X");
    676         }
    677 
    678         /**
    679          * Process the message. If the current state doesn't handle
    680          * it, call the states parent and so on. If it is never handled then
    681          * call the state machines unhandledMessage method.
    682          */
    683         private final void processMsg(Message msg) {
    684             StateInfo curStateInfo = mStateStack[mStateStackTopIndex];
    685             if (mDbg) {
    686                 Log.d(TAG, "processMsg: " + curStateInfo.state.getName());
    687             }
    688             while (!curStateInfo.state.processMessage(msg)) {
    689                 /**
    690                  * Not processed
    691                  */
    692                 curStateInfo = curStateInfo.parentStateInfo;
    693                 if (curStateInfo == null) {
    694                     /**
    695                      * No parents left so it's not handled
    696                      */
    697                     mHsm.unhandledMessage(msg);
    698                     if (isQuit(msg)) {
    699                         transitionTo(mQuittingState);
    700                     }
    701                     break;
    702                 }
    703                 if (mDbg) {
    704                     Log.d(TAG, "processMsg: " + curStateInfo.state.getName());
    705                 }
    706             }
    707 
    708             /**
    709              * Record that we processed the message
    710              */
    711             if (curStateInfo != null) {
    712                 HierarchicalState orgState = mStateStack[mStateStackTopIndex].state;
    713                 mProcessedMessages.add(msg, curStateInfo.state, orgState);
    714             } else {
    715                 mProcessedMessages.add(msg, null, null);
    716             }
    717         }
    718 
    719         /**
    720          * Call the exit method for each state from the top of stack
    721          * up to the common ancestor state.
    722          */
    723         private final void invokeExitMethods(StateInfo commonStateInfo) {
    724             while ((mStateStackTopIndex >= 0) &&
    725                     (mStateStack[mStateStackTopIndex] != commonStateInfo)) {
    726                 HierarchicalState curState = mStateStack[mStateStackTopIndex].state;
    727                 if (mDbg) Log.d(TAG, "invokeExitMethods: " + curState.getName());
    728                 curState.exit();
    729                 mStateStack[mStateStackTopIndex].active = false;
    730                 mStateStackTopIndex -= 1;
    731             }
    732         }
    733 
    734         /**
    735          * Invoke the enter method starting at the entering index to top of state stack
    736          */
    737         private final void invokeEnterMethods(int stateStackEnteringIndex) {
    738             for (int i = stateStackEnteringIndex; i <= mStateStackTopIndex; i++) {
    739                 if (mDbg) Log.d(TAG, "invokeEnterMethods: " + mStateStack[i].state.getName());
    740                 mStateStack[i].state.enter();
    741                 mStateStack[i].active = true;
    742             }
    743         }
    744 
    745         /**
    746          * Move the deferred message to the front of the message queue.
    747          */
    748         private final void moveDeferredMessageAtFrontOfQueue() {
    749             /**
    750              * The oldest messages on the deferred list must be at
    751              * the front of the queue so start at the back, which
    752              * as the most resent message and end with the oldest
    753              * messages at the front of the queue.
    754              */
    755             for (int i = mDeferredMessages.size() - 1; i >= 0; i-- ) {
    756                 Message curMsg = mDeferredMessages.get(i);
    757                 if (mDbg) Log.d(TAG, "moveDeferredMessageAtFrontOfQueue; what=" + curMsg.what);
    758                 sendMessageAtFrontOfQueue(curMsg);
    759             }
    760             mDeferredMessages.clear();
    761         }
    762 
    763         /**
    764          * Move the contents of the temporary stack to the state stack
    765          * reversing the order of the items on the temporary stack as
    766          * they are moved.
    767          *
    768          * @return index into mStateState where entering needs to start
    769          */
    770         private final int moveTempStateStackToStateStack() {
    771             int startingIndex = mStateStackTopIndex + 1;
    772             int i = mTempStateStackCount - 1;
    773             int j = startingIndex;
    774             while (i >= 0) {
    775                 if (mDbg) Log.d(TAG, "moveTempStackToStateStack: i=" + i + ",j=" + j);
    776                 mStateStack[j] = mTempStateStack[i];
    777                 j += 1;
    778                 i -= 1;
    779             }
    780 
    781             mStateStackTopIndex = j - 1;
    782             if (mDbg) {
    783                 Log.d(TAG, "moveTempStackToStateStack: X mStateStackTop="
    784                       + mStateStackTopIndex + ",startingIndex=" + startingIndex
    785                       + ",Top=" + mStateStack[mStateStackTopIndex].state.getName());
    786             }
    787             return startingIndex;
    788         }
    789 
    790         /**
    791          * Setup the mTempStateStack with the states we are going to enter.
    792          *
    793          * This is found by searching up the destState's ancestors for a
    794          * state that is already active i.e. StateInfo.active == true.
    795          * The destStae and all of its inactive parents will be on the
    796          * TempStateStack as the list of states to enter.
    797          *
    798          * @return StateInfo of the common ancestor for the destState and
    799          * current state or null if there is no common parent.
    800          */
    801         private final StateInfo setupTempStateStackWithStatesToEnter(HierarchicalState destState) {
    802             /**
    803              * Search up the parent list of the destination state for an active
    804              * state. Use a do while() loop as the destState must always be entered
    805              * even if it is active. This can happen if we are exiting/entering
    806              * the current state.
    807              */
    808             mTempStateStackCount = 0;
    809             StateInfo curStateInfo = mStateInfo.get(destState);
    810             do {
    811                 mTempStateStack[mTempStateStackCount++] = curStateInfo;
    812                 curStateInfo = curStateInfo.parentStateInfo;
    813             } while ((curStateInfo != null) && !curStateInfo.active);
    814 
    815             if (mDbg) {
    816                 Log.d(TAG, "setupTempStateStackWithStatesToEnter: X mTempStateStackCount="
    817                       + mTempStateStackCount + ",curStateInfo: " + curStateInfo);
    818             }
    819             return curStateInfo;
    820         }
    821 
    822         /**
    823          * Initialize StateStack to mInitialState.
    824          */
    825         private final void setupInitialStateStack() {
    826             if (mDbg) {
    827                 Log.d(TAG, "setupInitialStateStack: E mInitialState="
    828                     + mInitialState.getName());
    829             }
    830 
    831             StateInfo curStateInfo = mStateInfo.get(mInitialState);
    832             for (mTempStateStackCount = 0; curStateInfo != null; mTempStateStackCount++) {
    833                 mTempStateStack[mTempStateStackCount] = curStateInfo;
    834                 curStateInfo = curStateInfo.parentStateInfo;
    835             }
    836 
    837             // Empty the StateStack
    838             mStateStackTopIndex = -1;
    839 
    840             moveTempStateStackToStateStack();
    841         }
    842 
    843         /**
    844          * @return current message
    845          */
    846         private final Message getCurrentMessage() {
    847             return mMsg;
    848         }
    849 
    850         /**
    851          * @return current state
    852          */
    853         private final HierarchicalState getCurrentState() {
    854             return mStateStack[mStateStackTopIndex].state;
    855         }
    856 
    857         /**
    858          * Add a new state to the state machine. Bottom up addition
    859          * of states is allowed but the same state may only exist
    860          * in one hierarchy.
    861          *
    862          * @param state the state to add
    863          * @param parent the parent of state
    864          * @return stateInfo for this state
    865          */
    866         private final StateInfo addState(HierarchicalState state, HierarchicalState parent) {
    867             if (mDbg) {
    868                 Log.d(TAG, "addStateInternal: E state=" + state.getName()
    869                         + ",parent=" + ((parent == null) ? "" : parent.getName()));
    870             }
    871             StateInfo parentStateInfo = null;
    872             if (parent != null) {
    873                 parentStateInfo = mStateInfo.get(parent);
    874                 if (parentStateInfo == null) {
    875                     // Recursively add our parent as it's not been added yet.
    876                     parentStateInfo = addState(parent, null);
    877                 }
    878             }
    879             StateInfo stateInfo = mStateInfo.get(state);
    880             if (stateInfo == null) {
    881                 stateInfo = new StateInfo();
    882                 mStateInfo.put(state, stateInfo);
    883             }
    884 
    885             // Validate that we aren't adding the same state in two different hierarchies.
    886             if ((stateInfo.parentStateInfo != null) &&
    887                     (stateInfo.parentStateInfo != parentStateInfo)) {
    888                     throw new RuntimeException("state already added");
    889             }
    890             stateInfo.state = state;
    891             stateInfo.parentStateInfo = parentStateInfo;
    892             stateInfo.active = false;
    893             if (mDbg) Log.d(TAG, "addStateInternal: X stateInfo: " + stateInfo);
    894             return stateInfo;
    895         }
    896 
    897         /**
    898          * Constructor
    899          *
    900          * @param looper for dispatching messages
    901          * @param hsm the hierarchical state machine
    902          */
    903         private HsmHandler(Looper looper, HierarchicalStateMachine hsm) {
    904             super(looper);
    905             mHsm = hsm;
    906 
    907             addState(mHaltingState, null);
    908             addState(mQuittingState, null);
    909         }
    910 
    911         /** @see HierarchicalStateMachine#setInitialState(HierarchicalState) */
    912         private final void setInitialState(HierarchicalState initialState) {
    913             if (mDbg) Log.d(TAG, "setInitialState: initialState" + initialState.getName());
    914             mInitialState = initialState;
    915         }
    916 
    917         /** @see HierarchicalStateMachine#transitionTo(HierarchicalState) */
    918         private final void transitionTo(HierarchicalState destState) {
    919             if (mDbg) Log.d(TAG, "StateMachine.transitionTo EX destState" + destState.getName());
    920             mDestState = destState;
    921         }
    922 
    923         /** @see HierarchicalStateMachine#deferMessage(Message) */
    924         private final void deferMessage(Message msg) {
    925             if (mDbg) Log.d(TAG, "deferMessage: msg=" + msg.what);
    926 
    927             /* Copy the "msg" to "newMsg" as "msg" will be recycled */
    928             Message newMsg = obtainMessage();
    929             newMsg.copyFrom(msg);
    930 
    931             mDeferredMessages.add(newMsg);
    932         }
    933 
    934         /** @see HierarchicalStateMachine#deferMessage(Message) */
    935         private final void quit() {
    936             if (mDbg) Log.d(TAG, "quit:");
    937             sendMessage(obtainMessage(HSM_QUIT_CMD, mQuitObj));
    938         }
    939 
    940         /** @see HierarchicalStateMachine#isQuit(Message) */
    941         private final boolean isQuit(Message msg) {
    942             return (msg.what == HSM_QUIT_CMD) && (msg.obj == mQuitObj);
    943         }
    944 
    945         /** @see HierarchicalStateMachine#isDbg() */
    946         private final boolean isDbg() {
    947             return mDbg;
    948         }
    949 
    950         /** @see HierarchicalStateMachine#setDbg(boolean) */
    951         private final void setDbg(boolean dbg) {
    952             mDbg = dbg;
    953         }
    954 
    955         /** @see HierarchicalStateMachine#setProcessedMessagesSize(int) */
    956         private final void setProcessedMessagesSize(int maxSize) {
    957             mProcessedMessages.setSize(maxSize);
    958         }
    959 
    960         /** @see HierarchicalStateMachine#getProcessedMessagesSize() */
    961         private final int getProcessedMessagesSize() {
    962             return mProcessedMessages.size();
    963         }
    964 
    965         /** @see HierarchicalStateMachine#getProcessedMessagesCount() */
    966         private final int getProcessedMessagesCount() {
    967             return mProcessedMessages.count();
    968         }
    969 
    970         /** @see HierarchicalStateMachine#getProcessedMessage(int) */
    971         private final ProcessedMessages.Info getProcessedMessage(int index) {
    972             return mProcessedMessages.get(index);
    973         }
    974 
    975     }
    976 
    977     private HsmHandler mHsmHandler;
    978     private HandlerThread mHsmThread;
    979 
    980     /**
    981      * Initialize.
    982      *
    983      * @param looper for this state machine
    984      * @param name of the state machine
    985      */
    986     private void initStateMachine(String name, Looper looper) {
    987         mName = name;
    988         mHsmHandler = new HsmHandler(looper, this);
    989     }
    990 
    991     /**
    992      * Constructor creates an HSM with its own thread.
    993      *
    994      * @param name of the state machine
    995      */
    996     protected HierarchicalStateMachine(String name) {
    997         mHsmThread = new HandlerThread(name);
    998         mHsmThread.start();
    999         Looper looper = mHsmThread.getLooper();
   1000 
   1001         initStateMachine(name, looper);
   1002     }
   1003 
   1004     /**
   1005      * Constructor creates an HSMStateMachine using the looper.
   1006      *
   1007      * @param name of the state machine
   1008      */
   1009     protected HierarchicalStateMachine(String name, Looper looper) {
   1010         initStateMachine(name, looper);
   1011     }
   1012 
   1013     /**
   1014      * Add a new state to the state machine
   1015      * @param state the state to add
   1016      * @param parent the parent of state
   1017      */
   1018     protected final void addState(HierarchicalState state, HierarchicalState parent) {
   1019         mHsmHandler.addState(state, parent);
   1020     }
   1021 
   1022     /**
   1023      * @return current message
   1024      */
   1025     protected final Message getCurrentMessage() {
   1026         return mHsmHandler.getCurrentMessage();
   1027     }
   1028 
   1029     /**
   1030      * @return current state
   1031      */
   1032     protected final HierarchicalState getCurrentState() {
   1033         return mHsmHandler.getCurrentState();
   1034     }
   1035 
   1036     /**
   1037      * Add a new state to the state machine, parent will be null
   1038      * @param state to add
   1039      */
   1040     protected final void addState(HierarchicalState state) {
   1041         mHsmHandler.addState(state, null);
   1042     }
   1043 
   1044     /**
   1045      * Set the initial state. This must be invoked before
   1046      * and messages are sent to the state machine.
   1047      *
   1048      * @param initialState is the state which will receive the first message.
   1049      */
   1050     protected final void setInitialState(HierarchicalState initialState) {
   1051         mHsmHandler.setInitialState(initialState);
   1052     }
   1053 
   1054     /**
   1055      * transition to destination state. Upon returning
   1056      * from processMessage the current state's exit will
   1057      * be executed and upon the next message arriving
   1058      * destState.enter will be invoked.
   1059      *
   1060      * @param destState will be the state that receives the next message.
   1061      */
   1062     protected final void transitionTo(HierarchicalState destState) {
   1063         mHsmHandler.transitionTo(destState);
   1064     }
   1065 
   1066     /**
   1067      * transition to halt state. Upon returning
   1068      * from processMessage we will exit all current
   1069      * states, execute the halting() method and then
   1070      * all subsequent messages haltedProcessMesage
   1071      * will be called.
   1072      */
   1073     protected final void transitionToHaltingState() {
   1074         mHsmHandler.transitionTo(mHsmHandler.mHaltingState);
   1075     }
   1076 
   1077     /**
   1078      * Defer this message until next state transition.
   1079      * Upon transitioning all deferred messages will be
   1080      * placed on the queue and reprocessed in the original
   1081      * order. (i.e. The next state the oldest messages will
   1082      * be processed first)
   1083      *
   1084      * @param msg is deferred until the next transition.
   1085      */
   1086     protected final void deferMessage(Message msg) {
   1087         mHsmHandler.deferMessage(msg);
   1088     }
   1089 
   1090 
   1091     /**
   1092      * Called when message wasn't handled
   1093      *
   1094      * @param msg that couldn't be handled.
   1095      */
   1096     protected void unhandledMessage(Message msg) {
   1097         if (false) {
   1098             Log.e(TAG, mName + " - unhandledMessage: msg.what=" + msg.what);
   1099         }
   1100     }
   1101 
   1102     /**
   1103      * Called for any message that is received after
   1104      * transitionToHalting is called.
   1105      */
   1106     protected void haltedProcessMessage(Message msg) {
   1107     }
   1108 
   1109     /**
   1110      * Called after the message that called transitionToHalting
   1111      * is called and should be overridden by StateMachine's that
   1112      * call transitionToHalting.
   1113      */
   1114     protected void halting() {
   1115     }
   1116 
   1117     /**
   1118      * Called after the quitting message was NOT handled and
   1119      * just before the quit actually occurs.
   1120      */
   1121     protected void quitting() {
   1122     }
   1123 
   1124     /**
   1125      * @return the name
   1126      */
   1127     public final String getName() {
   1128         return mName;
   1129     }
   1130 
   1131     /**
   1132      * Set size of messages to maintain and clears all current messages.
   1133      *
   1134      * @param maxSize number of messages to maintain at anyone time.
   1135      */
   1136     public final void setProcessedMessagesSize(int maxSize) {
   1137         mHsmHandler.setProcessedMessagesSize(maxSize);
   1138     }
   1139 
   1140     /**
   1141      * @return number of messages processed
   1142      */
   1143     public final int getProcessedMessagesSize() {
   1144         return mHsmHandler.getProcessedMessagesSize();
   1145     }
   1146 
   1147     /**
   1148      * @return the total number of messages processed
   1149      */
   1150     public final int getProcessedMessagesCount() {
   1151         return mHsmHandler.getProcessedMessagesCount();
   1152     }
   1153 
   1154     /**
   1155      * @return a processed message
   1156      */
   1157     public final ProcessedMessages.Info getProcessedMessage(int index) {
   1158         return mHsmHandler.getProcessedMessage(index);
   1159     }
   1160 
   1161     /**
   1162      * @return Handler
   1163      */
   1164     public final Handler getHandler() {
   1165         return mHsmHandler;
   1166     }
   1167 
   1168     /**
   1169      * Get a message and set Message.target = this.
   1170      *
   1171      * @return message
   1172      */
   1173     public final Message obtainMessage()
   1174     {
   1175         return Message.obtain(mHsmHandler);
   1176     }
   1177 
   1178     /**
   1179      * Get a message and set Message.target = this and what
   1180      *
   1181      * @param what is the assigned to Message.what.
   1182      * @return message
   1183      */
   1184     public final Message obtainMessage(int what) {
   1185         return Message.obtain(mHsmHandler, what);
   1186     }
   1187 
   1188     /**
   1189      * Get a message and set Message.target = this,
   1190      * what and obj.
   1191      *
   1192      * @param what is the assigned to Message.what.
   1193      * @param obj is assigned to Message.obj.
   1194      * @return message
   1195      */
   1196     public final Message obtainMessage(int what, Object obj)
   1197     {
   1198         return Message.obtain(mHsmHandler, what, obj);
   1199     }
   1200 
   1201     /**
   1202      * Enqueue a message to this state machine.
   1203      */
   1204     public final void sendMessage(int what) {
   1205         mHsmHandler.sendMessage(obtainMessage(what));
   1206     }
   1207 
   1208     /**
   1209      * Enqueue a message to this state machine.
   1210      */
   1211     public final void sendMessage(int what, Object obj) {
   1212         mHsmHandler.sendMessage(obtainMessage(what,obj));
   1213     }
   1214 
   1215     /**
   1216      * Enqueue a message to this state machine.
   1217      */
   1218     public final void sendMessage(Message msg) {
   1219         mHsmHandler.sendMessage(msg);
   1220     }
   1221 
   1222     /**
   1223      * Enqueue a message to this state machine after a delay.
   1224      */
   1225     public final void sendMessageDelayed(int what, long delayMillis) {
   1226         mHsmHandler.sendMessageDelayed(obtainMessage(what), delayMillis);
   1227     }
   1228 
   1229     /**
   1230      * Enqueue a message to this state machine after a delay.
   1231      */
   1232     public final void sendMessageDelayed(int what, Object obj, long delayMillis) {
   1233         mHsmHandler.sendMessageDelayed(obtainMessage(what, obj), delayMillis);
   1234     }
   1235 
   1236     /**
   1237      * Enqueue a message to this state machine after a delay.
   1238      */
   1239     public final void sendMessageDelayed(Message msg, long delayMillis) {
   1240         mHsmHandler.sendMessageDelayed(msg, delayMillis);
   1241     }
   1242 
   1243     /**
   1244      * Enqueue a message to the front of the queue for this state machine.
   1245      * Protected, may only be called by instances of HierarchicalStateMachine.
   1246      */
   1247     protected final void sendMessageAtFrontOfQueue(int what, Object obj) {
   1248         mHsmHandler.sendMessageAtFrontOfQueue(obtainMessage(what, obj));
   1249     }
   1250 
   1251     /**
   1252      * Enqueue a message to the front of the queue for this state machine.
   1253      * Protected, may only be called by instances of HierarchicalStateMachine.
   1254      */
   1255     protected final void sendMessageAtFrontOfQueue(int what) {
   1256         mHsmHandler.sendMessageAtFrontOfQueue(obtainMessage(what));
   1257     }
   1258 
   1259     /**
   1260      * Enqueue a message to the front of the queue for this state machine.
   1261      * Protected, may only be called by instances of HierarchicalStateMachine.
   1262      */
   1263     protected final void sendMessageAtFrontOfQueue(Message msg) {
   1264         mHsmHandler.sendMessageAtFrontOfQueue(msg);
   1265     }
   1266 
   1267     /**
   1268      * Conditionally quit the looper and stop execution.
   1269      *
   1270      * This sends the HSM_QUIT_MSG to the state machine and
   1271      * if not handled by any state's processMessage then the
   1272      * state machine will be stopped and no further messages
   1273      * will be processed.
   1274      */
   1275     public final void quit() {
   1276         mHsmHandler.quit();
   1277     }
   1278 
   1279     /**
   1280      * @return ture if msg is quit
   1281      */
   1282     protected final boolean isQuit(Message msg) {
   1283         return mHsmHandler.isQuit(msg);
   1284     }
   1285 
   1286     /**
   1287      * @return if debugging is enabled
   1288      */
   1289     public boolean isDbg() {
   1290         return mHsmHandler.isDbg();
   1291     }
   1292 
   1293     /**
   1294      * Set debug enable/disabled.
   1295      *
   1296      * @param dbg is true to enable debugging.
   1297      */
   1298     public void setDbg(boolean dbg) {
   1299         mHsmHandler.setDbg(dbg);
   1300     }
   1301 
   1302     /**
   1303      * Start the state machine.
   1304      */
   1305     public void start() {
   1306         /** Send the complete construction message */
   1307         mHsmHandler.completeConstruction();
   1308     }
   1309 }
   1310