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