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