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