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