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