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