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