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