StateMachine.java revision 07e317c3cd9fe38954813b75ac582dd64f0c41f1
194afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood/**
294afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood * Copyright (C) 2009 The Android Open Source Project
394afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood *
494afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood * Licensed under the Apache License, Version 2.0 (the "License");
594afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood * you may not use this file except in compliance with the License.
60b41ad5d6ec86cd2d481969dcff7e88f2805324fElliott Hughes * You may obtain a copy of the License at
713df5f5f8fbc5a3fdfdd5d1ba5dc853cf3f017f0Mark Salyzyn *
813df5f5f8fbc5a3fdfdd5d1ba5dc853cf3f017f0Mark Salyzyn *      http://www.apache.org/licenses/LICENSE-2.0
913df5f5f8fbc5a3fdfdd5d1ba5dc853cf3f017f0Mark Salyzyn *
1094afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood * Unless required by applicable law or agreed to in writing, software
116b9c6d23f69a64f76fc26cda6989183042b0621cArve Hjønnevåg * distributed under the License is distributed on an "AS IS" BASIS,
126b9c6d23f69a64f76fc26cda6989183042b0621cArve Hjønnevåg * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1394afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood * See the License for the specific language governing permissions and
1494afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood * limitations under the License.
1569154df9efd3ffb7580b72a0138f58a2f5443db6Riley Spahn */
167d42a3c31ba78a418f9bdde0e0ab951469f321b5Nick Kralevich
1769154df9efd3ffb7580b72a0138f58a2f5443db6Riley Spahnpackage com.android.internal.util;
1894afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood
1994afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwoodimport android.os.Handler;
2094afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwoodimport android.os.HandlerThread;
2194afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwoodimport android.os.Looper;
2294afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwoodimport android.os.Message;
2394afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwoodimport android.text.TextUtils;
2494afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwoodimport android.util.Log;
2594afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood
2694afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwoodimport java.io.FileDescriptor;
2794afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwoodimport java.io.PrintWriter;
288fb0f92e0fe3c286ebb120bae9e1570ee109fa12William Robertsimport java.io.StringWriter;
298fb0f92e0fe3c286ebb120bae9e1570ee109fa12William Robertsimport java.util.ArrayList;
308fb0f92e0fe3c286ebb120bae9e1570ee109fa12William Robertsimport java.util.Calendar;
318fb0f92e0fe3c286ebb120bae9e1570ee109fa12William Robertsimport java.util.Collection;
328fb0f92e0fe3c286ebb120bae9e1570ee109fa12William Robertsimport java.util.HashMap;
338fb0f92e0fe3c286ebb120bae9e1570ee109fa12William Robertsimport java.util.Iterator;
347d42a3c31ba78a418f9bdde0e0ab951469f321b5Nick Kralevichimport java.util.Vector;
3594afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood
3694afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood/**
377d42a3c31ba78a418f9bdde0e0ab951469f321b5Nick Kralevich * {@hide}
3894afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood *
3994afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood * <p>The state machine defined here is a hierarchical state machine which processes messages
407d42a3c31ba78a418f9bdde0e0ab951469f321b5Nick Kralevich * and can have states arranged hierarchically.</p>
417d42a3c31ba78a418f9bdde0e0ab951469f321b5Nick Kralevich *
427d42a3c31ba78a418f9bdde0e0ab951469f321b5Nick Kralevich * <p>A state is a <code>State</code> object and must implement
437d42a3c31ba78a418f9bdde0e0ab951469f321b5Nick Kralevich * <code>processMessage</code> and optionally <code>enter/exit/getName</code>.
4494afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood * The enter/exit methods are equivalent to the construction and destruction
457d42a3c31ba78a418f9bdde0e0ab951469f321b5Nick Kralevich * in Object Oriented programming and are used to perform initialization and
4694afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood * cleanup of the state respectively. The <code>getName</code> method returns the
477d42a3c31ba78a418f9bdde0e0ab951469f321b5Nick Kralevich * name of the state; the default implementation returns the class name. It may be
4894afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood * desirable to have <code>getName</code> return the the state instance name instead,
4994afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood * in particular if a particular state class has multiple instances.</p>
5094afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood *
5194afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood * <p>When a state machine is created, <code>addState</code> is used to build the
5294afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood * hierarchy and <code>setInitialState</code> is used to identify which of these
5394afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood * is the initial state. After construction the programmer calls <code>start</code>
549b738bb4110926b85da65d36b2e6f1a50199ec4cSerban Constantinescu * which initializes and starts the state machine. The first action the StateMachine
5594afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood * is to the invoke <code>enter</code> for all of the initial state's hierarchy,
5694afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood * starting at its eldest parent. The calls to enter will be done in the context
5794afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood * of the StateMachine's Handler, not in the context of the call to start, and they
5894afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood * will be invoked before any messages are processed. For example, given the simple
5994afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood * state machine below, mP1.enter will be invoked and then mS1.enter. Finally,
6094afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood * messages sent to the state machine will be processed by the current state;
6194afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood * in our simple state machine below that would initially be mS1.processMessage.</p>
6294afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood<pre>
63c67e6307cadb1f2cd876907c42d39b8374b93acdRiley Spahn        mP1
6469154df9efd3ffb7580b72a0138f58a2f5443db6Riley Spahn       /   \
6569154df9efd3ffb7580b72a0138f58a2f5443db6Riley Spahn      mS2   mS1 ----&gt; initial state
668fb0f92e0fe3c286ebb120bae9e1570ee109fa12William Roberts</pre>
6769154df9efd3ffb7580b72a0138f58a2f5443db6Riley Spahn * <p>After the state machine is created and started, messages are sent to a state
6869154df9efd3ffb7580b72a0138f58a2f5443db6Riley Spahn * machine using <code>sendMessage</code> and the messages are created using
69c67e6307cadb1f2cd876907c42d39b8374b93acdRiley Spahn * <code>obtainMessage</code>. When the state machine receives a message the
70c67e6307cadb1f2cd876907c42d39b8374b93acdRiley Spahn * current state's <code>processMessage</code> is invoked. In the above example
718fb0f92e0fe3c286ebb120bae9e1570ee109fa12William Roberts * mS1.processMessage will be invoked first. The state may use <code>transitionTo</code>
7269154df9efd3ffb7580b72a0138f58a2f5443db6Riley Spahn * to change the current state to a new state.</p>
73c67e6307cadb1f2cd876907c42d39b8374b93acdRiley Spahn *
74c67e6307cadb1f2cd876907c42d39b8374b93acdRiley Spahn * <p>Each state in the state machine may have a zero or one parent states. If
7569154df9efd3ffb7580b72a0138f58a2f5443db6Riley Spahn * a child state is unable to handle a message it may have the message processed
7669154df9efd3ffb7580b72a0138f58a2f5443db6Riley Spahn * by its parent by returning false or NOT_HANDLED. If a message is not handled by
7769154df9efd3ffb7580b72a0138f58a2f5443db6Riley Spahn * a child state or any of its ancestors, <code>unhandledMessage</code> will be invoked
788fb0f92e0fe3c286ebb120bae9e1570ee109fa12William Roberts * to give one last chance for the state machine to process the message.</p>
798fb0f92e0fe3c286ebb120bae9e1570ee109fa12William Roberts *
808fb0f92e0fe3c286ebb120bae9e1570ee109fa12William Roberts * <p>When all processing is completed a state machine may choose to call
818fb0f92e0fe3c286ebb120bae9e1570ee109fa12William Roberts * <code>transitionToHaltingState</code>. When the current <code>processingMessage</code>
828fb0f92e0fe3c286ebb120bae9e1570ee109fa12William Roberts * returns the state machine will transfer to an internal <code>HaltingState</code>
83c67e6307cadb1f2cd876907c42d39b8374b93acdRiley Spahn * and invoke <code>halting</code>. Any message subsequently received by the state
84c67e6307cadb1f2cd876907c42d39b8374b93acdRiley Spahn * machine will cause <code>haltedProcessMessage</code> to be invoked.</p>
85c67e6307cadb1f2cd876907c42d39b8374b93acdRiley Spahn *
86c67e6307cadb1f2cd876907c42d39b8374b93acdRiley Spahn * <p>If it is desirable to completely stop the state machine call <code>quit</code> or
87c67e6307cadb1f2cd876907c42d39b8374b93acdRiley Spahn * <code>quitNow</code>. These will call <code>exit</code> of the current state and its parents,
88c67e6307cadb1f2cd876907c42d39b8374b93acdRiley Spahn * call <code>onQuitting</code> and then exit Thread/Loopers.</p>
898fb0f92e0fe3c286ebb120bae9e1570ee109fa12William Roberts *
90c67e6307cadb1f2cd876907c42d39b8374b93acdRiley Spahn * <p>In addition to <code>processMessage</code> each <code>State</code> has
918fb0f92e0fe3c286ebb120bae9e1570ee109fa12William Roberts * an <code>enter</code> method and <code>exit</code> method which may be overridden.</p>
92c67e6307cadb1f2cd876907c42d39b8374b93acdRiley Spahn *
93c67e6307cadb1f2cd876907c42d39b8374b93acdRiley Spahn * <p>Since the states are arranged in a hierarchy transitioning to a new state
948fb0f92e0fe3c286ebb120bae9e1570ee109fa12William Roberts * causes current states to be exited and new states to be entered. To determine
95c67e6307cadb1f2cd876907c42d39b8374b93acdRiley Spahn * the list of states to be entered/exited the common parent closest to
96c67e6307cadb1f2cd876907c42d39b8374b93acdRiley Spahn * the current state is found. We then exit from the current state and its
97c67e6307cadb1f2cd876907c42d39b8374b93acdRiley Spahn * parent's up to but not including the common parent state and then enter all
98c67e6307cadb1f2cd876907c42d39b8374b93acdRiley Spahn * of the new states below the common parent down to the destination state.
99c67e6307cadb1f2cd876907c42d39b8374b93acdRiley Spahn * If there is no common parent all states are exited and then the new states
100c67e6307cadb1f2cd876907c42d39b8374b93acdRiley Spahn * are entered.</p>
101c67e6307cadb1f2cd876907c42d39b8374b93acdRiley Spahn *
10269154df9efd3ffb7580b72a0138f58a2f5443db6Riley Spahn * <p>Two other methods that states can use are <code>deferMessage</code> and
10369154df9efd3ffb7580b72a0138f58a2f5443db6Riley Spahn * <code>sendMessageAtFrontOfQueue</code>. The <code>sendMessageAtFrontOfQueue</code> sends
104c67e6307cadb1f2cd876907c42d39b8374b93acdRiley Spahn * a message but places it on the front of the queue rather than the back. The
105c67e6307cadb1f2cd876907c42d39b8374b93acdRiley Spahn * <code>deferMessage</code> causes the message to be saved on a list until a
10669154df9efd3ffb7580b72a0138f58a2f5443db6Riley Spahn * transition is made to a new state. At which time all of the deferred messages
10769154df9efd3ffb7580b72a0138f58a2f5443db6Riley Spahn * will be put on the front of the state machine queue with the oldest message
10869154df9efd3ffb7580b72a0138f58a2f5443db6Riley Spahn * at the front. These will then be processed by the new current state before
1098fb0f92e0fe3c286ebb120bae9e1570ee109fa12William Roberts * any other messages that are on the queue or might be added later. Both of
11069154df9efd3ffb7580b72a0138f58a2f5443db6Riley Spahn * these are protected and may only be invoked from within a state machine.</p>
11169154df9efd3ffb7580b72a0138f58a2f5443db6Riley Spahn *
11269154df9efd3ffb7580b72a0138f58a2f5443db6Riley Spahn * <p>To illustrate some of these properties we'll use state machine with an 8
11369154df9efd3ffb7580b72a0138f58a2f5443db6Riley Spahn * state hierarchy:</p>
1148fb0f92e0fe3c286ebb120bae9e1570ee109fa12William Roberts<pre>
115c67e6307cadb1f2cd876907c42d39b8374b93acdRiley Spahn          mP0
116c67e6307cadb1f2cd876907c42d39b8374b93acdRiley Spahn         /   \
1175fa90a063f77054fc6068f45f6a21f43d64364b6Arve Hjønnevåg        mP1   mS0
1186b9c6d23f69a64f76fc26cda6989183042b0621cArve Hjønnevåg       /   \
1195fa90a063f77054fc6068f45f6a21f43d64364b6Arve Hjønnevåg      mS2   mS1
1205fa90a063f77054fc6068f45f6a21f43d64364b6Arve Hjønnevåg     /  \    \
1215fa90a063f77054fc6068f45f6a21f43d64364b6Arve Hjønnevåg    mS3  mS4  mS5  ---&gt; initial state
1228fb0f92e0fe3c286ebb120bae9e1570ee109fa12William Roberts</pre>
123c67e6307cadb1f2cd876907c42d39b8374b93acdRiley Spahn * <p>After starting mS5 the list of active states is mP0, mP1, mS1 and mS5.
124c67e6307cadb1f2cd876907c42d39b8374b93acdRiley Spahn * So the order of calling processMessage when a message is received is mS5,
1258fb0f92e0fe3c286ebb120bae9e1570ee109fa12William Roberts * mS1, mP1, mP0 assuming each processMessage indicates it can't handle this
12694afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood * message by returning false or NOT_HANDLED.</p>
127c67e6307cadb1f2cd876907c42d39b8374b93acdRiley Spahn *
1288fb0f92e0fe3c286ebb120bae9e1570ee109fa12William Roberts * <p>Now assume mS5.processMessage receives a message it can handle, and during
129c67e6307cadb1f2cd876907c42d39b8374b93acdRiley Spahn * the handling determines the machine should change states. It could call
130c67e6307cadb1f2cd876907c42d39b8374b93acdRiley Spahn * transitionTo(mS4) and return true or HANDLED. Immediately after returning from
1318fb0f92e0fe3c286ebb120bae9e1570ee109fa12William Roberts * processMessage the state machine runtime will find the common parent,
132c67e6307cadb1f2cd876907c42d39b8374b93acdRiley Spahn * which is mP1. It will then call mS5.exit, mS1.exit, mS2.enter and then
133c67e6307cadb1f2cd876907c42d39b8374b93acdRiley Spahn * mS4.enter. The new list of active states is mP0, mP1, mS2 and mS4. So
1348fb0f92e0fe3c286ebb120bae9e1570ee109fa12William Roberts * when the next message is received mS4.processMessage will be invoked.</p>
13594afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood *
13694afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood * <p>Now for some concrete examples, here is the canonical HelloWorld as a state machine.
1379b738bb4110926b85da65d36b2e6f1a50199ec4cSerban Constantinescu * It responds with "Hello World" being printed to the log for every message.</p>
13894afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood<pre>
13994afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwoodclass HelloWorld extends StateMachine {
1405fb1b8836aa5cf0f38b49bc7bfb8343b84fdf9bfSerban Constantinescu    HelloWorld(String name) {
14194afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood        super(name);
14294afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood        addState(mState1);
1439b738bb4110926b85da65d36b2e6f1a50199ec4cSerban Constantinescu        setInitialState(mState1);
14494afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood    }
14594afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood
14694afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood    public static HelloWorld makeHelloWorld() {
1479b738bb4110926b85da65d36b2e6f1a50199ec4cSerban Constantinescu        HelloWorld hw = new HelloWorld("hw");
14894afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood        hw.start();
1499b738bb4110926b85da65d36b2e6f1a50199ec4cSerban Constantinescu        return hw;
15094afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood    }
15194afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood
15294afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood    class State1 extends State {
15394afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood        &#64;Override public boolean processMessage(Message message) {
15494afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood            log("Hello World");
15594afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood            return HANDLED;
15694afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood        }
15794afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood    }
15894afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood    State1 mState1 = new State1();
1599b738bb4110926b85da65d36b2e6f1a50199ec4cSerban Constantinescu}
16094afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood
16194afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwoodvoid testHelloWorld() {
16294afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood    HelloWorld hw = makeHelloWorld();
16394afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood    hw.sendMessage(hw.obtainMessage());
1649b738bb4110926b85da65d36b2e6f1a50199ec4cSerban Constantinescu}
1659b738bb4110926b85da65d36b2e6f1a50199ec4cSerban Constantinescu</pre>
1667d42a3c31ba78a418f9bdde0e0ab951469f321b5Nick Kralevich * <p>A more interesting state machine is one with four states
1675fb1b8836aa5cf0f38b49bc7bfb8343b84fdf9bfSerban Constantinescu * with two independent parent states.</p>
1685fb1b8836aa5cf0f38b49bc7bfb8343b84fdf9bfSerban Constantinescu<pre>
1695fb1b8836aa5cf0f38b49bc7bfb8343b84fdf9bfSerban Constantinescu        mP1      mP2
1709b738bb4110926b85da65d36b2e6f1a50199ec4cSerban Constantinescu       /   \
17194afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood      mS2   mS1
17294afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood</pre>
1739b738bb4110926b85da65d36b2e6f1a50199ec4cSerban Constantinescu * <p>Here is a description of this state machine using pseudo code.</p>
17494afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood <pre>
1759b738bb4110926b85da65d36b2e6f1a50199ec4cSerban Constantinescustate mP1 {
17694afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood     enter { log("mP1.enter"); }
17794afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood     exit { log("mP1.exit");  }
1789b738bb4110926b85da65d36b2e6f1a50199ec4cSerban Constantinescu     on msg {
179d57d9b900da83b1b5431d90e250f86c0047c618aIan Pedowitz         CMD_2 {
18094afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood             send(CMD_3);
181b27bbd18bb65b3744ae066fcd6826285dec8b469Nick Kralevich             defer(msg);
18294afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood             transitionTo(mS2);
183b27bbd18bb65b3744ae066fcd6826285dec8b469Nick Kralevich             return HANDLED;
184c67e6307cadb1f2cd876907c42d39b8374b93acdRiley Spahn         }
185c67e6307cadb1f2cd876907c42d39b8374b93acdRiley Spahn         return NOT_HANDLED;
186b27bbd18bb65b3744ae066fcd6826285dec8b469Nick Kralevich     }
187b27bbd18bb65b3744ae066fcd6826285dec8b469Nick Kralevich}
188b27bbd18bb65b3744ae066fcd6826285dec8b469Nick Kralevich
189b27bbd18bb65b3744ae066fcd6826285dec8b469Nick KralevichINITIAL
190b27bbd18bb65b3744ae066fcd6826285dec8b469Nick Kralevichstate mS1 parent mP1 {
191b27bbd18bb65b3744ae066fcd6826285dec8b469Nick Kralevich     enter { log("mS1.enter"); }
192b27bbd18bb65b3744ae066fcd6826285dec8b469Nick Kralevich     exit  { log("mS1.exit");  }
19394afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood     on msg {
194b27bbd18bb65b3744ae066fcd6826285dec8b469Nick Kralevich         CMD_1 {
195b27bbd18bb65b3744ae066fcd6826285dec8b469Nick Kralevich             transitionTo(mS1);
1968fb0f92e0fe3c286ebb120bae9e1570ee109fa12William Roberts             return HANDLED;
19794afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood         }
19894afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood         return NOT_HANDLED;
199b27bbd18bb65b3744ae066fcd6826285dec8b469Nick Kralevich     }
200b27bbd18bb65b3744ae066fcd6826285dec8b469Nick Kralevich}
20194afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood
20294afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwoodstate mS2 parent mP1 {
20394afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood     enter { log("mS2.enter"); }
2049b738bb4110926b85da65d36b2e6f1a50199ec4cSerban Constantinescu     exit  { log("mS2.exit");  }
20569154df9efd3ffb7580b72a0138f58a2f5443db6Riley Spahn     on msg {
20669154df9efd3ffb7580b72a0138f58a2f5443db6Riley Spahn         CMD_2 {
20794afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood             send(CMD_4);
20894afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood             return HANDLED;
2093a345f0df5f62d77e875a289e9aee89f0d1b526eSerban Constantinescu         }
2107d42a3c31ba78a418f9bdde0e0ab951469f321b5Nick Kralevich         CMD_3 {
21194afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood             defer(msg);
21294afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood             transitionTo(mP2);
2135fb1b8836aa5cf0f38b49bc7bfb8343b84fdf9bfSerban Constantinescu             return HANDLED;
21494afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood         }
21594afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood         return NOT_HANDLED;
2168fb0f92e0fe3c286ebb120bae9e1570ee109fa12William Roberts     }
2175fb1b8836aa5cf0f38b49bc7bfb8343b84fdf9bfSerban Constantinescu}
2187d42a3c31ba78a418f9bdde0e0ab951469f321b5Nick Kralevich
21994afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwoodstate mP2 {
22094afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood     enter {
22194afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood         log("mP2.enter");
22294afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood         send(CMD_5);
22394afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood     }
2245fb1b8836aa5cf0f38b49bc7bfb8343b84fdf9bfSerban Constantinescu     exit { log("mP2.exit"); }
2255fb1b8836aa5cf0f38b49bc7bfb8343b84fdf9bfSerban Constantinescu     on msg {
2267d42a3c31ba78a418f9bdde0e0ab951469f321b5Nick Kralevich         CMD_3, CMD_4 { return HANDLED; }
22794afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood         CMD_5 {
22894afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood             transitionTo(HaltingState);
2295fb1b8836aa5cf0f38b49bc7bfb8343b84fdf9bfSerban Constantinescu             return HANDLED;
23094afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood         }
23194afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood         return NOT_HANDLED;
23294afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood     }
2335fb1b8836aa5cf0f38b49bc7bfb8343b84fdf9bfSerban Constantinescu}
2347d42a3c31ba78a418f9bdde0e0ab951469f321b5Nick Kralevich</pre>
23594afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood * <p>The implementation is below and also in StateMachineTest:</p>
23694afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood<pre>
2375fb1b8836aa5cf0f38b49bc7bfb8343b84fdf9bfSerban Constantinescuclass Hsm1 extends StateMachine {
23894afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood    public static final int CMD_1 = 1;
23994afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood    public static final int CMD_2 = 2;
24094afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood    public static final int CMD_3 = 3;
2419b738bb4110926b85da65d36b2e6f1a50199ec4cSerban Constantinescu    public static final int CMD_4 = 4;
24294afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood    public static final int CMD_5 = 5;
24394afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood
24494afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood    public static Hsm1 makeHsm1() {
24594afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood        log("makeHsm1 E");
24694afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood        Hsm1 sm = new Hsm1("hsm1");
24794afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood        sm.start();
2485fb1b8836aa5cf0f38b49bc7bfb8343b84fdf9bfSerban Constantinescu        log("makeHsm1 X");
2495fb1b8836aa5cf0f38b49bc7bfb8343b84fdf9bfSerban Constantinescu        return sm;
25094afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood    }
25194afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood
25294afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood    Hsm1(String name) {
25394afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood        super(name);
254bcf38880c65297da58194eb0c0ce8d6e2bab7d94Serban Constantinescu        log("ctor E");
25594afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood
25694afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood        // Add states, use indentation to show hierarchy
25794afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood        addState(mP1);
25894afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood            addState(mS1, mP1);
25994afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood            addState(mS2, mP1);
2609b738bb4110926b85da65d36b2e6f1a50199ec4cSerban Constantinescu        addState(mP2);
2615fb1b8836aa5cf0f38b49bc7bfb8343b84fdf9bfSerban Constantinescu
26294afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood        // Set the initial state
26394afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood        setInitialState(mS1);
26494afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood        log("ctor X");
2650b41ad5d6ec86cd2d481969dcff7e88f2805324fElliott Hughes    }
2660b41ad5d6ec86cd2d481969dcff7e88f2805324fElliott Hughes
26794afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood    class P1 extends State {
2680b41ad5d6ec86cd2d481969dcff7e88f2805324fElliott Hughes        &#64;Override public void enter() {
26994afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood            log("mP1.enter");
27094afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood        }
271e5245cbf5d4e830cf605ef07f5d284d7c5d2867eArve Hjønnevåg        &#64;Override public boolean processMessage(Message message) {
272e5245cbf5d4e830cf605ef07f5d284d7c5d2867eArve Hjønnevåg            boolean retVal;
273e5245cbf5d4e830cf605ef07f5d284d7c5d2867eArve Hjønnevåg            log("mP1.processMessage what=" + message.what);
27494afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood            switch(message.what) {
27594afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood            case CMD_2:
27694afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood                // CMD_2 will arrive in mS2 before CMD_3
27794afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood                sendMessage(obtainMessage(CMD_3));
27894afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood                deferMessage(message);
27994afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood                transitionTo(mS2);
2807d42a3c31ba78a418f9bdde0e0ab951469f321b5Nick Kralevich                retVal = HANDLED;
2817d42a3c31ba78a418f9bdde0e0ab951469f321b5Nick Kralevich                break;
2827d42a3c31ba78a418f9bdde0e0ab951469f321b5Nick Kralevich            default:
2837d42a3c31ba78a418f9bdde0e0ab951469f321b5Nick Kralevich                // Any message we don't understand in this state invokes unhandledMessage
28494afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood                retVal = NOT_HANDLED;
28594afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood                break;
2867d42a3c31ba78a418f9bdde0e0ab951469f321b5Nick Kralevich            }
28794afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood            return retVal;
28894afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood        }
28994afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood        &#64;Override public void exit() {
29069154df9efd3ffb7580b72a0138f58a2f5443db6Riley Spahn            log("mP1.exit");
29169154df9efd3ffb7580b72a0138f58a2f5443db6Riley Spahn        }
29269154df9efd3ffb7580b72a0138f58a2f5443db6Riley Spahn    }
29369154df9efd3ffb7580b72a0138f58a2f5443db6Riley Spahn
29469154df9efd3ffb7580b72a0138f58a2f5443db6Riley Spahn    class S1 extends State {
29569154df9efd3ffb7580b72a0138f58a2f5443db6Riley Spahn        &#64;Override public void enter() {
29669154df9efd3ffb7580b72a0138f58a2f5443db6Riley Spahn            log("mS1.enter");
29769154df9efd3ffb7580b72a0138f58a2f5443db6Riley Spahn        }
29894afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood        &#64;Override public boolean processMessage(Message message) {
29994afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood            log("S1.processMessage what=" + message.what);
30094afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood            if (message.what == CMD_1) {
30194afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood                // Transition to ourself to show that enter/exit is called
3027d42a3c31ba78a418f9bdde0e0ab951469f321b5Nick Kralevich                transitionTo(mS1);
3037d42a3c31ba78a418f9bdde0e0ab951469f321b5Nick Kralevich                return HANDLED;
3047d42a3c31ba78a418f9bdde0e0ab951469f321b5Nick Kralevich            } else {
305d57d9b900da83b1b5431d90e250f86c0047c618aIan Pedowitz                // Let parent process all other messages
3065fb1b8836aa5cf0f38b49bc7bfb8343b84fdf9bfSerban Constantinescu                return NOT_HANDLED;
30794afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood            }
3085fb1b8836aa5cf0f38b49bc7bfb8343b84fdf9bfSerban Constantinescu        }
30994afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood        &#64;Override public void exit() {
31094afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood            log("mS1.exit");
31194afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood        }
31294afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood    }
3137d42a3c31ba78a418f9bdde0e0ab951469f321b5Nick Kralevich
3147d42a3c31ba78a418f9bdde0e0ab951469f321b5Nick Kralevich    class S2 extends State {
3157d42a3c31ba78a418f9bdde0e0ab951469f321b5Nick Kralevich        &#64;Override public void enter() {
3165fb1b8836aa5cf0f38b49bc7bfb8343b84fdf9bfSerban Constantinescu            log("mS2.enter");
31794afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood        }
31869154df9efd3ffb7580b72a0138f58a2f5443db6Riley Spahn        &#64;Override public boolean processMessage(Message message) {
31969154df9efd3ffb7580b72a0138f58a2f5443db6Riley Spahn            boolean retVal;
32094afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood            log("mS2.processMessage what=" + message.what);
32194afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood            switch(message.what) {
32294afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood            case(CMD_2):
32394afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood                sendMessage(obtainMessage(CMD_4));
3243a345f0df5f62d77e875a289e9aee89f0d1b526eSerban Constantinescu                retVal = HANDLED;
32594afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood                break;
3268fb0f92e0fe3c286ebb120bae9e1570ee109fa12William Roberts            case(CMD_3):
327c67e6307cadb1f2cd876907c42d39b8374b93acdRiley Spahn                deferMessage(message);
328c67e6307cadb1f2cd876907c42d39b8374b93acdRiley Spahn                transitionTo(mP2);
329c67e6307cadb1f2cd876907c42d39b8374b93acdRiley Spahn                retVal = HANDLED;
330c67e6307cadb1f2cd876907c42d39b8374b93acdRiley Spahn                break;
33194afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood            default:
33294afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood                retVal = NOT_HANDLED;
33394afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood                break;
33494afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood            }
33594afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood            return retVal;
33694afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood        }
33794afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood        &#64;Override public void exit() {
33894afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood            log("mS2.exit");
33994afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood        }
34094afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood    }
34194afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood
34294afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood    class P2 extends State {
34394afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood        &#64;Override public void enter() {
34494afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood            log("mP2.enter");
34594afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood            sendMessage(obtainMessage(CMD_5));
34694afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood        }
34794afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood        &#64;Override public boolean processMessage(Message message) {
34894afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood            log("P2.processMessage what=" + message.what);
34969154df9efd3ffb7580b72a0138f58a2f5443db6Riley Spahn            switch(message.what) {
350d57d9b900da83b1b5431d90e250f86c0047c618aIan Pedowitz            case(CMD_3):
35169154df9efd3ffb7580b72a0138f58a2f5443db6Riley Spahn                break;
3528fb0f92e0fe3c286ebb120bae9e1570ee109fa12William Roberts            case(CMD_4):
3538fb0f92e0fe3c286ebb120bae9e1570ee109fa12William Roberts                break;
3548fb0f92e0fe3c286ebb120bae9e1570ee109fa12William Roberts            case(CMD_5):
3558fb0f92e0fe3c286ebb120bae9e1570ee109fa12William Roberts                transitionToHaltingState();
3568fb0f92e0fe3c286ebb120bae9e1570ee109fa12William Roberts                break;
3578fb0f92e0fe3c286ebb120bae9e1570ee109fa12William Roberts            }
3588fb0f92e0fe3c286ebb120bae9e1570ee109fa12William Roberts            return HANDLED;
3598fb0f92e0fe3c286ebb120bae9e1570ee109fa12William Roberts        }
36069154df9efd3ffb7580b72a0138f58a2f5443db6Riley Spahn        &#64;Override public void exit() {
36169154df9efd3ffb7580b72a0138f58a2f5443db6Riley Spahn            log("mP2.exit");
36269154df9efd3ffb7580b72a0138f58a2f5443db6Riley Spahn        }
363d57d9b900da83b1b5431d90e250f86c0047c618aIan Pedowitz    }
36494afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood
36594afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood    &#64;Override
36694afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood    void onHalting() {
36794afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood        log("halting");
368a44542ca74b7da5b44ba30c205c3244805bb0600Serban Constantinescu        synchronized (this) {
369a44542ca74b7da5b44ba30c205c3244805bb0600Serban Constantinescu            this.notifyAll();
370a44542ca74b7da5b44ba30c205c3244805bb0600Serban Constantinescu        }
371a44542ca74b7da5b44ba30c205c3244805bb0600Serban Constantinescu    }
37294afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood
37394afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood    P1 mP1 = new P1();
37494afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood    S1 mS1 = new S1();
37594afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood    S2 mS2 = new S2();
37694afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood    P2 mP2 = new P2();
37794afecf4b6f437b3ee9a076242402e421c6c07a6Mike Lockwood}
37869154df9efd3ffb7580b72a0138f58a2f5443db6Riley Spahn</pre>
379bea0746b241d15626cf0a56828efc1d4640dbda7Stephen Smalley * <p>If this is executed by sending two messages CMD_1 and CMD_2
38069154df9efd3ffb7580b72a0138f58a2f5443db6Riley Spahn * (Note the synchronize is only needed because we use hsm.wait())</p>
381eb4d5cbde02b17259e968321ef457b158ce1da4eNick Kralevich<pre>
382eb4d5cbde02b17259e968321ef457b158ce1da4eNick KralevichHsm1 hsm = makeHsm1();
383eb4d5cbde02b17259e968321ef457b158ce1da4eNick Kralevichsynchronize(hsm) {
384eb4d5cbde02b17259e968321ef457b158ce1da4eNick Kralevich     hsm.sendMessage(obtainMessage(hsm.CMD_1));
385c67e6307cadb1f2cd876907c42d39b8374b93acdRiley Spahn     hsm.sendMessage(obtainMessage(hsm.CMD_2));
386eb4d5cbde02b17259e968321ef457b158ce1da4eNick Kralevich     try {
387eb4d5cbde02b17259e968321ef457b158ce1da4eNick Kralevich          // wait for the messages to be handled
388eb4d5cbde02b17259e968321ef457b158ce1da4eNick Kralevich          hsm.wait();
389c67e6307cadb1f2cd876907c42d39b8374b93acdRiley Spahn     } catch (InterruptedException e) {
390c67e6307cadb1f2cd876907c42d39b8374b93acdRiley Spahn          loge("exception while waiting " + e.getMessage());
39169154df9efd3ffb7580b72a0138f58a2f5443db6Riley Spahn     }
39269154df9efd3ffb7580b72a0138f58a2f5443db6Riley Spahn}
39369154df9efd3ffb7580b72a0138f58a2f5443db6Riley Spahn</pre>
39469154df9efd3ffb7580b72a0138f58a2f5443db6Riley Spahn * <p>The output is:</p>
39569154df9efd3ffb7580b72a0138f58a2f5443db6Riley Spahn<pre>
39669154df9efd3ffb7580b72a0138f58a2f5443db6Riley SpahnD/hsm1    ( 1999): makeHsm1 E
39794afecf4b6f437b3ee9a076242402e421c6c07a6Mike LockwoodD/hsm1    ( 1999): ctor E
3989b738bb4110926b85da65d36b2e6f1a50199ec4cSerban ConstantinescuD/hsm1    ( 1999): ctor X
39994afecf4b6f437b3ee9a076242402e421c6c07a6Mike LockwoodD/hsm1    ( 1999): mP1.enter
40094afecf4b6f437b3ee9a076242402e421c6c07a6Mike LockwoodD/hsm1    ( 1999): mS1.enter
401D/hsm1    ( 1999): makeHsm1 X
402D/hsm1    ( 1999): mS1.processMessage what=1
403D/hsm1    ( 1999): mS1.exit
404D/hsm1    ( 1999): mS1.enter
405D/hsm1    ( 1999): mS1.processMessage what=2
406D/hsm1    ( 1999): mP1.processMessage what=2
407D/hsm1    ( 1999): mS1.exit
408D/hsm1    ( 1999): mS2.enter
409D/hsm1    ( 1999): mS2.processMessage what=2
410D/hsm1    ( 1999): mS2.processMessage what=3
411D/hsm1    ( 1999): mS2.exit
412D/hsm1    ( 1999): mP1.exit
413D/hsm1    ( 1999): mP2.enter
414D/hsm1    ( 1999): mP2.processMessage what=3
415D/hsm1    ( 1999): mP2.processMessage what=4
416D/hsm1    ( 1999): mP2.processMessage what=5
417D/hsm1    ( 1999): mP2.exit
418D/hsm1    ( 1999): halting
419</pre>
420 */
421public class StateMachine {
422    // Name of the state machine and used as logging tag
423    private String mName;
424
425    /** Message.what value when quitting */
426    private static final int SM_QUIT_CMD = -1;
427
428    /** Message.what value when initializing */
429    private static final int SM_INIT_CMD = -2;
430
431    /**
432     * Convenience constant that maybe returned by processMessage
433     * to indicate the the message was processed and is not to be
434     * processed by parent states
435     */
436    public static final boolean HANDLED = true;
437
438    /**
439     * Convenience constant that maybe returned by processMessage
440     * to indicate the the message was NOT processed and is to be
441     * processed by parent states
442     */
443    public static final boolean NOT_HANDLED = false;
444
445    /**
446     * StateMachine logging record.
447     * {@hide}
448     */
449    public static class LogRec {
450        private StateMachine mSm;
451        private long mTime;
452        private int mWhat;
453        private String mInfo;
454        private IState mState;
455        private IState mOrgState;
456        private IState mDstState;
457
458        /**
459         * Constructor
460         *
461         * @param msg
462         * @param state the state which handled the message
463         * @param orgState is the first state the received the message but
464         * did not processes the message.
465         * @param transToState is the state that was transitioned to after the message was
466         * processed.
467         */
468        LogRec(StateMachine sm, Message msg, String info, IState state, IState orgState,
469                IState transToState) {
470            update(sm, msg, info, state, orgState, transToState);
471        }
472
473        /**
474         * Update the information in the record.
475         * @param state that handled the message
476         * @param orgState is the first state the received the message
477         * @param dstState is the state that was the transition target when logging
478         */
479        public void update(StateMachine sm, Message msg, String info, IState state, IState orgState,
480                IState dstState) {
481            mSm = sm;
482            mTime = System.currentTimeMillis();
483            mWhat = (msg != null) ? msg.what : 0;
484            mInfo = info;
485            mState = state;
486            mOrgState = orgState;
487            mDstState = dstState;
488        }
489
490        /**
491         * @return time stamp
492         */
493        public long getTime() {
494            return mTime;
495        }
496
497        /**
498         * @return msg.what
499         */
500        public long getWhat() {
501            return mWhat;
502        }
503
504        /**
505         * @return the command that was executing
506         */
507        public String getInfo() {
508            return mInfo;
509        }
510
511        /**
512         * @return the state that handled this message
513         */
514        public IState getState() {
515            return mState;
516        }
517
518        /**
519         * @return the state destination state if a transition is occurring or null if none.
520         */
521        public IState getDestState() {
522            return mDstState;
523        }
524
525        /**
526         * @return the original state that received the message.
527         */
528        public IState getOriginalState() {
529            return mOrgState;
530        }
531
532        @Override
533        public String toString() {
534            StringBuilder sb = new StringBuilder();
535            sb.append("time=");
536            Calendar c = Calendar.getInstance();
537            c.setTimeInMillis(mTime);
538            sb.append(String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c));
539            sb.append(" processed=");
540            sb.append(mState == null ? "<null>" : mState.getName());
541            sb.append(" org=");
542            sb.append(mOrgState == null ? "<null>" : mOrgState.getName());
543            sb.append(" dest=");
544            sb.append(mDstState == null ? "<null>" : mDstState.getName());
545            sb.append(" what=");
546            String what = mSm != null ? mSm.getWhatToString(mWhat) : "";
547            if (TextUtils.isEmpty(what)) {
548                sb.append(mWhat);
549                sb.append("(0x");
550                sb.append(Integer.toHexString(mWhat));
551                sb.append(")");
552            } else {
553                sb.append(what);
554            }
555            if (!TextUtils.isEmpty(mInfo)) {
556                sb.append(" ");
557                sb.append(mInfo);
558            }
559            return sb.toString();
560        }
561    }
562
563    /**
564     * A list of log records including messages recently processed by the state machine.
565     *
566     * The class maintains a list of log records including messages
567     * recently processed. The list is finite and may be set in the
568     * constructor or by calling setSize. The public interface also
569     * includes size which returns the number of recent records,
570     * count which is the number of records processed since the
571     * the last setSize, get which returns a record and
572     * add which adds a record.
573     */
574    private static class LogRecords {
575
576        private static final int DEFAULT_SIZE = 20;
577
578        private Vector<LogRec> mLogRecVector = new Vector<LogRec>();
579        private int mMaxSize = DEFAULT_SIZE;
580        private int mOldestIndex = 0;
581        private int mCount = 0;
582        private boolean mLogOnlyTransitions = false;
583
584        /**
585         * private constructor use add
586         */
587        private LogRecords() {
588        }
589
590        /**
591         * Set size of messages to maintain and clears all current records.
592         *
593         * @param maxSize number of records to maintain at anyone time.
594        */
595        synchronized void setSize(int maxSize) {
596            // TODO: once b/28217358 is fixed, add unit tests  to verify that these variables are
597            // cleared after calling this method, and that subsequent calls to get() function as
598            // expected.
599            mMaxSize = maxSize;
600            mOldestIndex = 0;
601            mCount = 0;
602            mLogRecVector.clear();
603        }
604
605        synchronized void setLogOnlyTransitions(boolean enable) {
606            mLogOnlyTransitions = enable;
607        }
608
609        synchronized boolean logOnlyTransitions() {
610            return mLogOnlyTransitions;
611        }
612
613        /**
614         * @return the number of recent records.
615         */
616        synchronized int size() {
617            return mLogRecVector.size();
618        }
619
620        /**
621         * @return the total number of records processed since size was set.
622         */
623        synchronized int count() {
624            return mCount;
625        }
626
627        /**
628         * Clear the list of records.
629         */
630        synchronized void cleanup() {
631            mLogRecVector.clear();
632        }
633
634        /**
635         * @return the information on a particular record. 0 is the oldest
636         * record and size()-1 is the newest record. If the index is to
637         * large null is returned.
638         */
639        synchronized LogRec get(int index) {
640            int nextIndex = mOldestIndex + index;
641            if (nextIndex >= mMaxSize) {
642                nextIndex -= mMaxSize;
643            }
644            if (nextIndex >= size()) {
645                return null;
646            } else {
647                return mLogRecVector.get(nextIndex);
648            }
649        }
650
651        /**
652         * Add a processed message.
653         *
654         * @param msg
655         * @param messageInfo to be stored
656         * @param state that handled the message
657         * @param orgState is the first state the received the message but
658         * did not processes the message.
659         * @param transToState is the state that was transitioned to after the message was
660         * processed.
661         *
662         */
663        synchronized void add(StateMachine sm, Message msg, String messageInfo, IState state,
664                IState orgState, IState transToState) {
665            mCount += 1;
666            if (mLogRecVector.size() < mMaxSize) {
667                mLogRecVector.add(new LogRec(sm, msg, messageInfo, state, orgState, transToState));
668            } else {
669                LogRec pmi = mLogRecVector.get(mOldestIndex);
670                mOldestIndex += 1;
671                if (mOldestIndex >= mMaxSize) {
672                    mOldestIndex = 0;
673                }
674                pmi.update(sm, msg, messageInfo, state, orgState, transToState);
675            }
676        }
677    }
678
679    private static class SmHandler extends Handler {
680
681        /** true if StateMachine has quit */
682        private boolean mHasQuit = false;
683
684        /** The debug flag */
685        private boolean mDbg = false;
686
687        /** The SmHandler object, identifies that message is internal */
688        private static final Object mSmHandlerObj = new Object();
689
690        /** The current message */
691        private Message mMsg;
692
693        /** A list of log records including messages this state machine has processed */
694        private LogRecords mLogRecords = new LogRecords();
695
696        /** true if construction of the state machine has not been completed */
697        private boolean mIsConstructionCompleted;
698
699        /** Stack used to manage the current hierarchy of states */
700        private StateInfo mStateStack[];
701
702        /** Top of mStateStack */
703        private int mStateStackTopIndex = -1;
704
705        /** A temporary stack used to manage the state stack */
706        private StateInfo mTempStateStack[];
707
708        /** The top of the mTempStateStack */
709        private int mTempStateStackCount;
710
711        /** State used when state machine is halted */
712        private HaltingState mHaltingState = new HaltingState();
713
714        /** State used when state machine is quitting */
715        private QuittingState mQuittingState = new QuittingState();
716
717        /** Reference to the StateMachine */
718        private StateMachine mSm;
719
720        /**
721         * Information about a state.
722         * Used to maintain the hierarchy.
723         */
724        private class StateInfo {
725            /** The state */
726            State state;
727
728            /** The parent of this state, null if there is no parent */
729            StateInfo parentStateInfo;
730
731            /** True when the state has been entered and on the stack */
732            boolean active;
733
734            /**
735             * Convert StateInfo to string
736             */
737            @Override
738            public String toString() {
739                return "state=" + state.getName() + ",active=" + active + ",parent="
740                        + ((parentStateInfo == null) ? "null" : parentStateInfo.state.getName());
741            }
742        }
743
744        /** The map of all of the states in the state machine */
745        private HashMap<State, StateInfo> mStateInfo = new HashMap<State, StateInfo>();
746
747        /** The initial state that will process the first message */
748        private State mInitialState;
749
750        /** The destination state when transitionTo has been invoked */
751        private State mDestState;
752
753        /**
754         * Indicates if a transition is in progress
755         *
756         * This will be true for all calls of State.exit and all calls of State.enter except for the
757         * last enter call for the current destination state.
758         */
759        private boolean mTransitionInProgress = false;
760
761        /** The list of deferred messages */
762        private ArrayList<Message> mDeferredMessages = new ArrayList<Message>();
763
764        /**
765         * State entered when transitionToHaltingState is called.
766         */
767        private class HaltingState extends State {
768            @Override
769            public boolean processMessage(Message msg) {
770                mSm.haltedProcessMessage(msg);
771                return true;
772            }
773        }
774
775        /**
776         * State entered when a valid quit message is handled.
777         */
778        private class QuittingState extends State {
779            @Override
780            public boolean processMessage(Message msg) {
781                return NOT_HANDLED;
782            }
783        }
784
785        /**
786         * Handle messages sent to the state machine by calling
787         * the current state's processMessage. It also handles
788         * the enter/exit calls and placing any deferred messages
789         * back onto the queue when transitioning to a new state.
790         */
791        @Override
792        public final void handleMessage(Message msg) {
793            if (!mHasQuit) {
794                if (mSm != null && msg.what != SM_INIT_CMD && msg.what != SM_QUIT_CMD) {
795                    mSm.onPreHandleMessage(msg);
796                }
797
798                if (mDbg) mSm.log("handleMessage: E msg.what=" + msg.what);
799
800                /** Save the current message */
801                mMsg = msg;
802
803                /** State that processed the message */
804                State msgProcessedState = null;
805                if (mIsConstructionCompleted) {
806                    /** Normal path */
807                    msgProcessedState = processMsg(msg);
808                } else if (!mIsConstructionCompleted && (mMsg.what == SM_INIT_CMD)
809                        && (mMsg.obj == mSmHandlerObj)) {
810                    /** Initial one time path. */
811                    mIsConstructionCompleted = true;
812                    invokeEnterMethods(0);
813                } else {
814                    throw new RuntimeException("StateMachine.handleMessage: "
815                            + "The start method not called, received msg: " + msg);
816                }
817                performTransitions(msgProcessedState, msg);
818
819                // We need to check if mSm == null here as we could be quitting.
820                if (mDbg && mSm != null) mSm.log("handleMessage: X");
821
822                if (mSm != null && msg.what != SM_INIT_CMD && msg.what != SM_QUIT_CMD) {
823                    mSm.onPostHandleMessage(msg);
824                }
825            }
826        }
827
828        /**
829         * Do any transitions
830         * @param msgProcessedState is the state that processed the message
831         */
832        private void performTransitions(State msgProcessedState, Message msg) {
833            /**
834             * If transitionTo has been called, exit and then enter
835             * the appropriate states. We loop on this to allow
836             * enter and exit methods to use transitionTo.
837             */
838            State orgState = mStateStack[mStateStackTopIndex].state;
839
840            /**
841             * Record whether message needs to be logged before we transition and
842             * and we won't log special messages SM_INIT_CMD or SM_QUIT_CMD which
843             * always set msg.obj to the handler.
844             */
845            boolean recordLogMsg = mSm.recordLogRec(mMsg) && (msg.obj != mSmHandlerObj);
846
847            if (mLogRecords.logOnlyTransitions()) {
848                /** Record only if there is a transition */
849                if (mDestState != null) {
850                    mLogRecords.add(mSm, mMsg, mSm.getLogRecString(mMsg), msgProcessedState,
851                            orgState, mDestState);
852                }
853            } else if (recordLogMsg) {
854                /** Record message */
855                mLogRecords.add(mSm, mMsg, mSm.getLogRecString(mMsg), msgProcessedState, orgState,
856                        mDestState);
857            }
858
859            State destState = mDestState;
860            if (destState != null) {
861                /**
862                 * Process the transitions including transitions in the enter/exit methods
863                 */
864                while (true) {
865                    if (mDbg) mSm.log("handleMessage: new destination call exit/enter");
866
867                    /**
868                     * Determine the states to exit and enter and return the
869                     * common ancestor state of the enter/exit states. Then
870                     * invoke the exit methods then the enter methods.
871                     */
872                    StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState);
873                    // flag is cleared in invokeEnterMethods before entering the target state
874                    mTransitionInProgress = true;
875                    invokeExitMethods(commonStateInfo);
876                    int stateStackEnteringIndex = moveTempStateStackToStateStack();
877                    invokeEnterMethods(stateStackEnteringIndex);
878
879                    /**
880                     * Since we have transitioned to a new state we need to have
881                     * any deferred messages moved to the front of the message queue
882                     * so they will be processed before any other messages in the
883                     * message queue.
884                     */
885                    moveDeferredMessageAtFrontOfQueue();
886
887                    if (destState != mDestState) {
888                        // A new mDestState so continue looping
889                        destState = mDestState;
890                    } else {
891                        // No change in mDestState so we're done
892                        break;
893                    }
894                }
895                mDestState = null;
896            }
897
898            /**
899             * After processing all transitions check and
900             * see if the last transition was to quit or halt.
901             */
902            if (destState != null) {
903                if (destState == mQuittingState) {
904                    /**
905                     * Call onQuitting to let subclasses cleanup.
906                     */
907                    mSm.onQuitting();
908                    cleanupAfterQuitting();
909                } else if (destState == mHaltingState) {
910                    /**
911                     * Call onHalting() if we've transitioned to the halting
912                     * state. All subsequent messages will be processed in
913                     * in the halting state which invokes haltedProcessMessage(msg);
914                     */
915                    mSm.onHalting();
916                }
917            }
918        }
919
920        /**
921         * Cleanup all the static variables and the looper after the SM has been quit.
922         */
923        private final void cleanupAfterQuitting() {
924            if (mSm.mSmThread != null) {
925                // If we made the thread then quit looper which stops the thread.
926                getLooper().quit();
927                mSm.mSmThread = null;
928            }
929
930            mSm.mSmHandler = null;
931            mSm = null;
932            mMsg = null;
933            mLogRecords.cleanup();
934            mStateStack = null;
935            mTempStateStack = null;
936            mStateInfo.clear();
937            mInitialState = null;
938            mDestState = null;
939            mDeferredMessages.clear();
940            mHasQuit = true;
941        }
942
943        /**
944         * Complete the construction of the state machine.
945         */
946        private final void completeConstruction() {
947            if (mDbg) mSm.log("completeConstruction: E");
948
949            /**
950             * Determine the maximum depth of the state hierarchy
951             * so we can allocate the state stacks.
952             */
953            int maxDepth = 0;
954            for (StateInfo si : mStateInfo.values()) {
955                int depth = 0;
956                for (StateInfo i = si; i != null; depth++) {
957                    i = i.parentStateInfo;
958                }
959                if (maxDepth < depth) {
960                    maxDepth = depth;
961                }
962            }
963            if (mDbg) mSm.log("completeConstruction: maxDepth=" + maxDepth);
964
965            mStateStack = new StateInfo[maxDepth];
966            mTempStateStack = new StateInfo[maxDepth];
967            setupInitialStateStack();
968
969            /** Sending SM_INIT_CMD message to invoke enter methods asynchronously */
970            sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj));
971
972            if (mDbg) mSm.log("completeConstruction: X");
973        }
974
975        /**
976         * Process the message. If the current state doesn't handle
977         * it, call the states parent and so on. If it is never handled then
978         * call the state machines unhandledMessage method.
979         * @return the state that processed the message
980         */
981        private final State processMsg(Message msg) {
982            StateInfo curStateInfo = mStateStack[mStateStackTopIndex];
983            if (mDbg) {
984                mSm.log("processMsg: " + curStateInfo.state.getName());
985            }
986
987            if (isQuit(msg)) {
988                transitionTo(mQuittingState);
989            } else {
990                while (!curStateInfo.state.processMessage(msg)) {
991                    /**
992                     * Not processed
993                     */
994                    curStateInfo = curStateInfo.parentStateInfo;
995                    if (curStateInfo == null) {
996                        /**
997                         * No parents left so it's not handled
998                         */
999                        mSm.unhandledMessage(msg);
1000                        break;
1001                    }
1002                    if (mDbg) {
1003                        mSm.log("processMsg: " + curStateInfo.state.getName());
1004                    }
1005                }
1006            }
1007            return (curStateInfo != null) ? curStateInfo.state : null;
1008        }
1009
1010        /**
1011         * Call the exit method for each state from the top of stack
1012         * up to the common ancestor state.
1013         */
1014        private final void invokeExitMethods(StateInfo commonStateInfo) {
1015            while ((mStateStackTopIndex >= 0)
1016                    && (mStateStack[mStateStackTopIndex] != commonStateInfo)) {
1017                State curState = mStateStack[mStateStackTopIndex].state;
1018                if (mDbg) mSm.log("invokeExitMethods: " + curState.getName());
1019                curState.exit();
1020                mStateStack[mStateStackTopIndex].active = false;
1021                mStateStackTopIndex -= 1;
1022            }
1023        }
1024
1025        /**
1026         * Invoke the enter method starting at the entering index to top of state stack
1027         */
1028        private final void invokeEnterMethods(int stateStackEnteringIndex) {
1029            for (int i = stateStackEnteringIndex; i <= mStateStackTopIndex; i++) {
1030                if (stateStackEnteringIndex == mStateStackTopIndex) {
1031                    // Last enter state for transition
1032                    mTransitionInProgress = false;
1033                }
1034                if (mDbg) mSm.log("invokeEnterMethods: " + mStateStack[i].state.getName());
1035                mStateStack[i].state.enter();
1036                mStateStack[i].active = true;
1037            }
1038            mTransitionInProgress = false; // ensure flag set to false if no methods called
1039        }
1040
1041        /**
1042         * Move the deferred message to the front of the message queue.
1043         */
1044        private final void moveDeferredMessageAtFrontOfQueue() {
1045            /**
1046             * The oldest messages on the deferred list must be at
1047             * the front of the queue so start at the back, which
1048             * as the most resent message and end with the oldest
1049             * messages at the front of the queue.
1050             */
1051            for (int i = mDeferredMessages.size() - 1; i >= 0; i--) {
1052                Message curMsg = mDeferredMessages.get(i);
1053                if (mDbg) mSm.log("moveDeferredMessageAtFrontOfQueue; what=" + curMsg.what);
1054                sendMessageAtFrontOfQueue(curMsg);
1055            }
1056            mDeferredMessages.clear();
1057        }
1058
1059        /**
1060         * Move the contents of the temporary stack to the state stack
1061         * reversing the order of the items on the temporary stack as
1062         * they are moved.
1063         *
1064         * @return index into mStateStack where entering needs to start
1065         */
1066        private final int moveTempStateStackToStateStack() {
1067            int startingIndex = mStateStackTopIndex + 1;
1068            int i = mTempStateStackCount - 1;
1069            int j = startingIndex;
1070            while (i >= 0) {
1071                if (mDbg) mSm.log("moveTempStackToStateStack: i=" + i + ",j=" + j);
1072                mStateStack[j] = mTempStateStack[i];
1073                j += 1;
1074                i -= 1;
1075            }
1076
1077            mStateStackTopIndex = j - 1;
1078            if (mDbg) {
1079                mSm.log("moveTempStackToStateStack: X mStateStackTop=" + mStateStackTopIndex
1080                        + ",startingIndex=" + startingIndex + ",Top="
1081                        + mStateStack[mStateStackTopIndex].state.getName());
1082            }
1083            return startingIndex;
1084        }
1085
1086        /**
1087         * Setup the mTempStateStack with the states we are going to enter.
1088         *
1089         * This is found by searching up the destState's ancestors for a
1090         * state that is already active i.e. StateInfo.active == true.
1091         * The destStae and all of its inactive parents will be on the
1092         * TempStateStack as the list of states to enter.
1093         *
1094         * @return StateInfo of the common ancestor for the destState and
1095         * current state or null if there is no common parent.
1096         */
1097        private final StateInfo setupTempStateStackWithStatesToEnter(State destState) {
1098            /**
1099             * Search up the parent list of the destination state for an active
1100             * state. Use a do while() loop as the destState must always be entered
1101             * even if it is active. This can happen if we are exiting/entering
1102             * the current state.
1103             */
1104            mTempStateStackCount = 0;
1105            StateInfo curStateInfo = mStateInfo.get(destState);
1106            do {
1107                mTempStateStack[mTempStateStackCount++] = curStateInfo;
1108                curStateInfo = curStateInfo.parentStateInfo;
1109            } while ((curStateInfo != null) && !curStateInfo.active);
1110
1111            if (mDbg) {
1112                mSm.log("setupTempStateStackWithStatesToEnter: X mTempStateStackCount="
1113                        + mTempStateStackCount + ",curStateInfo: " + curStateInfo);
1114            }
1115            return curStateInfo;
1116        }
1117
1118        /**
1119         * Initialize StateStack to mInitialState.
1120         */
1121        private final void setupInitialStateStack() {
1122            if (mDbg) {
1123                mSm.log("setupInitialStateStack: E mInitialState=" + mInitialState.getName());
1124            }
1125
1126            StateInfo curStateInfo = mStateInfo.get(mInitialState);
1127            for (mTempStateStackCount = 0; curStateInfo != null; mTempStateStackCount++) {
1128                mTempStateStack[mTempStateStackCount] = curStateInfo;
1129                curStateInfo = curStateInfo.parentStateInfo;
1130            }
1131
1132            // Empty the StateStack
1133            mStateStackTopIndex = -1;
1134
1135            moveTempStateStackToStateStack();
1136        }
1137
1138        /**
1139         * @return current message
1140         */
1141        private final Message getCurrentMessage() {
1142            return mMsg;
1143        }
1144
1145        /**
1146         * @return current state
1147         */
1148        private final IState getCurrentState() {
1149            return mStateStack[mStateStackTopIndex].state;
1150        }
1151
1152        /**
1153         * Add a new state to the state machine. Bottom up addition
1154         * of states is allowed but the same state may only exist
1155         * in one hierarchy.
1156         *
1157         * @param state the state to add
1158         * @param parent the parent of state
1159         * @return stateInfo for this state
1160         */
1161        private final StateInfo addState(State state, State parent) {
1162            if (mDbg) {
1163                mSm.log("addStateInternal: E state=" + state.getName() + ",parent="
1164                        + ((parent == null) ? "" : parent.getName()));
1165            }
1166            StateInfo parentStateInfo = null;
1167            if (parent != null) {
1168                parentStateInfo = mStateInfo.get(parent);
1169                if (parentStateInfo == null) {
1170                    // Recursively add our parent as it's not been added yet.
1171                    parentStateInfo = addState(parent, null);
1172                }
1173            }
1174            StateInfo stateInfo = mStateInfo.get(state);
1175            if (stateInfo == null) {
1176                stateInfo = new StateInfo();
1177                mStateInfo.put(state, stateInfo);
1178            }
1179
1180            // Validate that we aren't adding the same state in two different hierarchies.
1181            if ((stateInfo.parentStateInfo != null)
1182                    && (stateInfo.parentStateInfo != parentStateInfo)) {
1183                throw new RuntimeException("state already added");
1184            }
1185            stateInfo.state = state;
1186            stateInfo.parentStateInfo = parentStateInfo;
1187            stateInfo.active = false;
1188            if (mDbg) mSm.log("addStateInternal: X stateInfo: " + stateInfo);
1189            return stateInfo;
1190        }
1191
1192        /**
1193         * Constructor
1194         *
1195         * @param looper for dispatching messages
1196         * @param sm the hierarchical state machine
1197         */
1198        private SmHandler(Looper looper, StateMachine sm) {
1199            super(looper);
1200            mSm = sm;
1201
1202            addState(mHaltingState, null);
1203            addState(mQuittingState, null);
1204        }
1205
1206        /** @see StateMachine#setInitialState(State) */
1207        private final void setInitialState(State initialState) {
1208            if (mDbg) mSm.log("setInitialState: initialState=" + initialState.getName());
1209            mInitialState = initialState;
1210        }
1211
1212        /** @see StateMachine#transitionTo(IState) */
1213        private final void transitionTo(IState destState) {
1214            if (mTransitionInProgress) {
1215                Log.wtf(mSm.mName, "transitionTo called while transition already in progress to " +
1216                        mDestState + ", new target state=" + destState);
1217            }
1218            mDestState = (State) destState;
1219            if (mDbg) mSm.log("transitionTo: destState=" + mDestState.getName());
1220        }
1221
1222        /** @see StateMachine#deferMessage(Message) */
1223        private final void deferMessage(Message msg) {
1224            if (mDbg) mSm.log("deferMessage: msg=" + msg.what);
1225
1226            /* Copy the "msg" to "newMsg" as "msg" will be recycled */
1227            Message newMsg = obtainMessage();
1228            newMsg.copyFrom(msg);
1229
1230            mDeferredMessages.add(newMsg);
1231        }
1232
1233        /** @see StateMachine#quit() */
1234        private final void quit() {
1235            if (mDbg) mSm.log("quit:");
1236            sendMessage(obtainMessage(SM_QUIT_CMD, mSmHandlerObj));
1237        }
1238
1239        /** @see StateMachine#quitNow() */
1240        private final void quitNow() {
1241            if (mDbg) mSm.log("quitNow:");
1242            sendMessageAtFrontOfQueue(obtainMessage(SM_QUIT_CMD, mSmHandlerObj));
1243        }
1244
1245        /** Validate that the message was sent by quit or quitNow. */
1246        private final boolean isQuit(Message msg) {
1247            return (msg.what == SM_QUIT_CMD) && (msg.obj == mSmHandlerObj);
1248        }
1249
1250        /** @see StateMachine#isDbg() */
1251        private final boolean isDbg() {
1252            return mDbg;
1253        }
1254
1255        /** @see StateMachine#setDbg(boolean) */
1256        private final void setDbg(boolean dbg) {
1257            mDbg = dbg;
1258        }
1259
1260    }
1261
1262    private SmHandler mSmHandler;
1263    private HandlerThread mSmThread;
1264
1265    /**
1266     * Initialize.
1267     *
1268     * @param looper for this state machine
1269     * @param name of the state machine
1270     */
1271    private void initStateMachine(String name, Looper looper) {
1272        mName = name;
1273        mSmHandler = new SmHandler(looper, this);
1274    }
1275
1276    /**
1277     * Constructor creates a StateMachine with its own thread.
1278     *
1279     * @param name of the state machine
1280     */
1281    protected StateMachine(String name) {
1282        mSmThread = new HandlerThread(name);
1283        mSmThread.start();
1284        Looper looper = mSmThread.getLooper();
1285
1286        initStateMachine(name, looper);
1287    }
1288
1289    /**
1290     * Constructor creates a StateMachine using the looper.
1291     *
1292     * @param name of the state machine
1293     */
1294    protected StateMachine(String name, Looper looper) {
1295        initStateMachine(name, looper);
1296    }
1297
1298    /**
1299     * Constructor creates a StateMachine using the handler.
1300     *
1301     * @param name of the state machine
1302     */
1303    protected StateMachine(String name, Handler handler) {
1304        initStateMachine(name, handler.getLooper());
1305    }
1306
1307    /**
1308     * Notifies subclass that the StateMachine handler is about to process the Message msg
1309     * @param msg The message that is being handled
1310     */
1311    protected void onPreHandleMessage(Message msg) {
1312    }
1313
1314    /**
1315     * Notifies subclass that the StateMachine handler has finished processing the Message msg and
1316     * has possibly transitioned to a new state.
1317     * @param msg The message that is being handled
1318     */
1319    protected void onPostHandleMessage(Message msg) {
1320    }
1321
1322    /**
1323     * Add a new state to the state machine
1324     * @param state the state to add
1325     * @param parent the parent of state
1326     */
1327    public final void addState(State state, State parent) {
1328        mSmHandler.addState(state, parent);
1329    }
1330
1331    /**
1332     * Add a new state to the state machine, parent will be null
1333     * @param state to add
1334     */
1335    public final void addState(State state) {
1336        mSmHandler.addState(state, null);
1337    }
1338
1339    /**
1340     * Set the initial state. This must be invoked before
1341     * and messages are sent to the state machine.
1342     *
1343     * @param initialState is the state which will receive the first message.
1344     */
1345    public final void setInitialState(State initialState) {
1346        mSmHandler.setInitialState(initialState);
1347    }
1348
1349    /**
1350     * @return current message
1351     */
1352    public final Message getCurrentMessage() {
1353        // mSmHandler can be null if the state machine has quit.
1354        SmHandler smh = mSmHandler;
1355        if (smh == null) return null;
1356        return smh.getCurrentMessage();
1357    }
1358
1359    /**
1360     * @return current state
1361     */
1362    public final IState getCurrentState() {
1363        // mSmHandler can be null if the state machine has quit.
1364        SmHandler smh = mSmHandler;
1365        if (smh == null) return null;
1366        return smh.getCurrentState();
1367    }
1368
1369    /**
1370     * transition to destination state. Upon returning
1371     * from processMessage the current state's exit will
1372     * be executed and upon the next message arriving
1373     * destState.enter will be invoked.
1374     *
1375     * this function can also be called inside the enter function of the
1376     * previous transition target, but the behavior is undefined when it is
1377     * called mid-way through a previous transition (for example, calling this
1378     * in the enter() routine of a intermediate node when the current transition
1379     * target is one of the nodes descendants).
1380     *
1381     * @param destState will be the state that receives the next message.
1382     */
1383    public final void transitionTo(IState destState) {
1384        mSmHandler.transitionTo(destState);
1385    }
1386
1387    /**
1388     * transition to halt state. Upon returning
1389     * from processMessage we will exit all current
1390     * states, execute the onHalting() method and then
1391     * for all subsequent messages haltedProcessMessage
1392     * will be called.
1393     */
1394    public final void transitionToHaltingState() {
1395        mSmHandler.transitionTo(mSmHandler.mHaltingState);
1396    }
1397
1398    /**
1399     * Defer this message until next state transition.
1400     * Upon transitioning all deferred messages will be
1401     * placed on the queue and reprocessed in the original
1402     * order. (i.e. The next state the oldest messages will
1403     * be processed first)
1404     *
1405     * @param msg is deferred until the next transition.
1406     */
1407    public final void deferMessage(Message msg) {
1408        mSmHandler.deferMessage(msg);
1409    }
1410
1411    /**
1412     * Called when message wasn't handled
1413     *
1414     * @param msg that couldn't be handled.
1415     */
1416    protected void unhandledMessage(Message msg) {
1417        if (mSmHandler.mDbg) loge(" - unhandledMessage: msg.what=" + msg.what);
1418    }
1419
1420    /**
1421     * Called for any message that is received after
1422     * transitionToHalting is called.
1423     */
1424    protected void haltedProcessMessage(Message msg) {
1425    }
1426
1427    /**
1428     * This will be called once after handling a message that called
1429     * transitionToHalting. All subsequent messages will invoke
1430     * {@link StateMachine#haltedProcessMessage(Message)}
1431     */
1432    protected void onHalting() {
1433    }
1434
1435    /**
1436     * This will be called once after a quit message that was NOT handled by
1437     * the derived StateMachine. The StateMachine will stop and any subsequent messages will be
1438     * ignored. In addition, if this StateMachine created the thread, the thread will
1439     * be stopped after this method returns.
1440     */
1441    protected void onQuitting() {
1442    }
1443
1444    /**
1445     * @return the name
1446     */
1447    public final String getName() {
1448        return mName;
1449    }
1450
1451    /**
1452     * Set number of log records to maintain and clears all current records.
1453     *
1454     * @param maxSize number of messages to maintain at anyone time.
1455     */
1456    public final void setLogRecSize(int maxSize) {
1457        mSmHandler.mLogRecords.setSize(maxSize);
1458    }
1459
1460    /**
1461     * Set to log only messages that cause a state transition
1462     *
1463     * @param enable {@code true} to enable, {@code false} to disable
1464     */
1465    public final void setLogOnlyTransitions(boolean enable) {
1466        mSmHandler.mLogRecords.setLogOnlyTransitions(enable);
1467    }
1468
1469    /**
1470     * @return number of log records
1471     */
1472    public final int getLogRecSize() {
1473        // mSmHandler can be null if the state machine has quit.
1474        SmHandler smh = mSmHandler;
1475        if (smh == null) return 0;
1476        return smh.mLogRecords.size();
1477    }
1478
1479    /**
1480     * @return the total number of records processed
1481     */
1482    public final int getLogRecCount() {
1483        // mSmHandler can be null if the state machine has quit.
1484        SmHandler smh = mSmHandler;
1485        if (smh == null) return 0;
1486        return smh.mLogRecords.count();
1487    }
1488
1489    /**
1490     * @return a log record, or null if index is out of range
1491     */
1492    public final LogRec getLogRec(int index) {
1493        // mSmHandler can be null if the state machine has quit.
1494        SmHandler smh = mSmHandler;
1495        if (smh == null) return null;
1496        return smh.mLogRecords.get(index);
1497    }
1498
1499    /**
1500     * @return a copy of LogRecs as a collection
1501     */
1502    public final Collection<LogRec> copyLogRecs() {
1503        Vector<LogRec> vlr = new Vector<LogRec>();
1504        SmHandler smh = mSmHandler;
1505        if (smh != null) {
1506            for (LogRec lr : smh.mLogRecords.mLogRecVector) {
1507                vlr.add(lr);
1508            }
1509        }
1510        return vlr;
1511    }
1512
1513    /**
1514     * Add the string to LogRecords.
1515     *
1516     * @param string
1517     */
1518    public void addLogRec(String string) {
1519        // mSmHandler can be null if the state machine has quit.
1520        SmHandler smh = mSmHandler;
1521        if (smh == null) return;
1522        smh.mLogRecords.add(this, smh.getCurrentMessage(), string, smh.getCurrentState(),
1523                smh.mStateStack[smh.mStateStackTopIndex].state, smh.mDestState);
1524    }
1525
1526    /**
1527     * @return true if msg should be saved in the log, default is true.
1528     */
1529    protected boolean recordLogRec(Message msg) {
1530        return true;
1531    }
1532
1533    /**
1534     * Return a string to be logged by LogRec, default
1535     * is an empty string. Override if additional information is desired.
1536     *
1537     * @param msg that was processed
1538     * @return information to be logged as a String
1539     */
1540    protected String getLogRecString(Message msg) {
1541        return "";
1542    }
1543
1544    /**
1545     * @return the string for msg.what
1546     */
1547    protected String getWhatToString(int what) {
1548        return null;
1549    }
1550
1551    /**
1552     * @return Handler, maybe null if state machine has quit.
1553     */
1554    public final Handler getHandler() {
1555        return mSmHandler;
1556    }
1557
1558    /**
1559     * Get a message and set Message.target state machine handler.
1560     *
1561     * Note: The handler can be null if the state machine has quit,
1562     * which means target will be null and may cause a AndroidRuntimeException
1563     * in MessageQueue#enqueMessage if sent directly or if sent using
1564     * StateMachine#sendMessage the message will just be ignored.
1565     *
1566     * @return  A Message object from the global pool
1567     */
1568    public final Message obtainMessage() {
1569        return Message.obtain(mSmHandler);
1570    }
1571
1572    /**
1573     * Get a message and set Message.target state machine handler, what.
1574     *
1575     * Note: The handler can be null if the state machine has quit,
1576     * which means target will be null and may cause a AndroidRuntimeException
1577     * in MessageQueue#enqueMessage if sent directly or if sent using
1578     * StateMachine#sendMessage the message will just be ignored.
1579     *
1580     * @param what is the assigned to Message.what.
1581     * @return  A Message object from the global pool
1582     */
1583    public final Message obtainMessage(int what) {
1584        return Message.obtain(mSmHandler, what);
1585    }
1586
1587    /**
1588     * Get a message and set Message.target state machine handler,
1589     * what and obj.
1590     *
1591     * Note: The handler can be null if the state machine has quit,
1592     * which means target will be null and may cause a AndroidRuntimeException
1593     * in MessageQueue#enqueMessage if sent directly or if sent using
1594     * StateMachine#sendMessage the message will just be ignored.
1595     *
1596     * @param what is the assigned to Message.what.
1597     * @param obj is assigned to Message.obj.
1598     * @return  A Message object from the global pool
1599     */
1600    public final Message obtainMessage(int what, Object obj) {
1601        return Message.obtain(mSmHandler, what, obj);
1602    }
1603
1604    /**
1605     * Get a message and set Message.target state machine handler,
1606     * what, arg1 and arg2
1607     *
1608     * Note: The handler can be null if the state machine has quit,
1609     * which means target will be null and may cause a AndroidRuntimeException
1610     * in MessageQueue#enqueMessage if sent directly or if sent using
1611     * StateMachine#sendMessage the message will just be ignored.
1612     *
1613     * @param what  is assigned to Message.what
1614     * @param arg1  is assigned to Message.arg1
1615     * @return  A Message object from the global pool
1616     */
1617    public final Message obtainMessage(int what, int arg1) {
1618        // use this obtain so we don't match the obtain(h, what, Object) method
1619        return Message.obtain(mSmHandler, what, arg1, 0);
1620    }
1621
1622    /**
1623     * Get a message and set Message.target state machine handler,
1624     * what, arg1 and arg2
1625     *
1626     * Note: The handler can be null if the state machine has quit,
1627     * which means target will be null and may cause a AndroidRuntimeException
1628     * in MessageQueue#enqueMessage if sent directly or if sent using
1629     * StateMachine#sendMessage the message will just be ignored.
1630     *
1631     * @param what  is assigned to Message.what
1632     * @param arg1  is assigned to Message.arg1
1633     * @param arg2  is assigned to Message.arg2
1634     * @return  A Message object from the global pool
1635     */
1636    public final Message obtainMessage(int what, int arg1, int arg2) {
1637        return Message.obtain(mSmHandler, what, arg1, arg2);
1638    }
1639
1640    /**
1641     * Get a message and set Message.target state machine handler,
1642     * what, arg1, arg2 and obj
1643     *
1644     * Note: The handler can be null if the state machine has quit,
1645     * which means target will be null and may cause a AndroidRuntimeException
1646     * in MessageQueue#enqueMessage if sent directly or if sent using
1647     * StateMachine#sendMessage the message will just be ignored.
1648     *
1649     * @param what  is assigned to Message.what
1650     * @param arg1  is assigned to Message.arg1
1651     * @param arg2  is assigned to Message.arg2
1652     * @param obj is assigned to Message.obj
1653     * @return  A Message object from the global pool
1654     */
1655    public final Message obtainMessage(int what, int arg1, int arg2, Object obj) {
1656        return Message.obtain(mSmHandler, what, arg1, arg2, obj);
1657    }
1658
1659    /**
1660     * Enqueue a message to this state machine.
1661     *
1662     * Message is ignored if state machine has quit.
1663     */
1664    public void sendMessage(int what) {
1665        // mSmHandler can be null if the state machine has quit.
1666        SmHandler smh = mSmHandler;
1667        if (smh == null) return;
1668
1669        smh.sendMessage(obtainMessage(what));
1670    }
1671
1672    /**
1673     * Enqueue a message to this state machine.
1674     *
1675     * Message is ignored if state machine has quit.
1676     */
1677    public void sendMessage(int what, Object obj) {
1678        // mSmHandler can be null if the state machine has quit.
1679        SmHandler smh = mSmHandler;
1680        if (smh == null) return;
1681
1682        smh.sendMessage(obtainMessage(what, obj));
1683    }
1684
1685    /**
1686     * Enqueue a message to this state machine.
1687     *
1688     * Message is ignored if state machine has quit.
1689     */
1690    public void sendMessage(int what, int arg1) {
1691        // mSmHandler can be null if the state machine has quit.
1692        SmHandler smh = mSmHandler;
1693        if (smh == null) return;
1694
1695        smh.sendMessage(obtainMessage(what, arg1));
1696    }
1697
1698    /**
1699     * Enqueue a message to this state machine.
1700     *
1701     * Message is ignored if state machine has quit.
1702     */
1703    public void sendMessage(int what, int arg1, int arg2) {
1704        // mSmHandler can be null if the state machine has quit.
1705        SmHandler smh = mSmHandler;
1706        if (smh == null) return;
1707
1708        smh.sendMessage(obtainMessage(what, arg1, arg2));
1709    }
1710
1711    /**
1712     * Enqueue a message to this state machine.
1713     *
1714     * Message is ignored if state machine has quit.
1715     */
1716    public void sendMessage(int what, int arg1, int arg2, Object obj) {
1717        // mSmHandler can be null if the state machine has quit.
1718        SmHandler smh = mSmHandler;
1719        if (smh == null) return;
1720
1721        smh.sendMessage(obtainMessage(what, arg1, arg2, obj));
1722    }
1723
1724    /**
1725     * Enqueue a message to this state machine.
1726     *
1727     * Message is ignored if state machine has quit.
1728     */
1729    public void sendMessage(Message msg) {
1730        // mSmHandler can be null if the state machine has quit.
1731        SmHandler smh = mSmHandler;
1732        if (smh == null) return;
1733
1734        smh.sendMessage(msg);
1735    }
1736
1737    /**
1738     * Enqueue a message to this state machine after a delay.
1739     *
1740     * Message is ignored if state machine has quit.
1741     */
1742    public void sendMessageDelayed(int what, long delayMillis) {
1743        // mSmHandler can be null if the state machine has quit.
1744        SmHandler smh = mSmHandler;
1745        if (smh == null) return;
1746
1747        smh.sendMessageDelayed(obtainMessage(what), delayMillis);
1748    }
1749
1750    /**
1751     * Enqueue a message to this state machine after a delay.
1752     *
1753     * Message is ignored if state machine has quit.
1754     */
1755    public void sendMessageDelayed(int what, Object obj, long delayMillis) {
1756        // mSmHandler can be null if the state machine has quit.
1757        SmHandler smh = mSmHandler;
1758        if (smh == null) return;
1759
1760        smh.sendMessageDelayed(obtainMessage(what, obj), delayMillis);
1761    }
1762
1763    /**
1764     * Enqueue a message to this state machine after a delay.
1765     *
1766     * Message is ignored if state machine has quit.
1767     */
1768    public void sendMessageDelayed(int what, int arg1, long delayMillis) {
1769        // mSmHandler can be null if the state machine has quit.
1770        SmHandler smh = mSmHandler;
1771        if (smh == null) return;
1772
1773        smh.sendMessageDelayed(obtainMessage(what, arg1), delayMillis);
1774    }
1775
1776    /**
1777     * Enqueue a message to this state machine after a delay.
1778     *
1779     * Message is ignored if state machine has quit.
1780     */
1781    public void sendMessageDelayed(int what, int arg1, int arg2, long delayMillis) {
1782        // mSmHandler can be null if the state machine has quit.
1783        SmHandler smh = mSmHandler;
1784        if (smh == null) return;
1785
1786        smh.sendMessageDelayed(obtainMessage(what, arg1, arg2), delayMillis);
1787    }
1788
1789    /**
1790     * Enqueue a message to this state machine after a delay.
1791     *
1792     * Message is ignored if state machine has quit.
1793     */
1794    public void sendMessageDelayed(int what, int arg1, int arg2, Object obj,
1795            long delayMillis) {
1796        // mSmHandler can be null if the state machine has quit.
1797        SmHandler smh = mSmHandler;
1798        if (smh == null) return;
1799
1800        smh.sendMessageDelayed(obtainMessage(what, arg1, arg2, obj), delayMillis);
1801    }
1802
1803    /**
1804     * Enqueue a message to this state machine after a delay.
1805     *
1806     * Message is ignored if state machine has quit.
1807     */
1808    public void sendMessageDelayed(Message msg, long delayMillis) {
1809        // mSmHandler can be null if the state machine has quit.
1810        SmHandler smh = mSmHandler;
1811        if (smh == null) return;
1812
1813        smh.sendMessageDelayed(msg, delayMillis);
1814    }
1815
1816    /**
1817     * Enqueue a message to the front of the queue for this state machine.
1818     * Protected, may only be called by instances of StateMachine.
1819     *
1820     * Message is ignored if state machine has quit.
1821     */
1822    protected final void sendMessageAtFrontOfQueue(int what) {
1823        // mSmHandler can be null if the state machine has quit.
1824        SmHandler smh = mSmHandler;
1825        if (smh == null) return;
1826
1827        smh.sendMessageAtFrontOfQueue(obtainMessage(what));
1828    }
1829
1830    /**
1831     * Enqueue a message to the front of the queue for this state machine.
1832     * Protected, may only be called by instances of StateMachine.
1833     *
1834     * Message is ignored if state machine has quit.
1835     */
1836    protected final void sendMessageAtFrontOfQueue(int what, Object obj) {
1837        // mSmHandler can be null if the state machine has quit.
1838        SmHandler smh = mSmHandler;
1839        if (smh == null) return;
1840
1841        smh.sendMessageAtFrontOfQueue(obtainMessage(what, obj));
1842    }
1843
1844    /**
1845     * Enqueue a message to the front of the queue for this state machine.
1846     * Protected, may only be called by instances of StateMachine.
1847     *
1848     * Message is ignored if state machine has quit.
1849     */
1850    protected final void sendMessageAtFrontOfQueue(int what, int arg1) {
1851        // mSmHandler can be null if the state machine has quit.
1852        SmHandler smh = mSmHandler;
1853        if (smh == null) return;
1854
1855        smh.sendMessageAtFrontOfQueue(obtainMessage(what, arg1));
1856    }
1857
1858
1859    /**
1860     * Enqueue a message to the front of the queue for this state machine.
1861     * Protected, may only be called by instances of StateMachine.
1862     *
1863     * Message is ignored if state machine has quit.
1864     */
1865    protected final void sendMessageAtFrontOfQueue(int what, int arg1, int arg2) {
1866        // mSmHandler can be null if the state machine has quit.
1867        SmHandler smh = mSmHandler;
1868        if (smh == null) return;
1869
1870        smh.sendMessageAtFrontOfQueue(obtainMessage(what, arg1, arg2));
1871    }
1872
1873    /**
1874     * Enqueue a message to the front of the queue for this state machine.
1875     * Protected, may only be called by instances of StateMachine.
1876     *
1877     * Message is ignored if state machine has quit.
1878     */
1879    protected final void sendMessageAtFrontOfQueue(int what, int arg1, int arg2, Object obj) {
1880        // mSmHandler can be null if the state machine has quit.
1881        SmHandler smh = mSmHandler;
1882        if (smh == null) return;
1883
1884        smh.sendMessageAtFrontOfQueue(obtainMessage(what, arg1, arg2, obj));
1885    }
1886
1887    /**
1888     * Enqueue a message to the front of the queue for this state machine.
1889     * Protected, may only be called by instances of StateMachine.
1890     *
1891     * Message is ignored if state machine has quit.
1892     */
1893    protected final void sendMessageAtFrontOfQueue(Message msg) {
1894        // mSmHandler can be null if the state machine has quit.
1895        SmHandler smh = mSmHandler;
1896        if (smh == null) return;
1897
1898        smh.sendMessageAtFrontOfQueue(msg);
1899    }
1900
1901    /**
1902     * Removes a message from the message queue.
1903     * Protected, may only be called by instances of StateMachine.
1904     */
1905    protected final void removeMessages(int what) {
1906        // mSmHandler can be null if the state machine has quit.
1907        SmHandler smh = mSmHandler;
1908        if (smh == null) return;
1909
1910        smh.removeMessages(what);
1911    }
1912
1913    /**
1914     * Removes a message from the deferred messages queue.
1915     */
1916    protected final void removeDeferredMessages(int what) {
1917        SmHandler smh = mSmHandler;
1918        if (smh == null) return;
1919
1920        Iterator<Message> iterator = smh.mDeferredMessages.iterator();
1921        while (iterator.hasNext()) {
1922            Message msg = iterator.next();
1923            if (msg.what == what) iterator.remove();
1924        }
1925    }
1926
1927    /**
1928     * Check if there are any pending messages with code 'what' in deferred messages queue.
1929     */
1930    protected final boolean hasDeferredMessages(int what) {
1931        SmHandler smh = mSmHandler;
1932        if (smh == null) return false;
1933
1934        Iterator<Message> iterator = smh.mDeferredMessages.iterator();
1935        while (iterator.hasNext()) {
1936            Message msg = iterator.next();
1937            if (msg.what == what) return true;
1938        }
1939
1940        return false;
1941    }
1942
1943    /**
1944     * Check if there are any pending posts of messages with code 'what' in
1945     * the message queue. This does NOT check messages in deferred message queue.
1946     */
1947    protected final boolean hasMessages(int what) {
1948        SmHandler smh = mSmHandler;
1949        if (smh == null) return false;
1950
1951        return smh.hasMessages(what);
1952    }
1953
1954    /**
1955     * Validate that the message was sent by
1956     * {@link StateMachine#quit} or {@link StateMachine#quitNow}.
1957     * */
1958    protected final boolean isQuit(Message msg) {
1959        // mSmHandler can be null if the state machine has quit.
1960        SmHandler smh = mSmHandler;
1961        if (smh == null) return msg.what == SM_QUIT_CMD;
1962
1963        return smh.isQuit(msg);
1964    }
1965
1966    /**
1967     * Quit the state machine after all currently queued up messages are processed.
1968     */
1969    public final void quit() {
1970        // mSmHandler can be null if the state machine is already stopped.
1971        SmHandler smh = mSmHandler;
1972        if (smh == null) return;
1973
1974        smh.quit();
1975    }
1976
1977    /**
1978     * Quit the state machine immediately all currently queued messages will be discarded.
1979     */
1980    public final void quitNow() {
1981        // mSmHandler can be null if the state machine is already stopped.
1982        SmHandler smh = mSmHandler;
1983        if (smh == null) return;
1984
1985        smh.quitNow();
1986    }
1987
1988    /**
1989     * @return if debugging is enabled
1990     */
1991    public boolean isDbg() {
1992        // mSmHandler can be null if the state machine has quit.
1993        SmHandler smh = mSmHandler;
1994        if (smh == null) return false;
1995
1996        return smh.isDbg();
1997    }
1998
1999    /**
2000     * Set debug enable/disabled.
2001     *
2002     * @param dbg is true to enable debugging.
2003     */
2004    public void setDbg(boolean dbg) {
2005        // mSmHandler can be null if the state machine has quit.
2006        SmHandler smh = mSmHandler;
2007        if (smh == null) return;
2008
2009        smh.setDbg(dbg);
2010    }
2011
2012    /**
2013     * Start the state machine.
2014     */
2015    public void start() {
2016        // mSmHandler can be null if the state machine has quit.
2017        SmHandler smh = mSmHandler;
2018        if (smh == null) return;
2019
2020        /** Send the complete construction message */
2021        smh.completeConstruction();
2022    }
2023
2024    /**
2025     * Dump the current state.
2026     *
2027     * @param fd
2028     * @param pw
2029     * @param args
2030     */
2031    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2032        // Cannot just invoke pw.println(this.toString()) because if the
2033        // resulting string is to long it won't be displayed.
2034        pw.println(getName() + ":");
2035        pw.println(" total records=" + getLogRecCount());
2036        for (int i = 0; i < getLogRecSize(); i++) {
2037            pw.println(" rec[" + i + "]: " + getLogRec(i).toString());
2038            pw.flush();
2039        }
2040        pw.println("curState=" + getCurrentState().getName());
2041    }
2042
2043    @Override
2044    public String toString() {
2045        StringWriter sr = new StringWriter();
2046        PrintWriter pr = new PrintWriter(sr);
2047        dump(null, pr, null);
2048        pr.flush();
2049        pr.close();
2050        return sr.toString();
2051    }
2052
2053    /**
2054     * Log with debug and add to the LogRecords.
2055     *
2056     * @param s is string log
2057     */
2058    protected void logAndAddLogRec(String s) {
2059        addLogRec(s);
2060        log(s);
2061    }
2062
2063    /**
2064     * Log with debug
2065     *
2066     * @param s is string log
2067     */
2068    protected void log(String s) {
2069        Log.d(mName, s);
2070    }
2071
2072    /**
2073     * Log with debug attribute
2074     *
2075     * @param s is string log
2076     */
2077    protected void logd(String s) {
2078        Log.d(mName, s);
2079    }
2080
2081    /**
2082     * Log with verbose attribute
2083     *
2084     * @param s is string log
2085     */
2086    protected void logv(String s) {
2087        Log.v(mName, s);
2088    }
2089
2090    /**
2091     * Log with info attribute
2092     *
2093     * @param s is string log
2094     */
2095    protected void logi(String s) {
2096        Log.i(mName, s);
2097    }
2098
2099    /**
2100     * Log with warning attribute
2101     *
2102     * @param s is string log
2103     */
2104    protected void logw(String s) {
2105        Log.w(mName, s);
2106    }
2107
2108    /**
2109     * Log with error attribute
2110     *
2111     * @param s is string log
2112     */
2113    protected void loge(String s) {
2114        Log.e(mName, s);
2115    }
2116
2117    /**
2118     * Log with error attribute
2119     *
2120     * @param s is string log
2121     * @param e is a Throwable which logs additional information.
2122     */
2123    protected void loge(String s, Throwable e) {
2124        Log.e(mName, s, e);
2125    }
2126}
2127