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