StateMachine.java revision c211978650580904a4b11b7c5b899e9d2f7ed294
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 java.io.FileDescriptor;
27import java.io.PrintWriter;
28import java.io.StringWriter;
29import java.util.ArrayList;
30import java.util.Calendar;
31import java.util.Collection;
32import java.util.Iterator;
33import java.util.HashMap;
34import 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 this return the name of 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 StateMachines 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<code>
63        mP1
64       /   \
65      mS2   mS1 ----> initial state
66</code>
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 and 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 never processed
77 * <code>unhandledMessage</code> will be invoked to give one last chance for the state machine
78 * 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>onQuiting</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</exit> 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<code>
115          mP0
116         /   \
117        mP1   mS0
118       /   \
119      mS2   mS1
120     /  \    \
121    mS3  mS4  mS5  ---> initial state
122</code>
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<code>
139class 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
161void testHelloWorld() {
162    HelloWorld hw = makeHelloWorld();
163    hw.sendMessage(hw.obtainMessage());
164}
165</code>
166 * <p>A more interesting state machine is one with four states
167 * with two independent parent states.</p>
168<code>
169        mP1      mP2
170       /   \
171      mS2   mS1
172</code>
173 * <p>Here is a description of this state machine using pseudo code.</p>
174 <code>
175state 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             transitonTo(mS2);
183             return HANDLED;
184         }
185         return NOT_HANDLED;
186     }
187}
188
189INITIAL
190state 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
202state 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
219state 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</code>
235 * <p>The implementation is below and also in StateMachineTest:</p>
236<code>
237class 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</code>
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<code>
382Hsm1 hsm = makeHsm1();
383synchronize(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</code>
394 * <p>The output is:</p>
395<code>
396D/hsm1    ( 1999): makeHsm1 E
397D/hsm1    ( 1999): ctor E
398D/hsm1    ( 1999): ctor X
399D/hsm1    ( 1999): mP1.enter
400D/hsm1    ( 1999): mS1.enter
401D/hsm1    ( 1999): makeHsm1 X
402D/hsm1    ( 1999): mS1.processMessage what=1
403D/hsm1    ( 1999): mS1.exit
404D/hsm1    ( 1999): mS1.enter
405D/hsm1    ( 1999): mS1.processMessage what=2
406D/hsm1    ( 1999): mP1.processMessage what=2
407D/hsm1    ( 1999): mS1.exit
408D/hsm1    ( 1999): mS2.enter
409D/hsm1    ( 1999): mS2.processMessage what=2
410D/hsm1    ( 1999): mS2.processMessage what=3
411D/hsm1    ( 1999): mS2.exit
412D/hsm1    ( 1999): mP1.exit
413D/hsm1    ( 1999): mP2.enter
414D/hsm1    ( 1999): mP2.processMessage what=3
415D/hsm1    ( 1999): mP2.processMessage what=4
416D/hsm1    ( 1999): mP2.processMessage what=5
417D/hsm1    ( 1999): mP2.exit
418D/hsm1    ( 1999): halting
419</code>
420 */
421public 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         */
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         */
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         */
493        public long getTime() {
494            return mTime;
495        }
496
497        /**
498         * @return msg.what
499         */
500        public long getWhat() {
501            return mWhat;
502        }
503
504        /**
505         * @return the command that was executing
506         */
507        public String getInfo() {
508            return mInfo;
509        }
510
511        /**
512         * @return the state that handled this message
513         */
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         */
521        public IState getDestState() {
522            return mDstState;
523        }
524
525        /**
526         * @return the original state that received the message.
527         */
528        public IState getOriginalState() {
529            return mOrgState;
530        }
531
532        @Override
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         */
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        */
595        synchronized void setSize(int maxSize) {
596            mMaxSize = maxSize;
597            mCount = 0;
598            mLogRecVector.clear();
599        }
600
601        synchronized void setLogOnlyTransitions(boolean enable) {
602            mLogOnlyTransitions = enable;
603        }
604
605        synchronized boolean logOnlyTransitions() {
606            return mLogOnlyTransitions;
607        }
608
609        /**
610         * @return the number of recent records.
611         */
612        synchronized int size() {
613            return mLogRecVector.size();
614        }
615
616        /**
617         * @return the total number of records processed since size was set.
618         */
619        synchronized int count() {
620            return mCount;
621        }
622
623        /**
624         * Clear the list of records.
625         */
626        synchronized void cleanup() {
627            mLogRecVector.clear();
628        }
629
630        /**
631         * @return the information on a particular record. 0 is the oldest
632         * record and size()-1 is the newest record. If the index is to
633         * large null is returned.
634         */
635        synchronized LogRec get(int index) {
636            int nextIndex = mOldestIndex + index;
637            if (nextIndex >= mMaxSize) {
638                nextIndex -= mMaxSize;
639            }
640            if (nextIndex >= size()) {
641                return null;
642            } else {
643                return mLogRecVector.get(nextIndex);
644            }
645        }
646
647        /**
648         * Add a processed message.
649         *
650         * @param msg
651         * @param messageInfo to be stored
652         * @param state that handled the message
653         * @param orgState is the first state the received the message but
654         * did not processes the message.
655         * @param transToState is the state that was transitioned to after the message was
656         * processed.
657         *
658         */
659        synchronized void add(StateMachine sm, Message msg, String messageInfo, IState state,
660                IState orgState, IState transToState) {
661            mCount += 1;
662            if (mLogRecVector.size() < mMaxSize) {
663                mLogRecVector.add(new LogRec(sm, msg, messageInfo, state, orgState, transToState));
664            } else {
665                LogRec pmi = mLogRecVector.get(mOldestIndex);
666                mOldestIndex += 1;
667                if (mOldestIndex >= mMaxSize) {
668                    mOldestIndex = 0;
669                }
670                pmi.update(sm, msg, messageInfo, state, orgState, transToState);
671            }
672        }
673    }
674
675    private static class SmHandler extends Handler {
676
677        /** true if StateMachine has quit */
678        private boolean mHasQuit = false;
679
680        /** The debug flag */
681        private boolean mDbg = false;
682
683        /** The SmHandler object, identifies that message is internal */
684        private static final Object mSmHandlerObj = new Object();
685
686        /** The current message */
687        private Message mMsg;
688
689        /** A list of log records including messages this state machine has processed */
690        private LogRecords mLogRecords = new LogRecords();
691
692        /** true if construction of the state machine has not been completed */
693        private boolean mIsConstructionCompleted;
694
695        /** Stack used to manage the current hierarchy of states */
696        private StateInfo mStateStack[];
697
698        /** Top of mStateStack */
699        private int mStateStackTopIndex = -1;
700
701        /** A temporary stack used to manage the state stack */
702        private StateInfo mTempStateStack[];
703
704        /** The top of the mTempStateStack */
705        private int mTempStateStackCount;
706
707        /** State used when state machine is halted */
708        private HaltingState mHaltingState = new HaltingState();
709
710        /** State used when state machine is quitting */
711        private QuittingState mQuittingState = new QuittingState();
712
713        /** Reference to the StateMachine */
714        private StateMachine mSm;
715
716        /**
717         * Information about a state.
718         * Used to maintain the hierarchy.
719         */
720        private class StateInfo {
721            /** The state */
722            State state;
723
724            /** The parent of this state, null if there is no parent */
725            StateInfo parentStateInfo;
726
727            /** True when the state has been entered and on the stack */
728            boolean active;
729
730            /**
731             * Convert StateInfo to string
732             */
733            @Override
734            public String toString() {
735                return "state=" + state.getName() + ",active=" + active + ",parent="
736                        + ((parentStateInfo == null) ? "null" : parentStateInfo.state.getName());
737            }
738        }
739
740        /** The map of all of the states in the state machine */
741        private HashMap<State, StateInfo> mStateInfo = new HashMap<State, StateInfo>();
742
743        /** The initial state that will process the first message */
744        private State mInitialState;
745
746        /** The destination state when transitionTo has been invoked */
747        private State mDestState;
748
749        /** The list of deferred messages */
750        private ArrayList<Message> mDeferredMessages = new ArrayList<Message>();
751
752        /**
753         * State entered when transitionToHaltingState is called.
754         */
755        private class HaltingState extends State {
756            @Override
757            public boolean processMessage(Message msg) {
758                mSm.haltedProcessMessage(msg);
759                return true;
760            }
761        }
762
763        /**
764         * State entered when a valid quit message is handled.
765         */
766        private class QuittingState extends State {
767            @Override
768            public boolean processMessage(Message msg) {
769                return NOT_HANDLED;
770            }
771        }
772
773        /**
774         * Handle messages sent to the state machine by calling
775         * the current state's processMessage. It also handles
776         * the enter/exit calls and placing any deferred messages
777         * back onto the queue when transitioning to a new state.
778         */
779        @Override
780        public final void handleMessage(Message msg) {
781            if (!mHasQuit) {
782                if (mDbg) mSm.log("handleMessage: E msg.what=" + msg.what);
783
784                /** Save the current message */
785                mMsg = msg;
786
787                /** State that processed the message */
788                State msgProcessedState = null;
789                if (mIsConstructionCompleted) {
790                    /** Normal path */
791                    msgProcessedState = processMsg(msg);
792                } else if (!mIsConstructionCompleted && (mMsg.what == SM_INIT_CMD)
793                        && (mMsg.obj == mSmHandlerObj)) {
794                    /** Initial one time path. */
795                    mIsConstructionCompleted = true;
796                    invokeEnterMethods(0);
797                } else {
798                    throw new RuntimeException("StateMachine.handleMessage: "
799                            + "The start method not called, received msg: " + msg);
800                }
801                performTransitions(msgProcessedState, msg);
802
803                // We need to check if mSm == null here as we could be quitting.
804                if (mDbg && mSm != null) mSm.log("handleMessage: X");
805            }
806        }
807
808        /**
809         * Do any transitions
810         * @param msgProcessedState is the state that processed the message
811         */
812        private void performTransitions(State msgProcessedState, Message msg) {
813            /**
814             * If transitionTo has been called, exit and then enter
815             * the appropriate states. We loop on this to allow
816             * enter and exit methods to use transitionTo.
817             */
818            State orgState = mStateStack[mStateStackTopIndex].state;
819
820            /**
821             * Record whether message needs to be logged before we transition and
822             * and we won't log special messages SM_INIT_CMD or SM_QUIT_CMD which
823             * always set msg.obj to the handler.
824             */
825            boolean recordLogMsg = mSm.recordLogRec(mMsg) && (msg.obj != mSmHandlerObj);
826
827            if (mLogRecords.logOnlyTransitions()) {
828                /** Record only if there is a transition */
829                if (mDestState != null) {
830                    mLogRecords.add(mSm, mMsg, mSm.getLogRecString(mMsg), msgProcessedState,
831                            orgState, mDestState);
832                }
833            } else if (recordLogMsg) {
834                /** Record message */
835                mLogRecords.add(mSm, mMsg, mSm.getLogRecString(mMsg), msgProcessedState, orgState,
836                        mDestState);
837            }
838
839            State destState = mDestState;
840            if (destState != null) {
841                /**
842                 * Process the transitions including transitions in the enter/exit methods
843                 */
844                while (true) {
845                    if (mDbg) mSm.log("handleMessage: new destination call exit/enter");
846
847                    /**
848                     * Determine the states to exit and enter and return the
849                     * common ancestor state of the enter/exit states. Then
850                     * invoke the exit methods then the enter methods.
851                     */
852                    StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState);
853                    invokeExitMethods(commonStateInfo);
854                    int stateStackEnteringIndex = moveTempStateStackToStateStack();
855                    invokeEnterMethods(stateStackEnteringIndex);
856
857                    /**
858                     * Since we have transitioned to a new state we need to have
859                     * any deferred messages moved to the front of the message queue
860                     * so they will be processed before any other messages in the
861                     * message queue.
862                     */
863                    moveDeferredMessageAtFrontOfQueue();
864
865                    if (destState != mDestState) {
866                        // A new mDestState so continue looping
867                        destState = mDestState;
868                    } else {
869                        // No change in mDestState so we're done
870                        break;
871                    }
872                }
873                mDestState = null;
874            }
875
876            /**
877             * After processing all transitions check and
878             * see if the last transition was to quit or halt.
879             */
880            if (destState != null) {
881                if (destState == mQuittingState) {
882                    /**
883                     * Call onQuitting to let subclasses cleanup.
884                     */
885                    mSm.onQuitting();
886                    cleanupAfterQuitting();
887                } else if (destState == mHaltingState) {
888                    /**
889                     * Call onHalting() if we've transitioned to the halting
890                     * state. All subsequent messages will be processed in
891                     * in the halting state which invokes haltedProcessMessage(msg);
892                     */
893                    mSm.onHalting();
894                }
895            }
896        }
897
898        /**
899         * Cleanup all the static variables and the looper after the SM has been quit.
900         */
901        private final void cleanupAfterQuitting() {
902            if (mSm.mSmThread != null) {
903                // If we made the thread then quit looper which stops the thread.
904                getLooper().quit();
905                mSm.mSmThread = null;
906            }
907
908            mSm.mSmHandler = null;
909            mSm = null;
910            mMsg = null;
911            mLogRecords.cleanup();
912            mStateStack = null;
913            mTempStateStack = null;
914            mStateInfo.clear();
915            mInitialState = null;
916            mDestState = null;
917            mDeferredMessages.clear();
918            mHasQuit = true;
919        }
920
921        /**
922         * Complete the construction of the state machine.
923         */
924        private final void completeConstruction() {
925            if (mDbg) mSm.log("completeConstruction: E");
926
927            /**
928             * Determine the maximum depth of the state hierarchy
929             * so we can allocate the state stacks.
930             */
931            int maxDepth = 0;
932            for (StateInfo si : mStateInfo.values()) {
933                int depth = 0;
934                for (StateInfo i = si; i != null; depth++) {
935                    i = i.parentStateInfo;
936                }
937                if (maxDepth < depth) {
938                    maxDepth = depth;
939                }
940            }
941            if (mDbg) mSm.log("completeConstruction: maxDepth=" + maxDepth);
942
943            mStateStack = new StateInfo[maxDepth];
944            mTempStateStack = new StateInfo[maxDepth];
945            setupInitialStateStack();
946
947            /** Sending SM_INIT_CMD message to invoke enter methods asynchronously */
948            sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj));
949
950            if (mDbg) mSm.log("completeConstruction: X");
951        }
952
953        /**
954         * Process the message. If the current state doesn't handle
955         * it, call the states parent and so on. If it is never handled then
956         * call the state machines unhandledMessage method.
957         * @return the state that processed the message
958         */
959        private final State processMsg(Message msg) {
960            StateInfo curStateInfo = mStateStack[mStateStackTopIndex];
961            if (mDbg) {
962                mSm.log("processMsg: " + curStateInfo.state.getName());
963            }
964
965            if (isQuit(msg)) {
966                transitionTo(mQuittingState);
967            } else {
968                while (!curStateInfo.state.processMessage(msg)) {
969                    /**
970                     * Not processed
971                     */
972                    curStateInfo = curStateInfo.parentStateInfo;
973                    if (curStateInfo == null) {
974                        /**
975                         * No parents left so it's not handled
976                         */
977                        mSm.unhandledMessage(msg);
978                        break;
979                    }
980                    if (mDbg) {
981                        mSm.log("processMsg: " + curStateInfo.state.getName());
982                    }
983                }
984            }
985            return (curStateInfo != null) ? curStateInfo.state : null;
986        }
987
988        /**
989         * Call the exit method for each state from the top of stack
990         * up to the common ancestor state.
991         */
992        private final void invokeExitMethods(StateInfo commonStateInfo) {
993            while ((mStateStackTopIndex >= 0)
994                    && (mStateStack[mStateStackTopIndex] != commonStateInfo)) {
995                State curState = mStateStack[mStateStackTopIndex].state;
996                if (mDbg) mSm.log("invokeExitMethods: " + curState.getName());
997                curState.exit();
998                mStateStack[mStateStackTopIndex].active = false;
999                mStateStackTopIndex -= 1;
1000            }
1001        }
1002
1003        /**
1004         * Invoke the enter method starting at the entering index to top of state stack
1005         */
1006        private final void invokeEnterMethods(int stateStackEnteringIndex) {
1007            for (int i = stateStackEnteringIndex; i <= mStateStackTopIndex; i++) {
1008                if (mDbg) mSm.log("invokeEnterMethods: " + mStateStack[i].state.getName());
1009                mStateStack[i].state.enter();
1010                mStateStack[i].active = true;
1011            }
1012        }
1013
1014        /**
1015         * Move the deferred message to the front of the message queue.
1016         */
1017        private final void moveDeferredMessageAtFrontOfQueue() {
1018            /**
1019             * The oldest messages on the deferred list must be at
1020             * the front of the queue so start at the back, which
1021             * as the most resent message and end with the oldest
1022             * messages at the front of the queue.
1023             */
1024            for (int i = mDeferredMessages.size() - 1; i >= 0; i--) {
1025                Message curMsg = mDeferredMessages.get(i);
1026                if (mDbg) mSm.log("moveDeferredMessageAtFrontOfQueue; what=" + curMsg.what);
1027                sendMessageAtFrontOfQueue(curMsg);
1028            }
1029            mDeferredMessages.clear();
1030        }
1031
1032        /**
1033         * Move the contents of the temporary stack to the state stack
1034         * reversing the order of the items on the temporary stack as
1035         * they are moved.
1036         *
1037         * @return index into mStateStack where entering needs to start
1038         */
1039        private final int moveTempStateStackToStateStack() {
1040            int startingIndex = mStateStackTopIndex + 1;
1041            int i = mTempStateStackCount - 1;
1042            int j = startingIndex;
1043            while (i >= 0) {
1044                if (mDbg) mSm.log("moveTempStackToStateStack: i=" + i + ",j=" + j);
1045                mStateStack[j] = mTempStateStack[i];
1046                j += 1;
1047                i -= 1;
1048            }
1049
1050            mStateStackTopIndex = j - 1;
1051            if (mDbg) {
1052                mSm.log("moveTempStackToStateStack: X mStateStackTop=" + mStateStackTopIndex
1053                        + ",startingIndex=" + startingIndex + ",Top="
1054                        + mStateStack[mStateStackTopIndex].state.getName());
1055            }
1056            return startingIndex;
1057        }
1058
1059        /**
1060         * Setup the mTempStateStack with the states we are going to enter.
1061         *
1062         * This is found by searching up the destState's ancestors for a
1063         * state that is already active i.e. StateInfo.active == true.
1064         * The destStae and all of its inactive parents will be on the
1065         * TempStateStack as the list of states to enter.
1066         *
1067         * @return StateInfo of the common ancestor for the destState and
1068         * current state or null if there is no common parent.
1069         */
1070        private final StateInfo setupTempStateStackWithStatesToEnter(State destState) {
1071            /**
1072             * Search up the parent list of the destination state for an active
1073             * state. Use a do while() loop as the destState must always be entered
1074             * even if it is active. This can happen if we are exiting/entering
1075             * the current state.
1076             */
1077            mTempStateStackCount = 0;
1078            StateInfo curStateInfo = mStateInfo.get(destState);
1079            do {
1080                mTempStateStack[mTempStateStackCount++] = curStateInfo;
1081                curStateInfo = curStateInfo.parentStateInfo;
1082            } while ((curStateInfo != null) && !curStateInfo.active);
1083
1084            if (mDbg) {
1085                mSm.log("setupTempStateStackWithStatesToEnter: X mTempStateStackCount="
1086                        + mTempStateStackCount + ",curStateInfo: " + curStateInfo);
1087            }
1088            return curStateInfo;
1089        }
1090
1091        /**
1092         * Initialize StateStack to mInitialState.
1093         */
1094        private final void setupInitialStateStack() {
1095            if (mDbg) {
1096                mSm.log("setupInitialStateStack: E mInitialState=" + mInitialState.getName());
1097            }
1098
1099            StateInfo curStateInfo = mStateInfo.get(mInitialState);
1100            for (mTempStateStackCount = 0; curStateInfo != null; mTempStateStackCount++) {
1101                mTempStateStack[mTempStateStackCount] = curStateInfo;
1102                curStateInfo = curStateInfo.parentStateInfo;
1103            }
1104
1105            // Empty the StateStack
1106            mStateStackTopIndex = -1;
1107
1108            moveTempStateStackToStateStack();
1109        }
1110
1111        /**
1112         * @return current message
1113         */
1114        private final Message getCurrentMessage() {
1115            return mMsg;
1116        }
1117
1118        /**
1119         * @return current state
1120         */
1121        private final IState getCurrentState() {
1122            return mStateStack[mStateStackTopIndex].state;
1123        }
1124
1125        /**
1126         * Add a new state to the state machine. Bottom up addition
1127         * of states is allowed but the same state may only exist
1128         * in one hierarchy.
1129         *
1130         * @param state the state to add
1131         * @param parent the parent of state
1132         * @return stateInfo for this state
1133         */
1134        private final StateInfo addState(State state, State parent) {
1135            if (mDbg) {
1136                mSm.log("addStateInternal: E state=" + state.getName() + ",parent="
1137                        + ((parent == null) ? "" : parent.getName()));
1138            }
1139            StateInfo parentStateInfo = null;
1140            if (parent != null) {
1141                parentStateInfo = mStateInfo.get(parent);
1142                if (parentStateInfo == null) {
1143                    // Recursively add our parent as it's not been added yet.
1144                    parentStateInfo = addState(parent, null);
1145                }
1146            }
1147            StateInfo stateInfo = mStateInfo.get(state);
1148            if (stateInfo == null) {
1149                stateInfo = new StateInfo();
1150                mStateInfo.put(state, stateInfo);
1151            }
1152
1153            // Validate that we aren't adding the same state in two different hierarchies.
1154            if ((stateInfo.parentStateInfo != null)
1155                    && (stateInfo.parentStateInfo != parentStateInfo)) {
1156                throw new RuntimeException("state already added");
1157            }
1158            stateInfo.state = state;
1159            stateInfo.parentStateInfo = parentStateInfo;
1160            stateInfo.active = false;
1161            if (mDbg) mSm.log("addStateInternal: X stateInfo: " + stateInfo);
1162            return stateInfo;
1163        }
1164
1165        /**
1166         * Constructor
1167         *
1168         * @param looper for dispatching messages
1169         * @param sm the hierarchical state machine
1170         */
1171        private SmHandler(Looper looper, StateMachine sm) {
1172            super(looper);
1173            mSm = sm;
1174
1175            addState(mHaltingState, null);
1176            addState(mQuittingState, null);
1177        }
1178
1179        /** @see StateMachine#setInitialState(State) */
1180        private final void setInitialState(State initialState) {
1181            if (mDbg) mSm.log("setInitialState: initialState=" + initialState.getName());
1182            mInitialState = initialState;
1183        }
1184
1185        /** @see StateMachine#transitionTo(IState) */
1186        private final void transitionTo(IState destState) {
1187            mDestState = (State) destState;
1188            if (mDbg) mSm.log("transitionTo: destState=" + mDestState.getName());
1189        }
1190
1191        /** @see StateMachine#deferMessage(Message) */
1192        private final void deferMessage(Message msg) {
1193            if (mDbg) mSm.log("deferMessage: msg=" + msg.what);
1194
1195            /* Copy the "msg" to "newMsg" as "msg" will be recycled */
1196            Message newMsg = obtainMessage();
1197            newMsg.copyFrom(msg);
1198
1199            mDeferredMessages.add(newMsg);
1200        }
1201
1202        /** @see StateMachine#quit() */
1203        private final void quit() {
1204            if (mDbg) mSm.log("quit:");
1205            sendMessage(obtainMessage(SM_QUIT_CMD, mSmHandlerObj));
1206        }
1207
1208        /** @see StateMachine#quitNow() */
1209        private final void quitNow() {
1210            if (mDbg) mSm.log("quitNow:");
1211            sendMessageAtFrontOfQueue(obtainMessage(SM_QUIT_CMD, mSmHandlerObj));
1212        }
1213
1214        /** Validate that the message was sent by quit or quitNow. */
1215        private final boolean isQuit(Message msg) {
1216            return (msg.what == SM_QUIT_CMD) && (msg.obj == mSmHandlerObj);
1217        }
1218
1219        /** @see StateMachine#isDbg() */
1220        private final boolean isDbg() {
1221            return mDbg;
1222        }
1223
1224        /** @see StateMachine#setDbg(boolean) */
1225        private final void setDbg(boolean dbg) {
1226            mDbg = dbg;
1227        }
1228
1229    }
1230
1231    private SmHandler mSmHandler;
1232    private HandlerThread mSmThread;
1233
1234    /**
1235     * Initialize.
1236     *
1237     * @param looper for this state machine
1238     * @param name of the state machine
1239     */
1240    private void initStateMachine(String name, Looper looper) {
1241        mName = name;
1242        mSmHandler = new SmHandler(looper, this);
1243    }
1244
1245    /**
1246     * Constructor creates a StateMachine with its own thread.
1247     *
1248     * @param name of the state machine
1249     */
1250    protected StateMachine(String name) {
1251        mSmThread = new HandlerThread(name);
1252        mSmThread.start();
1253        Looper looper = mSmThread.getLooper();
1254
1255        initStateMachine(name, looper);
1256    }
1257
1258    /**
1259     * Constructor creates a StateMachine using the looper.
1260     *
1261     * @param name of the state machine
1262     */
1263    protected StateMachine(String name, Looper looper) {
1264        initStateMachine(name, looper);
1265    }
1266
1267    /**
1268     * Constructor creates a StateMachine using the handler.
1269     *
1270     * @param name of the state machine
1271     */
1272    protected StateMachine(String name, Handler handler) {
1273        initStateMachine(name, handler.getLooper());
1274    }
1275
1276    /**
1277     * Add a new state to the state machine
1278     * @param state the state to add
1279     * @param parent the parent of state
1280     */
1281    protected final void addState(State state, State parent) {
1282        mSmHandler.addState(state, parent);
1283    }
1284
1285    /**
1286     * Add a new state to the state machine, parent will be null
1287     * @param state to add
1288     */
1289    protected final void addState(State state) {
1290        mSmHandler.addState(state, null);
1291    }
1292
1293    /**
1294     * Set the initial state. This must be invoked before
1295     * and messages are sent to the state machine.
1296     *
1297     * @param initialState is the state which will receive the first message.
1298     */
1299    protected final void setInitialState(State initialState) {
1300        mSmHandler.setInitialState(initialState);
1301    }
1302
1303    /**
1304     * @return current message
1305     */
1306    protected final Message getCurrentMessage() {
1307        // mSmHandler can be null if the state machine has quit.
1308        SmHandler smh = mSmHandler;
1309        if (smh == null) return null;
1310        return smh.getCurrentMessage();
1311    }
1312
1313    /**
1314     * @return current state
1315     */
1316    protected final IState getCurrentState() {
1317        // mSmHandler can be null if the state machine has quit.
1318        SmHandler smh = mSmHandler;
1319        if (smh == null) return null;
1320        return smh.getCurrentState();
1321    }
1322
1323    /**
1324     * transition to destination state. Upon returning
1325     * from processMessage the current state's exit will
1326     * be executed and upon the next message arriving
1327     * destState.enter will be invoked.
1328     *
1329     * this function can also be called inside the enter function of the
1330     * previous transition target, but the behavior is undefined when it is
1331     * called mid-way through a previous transition (for example, calling this
1332     * in the enter() routine of a intermediate node when the current transition
1333     * target is one of the nodes descendants).
1334     *
1335     * @param destState will be the state that receives the next message.
1336     */
1337    protected final void transitionTo(IState destState) {
1338        mSmHandler.transitionTo(destState);
1339    }
1340
1341    /**
1342     * transition to halt state. Upon returning
1343     * from processMessage we will exit all current
1344     * states, execute the onHalting() method and then
1345     * for all subsequent messages haltedProcessMessage
1346     * will be called.
1347     */
1348    protected final void transitionToHaltingState() {
1349        mSmHandler.transitionTo(mSmHandler.mHaltingState);
1350    }
1351
1352    /**
1353     * Defer this message until next state transition.
1354     * Upon transitioning all deferred messages will be
1355     * placed on the queue and reprocessed in the original
1356     * order. (i.e. The next state the oldest messages will
1357     * be processed first)
1358     *
1359     * @param msg is deferred until the next transition.
1360     */
1361    protected final void deferMessage(Message msg) {
1362        mSmHandler.deferMessage(msg);
1363    }
1364
1365    /**
1366     * Called when message wasn't handled
1367     *
1368     * @param msg that couldn't be handled.
1369     */
1370    protected void unhandledMessage(Message msg) {
1371        if (mSmHandler.mDbg) loge(" - unhandledMessage: msg.what=" + msg.what);
1372    }
1373
1374    /**
1375     * Called for any message that is received after
1376     * transitionToHalting is called.
1377     */
1378    protected void haltedProcessMessage(Message msg) {
1379    }
1380
1381    /**
1382     * This will be called once after handling a message that called
1383     * transitionToHalting. All subsequent messages will invoke
1384     * {@link StateMachine#haltedProcessMessage(Message)}
1385     */
1386    protected void onHalting() {
1387    }
1388
1389    /**
1390     * This will be called once after a quit message that was NOT handled by
1391     * the derived StateMachine. The StateMachine will stop and any subsequent messages will be
1392     * ignored. In addition, if this StateMachine created the thread, the thread will
1393     * be stopped after this method returns.
1394     */
1395    protected void onQuitting() {
1396    }
1397
1398    /**
1399     * @return the name
1400     */
1401    public final String getName() {
1402        return mName;
1403    }
1404
1405    /**
1406     * Set number of log records to maintain and clears all current records.
1407     *
1408     * @param maxSize number of messages to maintain at anyone time.
1409     */
1410    public final void setLogRecSize(int maxSize) {
1411        mSmHandler.mLogRecords.setSize(maxSize);
1412    }
1413
1414    /**
1415     * Set to log only messages that cause a state transition
1416     *
1417     * @param enable {@code true} to enable, {@code false} to disable
1418     */
1419    public final void setLogOnlyTransitions(boolean enable) {
1420        mSmHandler.mLogRecords.setLogOnlyTransitions(enable);
1421    }
1422
1423    /**
1424     * @return number of log records
1425     */
1426    public final int getLogRecSize() {
1427        // mSmHandler can be null if the state machine has quit.
1428        SmHandler smh = mSmHandler;
1429        if (smh == null) return 0;
1430        return smh.mLogRecords.size();
1431    }
1432
1433    /**
1434     * @return the total number of records processed
1435     */
1436    public final int getLogRecCount() {
1437        // mSmHandler can be null if the state machine has quit.
1438        SmHandler smh = mSmHandler;
1439        if (smh == null) return 0;
1440        return smh.mLogRecords.count();
1441    }
1442
1443    /**
1444     * @return a log record, or null if index is out of range
1445     */
1446    public final LogRec getLogRec(int index) {
1447        // mSmHandler can be null if the state machine has quit.
1448        SmHandler smh = mSmHandler;
1449        if (smh == null) return null;
1450        return smh.mLogRecords.get(index);
1451    }
1452
1453    /**
1454     * @return a copy of LogRecs as a collection
1455     */
1456    public final Collection<LogRec> copyLogRecs() {
1457        Vector<LogRec> vlr = new Vector<LogRec>();
1458        SmHandler smh = mSmHandler;
1459        if (smh != null) {
1460            for (LogRec lr : smh.mLogRecords.mLogRecVector) {
1461                vlr.add(lr);
1462            }
1463        }
1464        return vlr;
1465    }
1466
1467    /**
1468     * Add the string to LogRecords.
1469     *
1470     * @param string
1471     */
1472    protected void addLogRec(String string) {
1473        // mSmHandler can be null if the state machine has quit.
1474        SmHandler smh = mSmHandler;
1475        if (smh == null) return;
1476        smh.mLogRecords.add(this, smh.getCurrentMessage(), string, smh.getCurrentState(),
1477                smh.mStateStack[smh.mStateStackTopIndex].state, smh.mDestState);
1478    }
1479
1480    /**
1481     * @return true if msg should be saved in the log, default is true.
1482     */
1483    protected boolean recordLogRec(Message msg) {
1484        return true;
1485    }
1486
1487    /**
1488     * Return a string to be logged by LogRec, default
1489     * is an empty string. Override if additional information is desired.
1490     *
1491     * @param msg that was processed
1492     * @return information to be logged as a String
1493     */
1494    protected String getLogRecString(Message msg) {
1495        return "";
1496    }
1497
1498    /**
1499     * @return the string for msg.what
1500     */
1501    protected String getWhatToString(int what) {
1502        return null;
1503    }
1504
1505    /**
1506     * @return Handler, maybe null if state machine has quit.
1507     */
1508    public final Handler getHandler() {
1509        return mSmHandler;
1510    }
1511
1512    /**
1513     * Get a message and set Message.target state machine handler.
1514     *
1515     * Note: The handler can be null if the state machine has quit,
1516     * which means target will be null and may cause a AndroidRuntimeException
1517     * in MessageQueue#enqueMessage if sent directly or if sent using
1518     * StateMachine#sendMessage the message will just be ignored.
1519     *
1520     * @return  A Message object from the global pool
1521     */
1522    public final Message obtainMessage() {
1523        return Message.obtain(mSmHandler);
1524    }
1525
1526    /**
1527     * Get a message and set Message.target state machine handler, what.
1528     *
1529     * Note: The handler can be null if the state machine has quit,
1530     * which means target will be null and may cause a AndroidRuntimeException
1531     * in MessageQueue#enqueMessage if sent directly or if sent using
1532     * StateMachine#sendMessage the message will just be ignored.
1533     *
1534     * @param what is the assigned to Message.what.
1535     * @return  A Message object from the global pool
1536     */
1537    public final Message obtainMessage(int what) {
1538        return Message.obtain(mSmHandler, what);
1539    }
1540
1541    /**
1542     * Get a message and set Message.target state machine handler,
1543     * what and obj.
1544     *
1545     * Note: The handler can be null if the state machine has quit,
1546     * which means target will be null and may cause a AndroidRuntimeException
1547     * in MessageQueue#enqueMessage if sent directly or if sent using
1548     * StateMachine#sendMessage the message will just be ignored.
1549     *
1550     * @param what is the assigned to Message.what.
1551     * @param obj is assigned to Message.obj.
1552     * @return  A Message object from the global pool
1553     */
1554    public final Message obtainMessage(int what, Object obj) {
1555        return Message.obtain(mSmHandler, what, obj);
1556    }
1557
1558    /**
1559     * Get a message and set Message.target state machine handler,
1560     * what, arg1 and arg2
1561     *
1562     * Note: The handler can be null if the state machine has quit,
1563     * which means target will be null and may cause a AndroidRuntimeException
1564     * in MessageQueue#enqueMessage if sent directly or if sent using
1565     * StateMachine#sendMessage the message will just be ignored.
1566     *
1567     * @param what  is assigned to Message.what
1568     * @param arg1  is assigned to Message.arg1
1569     * @return  A Message object from the global pool
1570     */
1571    public final Message obtainMessage(int what, int arg1) {
1572        // use this obtain so we don't match the obtain(h, what, Object) method
1573        return Message.obtain(mSmHandler, what, arg1, 0);
1574    }
1575
1576    /**
1577     * Get a message and set Message.target state machine handler,
1578     * what, arg1 and arg2
1579     *
1580     * Note: The handler can be null if the state machine has quit,
1581     * which means target will be null and may cause a AndroidRuntimeException
1582     * in MessageQueue#enqueMessage if sent directly or if sent using
1583     * StateMachine#sendMessage the message will just be ignored.
1584     *
1585     * @param what  is assigned to Message.what
1586     * @param arg1  is assigned to Message.arg1
1587     * @param arg2  is assigned to Message.arg2
1588     * @return  A Message object from the global pool
1589     */
1590    public final Message obtainMessage(int what, int arg1, int arg2) {
1591        return Message.obtain(mSmHandler, what, arg1, arg2);
1592    }
1593
1594    /**
1595     * Get a message and set Message.target state machine handler,
1596     * what, arg1, arg2 and obj
1597     *
1598     * Note: The handler can be null if the state machine has quit,
1599     * which means target will be null and may cause a AndroidRuntimeException
1600     * in MessageQueue#enqueMessage if sent directly or if sent using
1601     * StateMachine#sendMessage the message will just be ignored.
1602     *
1603     * @param what  is assigned to Message.what
1604     * @param arg1  is assigned to Message.arg1
1605     * @param arg2  is assigned to Message.arg2
1606     * @param obj is assigned to Message.obj
1607     * @return  A Message object from the global pool
1608     */
1609    public final Message obtainMessage(int what, int arg1, int arg2, Object obj) {
1610        return Message.obtain(mSmHandler, what, arg1, arg2, obj);
1611    }
1612
1613    /**
1614     * Enqueue a message to this state machine.
1615     *
1616     * Message is ignored if state machine has quit.
1617     */
1618    public final void sendMessage(int what) {
1619        // mSmHandler can be null if the state machine has quit.
1620        SmHandler smh = mSmHandler;
1621        if (smh == null) return;
1622
1623        smh.sendMessage(obtainMessage(what));
1624    }
1625
1626    /**
1627     * Enqueue a message to this state machine.
1628     *
1629     * Message is ignored if state machine has quit.
1630     */
1631    public final void sendMessage(int what, Object obj) {
1632        // mSmHandler can be null if the state machine has quit.
1633        SmHandler smh = mSmHandler;
1634        if (smh == null) return;
1635
1636        smh.sendMessage(obtainMessage(what, obj));
1637    }
1638
1639    /**
1640     * Enqueue a message to this state machine.
1641     *
1642     * Message is ignored if state machine has quit.
1643     */
1644    public final void sendMessage(int what, int arg1) {
1645        // mSmHandler can be null if the state machine has quit.
1646        SmHandler smh = mSmHandler;
1647        if (smh == null) return;
1648
1649        smh.sendMessage(obtainMessage(what, arg1));
1650    }
1651
1652    /**
1653     * Enqueue a message to this state machine.
1654     *
1655     * Message is ignored if state machine has quit.
1656     */
1657    public final void sendMessage(int what, int arg1, int arg2) {
1658        // mSmHandler can be null if the state machine has quit.
1659        SmHandler smh = mSmHandler;
1660        if (smh == null) return;
1661
1662        smh.sendMessage(obtainMessage(what, arg1, arg2));
1663    }
1664
1665    /**
1666     * Enqueue a message to this state machine.
1667     *
1668     * Message is ignored if state machine has quit.
1669     */
1670    public final void sendMessage(int what, int arg1, int arg2, Object obj) {
1671        // mSmHandler can be null if the state machine has quit.
1672        SmHandler smh = mSmHandler;
1673        if (smh == null) return;
1674
1675        smh.sendMessage(obtainMessage(what, arg1, arg2, obj));
1676    }
1677
1678    /**
1679     * Enqueue a message to this state machine.
1680     *
1681     * Message is ignored if state machine has quit.
1682     */
1683    public final void sendMessage(Message msg) {
1684        // mSmHandler can be null if the state machine has quit.
1685        SmHandler smh = mSmHandler;
1686        if (smh == null) return;
1687
1688        smh.sendMessage(msg);
1689    }
1690
1691    /**
1692     * Enqueue a message to this state machine after a delay.
1693     *
1694     * Message is ignored if state machine has quit.
1695     */
1696    public final void sendMessageDelayed(int what, long delayMillis) {
1697        // mSmHandler can be null if the state machine has quit.
1698        SmHandler smh = mSmHandler;
1699        if (smh == null) return;
1700
1701        smh.sendMessageDelayed(obtainMessage(what), delayMillis);
1702    }
1703
1704    /**
1705     * Enqueue a message to this state machine after a delay.
1706     *
1707     * Message is ignored if state machine has quit.
1708     */
1709    public final void sendMessageDelayed(int what, Object obj, long delayMillis) {
1710        // mSmHandler can be null if the state machine has quit.
1711        SmHandler smh = mSmHandler;
1712        if (smh == null) return;
1713
1714        smh.sendMessageDelayed(obtainMessage(what, obj), delayMillis);
1715    }
1716
1717    /**
1718     * Enqueue a message to this state machine after a delay.
1719     *
1720     * Message is ignored if state machine has quit.
1721     */
1722    public final void sendMessageDelayed(int what, int arg1, long delayMillis) {
1723        // mSmHandler can be null if the state machine has quit.
1724        SmHandler smh = mSmHandler;
1725        if (smh == null) return;
1726
1727        smh.sendMessageDelayed(obtainMessage(what, arg1), delayMillis);
1728    }
1729
1730    /**
1731     * Enqueue a message to this state machine after a delay.
1732     *
1733     * Message is ignored if state machine has quit.
1734     */
1735    public final void sendMessageDelayed(int what, int arg1, int arg2, long delayMillis) {
1736        // mSmHandler can be null if the state machine has quit.
1737        SmHandler smh = mSmHandler;
1738        if (smh == null) return;
1739
1740        smh.sendMessageDelayed(obtainMessage(what, arg1, arg2), delayMillis);
1741    }
1742
1743    /**
1744     * Enqueue a message to this state machine after a delay.
1745     *
1746     * Message is ignored if state machine has quit.
1747     */
1748    public final void sendMessageDelayed(int what, int arg1, int arg2, Object obj,
1749            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, arg2, obj), 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     */
1762    public final void sendMessageDelayed(Message msg, 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(msg, delayMillis);
1768    }
1769
1770    /**
1771     * Enqueue a message to the front of the queue for this state machine.
1772     * Protected, may only be called by instances of StateMachine.
1773     *
1774     * Message is ignored if state machine has quit.
1775     */
1776    protected final void sendMessageAtFrontOfQueue(int what) {
1777        // mSmHandler can be null if the state machine has quit.
1778        SmHandler smh = mSmHandler;
1779        if (smh == null) return;
1780
1781        smh.sendMessageAtFrontOfQueue(obtainMessage(what));
1782    }
1783
1784    /**
1785     * Enqueue a message to the front of the queue for this state machine.
1786     * Protected, may only be called by instances of StateMachine.
1787     *
1788     * Message is ignored if state machine has quit.
1789     */
1790    protected final void sendMessageAtFrontOfQueue(int what, Object obj) {
1791        // mSmHandler can be null if the state machine has quit.
1792        SmHandler smh = mSmHandler;
1793        if (smh == null) return;
1794
1795        smh.sendMessageAtFrontOfQueue(obtainMessage(what, obj));
1796    }
1797
1798    /**
1799     * Enqueue a message to the front of the queue for this state machine.
1800     * Protected, may only be called by instances of StateMachine.
1801     *
1802     * Message is ignored if state machine has quit.
1803     */
1804    protected final void sendMessageAtFrontOfQueue(int what, int arg1) {
1805        // mSmHandler can be null if the state machine has quit.
1806        SmHandler smh = mSmHandler;
1807        if (smh == null) return;
1808
1809        smh.sendMessageAtFrontOfQueue(obtainMessage(what, arg1));
1810    }
1811
1812
1813    /**
1814     * Enqueue a message to the front of the queue for this state machine.
1815     * Protected, may only be called by instances of StateMachine.
1816     *
1817     * Message is ignored if state machine has quit.
1818     */
1819    protected final void sendMessageAtFrontOfQueue(int what, int arg1, int arg2) {
1820        // mSmHandler can be null if the state machine has quit.
1821        SmHandler smh = mSmHandler;
1822        if (smh == null) return;
1823
1824        smh.sendMessageAtFrontOfQueue(obtainMessage(what, arg1, arg2));
1825    }
1826
1827    /**
1828     * Enqueue a message to the front of the queue for this state machine.
1829     * Protected, may only be called by instances of StateMachine.
1830     *
1831     * Message is ignored if state machine has quit.
1832     */
1833    protected final void sendMessageAtFrontOfQueue(int what, int arg1, int arg2, Object obj) {
1834        // mSmHandler can be null if the state machine has quit.
1835        SmHandler smh = mSmHandler;
1836        if (smh == null) return;
1837
1838        smh.sendMessageAtFrontOfQueue(obtainMessage(what, arg1, arg2, obj));
1839    }
1840
1841    /**
1842     * Enqueue a message to the front of the queue for this state machine.
1843     * Protected, may only be called by instances of StateMachine.
1844     *
1845     * Message is ignored if state machine has quit.
1846     */
1847    protected final void sendMessageAtFrontOfQueue(Message msg) {
1848        // mSmHandler can be null if the state machine has quit.
1849        SmHandler smh = mSmHandler;
1850        if (smh == null) return;
1851
1852        smh.sendMessageAtFrontOfQueue(msg);
1853    }
1854
1855    /**
1856     * Removes a message from the message queue.
1857     * Protected, may only be called by instances of StateMachine.
1858     */
1859    protected final void removeMessages(int what) {
1860        // mSmHandler can be null if the state machine has quit.
1861        SmHandler smh = mSmHandler;
1862        if (smh == null) return;
1863
1864        smh.removeMessages(what);
1865    }
1866
1867    /**
1868     * Removes a message from the deferred messages queue.
1869     */
1870    protected final void removeDeferredMessages(int what) {
1871        SmHandler smh = mSmHandler;
1872        if (smh == null) return;
1873
1874        Iterator<Message> iterator = smh.mDeferredMessages.iterator();
1875        while (iterator.hasNext()) {
1876            Message msg = iterator.next();
1877            if (msg.what == what) iterator.remove();
1878        }
1879    }
1880
1881    /**
1882     * Validate that the message was sent by
1883     * {@link StateMachine#quit} or {@link StateMachine#quitNow}.
1884     * */
1885    protected final boolean isQuit(Message msg) {
1886        // mSmHandler can be null if the state machine has quit.
1887        SmHandler smh = mSmHandler;
1888        if (smh == null) return msg.what == SM_QUIT_CMD;
1889
1890        return smh.isQuit(msg);
1891    }
1892
1893    /**
1894     * Quit the state machine after all currently queued up messages are processed.
1895     */
1896    protected final void quit() {
1897        // mSmHandler can be null if the state machine is already stopped.
1898        SmHandler smh = mSmHandler;
1899        if (smh == null) return;
1900
1901        smh.quit();
1902    }
1903
1904    /**
1905     * Quit the state machine immediately all currently queued messages will be discarded.
1906     */
1907    protected final void quitNow() {
1908        // mSmHandler can be null if the state machine is already stopped.
1909        SmHandler smh = mSmHandler;
1910        if (smh == null) return;
1911
1912        smh.quitNow();
1913    }
1914
1915    /**
1916     * @return if debugging is enabled
1917     */
1918    public boolean isDbg() {
1919        // mSmHandler can be null if the state machine has quit.
1920        SmHandler smh = mSmHandler;
1921        if (smh == null) return false;
1922
1923        return smh.isDbg();
1924    }
1925
1926    /**
1927     * Set debug enable/disabled.
1928     *
1929     * @param dbg is true to enable debugging.
1930     */
1931    public void setDbg(boolean dbg) {
1932        // mSmHandler can be null if the state machine has quit.
1933        SmHandler smh = mSmHandler;
1934        if (smh == null) return;
1935
1936        smh.setDbg(dbg);
1937    }
1938
1939    /**
1940     * Start the state machine.
1941     */
1942    public void start() {
1943        // mSmHandler can be null if the state machine has quit.
1944        SmHandler smh = mSmHandler;
1945        if (smh == null) return;
1946
1947        /** Send the complete construction message */
1948        smh.completeConstruction();
1949    }
1950
1951    /**
1952     * Dump the current state.
1953     *
1954     * @param fd
1955     * @param pw
1956     * @param args
1957     */
1958    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1959        // Cannot just invoke pw.println(this.toString()) because if the
1960        // resulting string is to long it won't be displayed.
1961        pw.println(getName() + ":");
1962        pw.println(" total records=" + getLogRecCount());
1963        for (int i = 0; i < getLogRecSize(); i++) {
1964            pw.println(" rec[" + i + "]: " + getLogRec(i).toString());
1965            pw.flush();
1966        }
1967        pw.println("curState=" + getCurrentState().getName());
1968    }
1969
1970    @Override
1971    public String toString() {
1972        StringWriter sr = new StringWriter();
1973        PrintWriter pr = new PrintWriter(sr);
1974        dump(null, pr, null);
1975        pr.flush();
1976        pr.close();
1977        return sr.toString();
1978    }
1979
1980    /**
1981     * Log with debug and add to the LogRecords.
1982     *
1983     * @param s is string log
1984     */
1985    protected void logAndAddLogRec(String s) {
1986        addLogRec(s);
1987        log(s);
1988    }
1989
1990    /**
1991     * Log with debug
1992     *
1993     * @param s is string log
1994     */
1995    protected void log(String s) {
1996        Log.d(mName, s);
1997    }
1998
1999    /**
2000     * Log with debug attribute
2001     *
2002     * @param s is string log
2003     */
2004    protected void logd(String s) {
2005        Log.d(mName, s);
2006    }
2007
2008    /**
2009     * Log with verbose attribute
2010     *
2011     * @param s is string log
2012     */
2013    protected void logv(String s) {
2014        Log.v(mName, s);
2015    }
2016
2017    /**
2018     * Log with info attribute
2019     *
2020     * @param s is string log
2021     */
2022    protected void logi(String s) {
2023        Log.i(mName, s);
2024    }
2025
2026    /**
2027     * Log with warning attribute
2028     *
2029     * @param s is string log
2030     */
2031    protected void logw(String s) {
2032        Log.w(mName, s);
2033    }
2034
2035    /**
2036     * Log with error attribute
2037     *
2038     * @param s is string log
2039     */
2040    protected void loge(String s) {
2041        Log.e(mName, s);
2042    }
2043
2044    /**
2045     * Log with error attribute
2046     *
2047     * @param s is string log
2048     * @param e is a Throwable which logs additional information.
2049     */
2050    protected void loge(String s, Throwable e) {
2051        Log.e(mName, s, e);
2052    }
2053}
2054