StateMachine.java revision cea056f5894543819898a0e62f98dc7870b27758
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.util.Log;
24
25import java.util.ArrayList;
26import java.util.HashMap;
27import java.util.Vector;
28
29/**
30 * {@hide}
31 *
32 * <p>The state machine defined here is a hierarchical state machine which processes messages
33 * and can have states arranged hierarchically.</p>
34 *
35 * <p>A state is a <code>State</code> object and must implement
36 * <code>processMessage</code> and optionally <code>enter/exit/getName</code>.
37 * The enter/exit methods are equivalent to the construction and destruction
38 * in Object Oriented programming and are used to perform initialization and
39 * cleanup of the state respectively. The <code>getName</code> method returns the
40 * name of the state the default implementation returns the class name it may be
41 * desirable to have this return the name of the state instance name instead.
42 * In particular if a particular state class has multiple instances.</p>
43 *
44 * <p>When a state machine is created <code>addState</code> is used to build the
45 * hierarchy and <code>setInitialState</code> is used to identify which of these
46 * is the initial state. After construction the programmer calls <code>start</code>
47 * which initializes and starts the state machine. The first action the StateMachine
48 * is to the invoke <code>enter</code> for all of the initial state's hierarchy,
49 * starting at its eldest parent. The calls to enter will be done in the context
50 * of the StateMachines Handler not in the context of the call to start and they
51 * will be invoked before any messages are processed. For example, given the simple
52 * state machine below mP1.enter will be invoked and then mS1.enter. Finally,
53 * messages sent to the state machine will be processed by the current state,
54 * in our simple state machine below that would initially be mS1.processMessage.</p>
55<code>
56        mP1
57       /   \
58      mS2   mS1 ----> initial state
59</code>
60 * <p>After the state machine is created and started, messages are sent to a state
61 * machine using <code>sendMessage</code> and the messages are created using
62 * <code>obtainMessage</code>. When the state machine receives a message the
63 * current state's <code>processMessage</code> is invoked. In the above example
64 * mS1.processMessage will be invoked first. The state may use <code>transitionTo</code>
65 * to change the current state to a new state</p>
66 *
67 * <p>Each state in the state machine may have a zero or one parent states and if
68 * a child state is unable to handle a message it may have the message processed
69 * by its parent by returning false or NOT_HANDLED. If a message is never processed
70 * <code>unhandledMessage</code> will be invoked to give one last chance for the state machine
71 * to process the message.</p>
72 *
73 * <p>When all processing is completed a state machine may choose to call
74 * <code>transitionToHaltingState</code>. When the current <code>processingMessage</code>
75 * returns the state machine will transfer to an internal <code>HaltingState</code>
76 * and invoke <code>halting</code>. Any message subsequently received by the state
77 * machine will cause <code>haltedProcessMessage</code> to be invoked.</p>
78 *
79 * <p>If it is desirable to completely stop the state machine call <code>quit</code>. This
80 * will exit the current state and its parent and then exit from the controlling thread
81 * and no further messages will be processed.</p>
82 *
83 * <p>In addition to <code>processMessage</code> each <code>State</code> has
84 * an <code>enter</code> method and <code>exit</exit> method which may be overridden.</p>
85 *
86 * <p>Since the states are arranged in a hierarchy transitioning to a new state
87 * causes current states to be exited and new states to be entered. To determine
88 * the list of states to be entered/exited the common parent closest to
89 * the current state is found. We then exit from the current state and its
90 * parent's up to but not including the common parent state and then enter all
91 * of the new states below the common parent down to the destination state.
92 * If there is no common parent all states are exited and then the new states
93 * are entered.</p>
94 *
95 * <p>Two other methods that states can use are <code>deferMessage</code> and
96 * <code>sendMessageAtFrontOfQueue</code>. The <code>sendMessageAtFrontOfQueue</code> sends
97 * a message but places it on the front of the queue rather than the back. The
98 * <code>deferMessage</code> causes the message to be saved on a list until a
99 * transition is made to a new state. At which time all of the deferred messages
100 * will be put on the front of the state machine queue with the oldest message
101 * at the front. These will then be processed by the new current state before
102 * any other messages that are on the queue or might be added later. Both of
103 * these are protected and may only be invoked from within a state machine.</p>
104 *
105 * <p>To illustrate some of these properties we'll use state machine with an 8
106 * state hierarchy:</p>
107<code>
108          mP0
109         /   \
110        mP1   mS0
111       /   \
112      mS2   mS1
113     /  \    \
114    mS3  mS4  mS5  ---> initial state
115</code>
116 * <p>After starting mS5 the list of active states is mP0, mP1, mS1 and mS5.
117 * So the order of calling processMessage when a message is received is mS5,
118 * mS1, mP1, mP0 assuming each processMessage indicates it can't handle this
119 * message by returning false or NOT_HANDLED.</p>
120 *
121 * <p>Now assume mS5.processMessage receives a message it can handle, and during
122 * the handling determines the machine should change states. It could call
123 * transitionTo(mS4) and return true or HANDLED. Immediately after returning from
124 * processMessage the state machine runtime will find the common parent,
125 * which is mP1. It will then call mS5.exit, mS1.exit, mS2.enter and then
126 * mS4.enter. The new list of active states is mP0, mP1, mS2 and mS4. So
127 * when the next message is received mS4.processMessage will be invoked.</p>
128 *
129 * <p>Now for some concrete examples, here is the canonical HelloWorld as a state machine.
130 * It responds with "Hello World" being printed to the log for every message.</p>
131<code>
132class HelloWorld extends StateMachine {
133    HelloWorld(String name) {
134        super(name);
135        addState(mState1);
136        setInitialState(mState1);
137    }
138
139    public static HelloWorld makeHelloWorld() {
140        HelloWorld hw = new HelloWorld("hw");
141        hw.start();
142        return hw;
143    }
144
145    class State1 extends State {
146        &#64;Override public boolean processMessage(Message message) {
147            Log.d(TAG, "Hello World");
148            return HANDLED;
149        }
150    }
151    State1 mState1 = new State1();
152}
153
154void testHelloWorld() {
155    HelloWorld hw = makeHelloWorld();
156    hw.sendMessage(hw.obtainMessage());
157}
158</code>
159 * <p>A more interesting state machine is one with four states
160 * with two independent parent states.</p>
161<code>
162        mP1      mP2
163       /   \
164      mS2   mS1
165</code>
166 * <p>Here is a description of this state machine using pseudo code.</p>
167 <code>
168state mP1 {
169     enter { log("mP1.enter"); }
170     exit { log("mP1.exit");  }
171     on msg {
172         CMD_2 {
173             send(CMD_3);
174             defer(msg);
175             transitonTo(mS2);
176             return HANDLED;
177         }
178         return NOT_HANDLED;
179     }
180}
181
182INITIAL
183state mS1 parent mP1 {
184     enter { log("mS1.enter"); }
185     exit  { log("mS1.exit");  }
186     on msg {
187         CMD_1 {
188             transitionTo(mS1);
189             return HANDLED;
190         }
191         return NOT_HANDLED;
192     }
193}
194
195state mS2 parent mP1 {
196     enter { log("mS2.enter"); }
197     exit  { log("mS2.exit");  }
198     on msg {
199         CMD_2 {
200             send(CMD_4);
201             return HANDLED;
202         }
203         CMD_3 {
204             defer(msg);
205             transitionTo(mP2);
206             return HANDLED;
207         }
208         return NOT_HANDLED;
209     }
210}
211
212state mP2 {
213     enter {
214         log("mP2.enter");
215         send(CMD_5);
216     }
217     exit { log("mP2.exit"); }
218     on msg {
219         CMD_3, CMD_4 { return HANDLED; }
220         CMD_5 {
221             transitionTo(HaltingState);
222             return HANDLED;
223         }
224         return NOT_HANDLED;
225     }
226}
227</code>
228 * <p>The implementation is below and also in StateMachineTest:</p>
229<code>
230class Hsm1 extends StateMachine {
231    private static final String TAG = "hsm1";
232
233    public static final int CMD_1 = 1;
234    public static final int CMD_2 = 2;
235    public static final int CMD_3 = 3;
236    public static final int CMD_4 = 4;
237    public static final int CMD_5 = 5;
238
239    public static Hsm1 makeHsm1() {
240        Log.d(TAG, "makeHsm1 E");
241        Hsm1 sm = new Hsm1("hsm1");
242        sm.start();
243        Log.d(TAG, "makeHsm1 X");
244        return sm;
245    }
246
247    Hsm1(String name) {
248        super(name);
249        Log.d(TAG, "ctor E");
250
251        // Add states, use indentation to show hierarchy
252        addState(mP1);
253            addState(mS1, mP1);
254            addState(mS2, mP1);
255        addState(mP2);
256
257        // Set the initial state
258        setInitialState(mS1);
259        Log.d(TAG, "ctor X");
260    }
261
262    class P1 extends State {
263        &#64;Override public void enter() {
264            Log.d(TAG, "mP1.enter");
265        }
266        &#64;Override public boolean processMessage(Message message) {
267            boolean retVal;
268            Log.d(TAG, "mP1.processMessage what=" + message.what);
269            switch(message.what) {
270            case CMD_2:
271                // CMD_2 will arrive in mS2 before CMD_3
272                sendMessage(obtainMessage(CMD_3));
273                deferMessage(message);
274                transitionTo(mS2);
275                retVal = HANDLED;
276                break;
277            default:
278                // Any message we don't understand in this state invokes unhandledMessage
279                retVal = NOT_HANDLED;
280                break;
281            }
282            return retVal;
283        }
284        &#64;Override public void exit() {
285            Log.d(TAG, "mP1.exit");
286        }
287    }
288
289    class S1 extends State {
290        &#64;Override public void enter() {
291            Log.d(TAG, "mS1.enter");
292        }
293        &#64;Override public boolean processMessage(Message message) {
294            Log.d(TAG, "S1.processMessage what=" + message.what);
295            if (message.what == CMD_1) {
296                // Transition to ourself to show that enter/exit is called
297                transitionTo(mS1);
298                return HANDLED;
299            } else {
300                // Let parent process all other messages
301                return NOT_HANDLED;
302            }
303        }
304        &#64;Override public void exit() {
305            Log.d(TAG, "mS1.exit");
306        }
307    }
308
309    class S2 extends State {
310        &#64;Override public void enter() {
311            Log.d(TAG, "mS2.enter");
312        }
313        &#64;Override public boolean processMessage(Message message) {
314            boolean retVal;
315            Log.d(TAG, "mS2.processMessage what=" + message.what);
316            switch(message.what) {
317            case(CMD_2):
318                sendMessage(obtainMessage(CMD_4));
319                retVal = HANDLED;
320                break;
321            case(CMD_3):
322                deferMessage(message);
323                transitionTo(mP2);
324                retVal = HANDLED;
325                break;
326            default:
327                retVal = NOT_HANDLED;
328                break;
329            }
330            return retVal;
331        }
332        &#64;Override public void exit() {
333            Log.d(TAG, "mS2.exit");
334        }
335    }
336
337    class P2 extends State {
338        &#64;Override public void enter() {
339            Log.d(TAG, "mP2.enter");
340            sendMessage(obtainMessage(CMD_5));
341        }
342        &#64;Override public boolean processMessage(Message message) {
343            Log.d(TAG, "P2.processMessage what=" + message.what);
344            switch(message.what) {
345            case(CMD_3):
346                break;
347            case(CMD_4):
348                break;
349            case(CMD_5):
350                transitionToHaltingState();
351                break;
352            }
353            return HANDLED;
354        }
355        &#64;Override public void exit() {
356            Log.d(TAG, "mP2.exit");
357        }
358    }
359
360    &#64;Override
361    void halting() {
362        Log.d(TAG, "halting");
363        synchronized (this) {
364            this.notifyAll();
365        }
366    }
367
368    P1 mP1 = new P1();
369    S1 mS1 = new S1();
370    S2 mS2 = new S2();
371    P2 mP2 = new P2();
372}
373</code>
374 * <p>If this is executed by sending two messages CMD_1 and CMD_2
375 * (Note the synchronize is only needed because we use hsm.wait())</p>
376<code>
377Hsm1 hsm = makeHsm1();
378synchronize(hsm) {
379     hsm.sendMessage(obtainMessage(hsm.CMD_1));
380     hsm.sendMessage(obtainMessage(hsm.CMD_2));
381     try {
382          // wait for the messages to be handled
383          hsm.wait();
384     } catch (InterruptedException e) {
385          Log.e(TAG, "exception while waiting " + e.getMessage());
386     }
387}
388</code>
389 * <p>The output is:</p>
390<code>
391D/hsm1    ( 1999): makeHsm1 E
392D/hsm1    ( 1999): ctor E
393D/hsm1    ( 1999): ctor X
394D/hsm1    ( 1999): mP1.enter
395D/hsm1    ( 1999): mS1.enter
396D/hsm1    ( 1999): makeHsm1 X
397D/hsm1    ( 1999): mS1.processMessage what=1
398D/hsm1    ( 1999): mS1.exit
399D/hsm1    ( 1999): mS1.enter
400D/hsm1    ( 1999): mS1.processMessage what=2
401D/hsm1    ( 1999): mP1.processMessage what=2
402D/hsm1    ( 1999): mS1.exit
403D/hsm1    ( 1999): mS2.enter
404D/hsm1    ( 1999): mS2.processMessage what=2
405D/hsm1    ( 1999): mS2.processMessage what=3
406D/hsm1    ( 1999): mS2.exit
407D/hsm1    ( 1999): mP1.exit
408D/hsm1    ( 1999): mP2.enter
409D/hsm1    ( 1999): mP2.processMessage what=3
410D/hsm1    ( 1999): mP2.processMessage what=4
411D/hsm1    ( 1999): mP2.processMessage what=5
412D/hsm1    ( 1999): mP2.exit
413D/hsm1    ( 1999): halting
414</code>
415 */
416public class StateMachine {
417
418    private static final String TAG = "StateMachine";
419    private String mName;
420
421    /** Message.what value when quitting */
422    public static final int SM_QUIT_CMD = -1;
423
424    /** Message.what value when initializing */
425    public static final int SM_INIT_CMD = -2;
426
427    /**
428     * Convenience constant that maybe returned by processMessage
429     * to indicate the the message was processed and is not to be
430     * processed by parent states
431     */
432    public static final boolean HANDLED = true;
433
434    /**
435     * Convenience constant that maybe returned by processMessage
436     * to indicate the the message was NOT processed and is to be
437     * processed by parent states
438     */
439    public static final boolean NOT_HANDLED = false;
440
441    /**
442     * {@hide}
443     *
444     * The information maintained for a processed message.
445     */
446    public static class ProcessedMessageInfo {
447        private int what;
448        private State state;
449        private State orgState;
450
451        /**
452         * Constructor
453         * @param message
454         * @param state that handled the message
455         * @param orgState is the first state the received the message but
456         * did not processes the message.
457         */
458        ProcessedMessageInfo(Message message, State state, State orgState) {
459            update(message, state, orgState);
460        }
461
462        /**
463         * Update the information in the record.
464         * @param state that handled the message
465         * @param orgState is the first state the received the message but
466         * did not processes the message.
467         */
468        public void update(Message message, State state, State orgState) {
469            this.what = message.what;
470            this.state = state;
471            this.orgState = orgState;
472        }
473
474        /**
475         * @return the command that was executing
476         */
477        public int getWhat() {
478            return what;
479        }
480
481        /**
482         * @return the state that handled this message
483         */
484        public State getState() {
485            return state;
486        }
487
488        /**
489         * @return the original state that received the message.
490         */
491        public State getOriginalState() {
492            return orgState;
493        }
494
495        /**
496         * @return as string
497         */
498        @Override
499        public String toString() {
500            StringBuilder sb = new StringBuilder();
501            sb.append("what=");
502            sb.append(what);
503            sb.append(" state=");
504            sb.append(cn(state));
505            sb.append(" orgState=");
506            sb.append(cn(orgState));
507            return sb.toString();
508        }
509
510        /**
511         * @return an objects class name
512         */
513        private String cn(Object n) {
514            if (n == null) {
515                return "null";
516            } else {
517                String name = n.getClass().getName();
518                int lastDollar = name.lastIndexOf('$');
519                return name.substring(lastDollar + 1);
520            }
521        }
522    }
523
524    /**
525     * A list of messages recently processed by the state machine.
526     *
527     * The class maintains a list of messages that have been most
528     * recently processed. The list is finite and may be set in the
529     * constructor or by calling setSize. The public interface also
530     * includes size which returns the number of recent messages,
531     * count which is the number of message processed since the
532     * the last setSize, get which returns a processed message and
533     * add which adds a processed messaged.
534     */
535    private static class ProcessedMessages {
536
537        private static final int DEFAULT_SIZE = 20;
538
539        private Vector<ProcessedMessageInfo> mMessages = new Vector<ProcessedMessageInfo>();
540        private int mMaxSize = DEFAULT_SIZE;
541        private int mOldestIndex = 0;
542        private int mCount = 0;
543
544        /**
545         * Constructor
546         */
547        ProcessedMessages() {
548        }
549
550        /**
551         * Set size of messages to maintain and clears all current messages.
552         *
553         * @param maxSize number of messages to maintain at anyone time.
554        */
555        void setSize(int maxSize) {
556            mMaxSize = maxSize;
557            mCount = 0;
558            mMessages.clear();
559        }
560
561        /**
562         * @return the number of recent messages.
563         */
564        int size() {
565            return mMessages.size();
566        }
567
568        /**
569         * @return the total number of messages processed since size was set.
570         */
571        int count() {
572            return mCount;
573        }
574
575        /**
576         * Clear the list of Processed Message Info.
577         */
578        void cleanup() {
579            mMessages.clear();
580        }
581
582        /**
583         * @return the information on a particular record. 0 is the oldest
584         * record and size()-1 is the newest record. If the index is to
585         * large null is returned.
586         */
587        ProcessedMessageInfo get(int index) {
588            int nextIndex = mOldestIndex + index;
589            if (nextIndex >= mMaxSize) {
590                nextIndex -= mMaxSize;
591            }
592            if (nextIndex >= size()) {
593                return null;
594            } else {
595                return mMessages.get(nextIndex);
596            }
597        }
598
599        /**
600         * Add a processed message.
601         *
602         * @param message
603         * @param state that handled the message
604         * @param orgState is the first state the received the message but
605         * did not processes the message.
606         */
607        void add(Message message, State state, State orgState) {
608            mCount += 1;
609            if (mMessages.size() < mMaxSize) {
610                mMessages.add(new ProcessedMessageInfo(message, state, orgState));
611            } else {
612                ProcessedMessageInfo pmi = mMessages.get(mOldestIndex);
613                mOldestIndex += 1;
614                if (mOldestIndex >= mMaxSize) {
615                    mOldestIndex = 0;
616                }
617                pmi.update(message, state, orgState);
618            }
619        }
620    }
621
622
623    private static class SmHandler extends Handler {
624
625        /** The debug flag */
626        private boolean mDbg = false;
627
628        /** The SmHandler object, identifies that message is internal */
629        private static final Object mSmHandlerObj = new Object();
630
631        /** The current message */
632        private Message mMsg;
633
634        /** A list of messages that this state machine has processed */
635        private ProcessedMessages mProcessedMessages = new ProcessedMessages();
636
637        /** true if construction of the state machine has not been completed */
638        private boolean mIsConstructionCompleted;
639
640        /** Stack used to manage the current hierarchy of states */
641        private StateInfo mStateStack[];
642
643        /** Top of mStateStack */
644        private int mStateStackTopIndex = -1;
645
646        /** A temporary stack used to manage the state stack */
647        private StateInfo mTempStateStack[];
648
649        /** The top of the mTempStateStack */
650        private int mTempStateStackCount;
651
652        /** State used when state machine is halted */
653        private HaltingState mHaltingState = new HaltingState();
654
655        /** State used when state machine is quitting */
656        private QuittingState mQuittingState = new QuittingState();
657
658        /** Reference to the StateMachine */
659        private StateMachine mSm;
660
661        /**
662         * Information about a state.
663         * Used to maintain the hierarchy.
664         */
665        private class StateInfo {
666            /** The state */
667            State state;
668
669            /** The parent of this state, null if there is no parent */
670            StateInfo parentStateInfo;
671
672            /** True when the state has been entered and on the stack */
673            boolean active;
674
675            /**
676             * Convert StateInfo to string
677             */
678            @Override
679            public String toString() {
680                return "state=" + state.getName() + ",active=" + active
681                        + ",parent=" + ((parentStateInfo == null) ?
682                                        "null" : parentStateInfo.state.getName());
683            }
684        }
685
686        /** The map of all of the states in the state machine */
687        private HashMap<State, StateInfo> mStateInfo =
688            new HashMap<State, StateInfo>();
689
690        /** The initial state that will process the first message */
691        private State mInitialState;
692
693        /** The destination state when transitionTo has been invoked */
694        private State mDestState;
695
696        /** The list of deferred messages */
697        private ArrayList<Message> mDeferredMessages = new ArrayList<Message>();
698
699        /**
700         * State entered when transitionToHaltingState is called.
701         */
702        private class HaltingState extends State {
703            @Override
704            public boolean processMessage(Message msg) {
705                mSm.haltedProcessMessage(msg);
706                return true;
707            }
708        }
709
710        /**
711         * State entered when a valid quit message is handled.
712         */
713        private class QuittingState extends State {
714            @Override
715            public boolean processMessage(Message msg) {
716                return NOT_HANDLED;
717            }
718        }
719
720        /**
721         * Handle messages sent to the state machine by calling
722         * the current state's processMessage. It also handles
723         * the enter/exit calls and placing any deferred messages
724         * back onto the queue when transitioning to a new state.
725         */
726        @Override
727        public final void handleMessage(Message msg) {
728            if (mDbg) Log.d(TAG, "handleMessage: E msg.what=" + msg.what);
729
730            /** Save the current message */
731            mMsg = msg;
732
733            if (mIsConstructionCompleted) {
734                /** Normal path */
735                processMsg(msg);
736            } else if (!mIsConstructionCompleted &&
737                    (mMsg.what == SM_INIT_CMD) && (mMsg.obj == mSmHandlerObj)) {
738                /** Initial one time path. */
739                mIsConstructionCompleted = true;
740                invokeEnterMethods(0);
741            } else {
742                throw new RuntimeException("StateMachine.handleMessage: " +
743                            "The start method not called, received msg: " + msg);
744            }
745            performTransitions();
746
747            if (mDbg) Log.d(TAG, "handleMessage: X");
748        }
749
750        /**
751         * Do any transitions
752         */
753        private void performTransitions() {
754            /**
755             * If transitionTo has been called, exit and then enter
756             * the appropriate states. We loop on this to allow
757             * enter and exit methods to use transitionTo.
758             */
759            State destState = null;
760            while (mDestState != null) {
761                if (mDbg) Log.d(TAG, "handleMessage: new destination call exit");
762
763                /**
764                 * Save mDestState locally and set to null
765                 * to know if enter/exit use transitionTo.
766                 */
767                destState = mDestState;
768                mDestState = null;
769
770                /**
771                 * Determine the states to exit and enter and return the
772                 * common ancestor state of the enter/exit states. Then
773                 * invoke the exit methods then the enter methods.
774                 */
775                StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState);
776                invokeExitMethods(commonStateInfo);
777                int stateStackEnteringIndex = moveTempStateStackToStateStack();
778                invokeEnterMethods(stateStackEnteringIndex);
779
780
781                /**
782                 * Since we have transitioned to a new state we need to have
783                 * any deferred messages moved to the front of the message queue
784                 * so they will be processed before any other messages in the
785                 * message queue.
786                 */
787                moveDeferredMessageAtFrontOfQueue();
788            }
789
790            /**
791             * After processing all transitions check and
792             * see if the last transition was to quit or halt.
793             */
794            if (destState != null) {
795                if (destState == mQuittingState) {
796                    cleanupAfterQuitting();
797
798                } else if (destState == mHaltingState) {
799                    /**
800                     * Call halting() if we've transitioned to the halting
801                     * state. All subsequent messages will be processed in
802                     * in the halting state which invokes haltedProcessMessage(msg);
803                     */
804                    mSm.halting();
805                }
806            }
807        }
808
809        /**
810         * Cleanup all the static variables and the looper after the SM has been quit.
811         */
812        private final void cleanupAfterQuitting() {
813            mSm.quitting();
814            if (mSm.mSmThread != null) {
815                // If we made the thread then quit looper which stops the thread.
816                getLooper().quit();
817                mSm.mSmThread = null;
818            }
819
820            mSm.mSmHandler = null;
821            mSm = null;
822            mMsg = null;
823            mProcessedMessages.cleanup();
824            mStateStack = null;
825            mTempStateStack = null;
826            mStateInfo.clear();
827            mInitialState = null;
828            mDestState = null;
829            mDeferredMessages.clear();
830        }
831
832        /**
833         * Complete the construction of the state machine.
834         */
835        private final void completeConstruction() {
836            if (mDbg) Log.d(TAG, "completeConstruction: E");
837
838            /**
839             * Determine the maximum depth of the state hierarchy
840             * so we can allocate the state stacks.
841             */
842            int maxDepth = 0;
843            for (StateInfo si : mStateInfo.values()) {
844                int depth = 0;
845                for (StateInfo i = si; i != null; depth++) {
846                    i = i.parentStateInfo;
847                }
848                if (maxDepth < depth) {
849                    maxDepth = depth;
850                }
851            }
852            if (mDbg) Log.d(TAG, "completeConstruction: maxDepth=" + maxDepth);
853
854            mStateStack = new StateInfo[maxDepth];
855            mTempStateStack = new StateInfo[maxDepth];
856            setupInitialStateStack();
857
858            /** Sending SM_INIT_CMD message to invoke enter methods asynchronously */
859            sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj));
860
861            if (mDbg) Log.d(TAG, "completeConstruction: X");
862        }
863
864        /**
865         * Process the message. If the current state doesn't handle
866         * it, call the states parent and so on. If it is never handled then
867         * call the state machines unhandledMessage method.
868         */
869        private final void processMsg(Message msg) {
870            StateInfo curStateInfo = mStateStack[mStateStackTopIndex];
871            if (mDbg) {
872                Log.d(TAG, "processMsg: " + curStateInfo.state.getName());
873            }
874            while (!curStateInfo.state.processMessage(msg)) {
875                /**
876                 * Not processed
877                 */
878                curStateInfo = curStateInfo.parentStateInfo;
879                if (curStateInfo == null) {
880                    /**
881                     * No parents left so it's not handled
882                     */
883                    mSm.unhandledMessage(msg);
884                    if (isQuit(msg)) {
885                        transitionTo(mQuittingState);
886                    }
887                    break;
888                }
889                if (mDbg) {
890                    Log.d(TAG, "processMsg: " + curStateInfo.state.getName());
891                }
892            }
893
894            /**
895             * Record that we processed the message
896             */
897            if (curStateInfo != null) {
898                State orgState = mStateStack[mStateStackTopIndex].state;
899                mProcessedMessages.add(msg, curStateInfo.state, orgState);
900            } else {
901                mProcessedMessages.add(msg, null, null);
902            }
903        }
904
905        /**
906         * Call the exit method for each state from the top of stack
907         * up to the common ancestor state.
908         */
909        private final void invokeExitMethods(StateInfo commonStateInfo) {
910            while ((mStateStackTopIndex >= 0) &&
911                    (mStateStack[mStateStackTopIndex] != commonStateInfo)) {
912                State curState = mStateStack[mStateStackTopIndex].state;
913                if (mDbg) Log.d(TAG, "invokeExitMethods: " + curState.getName());
914                curState.exit();
915                mStateStack[mStateStackTopIndex].active = false;
916                mStateStackTopIndex -= 1;
917            }
918        }
919
920        /**
921         * Invoke the enter method starting at the entering index to top of state stack
922         */
923        private final void invokeEnterMethods(int stateStackEnteringIndex) {
924            for (int i = stateStackEnteringIndex; i <= mStateStackTopIndex; i++) {
925                if (mDbg) Log.d(TAG, "invokeEnterMethods: " + mStateStack[i].state.getName());
926                mStateStack[i].state.enter();
927                mStateStack[i].active = true;
928            }
929        }
930
931        /**
932         * Move the deferred message to the front of the message queue.
933         */
934        private final void moveDeferredMessageAtFrontOfQueue() {
935            /**
936             * The oldest messages on the deferred list must be at
937             * the front of the queue so start at the back, which
938             * as the most resent message and end with the oldest
939             * messages at the front of the queue.
940             */
941            for (int i = mDeferredMessages.size() - 1; i >= 0; i-- ) {
942                Message curMsg = mDeferredMessages.get(i);
943                if (mDbg) Log.d(TAG, "moveDeferredMessageAtFrontOfQueue; what=" + curMsg.what);
944                sendMessageAtFrontOfQueue(curMsg);
945            }
946            mDeferredMessages.clear();
947        }
948
949        /**
950         * Move the contents of the temporary stack to the state stack
951         * reversing the order of the items on the temporary stack as
952         * they are moved.
953         *
954         * @return index into mStateStack where entering needs to start
955         */
956        private final int moveTempStateStackToStateStack() {
957            int startingIndex = mStateStackTopIndex + 1;
958            int i = mTempStateStackCount - 1;
959            int j = startingIndex;
960            while (i >= 0) {
961                if (mDbg) Log.d(TAG, "moveTempStackToStateStack: i=" + i + ",j=" + j);
962                mStateStack[j] = mTempStateStack[i];
963                j += 1;
964                i -= 1;
965            }
966
967            mStateStackTopIndex = j - 1;
968            if (mDbg) {
969                Log.d(TAG, "moveTempStackToStateStack: X mStateStackTop="
970                      + mStateStackTopIndex + ",startingIndex=" + startingIndex
971                      + ",Top=" + mStateStack[mStateStackTopIndex].state.getName());
972            }
973            return startingIndex;
974        }
975
976        /**
977         * Setup the mTempStateStack with the states we are going to enter.
978         *
979         * This is found by searching up the destState's ancestors for a
980         * state that is already active i.e. StateInfo.active == true.
981         * The destStae and all of its inactive parents will be on the
982         * TempStateStack as the list of states to enter.
983         *
984         * @return StateInfo of the common ancestor for the destState and
985         * current state or null if there is no common parent.
986         */
987        private final StateInfo setupTempStateStackWithStatesToEnter(State destState) {
988            /**
989             * Search up the parent list of the destination state for an active
990             * state. Use a do while() loop as the destState must always be entered
991             * even if it is active. This can happen if we are exiting/entering
992             * the current state.
993             */
994            mTempStateStackCount = 0;
995            StateInfo curStateInfo = mStateInfo.get(destState);
996            do {
997                mTempStateStack[mTempStateStackCount++] = curStateInfo;
998                curStateInfo = curStateInfo.parentStateInfo;
999            } while ((curStateInfo != null) && !curStateInfo.active);
1000
1001            if (mDbg) {
1002                Log.d(TAG, "setupTempStateStackWithStatesToEnter: X mTempStateStackCount="
1003                      + mTempStateStackCount + ",curStateInfo: " + curStateInfo);
1004            }
1005            return curStateInfo;
1006        }
1007
1008        /**
1009         * Initialize StateStack to mInitialState.
1010         */
1011        private final void setupInitialStateStack() {
1012            if (mDbg) {
1013                Log.d(TAG, "setupInitialStateStack: E mInitialState="
1014                    + mInitialState.getName());
1015            }
1016
1017            StateInfo curStateInfo = mStateInfo.get(mInitialState);
1018            for (mTempStateStackCount = 0; curStateInfo != null; mTempStateStackCount++) {
1019                mTempStateStack[mTempStateStackCount] = curStateInfo;
1020                curStateInfo = curStateInfo.parentStateInfo;
1021            }
1022
1023            // Empty the StateStack
1024            mStateStackTopIndex = -1;
1025
1026            moveTempStateStackToStateStack();
1027        }
1028
1029        /**
1030         * @return current message
1031         */
1032        private final Message getCurrentMessage() {
1033            return mMsg;
1034        }
1035
1036        /**
1037         * @return current state
1038         */
1039        private final IState getCurrentState() {
1040            return mStateStack[mStateStackTopIndex].state;
1041        }
1042
1043        /**
1044         * Add a new state to the state machine. Bottom up addition
1045         * of states is allowed but the same state may only exist
1046         * in one hierarchy.
1047         *
1048         * @param state the state to add
1049         * @param parent the parent of state
1050         * @return stateInfo for this state
1051         */
1052        private final StateInfo addState(State state, State parent) {
1053            if (mDbg) {
1054                Log.d(TAG, "addStateInternal: E state=" + state.getName()
1055                        + ",parent=" + ((parent == null) ? "" : parent.getName()));
1056            }
1057            StateInfo parentStateInfo = null;
1058            if (parent != null) {
1059                parentStateInfo = mStateInfo.get(parent);
1060                if (parentStateInfo == null) {
1061                    // Recursively add our parent as it's not been added yet.
1062                    parentStateInfo = addState(parent, null);
1063                }
1064            }
1065            StateInfo stateInfo = mStateInfo.get(state);
1066            if (stateInfo == null) {
1067                stateInfo = new StateInfo();
1068                mStateInfo.put(state, stateInfo);
1069            }
1070
1071            // Validate that we aren't adding the same state in two different hierarchies.
1072            if ((stateInfo.parentStateInfo != null) &&
1073                    (stateInfo.parentStateInfo != parentStateInfo)) {
1074                    throw new RuntimeException("state already added");
1075            }
1076            stateInfo.state = state;
1077            stateInfo.parentStateInfo = parentStateInfo;
1078            stateInfo.active = false;
1079            if (mDbg) Log.d(TAG, "addStateInternal: X stateInfo: " + stateInfo);
1080            return stateInfo;
1081        }
1082
1083        /**
1084         * Constructor
1085         *
1086         * @param looper for dispatching messages
1087         * @param sm the hierarchical state machine
1088         */
1089        private SmHandler(Looper looper, StateMachine sm) {
1090            super(looper);
1091            mSm = sm;
1092
1093            addState(mHaltingState, null);
1094            addState(mQuittingState, null);
1095        }
1096
1097        /** @see StateMachine#setInitialState(State) */
1098        private final void setInitialState(State initialState) {
1099            if (mDbg) Log.d(TAG, "setInitialState: initialState=" + initialState.getName());
1100            mInitialState = initialState;
1101        }
1102
1103        /** @see StateMachine#transitionTo(IState) */
1104        private final void transitionTo(IState destState) {
1105            mDestState = (State) destState;
1106            if (mDbg) Log.d(TAG, "transitionTo: destState=" + mDestState.getName());
1107        }
1108
1109        /** @see StateMachine#deferMessage(Message) */
1110        private final void deferMessage(Message msg) {
1111            if (mDbg) Log.d(TAG, "deferMessage: msg=" + msg.what);
1112
1113            /* Copy the "msg" to "newMsg" as "msg" will be recycled */
1114            Message newMsg = obtainMessage();
1115            newMsg.copyFrom(msg);
1116
1117            mDeferredMessages.add(newMsg);
1118        }
1119
1120        /** @see StateMachine#deferMessage(Message) */
1121        private final void quit() {
1122            if (mDbg) Log.d(TAG, "quit:");
1123            sendMessage(obtainMessage(SM_QUIT_CMD, mSmHandlerObj));
1124        }
1125
1126        /** @see StateMachine#isQuit(Message) */
1127        private final boolean isQuit(Message msg) {
1128            return (msg.what == SM_QUIT_CMD) && (msg.obj == mSmHandlerObj);
1129        }
1130
1131        /** @see StateMachine#isDbg() */
1132        private final boolean isDbg() {
1133            return mDbg;
1134        }
1135
1136        /** @see StateMachine#setDbg(boolean) */
1137        private final void setDbg(boolean dbg) {
1138            mDbg = dbg;
1139        }
1140
1141        /** @see StateMachine#setProcessedMessagesSize(int) */
1142        private final void setProcessedMessagesSize(int maxSize) {
1143            mProcessedMessages.setSize(maxSize);
1144        }
1145
1146        /** @see StateMachine#getProcessedMessagesSize() */
1147        private final int getProcessedMessagesSize() {
1148            return mProcessedMessages.size();
1149        }
1150
1151        /** @see StateMachine#getProcessedMessagesCount() */
1152        private final int getProcessedMessagesCount() {
1153            return mProcessedMessages.count();
1154        }
1155
1156        /** @see StateMachine#getProcessedMessageInfo(int) */
1157        private final ProcessedMessageInfo getProcessedMessageInfo(int index) {
1158            return mProcessedMessages.get(index);
1159        }
1160
1161    }
1162
1163    private SmHandler mSmHandler;
1164    private HandlerThread mSmThread;
1165
1166    /**
1167     * Initialize.
1168     *
1169     * @param looper for this state machine
1170     * @param name of the state machine
1171     */
1172    private void initStateMachine(String name, Looper looper) {
1173        mName = name;
1174        mSmHandler = new SmHandler(looper, this);
1175    }
1176
1177    /**
1178     * Constructor creates a StateMachine with its own thread.
1179     *
1180     * @param name of the state machine
1181     */
1182    protected StateMachine(String name) {
1183        mSmThread = new HandlerThread(name);
1184        mSmThread.start();
1185        Looper looper = mSmThread.getLooper();
1186
1187        initStateMachine(name, looper);
1188    }
1189
1190    /**
1191     * Constructor creates a StateMachine using the looper.
1192     *
1193     * @param name of the state machine
1194     */
1195    protected StateMachine(String name, Looper looper) {
1196        initStateMachine(name, looper);
1197    }
1198
1199    /**
1200     * Add a new state to the state machine
1201     * @param state the state to add
1202     * @param parent the parent of state
1203     */
1204    protected final void addState(State state, State parent) {
1205        mSmHandler.addState(state, parent);
1206    }
1207
1208    /**
1209     * @return current message
1210     */
1211    protected final Message getCurrentMessage() {
1212        return mSmHandler.getCurrentMessage();
1213    }
1214
1215    /**
1216     * @return current state
1217     */
1218    protected final IState getCurrentState() {
1219        return mSmHandler.getCurrentState();
1220    }
1221
1222    /**
1223     * Add a new state to the state machine, parent will be null
1224     * @param state to add
1225     */
1226    protected final void addState(State state) {
1227        mSmHandler.addState(state, null);
1228    }
1229
1230    /**
1231     * Set the initial state. This must be invoked before
1232     * and messages are sent to the state machine.
1233     *
1234     * @param initialState is the state which will receive the first message.
1235     */
1236    protected final void setInitialState(State initialState) {
1237        mSmHandler.setInitialState(initialState);
1238    }
1239
1240    /**
1241     * transition to destination state. Upon returning
1242     * from processMessage the current state's exit will
1243     * be executed and upon the next message arriving
1244     * destState.enter will be invoked.
1245     *
1246     * this function can also be called inside the enter function of the
1247     * previous transition target, but the behavior is undefined when it is
1248     * called mid-way through a previous transition (for example, calling this
1249     * in the enter() routine of a intermediate node when the current transition
1250     * target is one of the nodes descendants).
1251     *
1252     * @param destState will be the state that receives the next message.
1253     */
1254    protected final void transitionTo(IState destState) {
1255        mSmHandler.transitionTo(destState);
1256    }
1257
1258    /**
1259     * transition to halt state. Upon returning
1260     * from processMessage we will exit all current
1261     * states, execute the halting() method and then
1262     * all subsequent messages haltedProcessMesage
1263     * will be called.
1264     */
1265    protected final void transitionToHaltingState() {
1266        mSmHandler.transitionTo(mSmHandler.mHaltingState);
1267    }
1268
1269    /**
1270     * Defer this message until next state transition.
1271     * Upon transitioning all deferred messages will be
1272     * placed on the queue and reprocessed in the original
1273     * order. (i.e. The next state the oldest messages will
1274     * be processed first)
1275     *
1276     * @param msg is deferred until the next transition.
1277     */
1278    protected final void deferMessage(Message msg) {
1279        mSmHandler.deferMessage(msg);
1280    }
1281
1282
1283    /**
1284     * Called when message wasn't handled
1285     *
1286     * @param msg that couldn't be handled.
1287     */
1288    protected void unhandledMessage(Message msg) {
1289        if (mSmHandler.mDbg) Log.e(TAG, mName + " - unhandledMessage: msg.what=" + msg.what);
1290    }
1291
1292    /**
1293     * Called for any message that is received after
1294     * transitionToHalting is called.
1295     */
1296    protected void haltedProcessMessage(Message msg) {
1297    }
1298
1299    /**
1300     * This will be called once after handling a message that called
1301     * transitionToHalting. All subsequent messages will invoke
1302     * {@link StateMachine#haltedProcessMessage(Message)}
1303     */
1304    protected void halting() {
1305    }
1306
1307    /**
1308     * This will be called once after a quit message that was NOT handled by
1309     * the derived StateMachine. The StateMachine will stop and any subsequent messages will be
1310     * ignored. In addition, if this StateMachine created the thread, the thread will
1311     * be stopped after this method returns.
1312     */
1313    protected void quitting() {
1314    }
1315
1316    /**
1317     * @return the name
1318     */
1319    public final String getName() {
1320        return mName;
1321    }
1322
1323    /**
1324     * Set size of messages to maintain and clears all current messages.
1325     *
1326     * @param maxSize number of messages to maintain at anyone time.
1327     */
1328    public final void setProcessedMessagesSize(int maxSize) {
1329        mSmHandler.setProcessedMessagesSize(maxSize);
1330    }
1331
1332    /**
1333     * @return number of messages processed
1334     */
1335    public final int getProcessedMessagesSize() {
1336        return mSmHandler.getProcessedMessagesSize();
1337    }
1338
1339    /**
1340     * @return the total number of messages processed
1341     */
1342    public final int getProcessedMessagesCount() {
1343        return mSmHandler.getProcessedMessagesCount();
1344    }
1345
1346    /**
1347     * @return a processed message information
1348     */
1349    public final ProcessedMessageInfo getProcessedMessageInfo(int index) {
1350        return mSmHandler.getProcessedMessageInfo(index);
1351    }
1352
1353    /**
1354     * @return Handler
1355     */
1356    public final Handler getHandler() {
1357        return mSmHandler;
1358    }
1359
1360    /**
1361     * Get a message and set Message.target = this.
1362     *
1363     * @return message or null if SM has quit
1364     */
1365    public final Message obtainMessage()
1366    {
1367        if (mSmHandler == null) return null;
1368
1369        return Message.obtain(mSmHandler);
1370    }
1371
1372    /**
1373     * Get a message and set Message.target = this and what
1374     *
1375     * @param what is the assigned to Message.what.
1376     * @return message or null if SM has quit
1377     */
1378    public final Message obtainMessage(int what) {
1379        if (mSmHandler == null) return null;
1380
1381        return Message.obtain(mSmHandler, what);
1382    }
1383
1384    /**
1385     * Get a message and set Message.target = this,
1386     * what and obj.
1387     *
1388     * @param what is the assigned to Message.what.
1389     * @param obj is assigned to Message.obj.
1390     * @return message or null if SM has quit
1391     */
1392    public final Message obtainMessage(int what, Object obj)
1393    {
1394        if (mSmHandler == null) return null;
1395
1396        return Message.obtain(mSmHandler, what, obj);
1397    }
1398
1399    /**
1400     * Get a message and set Message.target = this,
1401     * what, arg1 and arg2
1402     *
1403     * @param what  is assigned to Message.what
1404     * @param arg1  is assigned to Message.arg1
1405     * @param arg2  is assigned to Message.arg2
1406     * @return  A Message object from the global pool or null if
1407     *          SM has quit
1408     */
1409    public final Message obtainMessage(int what, int arg1, int arg2)
1410    {
1411        if (mSmHandler == null) return null;
1412
1413        return Message.obtain(mSmHandler, what, arg1, arg2);
1414    }
1415
1416    /**
1417     * Get a message and set Message.target = this,
1418     * what, arg1, arg2 and obj
1419     *
1420     * @param what  is assigned to Message.what
1421     * @param arg1  is assigned to Message.arg1
1422     * @param arg2  is assigned to Message.arg2
1423     * @param obj is assigned to Message.obj
1424     * @return  A Message object from the global pool or null if
1425     *          SM has quit
1426     */
1427    public final Message obtainMessage(int what, int arg1, int arg2, Object obj)
1428    {
1429        if (mSmHandler == null) return null;
1430
1431        return Message.obtain(mSmHandler, what, arg1, arg2, obj);
1432    }
1433
1434    /**
1435     * Enqueue a message to this state machine.
1436     */
1437    public final void sendMessage(int what) {
1438        // mSmHandler can be null if the state machine has quit.
1439        if (mSmHandler == null) return;
1440
1441        mSmHandler.sendMessage(obtainMessage(what));
1442    }
1443
1444    /**
1445     * Enqueue a message to this state machine.
1446     */
1447    public final void sendMessage(int what, Object obj) {
1448        // mSmHandler can be null if the state machine has quit.
1449        if (mSmHandler == null) return;
1450
1451        mSmHandler.sendMessage(obtainMessage(what,obj));
1452    }
1453
1454    /**
1455     * Enqueue a message to this state machine.
1456     */
1457    public final void sendMessage(Message msg) {
1458        // mSmHandler can be null if the state machine has quit.
1459        if (mSmHandler == null) return;
1460
1461        mSmHandler.sendMessage(msg);
1462    }
1463
1464    /**
1465     * Enqueue a message to this state machine after a delay.
1466     */
1467    public final void sendMessageDelayed(int what, long delayMillis) {
1468        // mSmHandler can be null if the state machine has quit.
1469        if (mSmHandler == null) return;
1470
1471        mSmHandler.sendMessageDelayed(obtainMessage(what), delayMillis);
1472    }
1473
1474    /**
1475     * Enqueue a message to this state machine after a delay.
1476     */
1477    public final void sendMessageDelayed(int what, Object obj, long delayMillis) {
1478        // mSmHandler can be null if the state machine has quit.
1479        if (mSmHandler == null) return;
1480
1481        mSmHandler.sendMessageDelayed(obtainMessage(what, obj), delayMillis);
1482    }
1483
1484    /**
1485     * Enqueue a message to this state machine after a delay.
1486     */
1487    public final void sendMessageDelayed(Message msg, long delayMillis) {
1488        // mSmHandler can be null if the state machine has quit.
1489        if (mSmHandler == null) return;
1490
1491        mSmHandler.sendMessageDelayed(msg, delayMillis);
1492    }
1493
1494    /**
1495     * Enqueue a message to the front of the queue for this state machine.
1496     * Protected, may only be called by instances of StateMachine.
1497     */
1498    protected final void sendMessageAtFrontOfQueue(int what, Object obj) {
1499        mSmHandler.sendMessageAtFrontOfQueue(obtainMessage(what, obj));
1500    }
1501
1502    /**
1503     * Enqueue a message to the front of the queue for this state machine.
1504     * Protected, may only be called by instances of StateMachine.
1505     */
1506    protected final void sendMessageAtFrontOfQueue(int what) {
1507        mSmHandler.sendMessageAtFrontOfQueue(obtainMessage(what));
1508    }
1509
1510    /**
1511     * Enqueue a message to the front of the queue for this state machine.
1512     * Protected, may only be called by instances of StateMachine.
1513     */
1514    protected final void sendMessageAtFrontOfQueue(Message msg) {
1515        mSmHandler.sendMessageAtFrontOfQueue(msg);
1516    }
1517
1518    /**
1519     * Removes a message from the message queue.
1520     * Protected, may only be called by instances of StateMachine.
1521     */
1522    protected final void removeMessages(int what) {
1523        mSmHandler.removeMessages(what);
1524    }
1525
1526    /**
1527     * Conditionally quit the looper and stop execution.
1528     *
1529     * This sends the SM_QUIT_MSG to the state machine and
1530     * if not handled by any state's processMessage then the
1531     * state machine will be stopped and no further messages
1532     * will be processed.
1533     */
1534    public final void quit() {
1535        // mSmHandler can be null if the state machine has quit.
1536        if (mSmHandler == null) return;
1537
1538        mSmHandler.quit();
1539    }
1540
1541    /**
1542     * @return ture if msg is quit
1543     */
1544    protected final boolean isQuit(Message msg) {
1545        return mSmHandler.isQuit(msg);
1546    }
1547
1548    /**
1549     * @return if debugging is enabled
1550     */
1551    public boolean isDbg() {
1552        // mSmHandler can be null if the state machine has quit.
1553        if (mSmHandler == null) return false;
1554
1555        return mSmHandler.isDbg();
1556    }
1557
1558    /**
1559     * Set debug enable/disabled.
1560     *
1561     * @param dbg is true to enable debugging.
1562     */
1563    public void setDbg(boolean dbg) {
1564        // mSmHandler can be null if the state machine has quit.
1565        if (mSmHandler == null) return;
1566
1567        mSmHandler.setDbg(dbg);
1568    }
1569
1570    /**
1571     * Start the state machine.
1572     */
1573    public void start() {
1574        // mSmHandler can be null if the state machine has quit.
1575        if (mSmHandler == null) return;
1576
1577        /** Send the complete construction message */
1578        mSmHandler.completeConstruction();
1579    }
1580}
1581