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