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