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