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