blob: a5a3dba7ac128899ab32b449bd8517106fe7a134 [file] [log] [blame]
Wink Savillefc5b4802009-12-08 21:22:24 -08001/**
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.internal.util;
18
19import android.os.Handler;
20import android.os.HandlerThread;
21import android.os.Looper;
22import android.os.Message;
Wink Saville583eaaa2012-04-13 16:11:20 -070023import android.text.TextUtils;
Wink Savillefc5b4802009-12-08 21:22:24 -080024import android.util.Log;
25
Wink Saville583eaaa2012-04-13 16:11:20 -070026import java.io.FileDescriptor;
27import java.io.PrintWriter;
Wink Saville54b1b1a2015-01-12 11:34:20 -080028import java.io.StringWriter;
Wink Savillefc5b4802009-12-08 21:22:24 -080029import java.util.ArrayList;
Wink Saville583eaaa2012-04-13 16:11:20 -070030import java.util.Calendar;
Wink Savilleefcc3d32013-01-30 11:21:22 -080031import java.util.Collection;
Wink Savillefc5b4802009-12-08 21:22:24 -080032import java.util.HashMap;
Rebecca Silberstein0dbeb9e2016-05-13 13:57:34 -070033import java.util.Iterator;
Wink Savilled3059482011-04-11 11:51:28 -070034import java.util.Vector;
Wink Savillefc5b4802009-12-08 21:22:24 -080035
36/**
37 * {@hide}
38 *
Wink Saville64c42ca2011-04-18 14:55:10 -070039 * <p>The state machine defined here is a hierarchical state machine which processes messages
Wink Saville33c54e32010-11-15 10:50:34 -080040 * and can have states arranged hierarchically.</p>
Wink Saville58c73c32013-01-28 10:52:34 -080041 *
Wink Saville64c42ca2011-04-18 14:55:10 -070042 * <p>A state is a <code>State</code> object and must implement
Wink Saville33c54e32010-11-15 10:50:34 -080043 * <code>processMessage</code> and optionally <code>enter/exit/getName</code>.
Wink Savillefc5b4802009-12-08 21:22:24 -080044 * The enter/exit methods are equivalent to the construction and destruction
45 * in Object Oriented programming and are used to perform initialization and
46 * cleanup of the state respectively. The <code>getName</code> method returns the
Kevin Cernekeee318d8c2015-06-18 13:23:24 -070047 * name of the state; the default implementation returns the class name. It may be
48 * desirable to have <code>getName</code> return the the state instance name instead,
49 * in particular if a particular state class has multiple instances.</p>
Wink Savillefc5b4802009-12-08 21:22:24 -080050 *
Kevin Cernekeee318d8c2015-06-18 13:23:24 -070051 * <p>When a state machine is created, <code>addState</code> is used to build the
Wink Savillefc5b4802009-12-08 21:22:24 -080052 * hierarchy and <code>setInitialState</code> is used to identify which of these
53 * is the initial state. After construction the programmer calls <code>start</code>
Wink Savillecea056f2012-03-26 15:03:16 -070054 * which initializes and starts the state machine. The first action the StateMachine
55 * is to the invoke <code>enter</code> for all of the initial state's hierarchy,
56 * starting at its eldest parent. The calls to enter will be done in the context
Kevin Cernekeee318d8c2015-06-18 13:23:24 -070057 * of the StateMachine's Handler, not in the context of the call to start, and they
Wink Savillecea056f2012-03-26 15:03:16 -070058 * will be invoked before any messages are processed. For example, given the simple
Kevin Cernekeee318d8c2015-06-18 13:23:24 -070059 * state machine below, mP1.enter will be invoked and then mS1.enter. Finally,
60 * messages sent to the state machine will be processed by the current state;
Wink Savillecea056f2012-03-26 15:03:16 -070061 * in our simple state machine below that would initially be mS1.processMessage.</p>
Kevin Cernekeee318d8c2015-06-18 13:23:24 -070062<pre>
Wink Savillefc5b4802009-12-08 21:22:24 -080063 mP1
64 / \
Kevin Cernekeee318d8c2015-06-18 13:23:24 -070065 mS2 mS1 ----&gt; initial state
66</pre>
Wink Saville33c54e32010-11-15 10:50:34 -080067 * <p>After the state machine is created and started, messages are sent to a state
Wink Savillea4f3bec2010-05-19 09:11:38 -070068 * machine using <code>sendMessage</code> and the messages are created using
Wink Savillefc5b4802009-12-08 21:22:24 -080069 * <code>obtainMessage</code>. When the state machine receives a message the
70 * current state's <code>processMessage</code> is invoked. In the above example
71 * mS1.processMessage will be invoked first. The state may use <code>transitionTo</code>
Kevin Cernekeee318d8c2015-06-18 13:23:24 -070072 * to change the current state to a new state.</p>
Wink Savillefc5b4802009-12-08 21:22:24 -080073 *
Kevin Cernekeee318d8c2015-06-18 13:23:24 -070074 * <p>Each state in the state machine may have a zero or one parent states. If
Wink Savillefc5b4802009-12-08 21:22:24 -080075 * a child state is unable to handle a message it may have the message processed
Kevin Cernekeee318d8c2015-06-18 13:23:24 -070076 * by its parent by returning false or NOT_HANDLED. If a message is not handled by
77 * a child state or any of its ancestors, <code>unhandledMessage</code> will be invoked
78 * to give one last chance for the state machine to process the message.</p>
Wink Savillefc5b4802009-12-08 21:22:24 -080079 *
Wink Saville33c54e32010-11-15 10:50:34 -080080 * <p>When all processing is completed a state machine may choose to call
Wink Savillefc5b4802009-12-08 21:22:24 -080081 * <code>transitionToHaltingState</code>. When the current <code>processingMessage</code>
82 * returns the state machine will transfer to an internal <code>HaltingState</code>
83 * and invoke <code>halting</code>. Any message subsequently received by the state
Wink Saville33c54e32010-11-15 10:50:34 -080084 * machine will cause <code>haltedProcessMessage</code> to be invoked.</p>
Wink Savillefc5b4802009-12-08 21:22:24 -080085 *
Wink Savillebbf30dfd2012-05-29 12:40:46 -070086 * <p>If it is desirable to completely stop the state machine call <code>quit</code> or
Wink Savilleefcc3d32013-01-30 11:21:22 -080087 * <code>quitNow</code>. These will call <code>exit</code> of the current state and its parents,
Kevin Cernekeee318d8c2015-06-18 13:23:24 -070088 * call <code>onQuitting</code> and then exit Thread/Loopers.</p>
Wink Saville1b8b98b2010-03-11 11:49:54 -080089 *
Wink Saville64c42ca2011-04-18 14:55:10 -070090 * <p>In addition to <code>processMessage</code> each <code>State</code> has
Kevin Cernekeee318d8c2015-06-18 13:23:24 -070091 * an <code>enter</code> method and <code>exit</code> method which may be overridden.</p>
Wink Savillefc5b4802009-12-08 21:22:24 -080092 *
Wink Saville33c54e32010-11-15 10:50:34 -080093 * <p>Since the states are arranged in a hierarchy transitioning to a new state
Wink Savillefc5b4802009-12-08 21:22:24 -080094 * causes current states to be exited and new states to be entered. To determine
95 * the list of states to be entered/exited the common parent closest to
96 * the current state is found. We then exit from the current state and its
97 * parent's up to but not including the common parent state and then enter all
98 * of the new states below the common parent down to the destination state.
99 * If there is no common parent all states are exited and then the new states
Wink Saville33c54e32010-11-15 10:50:34 -0800100 * are entered.</p>
Wink Savillefc5b4802009-12-08 21:22:24 -0800101 *
Wink Saville33c54e32010-11-15 10:50:34 -0800102 * <p>Two other methods that states can use are <code>deferMessage</code> and
Wink Savillefc5b4802009-12-08 21:22:24 -0800103 * <code>sendMessageAtFrontOfQueue</code>. The <code>sendMessageAtFrontOfQueue</code> sends
104 * a message but places it on the front of the queue rather than the back. The
105 * <code>deferMessage</code> causes the message to be saved on a list until a
106 * transition is made to a new state. At which time all of the deferred messages
107 * will be put on the front of the state machine queue with the oldest message
108 * at the front. These will then be processed by the new current state before
109 * any other messages that are on the queue or might be added later. Both of
Wink Saville33c54e32010-11-15 10:50:34 -0800110 * these are protected and may only be invoked from within a state machine.</p>
Wink Savillefc5b4802009-12-08 21:22:24 -0800111 *
Wink Saville33c54e32010-11-15 10:50:34 -0800112 * <p>To illustrate some of these properties we'll use state machine with an 8
113 * state hierarchy:</p>
Kevin Cernekeee318d8c2015-06-18 13:23:24 -0700114<pre>
Wink Savillefc5b4802009-12-08 21:22:24 -0800115 mP0
116 / \
117 mP1 mS0
118 / \
119 mS2 mS1
120 / \ \
Kevin Cernekeee318d8c2015-06-18 13:23:24 -0700121 mS3 mS4 mS5 ---&gt; initial state
122</pre>
Wink Saville33c54e32010-11-15 10:50:34 -0800123 * <p>After starting mS5 the list of active states is mP0, mP1, mS1 and mS5.
Wink Savillefc5b4802009-12-08 21:22:24 -0800124 * So the order of calling processMessage when a message is received is mS5,
Wink Savillea4f3bec2010-05-19 09:11:38 -0700125 * mS1, mP1, mP0 assuming each processMessage indicates it can't handle this
Wink Saville33c54e32010-11-15 10:50:34 -0800126 * message by returning false or NOT_HANDLED.</p>
Wink Savillefc5b4802009-12-08 21:22:24 -0800127 *
Wink Saville33c54e32010-11-15 10:50:34 -0800128 * <p>Now assume mS5.processMessage receives a message it can handle, and during
Wink Savillea4f3bec2010-05-19 09:11:38 -0700129 * the handling determines the machine should change states. It could call
130 * transitionTo(mS4) and return true or HANDLED. Immediately after returning from
Wink Savillefc5b4802009-12-08 21:22:24 -0800131 * processMessage the state machine runtime will find the common parent,
132 * which is mP1. It will then call mS5.exit, mS1.exit, mS2.enter and then
133 * mS4.enter. The new list of active states is mP0, mP1, mS2 and mS4. So
Wink Saville33c54e32010-11-15 10:50:34 -0800134 * when the next message is received mS4.processMessage will be invoked.</p>
Wink Savillefc5b4802009-12-08 21:22:24 -0800135 *
Wink Saville64c42ca2011-04-18 14:55:10 -0700136 * <p>Now for some concrete examples, here is the canonical HelloWorld as a state machine.
Wink Saville33c54e32010-11-15 10:50:34 -0800137 * It responds with "Hello World" being printed to the log for every message.</p>
Kevin Cernekeee318d8c2015-06-18 13:23:24 -0700138<pre>
Wink Saville64c42ca2011-04-18 14:55:10 -0700139class HelloWorld extends StateMachine {
140 HelloWorld(String name) {
Wink Savillefc5b4802009-12-08 21:22:24 -0800141 super(name);
142 addState(mState1);
143 setInitialState(mState1);
144 }
145
146 public static HelloWorld makeHelloWorld() {
147 HelloWorld hw = new HelloWorld("hw");
148 hw.start();
149 return hw;
150 }
151
Wink Saville64c42ca2011-04-18 14:55:10 -0700152 class State1 extends State {
Joe Onorato0b6232d2010-09-16 11:55:35 -0400153 &#64;Override public boolean processMessage(Message message) {
Wink Saville58c73c32013-01-28 10:52:34 -0800154 log("Hello World");
Wink Savillea4f3bec2010-05-19 09:11:38 -0700155 return HANDLED;
Wink Savillefc5b4802009-12-08 21:22:24 -0800156 }
157 }
158 State1 mState1 = new State1();
159}
160
161void testHelloWorld() {
162 HelloWorld hw = makeHelloWorld();
163 hw.sendMessage(hw.obtainMessage());
164}
Kevin Cernekeee318d8c2015-06-18 13:23:24 -0700165</pre>
Wink Saville33c54e32010-11-15 10:50:34 -0800166 * <p>A more interesting state machine is one with four states
167 * with two independent parent states.</p>
Kevin Cernekeee318d8c2015-06-18 13:23:24 -0700168<pre>
Wink Savillefc5b4802009-12-08 21:22:24 -0800169 mP1 mP2
170 / \
171 mS2 mS1
Kevin Cernekeee318d8c2015-06-18 13:23:24 -0700172</pre>
Wink Saville33c54e32010-11-15 10:50:34 -0800173 * <p>Here is a description of this state machine using pseudo code.</p>
Kevin Cernekeee318d8c2015-06-18 13:23:24 -0700174 <pre>
Wink Saville33c54e32010-11-15 10:50:34 -0800175state mP1 {
176 enter { log("mP1.enter"); }
177 exit { log("mP1.exit"); }
178 on msg {
179 CMD_2 {
180 send(CMD_3);
181 defer(msg);
Kevin Cernekeee318d8c2015-06-18 13:23:24 -0700182 transitionTo(mS2);
Wink Saville33c54e32010-11-15 10:50:34 -0800183 return HANDLED;
184 }
185 return NOT_HANDLED;
186 }
187}
188
189INITIAL
190state mS1 parent mP1 {
191 enter { log("mS1.enter"); }
192 exit { log("mS1.exit"); }
193 on msg {
194 CMD_1 {
195 transitionTo(mS1);
196 return HANDLED;
197 }
198 return NOT_HANDLED;
199 }
200}
201
202state mS2 parent mP1 {
203 enter { log("mS2.enter"); }
204 exit { log("mS2.exit"); }
205 on msg {
206 CMD_2 {
207 send(CMD_4);
208 return HANDLED;
209 }
210 CMD_3 {
211 defer(msg);
212 transitionTo(mP2);
213 return HANDLED;
214 }
215 return NOT_HANDLED;
216 }
217}
218
219state mP2 {
220 enter {
221 log("mP2.enter");
222 send(CMD_5);
223 }
224 exit { log("mP2.exit"); }
225 on msg {
226 CMD_3, CMD_4 { return HANDLED; }
227 CMD_5 {
228 transitionTo(HaltingState);
229 return HANDLED;
230 }
231 return NOT_HANDLED;
232 }
233}
Kevin Cernekeee318d8c2015-06-18 13:23:24 -0700234</pre>
Wink Saville64c42ca2011-04-18 14:55:10 -0700235 * <p>The implementation is below and also in StateMachineTest:</p>
Kevin Cernekeee318d8c2015-06-18 13:23:24 -0700236<pre>
Wink Saville64c42ca2011-04-18 14:55:10 -0700237class Hsm1 extends StateMachine {
Wink Savillefc5b4802009-12-08 21:22:24 -0800238 public static final int CMD_1 = 1;
239 public static final int CMD_2 = 2;
240 public static final int CMD_3 = 3;
241 public static final int CMD_4 = 4;
242 public static final int CMD_5 = 5;
243
244 public static Hsm1 makeHsm1() {
Wink Saville58c73c32013-01-28 10:52:34 -0800245 log("makeHsm1 E");
Wink Savillefc5b4802009-12-08 21:22:24 -0800246 Hsm1 sm = new Hsm1("hsm1");
247 sm.start();
Wink Saville58c73c32013-01-28 10:52:34 -0800248 log("makeHsm1 X");
Wink Savillefc5b4802009-12-08 21:22:24 -0800249 return sm;
250 }
251
252 Hsm1(String name) {
253 super(name);
Wink Saville58c73c32013-01-28 10:52:34 -0800254 log("ctor E");
Wink Savillefc5b4802009-12-08 21:22:24 -0800255
256 // Add states, use indentation to show hierarchy
257 addState(mP1);
258 addState(mS1, mP1);
259 addState(mS2, mP1);
260 addState(mP2);
261
262 // Set the initial state
263 setInitialState(mS1);
Wink Saville58c73c32013-01-28 10:52:34 -0800264 log("ctor X");
Wink Savillefc5b4802009-12-08 21:22:24 -0800265 }
266
Wink Saville64c42ca2011-04-18 14:55:10 -0700267 class P1 extends State {
Joe Onorato0b6232d2010-09-16 11:55:35 -0400268 &#64;Override public void enter() {
Wink Saville58c73c32013-01-28 10:52:34 -0800269 log("mP1.enter");
Wink Savillefc5b4802009-12-08 21:22:24 -0800270 }
Joe Onorato0b6232d2010-09-16 11:55:35 -0400271 &#64;Override public boolean processMessage(Message message) {
Wink Savillefc5b4802009-12-08 21:22:24 -0800272 boolean retVal;
Wink Saville58c73c32013-01-28 10:52:34 -0800273 log("mP1.processMessage what=" + message.what);
Wink Savillefc5b4802009-12-08 21:22:24 -0800274 switch(message.what) {
275 case CMD_2:
276 // CMD_2 will arrive in mS2 before CMD_3
277 sendMessage(obtainMessage(CMD_3));
278 deferMessage(message);
279 transitionTo(mS2);
Wink Savillea4f3bec2010-05-19 09:11:38 -0700280 retVal = HANDLED;
Wink Savillefc5b4802009-12-08 21:22:24 -0800281 break;
282 default:
283 // Any message we don't understand in this state invokes unhandledMessage
Wink Savillea4f3bec2010-05-19 09:11:38 -0700284 retVal = NOT_HANDLED;
Wink Savillefc5b4802009-12-08 21:22:24 -0800285 break;
286 }
287 return retVal;
288 }
Joe Onorato0b6232d2010-09-16 11:55:35 -0400289 &#64;Override public void exit() {
Wink Saville58c73c32013-01-28 10:52:34 -0800290 log("mP1.exit");
Wink Savillefc5b4802009-12-08 21:22:24 -0800291 }
292 }
293
Wink Saville64c42ca2011-04-18 14:55:10 -0700294 class S1 extends State {
Joe Onorato0b6232d2010-09-16 11:55:35 -0400295 &#64;Override public void enter() {
Wink Saville58c73c32013-01-28 10:52:34 -0800296 log("mS1.enter");
Wink Savillefc5b4802009-12-08 21:22:24 -0800297 }
Joe Onorato0b6232d2010-09-16 11:55:35 -0400298 &#64;Override public boolean processMessage(Message message) {
Wink Saville58c73c32013-01-28 10:52:34 -0800299 log("S1.processMessage what=" + message.what);
Wink Savillefc5b4802009-12-08 21:22:24 -0800300 if (message.what == CMD_1) {
301 // Transition to ourself to show that enter/exit is called
302 transitionTo(mS1);
Wink Savillea4f3bec2010-05-19 09:11:38 -0700303 return HANDLED;
Wink Savillefc5b4802009-12-08 21:22:24 -0800304 } else {
305 // Let parent process all other messages
Wink Savillea4f3bec2010-05-19 09:11:38 -0700306 return NOT_HANDLED;
Wink Savillefc5b4802009-12-08 21:22:24 -0800307 }
308 }
Joe Onorato0b6232d2010-09-16 11:55:35 -0400309 &#64;Override public void exit() {
Wink Saville58c73c32013-01-28 10:52:34 -0800310 log("mS1.exit");
Wink Savillefc5b4802009-12-08 21:22:24 -0800311 }
312 }
313
Wink Saville64c42ca2011-04-18 14:55:10 -0700314 class S2 extends State {
Joe Onorato0b6232d2010-09-16 11:55:35 -0400315 &#64;Override public void enter() {
Wink Saville58c73c32013-01-28 10:52:34 -0800316 log("mS2.enter");
Wink Savillefc5b4802009-12-08 21:22:24 -0800317 }
Joe Onorato0b6232d2010-09-16 11:55:35 -0400318 &#64;Override public boolean processMessage(Message message) {
Wink Savillefc5b4802009-12-08 21:22:24 -0800319 boolean retVal;
Wink Saville58c73c32013-01-28 10:52:34 -0800320 log("mS2.processMessage what=" + message.what);
Wink Savillefc5b4802009-12-08 21:22:24 -0800321 switch(message.what) {
322 case(CMD_2):
323 sendMessage(obtainMessage(CMD_4));
Wink Savillea4f3bec2010-05-19 09:11:38 -0700324 retVal = HANDLED;
Wink Savillefc5b4802009-12-08 21:22:24 -0800325 break;
326 case(CMD_3):
327 deferMessage(message);
328 transitionTo(mP2);
Wink Savillea4f3bec2010-05-19 09:11:38 -0700329 retVal = HANDLED;
Wink Savillefc5b4802009-12-08 21:22:24 -0800330 break;
331 default:
Wink Savillea4f3bec2010-05-19 09:11:38 -0700332 retVal = NOT_HANDLED;
Wink Savillefc5b4802009-12-08 21:22:24 -0800333 break;
334 }
335 return retVal;
336 }
Joe Onorato0b6232d2010-09-16 11:55:35 -0400337 &#64;Override public void exit() {
Wink Saville58c73c32013-01-28 10:52:34 -0800338 log("mS2.exit");
Wink Savillefc5b4802009-12-08 21:22:24 -0800339 }
340 }
341
Wink Saville64c42ca2011-04-18 14:55:10 -0700342 class P2 extends State {
Joe Onorato0b6232d2010-09-16 11:55:35 -0400343 &#64;Override public void enter() {
Wink Saville58c73c32013-01-28 10:52:34 -0800344 log("mP2.enter");
Wink Savillefc5b4802009-12-08 21:22:24 -0800345 sendMessage(obtainMessage(CMD_5));
346 }
Joe Onorato0b6232d2010-09-16 11:55:35 -0400347 &#64;Override public boolean processMessage(Message message) {
Wink Saville58c73c32013-01-28 10:52:34 -0800348 log("P2.processMessage what=" + message.what);
Wink Savillefc5b4802009-12-08 21:22:24 -0800349 switch(message.what) {
350 case(CMD_3):
351 break;
352 case(CMD_4):
353 break;
354 case(CMD_5):
355 transitionToHaltingState();
356 break;
357 }
Wink Savillea4f3bec2010-05-19 09:11:38 -0700358 return HANDLED;
Wink Savillefc5b4802009-12-08 21:22:24 -0800359 }
Joe Onorato0b6232d2010-09-16 11:55:35 -0400360 &#64;Override public void exit() {
Wink Saville58c73c32013-01-28 10:52:34 -0800361 log("mP2.exit");
Wink Savillefc5b4802009-12-08 21:22:24 -0800362 }
363 }
364
Joe Onorato0b6232d2010-09-16 11:55:35 -0400365 &#64;Override
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700366 void onHalting() {
Wink Saville58c73c32013-01-28 10:52:34 -0800367 log("halting");
Wink Savillefc5b4802009-12-08 21:22:24 -0800368 synchronized (this) {
369 this.notifyAll();
370 }
371 }
372
373 P1 mP1 = new P1();
374 S1 mS1 = new S1();
375 S2 mS2 = new S2();
376 P2 mP2 = new P2();
377}
Kevin Cernekeee318d8c2015-06-18 13:23:24 -0700378</pre>
Wink Saville33c54e32010-11-15 10:50:34 -0800379 * <p>If this is executed by sending two messages CMD_1 and CMD_2
380 * (Note the synchronize is only needed because we use hsm.wait())</p>
Kevin Cernekeee318d8c2015-06-18 13:23:24 -0700381<pre>
Wink Saville33c54e32010-11-15 10:50:34 -0800382Hsm1 hsm = makeHsm1();
383synchronize(hsm) {
384 hsm.sendMessage(obtainMessage(hsm.CMD_1));
385 hsm.sendMessage(obtainMessage(hsm.CMD_2));
386 try {
387 // wait for the messages to be handled
388 hsm.wait();
389 } catch (InterruptedException e) {
Wink Saville58c73c32013-01-28 10:52:34 -0800390 loge("exception while waiting " + e.getMessage());
Wink Saville33c54e32010-11-15 10:50:34 -0800391 }
392}
Kevin Cernekeee318d8c2015-06-18 13:23:24 -0700393</pre>
Wink Saville33c54e32010-11-15 10:50:34 -0800394 * <p>The output is:</p>
Kevin Cernekeee318d8c2015-06-18 13:23:24 -0700395<pre>
Wink Saville33c54e32010-11-15 10:50:34 -0800396D/hsm1 ( 1999): makeHsm1 E
397D/hsm1 ( 1999): ctor E
398D/hsm1 ( 1999): ctor X
399D/hsm1 ( 1999): mP1.enter
400D/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
Kevin Cernekeee318d8c2015-06-18 13:23:24 -0700419</pre>
Wink Savillefc5b4802009-12-08 21:22:24 -0800420 */
Wink Saville64c42ca2011-04-18 14:55:10 -0700421public class StateMachine {
Wink Saville58c73c32013-01-28 10:52:34 -0800422 // Name of the state machine and used as logging tag
Wink Savillefc5b4802009-12-08 21:22:24 -0800423 private String mName;
424
Wink Savillea4f3bec2010-05-19 09:11:38 -0700425 /** Message.what value when quitting */
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700426 private static final int SM_QUIT_CMD = -1;
Wink Saville1b8b98b2010-03-11 11:49:54 -0800427
Wink Savillea4f3bec2010-05-19 09:11:38 -0700428 /** Message.what value when initializing */
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700429 private static final int SM_INIT_CMD = -2;
Wink Savillea4f3bec2010-05-19 09:11:38 -0700430
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
Wink Savilled3059482011-04-11 11:51:28 -0700445 /**
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700446 * StateMachine logging record.
Wink Savilled3059482011-04-11 11:51:28 -0700447 * {@hide}
Wink Savilled3059482011-04-11 11:51:28 -0700448 */
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700449 public static class LogRec {
Wink Savilleefcc3d32013-01-30 11:21:22 -0800450 private StateMachine mSm;
Wink Saville583eaaa2012-04-13 16:11:20 -0700451 private long mTime;
452 private int mWhat;
453 private String mInfo;
Wink Savilleefcc3d32013-01-30 11:21:22 -0800454 private IState mState;
455 private IState mOrgState;
456 private IState mDstState;
Wink Savilled3059482011-04-11 11:51:28 -0700457
458 /**
459 * Constructor
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700460 *
461 * @param msg
Irfan Sheriffef8da9f2012-11-29 09:42:13 -0800462 * @param state the state which handled the message
Wink Savilled3059482011-04-11 11:51:28 -0700463 * @param orgState is the first state the received the message but
464 * did not processes the message.
Irfan Sheriffef8da9f2012-11-29 09:42:13 -0800465 * @param transToState is the state that was transitioned to after the message was
466 * processed.
Wink Savilled3059482011-04-11 11:51:28 -0700467 */
Wink Savilleefcc3d32013-01-30 11:21:22 -0800468 LogRec(StateMachine sm, Message msg, String info, IState state, IState orgState,
469 IState transToState) {
470 update(sm, msg, info, state, orgState, transToState);
Wink Savilled3059482011-04-11 11:51:28 -0700471 }
472
473 /**
474 * Update the information in the record.
475 * @param state that handled the message
Wink Savilleefcc3d32013-01-30 11:21:22 -0800476 * @param orgState is the first state the received the message
477 * @param dstState is the state that was the transition target when logging
Wink Savilled3059482011-04-11 11:51:28 -0700478 */
Wink Savilleefcc3d32013-01-30 11:21:22 -0800479 public void update(StateMachine sm, Message msg, String info, IState state, IState orgState,
480 IState dstState) {
481 mSm = sm;
Wink Saville583eaaa2012-04-13 16:11:20 -0700482 mTime = System.currentTimeMillis();
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700483 mWhat = (msg != null) ? msg.what : 0;
Wink Saville583eaaa2012-04-13 16:11:20 -0700484 mInfo = info;
485 mState = state;
486 mOrgState = orgState;
Wink Savilleefcc3d32013-01-30 11:21:22 -0800487 mDstState = dstState;
Wink Saville583eaaa2012-04-13 16:11:20 -0700488 }
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;
Wink Savilled3059482011-04-11 11:51:28 -0700502 }
503
504 /**
505 * @return the command that was executing
506 */
Wink Saville583eaaa2012-04-13 16:11:20 -0700507 public String getInfo() {
508 return mInfo;
Wink Savilled3059482011-04-11 11:51:28 -0700509 }
510
511 /**
512 * @return the state that handled this message
513 */
Wink Savilleefcc3d32013-01-30 11:21:22 -0800514 public IState getState() {
Wink Saville583eaaa2012-04-13 16:11:20 -0700515 return mState;
Wink Savilled3059482011-04-11 11:51:28 -0700516 }
517
518 /**
Wink Savilleefcc3d32013-01-30 11:21:22 -0800519 * @return the state destination state if a transition is occurring or null if none.
520 */
521 public IState getDestState() {
522 return mDstState;
523 }
524
Wink Savilleefcc3d32013-01-30 11:21:22 -0800525 /**
Wink Savilled3059482011-04-11 11:51:28 -0700526 * @return the original state that received the message.
527 */
Wink Savilleefcc3d32013-01-30 11:21:22 -0800528 public IState getOriginalState() {
Wink Saville583eaaa2012-04-13 16:11:20 -0700529 return mOrgState;
Wink Savilled3059482011-04-11 11:51:28 -0700530 }
531
Wink Savilleefcc3d32013-01-30 11:21:22 -0800532 @Override
533 public String toString() {
Wink Savilled3059482011-04-11 11:51:28 -0700534 StringBuilder sb = new StringBuilder();
Wink Saville583eaaa2012-04-13 16:11:20 -0700535 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));
Irfan Sheriffef8da9f2012-11-29 09:42:13 -0800539 sb.append(" processed=");
Wink Saville583eaaa2012-04-13 16:11:20 -0700540 sb.append(mState == null ? "<null>" : mState.getName());
Irfan Sheriffef8da9f2012-11-29 09:42:13 -0800541 sb.append(" org=");
Wink Saville583eaaa2012-04-13 16:11:20 -0700542 sb.append(mOrgState == null ? "<null>" : mOrgState.getName());
Irfan Sheriffef8da9f2012-11-29 09:42:13 -0800543 sb.append(" dest=");
Wink Savilleefcc3d32013-01-30 11:21:22 -0800544 sb.append(mDstState == null ? "<null>" : mDstState.getName());
Wink Saville583eaaa2012-04-13 16:11:20 -0700545 sb.append(" what=");
Wink Savilleefcc3d32013-01-30 11:21:22 -0800546 String what = mSm != null ? mSm.getWhatToString(mWhat) : "";
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700547 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 }
Wink Savilleff4fcdb2013-02-24 07:21:45 -0800555 if (!TextUtils.isEmpty(mInfo)) {
Wink Saville583eaaa2012-04-13 16:11:20 -0700556 sb.append(" ");
557 sb.append(mInfo);
Wink Savilled3059482011-04-11 11:51:28 -0700558 }
Wink Saville583eaaa2012-04-13 16:11:20 -0700559 return sb.toString();
Wink Savilled3059482011-04-11 11:51:28 -0700560 }
561 }
562
563 /**
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700564 * A list of log records including messages recently processed by the state machine.
Wink Savilled3059482011-04-11 11:51:28 -0700565 *
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700566 * The class maintains a list of log records including messages
Wink Savilled3059482011-04-11 11:51:28 -0700567 * recently processed. The list is finite and may be set in the
568 * constructor or by calling setSize. The public interface also
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700569 * 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.
Wink Savilled3059482011-04-11 11:51:28 -0700573 */
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700574 private static class LogRecords {
Wink Savilled3059482011-04-11 11:51:28 -0700575
576 private static final int DEFAULT_SIZE = 20;
577
Irfan Sheriffef8da9f2012-11-29 09:42:13 -0800578 private Vector<LogRec> mLogRecVector = new Vector<LogRec>();
Wink Savilled3059482011-04-11 11:51:28 -0700579 private int mMaxSize = DEFAULT_SIZE;
580 private int mOldestIndex = 0;
581 private int mCount = 0;
Irfan Sheriffef8da9f2012-11-29 09:42:13 -0800582 private boolean mLogOnlyTransitions = false;
Wink Savilled3059482011-04-11 11:51:28 -0700583
584 /**
Wink Saville583eaaa2012-04-13 16:11:20 -0700585 * private constructor use add
Wink Savilled3059482011-04-11 11:51:28 -0700586 */
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700587 private LogRecords() {
Wink Savilled3059482011-04-11 11:51:28 -0700588 }
589
590 /**
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700591 * Set size of messages to maintain and clears all current records.
Wink Savilled3059482011-04-11 11:51:28 -0700592 *
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700593 * @param maxSize number of records to maintain at anyone time.
Wink Savilled3059482011-04-11 11:51:28 -0700594 */
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700595 synchronized void setSize(int maxSize) {
Samuel Tan2326e9b2016-04-15 13:06:12 -0700596 // 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.
Wink Savilled3059482011-04-11 11:51:28 -0700599 mMaxSize = maxSize;
Samuel Tan2326e9b2016-04-15 13:06:12 -0700600 mOldestIndex = 0;
Wink Savilled3059482011-04-11 11:51:28 -0700601 mCount = 0;
Irfan Sheriffef8da9f2012-11-29 09:42:13 -0800602 mLogRecVector.clear();
603 }
604
605 synchronized void setLogOnlyTransitions(boolean enable) {
606 mLogOnlyTransitions = enable;
607 }
608
609 synchronized boolean logOnlyTransitions() {
610 return mLogOnlyTransitions;
Wink Savilled3059482011-04-11 11:51:28 -0700611 }
612
613 /**
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700614 * @return the number of recent records.
Wink Savilled3059482011-04-11 11:51:28 -0700615 */
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700616 synchronized int size() {
Irfan Sheriffef8da9f2012-11-29 09:42:13 -0800617 return mLogRecVector.size();
Wink Savilled3059482011-04-11 11:51:28 -0700618 }
619
620 /**
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700621 * @return the total number of records processed since size was set.
Wink Savilled3059482011-04-11 11:51:28 -0700622 */
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700623 synchronized int count() {
Wink Savilled3059482011-04-11 11:51:28 -0700624 return mCount;
625 }
626
627 /**
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700628 * Clear the list of records.
Jaikumar Ganesh6f9a6162011-11-28 13:13:02 -0800629 */
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700630 synchronized void cleanup() {
Irfan Sheriffef8da9f2012-11-29 09:42:13 -0800631 mLogRecVector.clear();
Jaikumar Ganesh6f9a6162011-11-28 13:13:02 -0800632 }
633
634 /**
Wink Savilled3059482011-04-11 11:51:28 -0700635 * @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 */
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700639 synchronized LogRec get(int index) {
Wink Savilled3059482011-04-11 11:51:28 -0700640 int nextIndex = mOldestIndex + index;
641 if (nextIndex >= mMaxSize) {
642 nextIndex -= mMaxSize;
643 }
644 if (nextIndex >= size()) {
645 return null;
646 } else {
Irfan Sheriffef8da9f2012-11-29 09:42:13 -0800647 return mLogRecVector.get(nextIndex);
Wink Savilled3059482011-04-11 11:51:28 -0700648 }
649 }
650
651 /**
652 * Add a processed message.
653 *
Wink Saville583eaaa2012-04-13 16:11:20 -0700654 * @param msg
655 * @param messageInfo to be stored
Wink Savilled3059482011-04-11 11:51:28 -0700656 * @param state that handled the message
657 * @param orgState is the first state the received the message but
658 * did not processes the message.
Irfan Sheriffef8da9f2012-11-29 09:42:13 -0800659 * @param transToState is the state that was transitioned to after the message was
660 * processed.
661 *
Wink Savilled3059482011-04-11 11:51:28 -0700662 */
Wink Savilleefcc3d32013-01-30 11:21:22 -0800663 synchronized void add(StateMachine sm, Message msg, String messageInfo, IState state,
Wink Savilleff4fcdb2013-02-24 07:21:45 -0800664 IState orgState, IState transToState) {
Wink Savilled3059482011-04-11 11:51:28 -0700665 mCount += 1;
Irfan Sheriffef8da9f2012-11-29 09:42:13 -0800666 if (mLogRecVector.size() < mMaxSize) {
Wink Savilleefcc3d32013-01-30 11:21:22 -0800667 mLogRecVector.add(new LogRec(sm, msg, messageInfo, state, orgState, transToState));
Wink Savilled3059482011-04-11 11:51:28 -0700668 } else {
Irfan Sheriffef8da9f2012-11-29 09:42:13 -0800669 LogRec pmi = mLogRecVector.get(mOldestIndex);
Wink Savilled3059482011-04-11 11:51:28 -0700670 mOldestIndex += 1;
671 if (mOldestIndex >= mMaxSize) {
672 mOldestIndex = 0;
673 }
Wink Savilleefcc3d32013-01-30 11:21:22 -0800674 pmi.update(sm, msg, messageInfo, state, orgState, transToState);
Wink Savilled3059482011-04-11 11:51:28 -0700675 }
676 }
677 }
678
Wink Saville64c42ca2011-04-18 14:55:10 -0700679 private static class SmHandler extends Handler {
Wink Savillefc5b4802009-12-08 21:22:24 -0800680
Wink Saville03812c72013-04-16 13:21:00 -0700681 /** true if StateMachine has quit */
682 private boolean mHasQuit = false;
683
Wink Savillefc5b4802009-12-08 21:22:24 -0800684 /** The debug flag */
685 private boolean mDbg = false;
686
Wink Savillecea056f2012-03-26 15:03:16 -0700687 /** The SmHandler object, identifies that message is internal */
688 private static final Object mSmHandlerObj = new Object();
Wink Saville1b8b98b2010-03-11 11:49:54 -0800689
Wink Savillea4f3bec2010-05-19 09:11:38 -0700690 /** The current message */
691 private Message mMsg;
692
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700693 /** A list of log records including messages this state machine has processed */
694 private LogRecords mLogRecords = new LogRecords();
Wink Savillefc5b4802009-12-08 21:22:24 -0800695
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
Wink Saville1b8b98b2010-03-11 11:49:54 -0800714 /** State used when state machine is quitting */
715 private QuittingState mQuittingState = new QuittingState();
716
Wink Saville64c42ca2011-04-18 14:55:10 -0700717 /** Reference to the StateMachine */
718 private StateMachine mSm;
Wink Savillefc5b4802009-12-08 21:22:24 -0800719
720 /**
721 * Information about a state.
722 * Used to maintain the hierarchy.
723 */
724 private class StateInfo {
725 /** The state */
Wink Saville64c42ca2011-04-18 14:55:10 -0700726 State state;
Wink Savillefc5b4802009-12-08 21:22:24 -0800727
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() {
Wink Savilleff4fcdb2013-02-24 07:21:45 -0800739 return "state=" + state.getName() + ",active=" + active + ",parent="
740 + ((parentStateInfo == null) ? "null" : parentStateInfo.state.getName());
Wink Savillefc5b4802009-12-08 21:22:24 -0800741 }
742 }
743
744 /** The map of all of the states in the state machine */
Wink Savilleff4fcdb2013-02-24 07:21:45 -0800745 private HashMap<State, StateInfo> mStateInfo = new HashMap<State, StateInfo>();
Wink Savillefc5b4802009-12-08 21:22:24 -0800746
747 /** The initial state that will process the first message */
Wink Saville64c42ca2011-04-18 14:55:10 -0700748 private State mInitialState;
Wink Savillefc5b4802009-12-08 21:22:24 -0800749
750 /** The destination state when transitionTo has been invoked */
Wink Saville64c42ca2011-04-18 14:55:10 -0700751 private State mDestState;
Wink Savillefc5b4802009-12-08 21:22:24 -0800752
753 /** The list of deferred messages */
754 private ArrayList<Message> mDeferredMessages = new ArrayList<Message>();
755
756 /**
757 * State entered when transitionToHaltingState is called.
758 */
Wink Saville64c42ca2011-04-18 14:55:10 -0700759 private class HaltingState extends State {
Wink Savillefc5b4802009-12-08 21:22:24 -0800760 @Override
761 public boolean processMessage(Message msg) {
Wink Saville64c42ca2011-04-18 14:55:10 -0700762 mSm.haltedProcessMessage(msg);
Wink Savillefc5b4802009-12-08 21:22:24 -0800763 return true;
764 }
765 }
766
767 /**
Wink Saville1b8b98b2010-03-11 11:49:54 -0800768 * State entered when a valid quit message is handled.
769 */
Wink Saville64c42ca2011-04-18 14:55:10 -0700770 private class QuittingState extends State {
Wink Saville1b8b98b2010-03-11 11:49:54 -0800771 @Override
772 public boolean processMessage(Message msg) {
Wink Savillea4f3bec2010-05-19 09:11:38 -0700773 return NOT_HANDLED;
Wink Saville1b8b98b2010-03-11 11:49:54 -0800774 }
775 }
776
777 /**
Wink Savillefc5b4802009-12-08 21:22:24 -0800778 * Handle messages sent to the state machine by calling
779 * the current state's processMessage. It also handles
780 * the enter/exit calls and placing any deferred messages
781 * back onto the queue when transitioning to a new state.
782 */
783 @Override
784 public final void handleMessage(Message msg) {
Wink Saville03812c72013-04-16 13:21:00 -0700785 if (!mHasQuit) {
Hall Liu74d7d0f2016-01-04 15:17:53 -0800786 if (mSm != null && msg.what != SM_INIT_CMD && msg.what != SM_QUIT_CMD) {
Brad Ebinger355f1102015-12-14 08:46:49 -0800787 mSm.onPreHandleMessage(msg);
788 }
789
Wink Saville03812c72013-04-16 13:21:00 -0700790 if (mDbg) mSm.log("handleMessage: E msg.what=" + msg.what);
Wink Savillefc5b4802009-12-08 21:22:24 -0800791
Wink Saville03812c72013-04-16 13:21:00 -0700792 /** Save the current message */
793 mMsg = msg;
Wink Savillea4f3bec2010-05-19 09:11:38 -0700794
Wink Saville03812c72013-04-16 13:21:00 -0700795 /** State that processed the message */
796 State msgProcessedState = null;
797 if (mIsConstructionCompleted) {
798 /** Normal path */
799 msgProcessedState = processMsg(msg);
800 } else if (!mIsConstructionCompleted && (mMsg.what == SM_INIT_CMD)
801 && (mMsg.obj == mSmHandlerObj)) {
802 /** Initial one time path. */
803 mIsConstructionCompleted = true;
804 invokeEnterMethods(0);
805 } else {
806 throw new RuntimeException("StateMachine.handleMessage: "
807 + "The start method not called, received msg: " + msg);
808 }
809 performTransitions(msgProcessedState, msg);
810
811 // We need to check if mSm == null here as we could be quitting.
812 if (mDbg && mSm != null) mSm.log("handleMessage: X");
Brad Ebinger355f1102015-12-14 08:46:49 -0800813
Hall Liu74d7d0f2016-01-04 15:17:53 -0800814 if (mSm != null && msg.what != SM_INIT_CMD && msg.what != SM_QUIT_CMD) {
Brad Ebinger355f1102015-12-14 08:46:49 -0800815 mSm.onPostHandleMessage(msg);
816 }
Wink Savillefc5b4802009-12-08 21:22:24 -0800817 }
Wink Savillee7be6a82010-03-18 17:03:30 -0700818 }
819
820 /**
821 * Do any transitions
Irfan Sheriffef8da9f2012-11-29 09:42:13 -0800822 * @param msgProcessedState is the state that processed the message
Wink Savillee7be6a82010-03-18 17:03:30 -0700823 */
Wink Savilleefcc3d32013-01-30 11:21:22 -0800824 private void performTransitions(State msgProcessedState, Message msg) {
Wink Savillefc5b4802009-12-08 21:22:24 -0800825 /**
826 * If transitionTo has been called, exit and then enter
Wink Savillee7be6a82010-03-18 17:03:30 -0700827 * the appropriate states. We loop on this to allow
828 * enter and exit methods to use transitionTo.
Wink Savillefc5b4802009-12-08 21:22:24 -0800829 */
Irfan Sheriffef8da9f2012-11-29 09:42:13 -0800830 State orgState = mStateStack[mStateStackTopIndex].state;
831
Wink Savilleefcc3d32013-01-30 11:21:22 -0800832 /**
833 * Record whether message needs to be logged before we transition and
834 * and we won't log special messages SM_INIT_CMD or SM_QUIT_CMD which
835 * always set msg.obj to the handler.
836 */
Wink Savillef6430692013-02-11 16:16:02 -0800837 boolean recordLogMsg = mSm.recordLogRec(mMsg) && (msg.obj != mSmHandlerObj);
Irfan Sheriffef8da9f2012-11-29 09:42:13 -0800838
Wink Savilleefcc3d32013-01-30 11:21:22 -0800839 if (mLogRecords.logOnlyTransitions()) {
840 /** Record only if there is a transition */
841 if (mDestState != null) {
842 mLogRecords.add(mSm, mMsg, mSm.getLogRecString(mMsg), msgProcessedState,
843 orgState, mDestState);
844 }
845 } else if (recordLogMsg) {
846 /** Record message */
Wink Savilleff4fcdb2013-02-24 07:21:45 -0800847 mLogRecords.add(mSm, mMsg, mSm.getLogRecString(mMsg), msgProcessedState, orgState,
848 mDestState);
Wink Savilleefcc3d32013-01-30 11:21:22 -0800849 }
Wink Savillefc5b4802009-12-08 21:22:24 -0800850
Wink Savilleefcc3d32013-01-30 11:21:22 -0800851 State destState = mDestState;
852 if (destState != null) {
Wink Savillefc5b4802009-12-08 21:22:24 -0800853 /**
Wink Savilleefcc3d32013-01-30 11:21:22 -0800854 * Process the transitions including transitions in the enter/exit methods
Wink Savillee7be6a82010-03-18 17:03:30 -0700855 */
Wink Savilleefcc3d32013-01-30 11:21:22 -0800856 while (true) {
857 if (mDbg) mSm.log("handleMessage: new destination call exit/enter");
858
859 /**
860 * Determine the states to exit and enter and return the
861 * common ancestor state of the enter/exit states. Then
862 * invoke the exit methods then the enter methods.
863 */
864 StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState);
865 invokeExitMethods(commonStateInfo);
866 int stateStackEnteringIndex = moveTempStateStackToStateStack();
867 invokeEnterMethods(stateStackEnteringIndex);
868
Wink Savilleefcc3d32013-01-30 11:21:22 -0800869 /**
870 * Since we have transitioned to a new state we need to have
871 * any deferred messages moved to the front of the message queue
872 * so they will be processed before any other messages in the
873 * message queue.
874 */
875 moveDeferredMessageAtFrontOfQueue();
876
877 if (destState != mDestState) {
878 // A new mDestState so continue looping
879 destState = mDestState;
880 } else {
881 // No change in mDestState so we're done
882 break;
883 }
884 }
Wink Savillee7be6a82010-03-18 17:03:30 -0700885 mDestState = null;
Wink Savillee7be6a82010-03-18 17:03:30 -0700886 }
Wink Savillefc5b4802009-12-08 21:22:24 -0800887
Wink Savillee7be6a82010-03-18 17:03:30 -0700888 /**
889 * After processing all transitions check and
890 * see if the last transition was to quit or halt.
891 */
892 if (destState != null) {
893 if (destState == mQuittingState) {
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700894 /**
895 * Call onQuitting to let subclasses cleanup.
896 */
897 mSm.onQuitting();
Jaikumar Ganesh6f9a6162011-11-28 13:13:02 -0800898 cleanupAfterQuitting();
Wink Savillee7be6a82010-03-18 17:03:30 -0700899 } else if (destState == mHaltingState) {
900 /**
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700901 * Call onHalting() if we've transitioned to the halting
Wink Savillee7be6a82010-03-18 17:03:30 -0700902 * state. All subsequent messages will be processed in
903 * in the halting state which invokes haltedProcessMessage(msg);
904 */
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700905 mSm.onHalting();
Wink Savillefc5b4802009-12-08 21:22:24 -0800906 }
Wink Savillefc5b4802009-12-08 21:22:24 -0800907 }
Wink Savillefc5b4802009-12-08 21:22:24 -0800908 }
909
910 /**
Jaikumar Ganesh6f9a6162011-11-28 13:13:02 -0800911 * Cleanup all the static variables and the looper after the SM has been quit.
912 */
913 private final void cleanupAfterQuitting() {
Jaikumar Ganesh6f9a6162011-11-28 13:13:02 -0800914 if (mSm.mSmThread != null) {
915 // If we made the thread then quit looper which stops the thread.
916 getLooper().quit();
917 mSm.mSmThread = null;
918 }
919
920 mSm.mSmHandler = null;
921 mSm = null;
922 mMsg = null;
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700923 mLogRecords.cleanup();
Jaikumar Ganesh6f9a6162011-11-28 13:13:02 -0800924 mStateStack = null;
925 mTempStateStack = null;
926 mStateInfo.clear();
927 mInitialState = null;
928 mDestState = null;
929 mDeferredMessages.clear();
Wink Saville03812c72013-04-16 13:21:00 -0700930 mHasQuit = true;
Jaikumar Ganesh6f9a6162011-11-28 13:13:02 -0800931 }
932
933 /**
Wink Savillefc5b4802009-12-08 21:22:24 -0800934 * Complete the construction of the state machine.
935 */
936 private final void completeConstruction() {
Wink Saville58c73c32013-01-28 10:52:34 -0800937 if (mDbg) mSm.log("completeConstruction: E");
Wink Savillefc5b4802009-12-08 21:22:24 -0800938
939 /**
940 * Determine the maximum depth of the state hierarchy
941 * so we can allocate the state stacks.
942 */
943 int maxDepth = 0;
944 for (StateInfo si : mStateInfo.values()) {
945 int depth = 0;
946 for (StateInfo i = si; i != null; depth++) {
947 i = i.parentStateInfo;
948 }
949 if (maxDepth < depth) {
950 maxDepth = depth;
951 }
952 }
Wink Saville58c73c32013-01-28 10:52:34 -0800953 if (mDbg) mSm.log("completeConstruction: maxDepth=" + maxDepth);
Wink Savillefc5b4802009-12-08 21:22:24 -0800954
955 mStateStack = new StateInfo[maxDepth];
956 mTempStateStack = new StateInfo[maxDepth];
957 setupInitialStateStack();
958
Wink Savillecea056f2012-03-26 15:03:16 -0700959 /** Sending SM_INIT_CMD message to invoke enter methods asynchronously */
960 sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj));
Wink Savillee7be6a82010-03-18 17:03:30 -0700961
Wink Saville58c73c32013-01-28 10:52:34 -0800962 if (mDbg) mSm.log("completeConstruction: X");
Wink Savillefc5b4802009-12-08 21:22:24 -0800963 }
964
965 /**
966 * Process the message. If the current state doesn't handle
967 * it, call the states parent and so on. If it is never handled then
968 * call the state machines unhandledMessage method.
Irfan Sheriffef8da9f2012-11-29 09:42:13 -0800969 * @return the state that processed the message
Wink Savillefc5b4802009-12-08 21:22:24 -0800970 */
Irfan Sheriffef8da9f2012-11-29 09:42:13 -0800971 private final State processMsg(Message msg) {
Wink Savillefc5b4802009-12-08 21:22:24 -0800972 StateInfo curStateInfo = mStateStack[mStateStackTopIndex];
973 if (mDbg) {
Wink Saville58c73c32013-01-28 10:52:34 -0800974 mSm.log("processMsg: " + curStateInfo.state.getName());
Wink Savillefc5b4802009-12-08 21:22:24 -0800975 }
Wink Savillefc5b4802009-12-08 21:22:24 -0800976
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700977 if (isQuit(msg)) {
978 transitionTo(mQuittingState);
979 } else {
980 while (!curStateInfo.state.processMessage(msg)) {
981 /**
982 * Not processed
983 */
984 curStateInfo = curStateInfo.parentStateInfo;
985 if (curStateInfo == null) {
986 /**
987 * No parents left so it's not handled
988 */
989 mSm.unhandledMessage(msg);
990 break;
991 }
992 if (mDbg) {
Wink Saville58c73c32013-01-28 10:52:34 -0800993 mSm.log("processMsg: " + curStateInfo.state.getName());
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700994 }
995 }
Wink Savilleff4fcdb2013-02-24 07:21:45 -0800996 }
Irfan Sheriffef8da9f2012-11-29 09:42:13 -0800997 return (curStateInfo != null) ? curStateInfo.state : null;
Wink Savillefc5b4802009-12-08 21:22:24 -0800998 }
999
1000 /**
1001 * Call the exit method for each state from the top of stack
1002 * up to the common ancestor state.
1003 */
1004 private final void invokeExitMethods(StateInfo commonStateInfo) {
Wink Savilleff4fcdb2013-02-24 07:21:45 -08001005 while ((mStateStackTopIndex >= 0)
1006 && (mStateStack[mStateStackTopIndex] != commonStateInfo)) {
Wink Saville64c42ca2011-04-18 14:55:10 -07001007 State curState = mStateStack[mStateStackTopIndex].state;
Wink Saville58c73c32013-01-28 10:52:34 -08001008 if (mDbg) mSm.log("invokeExitMethods: " + curState.getName());
Wink Savillefc5b4802009-12-08 21:22:24 -08001009 curState.exit();
1010 mStateStack[mStateStackTopIndex].active = false;
1011 mStateStackTopIndex -= 1;
1012 }
1013 }
1014
1015 /**
1016 * Invoke the enter method starting at the entering index to top of state stack
1017 */
1018 private final void invokeEnterMethods(int stateStackEnteringIndex) {
1019 for (int i = stateStackEnteringIndex; i <= mStateStackTopIndex; i++) {
Wink Saville58c73c32013-01-28 10:52:34 -08001020 if (mDbg) mSm.log("invokeEnterMethods: " + mStateStack[i].state.getName());
Wink Savillefc5b4802009-12-08 21:22:24 -08001021 mStateStack[i].state.enter();
1022 mStateStack[i].active = true;
1023 }
1024 }
1025
1026 /**
1027 * Move the deferred message to the front of the message queue.
1028 */
1029 private final void moveDeferredMessageAtFrontOfQueue() {
1030 /**
1031 * The oldest messages on the deferred list must be at
1032 * the front of the queue so start at the back, which
1033 * as the most resent message and end with the oldest
1034 * messages at the front of the queue.
1035 */
Wink Savilleff4fcdb2013-02-24 07:21:45 -08001036 for (int i = mDeferredMessages.size() - 1; i >= 0; i--) {
Wink Savillefc5b4802009-12-08 21:22:24 -08001037 Message curMsg = mDeferredMessages.get(i);
Wink Saville58c73c32013-01-28 10:52:34 -08001038 if (mDbg) mSm.log("moveDeferredMessageAtFrontOfQueue; what=" + curMsg.what);
Wink Savillefc5b4802009-12-08 21:22:24 -08001039 sendMessageAtFrontOfQueue(curMsg);
1040 }
1041 mDeferredMessages.clear();
1042 }
1043
1044 /**
1045 * Move the contents of the temporary stack to the state stack
1046 * reversing the order of the items on the temporary stack as
1047 * they are moved.
1048 *
Wink Saville64c42ca2011-04-18 14:55:10 -07001049 * @return index into mStateStack where entering needs to start
Wink Savillefc5b4802009-12-08 21:22:24 -08001050 */
1051 private final int moveTempStateStackToStateStack() {
1052 int startingIndex = mStateStackTopIndex + 1;
1053 int i = mTempStateStackCount - 1;
1054 int j = startingIndex;
1055 while (i >= 0) {
Wink Saville58c73c32013-01-28 10:52:34 -08001056 if (mDbg) mSm.log("moveTempStackToStateStack: i=" + i + ",j=" + j);
Wink Savillefc5b4802009-12-08 21:22:24 -08001057 mStateStack[j] = mTempStateStack[i];
1058 j += 1;
1059 i -= 1;
1060 }
1061
1062 mStateStackTopIndex = j - 1;
1063 if (mDbg) {
Wink Savilleff4fcdb2013-02-24 07:21:45 -08001064 mSm.log("moveTempStackToStateStack: X mStateStackTop=" + mStateStackTopIndex
1065 + ",startingIndex=" + startingIndex + ",Top="
1066 + mStateStack[mStateStackTopIndex].state.getName());
Wink Savillefc5b4802009-12-08 21:22:24 -08001067 }
1068 return startingIndex;
1069 }
1070
1071 /**
1072 * Setup the mTempStateStack with the states we are going to enter.
1073 *
1074 * This is found by searching up the destState's ancestors for a
1075 * state that is already active i.e. StateInfo.active == true.
1076 * The destStae and all of its inactive parents will be on the
1077 * TempStateStack as the list of states to enter.
1078 *
1079 * @return StateInfo of the common ancestor for the destState and
1080 * current state or null if there is no common parent.
1081 */
Wink Saville64c42ca2011-04-18 14:55:10 -07001082 private final StateInfo setupTempStateStackWithStatesToEnter(State destState) {
Wink Savillefc5b4802009-12-08 21:22:24 -08001083 /**
1084 * Search up the parent list of the destination state for an active
1085 * state. Use a do while() loop as the destState must always be entered
1086 * even if it is active. This can happen if we are exiting/entering
1087 * the current state.
1088 */
1089 mTempStateStackCount = 0;
1090 StateInfo curStateInfo = mStateInfo.get(destState);
1091 do {
1092 mTempStateStack[mTempStateStackCount++] = curStateInfo;
1093 curStateInfo = curStateInfo.parentStateInfo;
1094 } while ((curStateInfo != null) && !curStateInfo.active);
1095
1096 if (mDbg) {
Wink Saville58c73c32013-01-28 10:52:34 -08001097 mSm.log("setupTempStateStackWithStatesToEnter: X mTempStateStackCount="
Wink Savilleff4fcdb2013-02-24 07:21:45 -08001098 + mTempStateStackCount + ",curStateInfo: " + curStateInfo);
Wink Savillefc5b4802009-12-08 21:22:24 -08001099 }
1100 return curStateInfo;
1101 }
1102
1103 /**
1104 * Initialize StateStack to mInitialState.
1105 */
1106 private final void setupInitialStateStack() {
1107 if (mDbg) {
Wink Savilleff4fcdb2013-02-24 07:21:45 -08001108 mSm.log("setupInitialStateStack: E mInitialState=" + mInitialState.getName());
Wink Savillefc5b4802009-12-08 21:22:24 -08001109 }
1110
1111 StateInfo curStateInfo = mStateInfo.get(mInitialState);
1112 for (mTempStateStackCount = 0; curStateInfo != null; mTempStateStackCount++) {
1113 mTempStateStack[mTempStateStackCount] = curStateInfo;
1114 curStateInfo = curStateInfo.parentStateInfo;
1115 }
1116
1117 // Empty the StateStack
1118 mStateStackTopIndex = -1;
1119
1120 moveTempStateStackToStateStack();
1121 }
1122
1123 /**
Wink Savillea4f3bec2010-05-19 09:11:38 -07001124 * @return current message
1125 */
1126 private final Message getCurrentMessage() {
1127 return mMsg;
1128 }
1129
1130 /**
Wink Savillefc5b4802009-12-08 21:22:24 -08001131 * @return current state
1132 */
Wink Saville64c42ca2011-04-18 14:55:10 -07001133 private final IState getCurrentState() {
Wink Savillefc5b4802009-12-08 21:22:24 -08001134 return mStateStack[mStateStackTopIndex].state;
1135 }
1136
1137 /**
1138 * Add a new state to the state machine. Bottom up addition
1139 * of states is allowed but the same state may only exist
1140 * in one hierarchy.
1141 *
1142 * @param state the state to add
1143 * @param parent the parent of state
1144 * @return stateInfo for this state
1145 */
Wink Saville64c42ca2011-04-18 14:55:10 -07001146 private final StateInfo addState(State state, State parent) {
Wink Savillefc5b4802009-12-08 21:22:24 -08001147 if (mDbg) {
Wink Savilleff4fcdb2013-02-24 07:21:45 -08001148 mSm.log("addStateInternal: E state=" + state.getName() + ",parent="
1149 + ((parent == null) ? "" : parent.getName()));
Wink Savillefc5b4802009-12-08 21:22:24 -08001150 }
1151 StateInfo parentStateInfo = null;
1152 if (parent != null) {
1153 parentStateInfo = mStateInfo.get(parent);
1154 if (parentStateInfo == null) {
1155 // Recursively add our parent as it's not been added yet.
1156 parentStateInfo = addState(parent, null);
1157 }
1158 }
1159 StateInfo stateInfo = mStateInfo.get(state);
1160 if (stateInfo == null) {
1161 stateInfo = new StateInfo();
1162 mStateInfo.put(state, stateInfo);
1163 }
1164
1165 // Validate that we aren't adding the same state in two different hierarchies.
Wink Savilleff4fcdb2013-02-24 07:21:45 -08001166 if ((stateInfo.parentStateInfo != null)
1167 && (stateInfo.parentStateInfo != parentStateInfo)) {
1168 throw new RuntimeException("state already added");
Wink Savillefc5b4802009-12-08 21:22:24 -08001169 }
1170 stateInfo.state = state;
1171 stateInfo.parentStateInfo = parentStateInfo;
1172 stateInfo.active = false;
Wink Saville58c73c32013-01-28 10:52:34 -08001173 if (mDbg) mSm.log("addStateInternal: X stateInfo: " + stateInfo);
Wink Savillefc5b4802009-12-08 21:22:24 -08001174 return stateInfo;
1175 }
1176
1177 /**
1178 * Constructor
1179 *
1180 * @param looper for dispatching messages
Wink Saville64c42ca2011-04-18 14:55:10 -07001181 * @param sm the hierarchical state machine
Wink Savillefc5b4802009-12-08 21:22:24 -08001182 */
Wink Saville64c42ca2011-04-18 14:55:10 -07001183 private SmHandler(Looper looper, StateMachine sm) {
Wink Savillefc5b4802009-12-08 21:22:24 -08001184 super(looper);
Wink Saville64c42ca2011-04-18 14:55:10 -07001185 mSm = sm;
Wink Savillefc5b4802009-12-08 21:22:24 -08001186
1187 addState(mHaltingState, null);
Wink Saville1b8b98b2010-03-11 11:49:54 -08001188 addState(mQuittingState, null);
Wink Savillefc5b4802009-12-08 21:22:24 -08001189 }
1190
Wink Saville64c42ca2011-04-18 14:55:10 -07001191 /** @see StateMachine#setInitialState(State) */
1192 private final void setInitialState(State initialState) {
Wink Saville58c73c32013-01-28 10:52:34 -08001193 if (mDbg) mSm.log("setInitialState: initialState=" + initialState.getName());
Wink Savillefc5b4802009-12-08 21:22:24 -08001194 mInitialState = initialState;
1195 }
1196
Wink Saville64c42ca2011-04-18 14:55:10 -07001197 /** @see StateMachine#transitionTo(IState) */
1198 private final void transitionTo(IState destState) {
1199 mDestState = (State) destState;
Wink Saville58c73c32013-01-28 10:52:34 -08001200 if (mDbg) mSm.log("transitionTo: destState=" + mDestState.getName());
Wink Savillefc5b4802009-12-08 21:22:24 -08001201 }
1202
Wink Saville64c42ca2011-04-18 14:55:10 -07001203 /** @see StateMachine#deferMessage(Message) */
Wink Savillefc5b4802009-12-08 21:22:24 -08001204 private final void deferMessage(Message msg) {
Wink Saville58c73c32013-01-28 10:52:34 -08001205 if (mDbg) mSm.log("deferMessage: msg=" + msg.what);
Wink Savillefc5b4802009-12-08 21:22:24 -08001206
1207 /* Copy the "msg" to "newMsg" as "msg" will be recycled */
1208 Message newMsg = obtainMessage();
1209 newMsg.copyFrom(msg);
1210
1211 mDeferredMessages.add(newMsg);
1212 }
1213
Wink Savillebbf30dfd2012-05-29 12:40:46 -07001214 /** @see StateMachine#quit() */
Wink Saville1b8b98b2010-03-11 11:49:54 -08001215 private final void quit() {
Wink Saville58c73c32013-01-28 10:52:34 -08001216 if (mDbg) mSm.log("quit:");
Wink Savillecea056f2012-03-26 15:03:16 -07001217 sendMessage(obtainMessage(SM_QUIT_CMD, mSmHandlerObj));
Wink Saville1b8b98b2010-03-11 11:49:54 -08001218 }
1219
Wink Savillebbf30dfd2012-05-29 12:40:46 -07001220 /** @see StateMachine#quitNow() */
1221 private final void quitNow() {
Wink Saville58c73c32013-01-28 10:52:34 -08001222 if (mDbg) mSm.log("quitNow:");
Wink Savillebbf30dfd2012-05-29 12:40:46 -07001223 sendMessageAtFrontOfQueue(obtainMessage(SM_QUIT_CMD, mSmHandlerObj));
1224 }
1225
Wink Saville58c73c32013-01-28 10:52:34 -08001226 /** Validate that the message was sent by quit or quitNow. */
Wink Saville1b8b98b2010-03-11 11:49:54 -08001227 private final boolean isQuit(Message msg) {
Wink Savillecea056f2012-03-26 15:03:16 -07001228 return (msg.what == SM_QUIT_CMD) && (msg.obj == mSmHandlerObj);
Wink Saville1b8b98b2010-03-11 11:49:54 -08001229 }
1230
Wink Saville64c42ca2011-04-18 14:55:10 -07001231 /** @see StateMachine#isDbg() */
Wink Savillefc5b4802009-12-08 21:22:24 -08001232 private final boolean isDbg() {
1233 return mDbg;
1234 }
1235
Wink Saville64c42ca2011-04-18 14:55:10 -07001236 /** @see StateMachine#setDbg(boolean) */
Wink Savillefc5b4802009-12-08 21:22:24 -08001237 private final void setDbg(boolean dbg) {
1238 mDbg = dbg;
1239 }
1240
Wink Savillefc5b4802009-12-08 21:22:24 -08001241 }
1242
Wink Saville64c42ca2011-04-18 14:55:10 -07001243 private SmHandler mSmHandler;
1244 private HandlerThread mSmThread;
Wink Savillefc5b4802009-12-08 21:22:24 -08001245
1246 /**
1247 * Initialize.
1248 *
1249 * @param looper for this state machine
1250 * @param name of the state machine
1251 */
Wink Savillef0f566e2010-03-11 14:03:50 -08001252 private void initStateMachine(String name, Looper looper) {
Wink Savillefc5b4802009-12-08 21:22:24 -08001253 mName = name;
Wink Saville64c42ca2011-04-18 14:55:10 -07001254 mSmHandler = new SmHandler(looper, this);
Wink Savillefc5b4802009-12-08 21:22:24 -08001255 }
1256
1257 /**
Wink Saville64c42ca2011-04-18 14:55:10 -07001258 * Constructor creates a StateMachine with its own thread.
Wink Savillefc5b4802009-12-08 21:22:24 -08001259 *
1260 * @param name of the state machine
1261 */
Wink Saville64c42ca2011-04-18 14:55:10 -07001262 protected StateMachine(String name) {
1263 mSmThread = new HandlerThread(name);
1264 mSmThread.start();
1265 Looper looper = mSmThread.getLooper();
Wink Savillefc5b4802009-12-08 21:22:24 -08001266
Wink Savillef0f566e2010-03-11 14:03:50 -08001267 initStateMachine(name, looper);
Wink Savillefc5b4802009-12-08 21:22:24 -08001268 }
1269
1270 /**
Ken Wakasaf76a50c2012-03-09 19:56:35 +09001271 * Constructor creates a StateMachine using the looper.
Wink Savillefc5b4802009-12-08 21:22:24 -08001272 *
1273 * @param name of the state machine
1274 */
Wink Saville64c42ca2011-04-18 14:55:10 -07001275 protected StateMachine(String name, Looper looper) {
Wink Savillef0f566e2010-03-11 14:03:50 -08001276 initStateMachine(name, looper);
Wink Savillefc5b4802009-12-08 21:22:24 -08001277 }
1278
1279 /**
Wink Saville24d248a2013-03-17 14:26:21 -07001280 * Constructor creates a StateMachine using the handler.
1281 *
1282 * @param name of the state machine
1283 */
1284 protected StateMachine(String name, Handler handler) {
1285 initStateMachine(name, handler.getLooper());
1286 }
1287
1288 /**
Brad Ebinger0c714042015-12-02 17:41:45 -08001289 * Notifies subclass that the StateMachine handler is about to process the Message msg
1290 * @param msg The message that is being handled
1291 */
1292 protected void onPreHandleMessage(Message msg) {
1293 }
1294
1295 /**
1296 * Notifies subclass that the StateMachine handler has finished processing the Message msg and
1297 * has possibly transitioned to a new state.
1298 * @param msg The message that is being handled
1299 */
1300 protected void onPostHandleMessage(Message msg) {
1301 }
1302
1303 /**
Wink Savillefc5b4802009-12-08 21:22:24 -08001304 * Add a new state to the state machine
1305 * @param state the state to add
1306 * @param parent the parent of state
1307 */
Wink Saville64c42ca2011-04-18 14:55:10 -07001308 protected final void addState(State state, State parent) {
1309 mSmHandler.addState(state, parent);
Wink Savillefc5b4802009-12-08 21:22:24 -08001310 }
Wink Savillea4f3bec2010-05-19 09:11:38 -07001311
1312 /**
Wink Savillefc5b4802009-12-08 21:22:24 -08001313 * Add a new state to the state machine, parent will be null
1314 * @param state to add
1315 */
Wink Saville64c42ca2011-04-18 14:55:10 -07001316 protected final void addState(State state) {
1317 mSmHandler.addState(state, null);
Wink Savillefc5b4802009-12-08 21:22:24 -08001318 }
1319
1320 /**
1321 * Set the initial state. This must be invoked before
1322 * and messages are sent to the state machine.
1323 *
1324 * @param initialState is the state which will receive the first message.
1325 */
Wink Saville64c42ca2011-04-18 14:55:10 -07001326 protected final void setInitialState(State initialState) {
1327 mSmHandler.setInitialState(initialState);
Wink Savillefc5b4802009-12-08 21:22:24 -08001328 }
1329
1330 /**
Wink Savilleefcc3d32013-01-30 11:21:22 -08001331 * @return current message
1332 */
1333 protected final Message getCurrentMessage() {
1334 // mSmHandler can be null if the state machine has quit.
1335 SmHandler smh = mSmHandler;
1336 if (smh == null) return null;
1337 return smh.getCurrentMessage();
1338 }
1339
1340 /**
1341 * @return current state
1342 */
1343 protected final IState getCurrentState() {
1344 // mSmHandler can be null if the state machine has quit.
1345 SmHandler smh = mSmHandler;
1346 if (smh == null) return null;
1347 return smh.getCurrentState();
1348 }
1349
1350 /**
Wink Savillefc5b4802009-12-08 21:22:24 -08001351 * transition to destination state. Upon returning
1352 * from processMessage the current state's exit will
1353 * be executed and upon the next message arriving
1354 * destState.enter will be invoked.
1355 *
Isaac Levyd2fe04b2011-07-22 08:48:26 -07001356 * this function can also be called inside the enter function of the
1357 * previous transition target, but the behavior is undefined when it is
1358 * called mid-way through a previous transition (for example, calling this
1359 * in the enter() routine of a intermediate node when the current transition
1360 * target is one of the nodes descendants).
1361 *
Wink Savillefc5b4802009-12-08 21:22:24 -08001362 * @param destState will be the state that receives the next message.
1363 */
Wink Saville64c42ca2011-04-18 14:55:10 -07001364 protected final void transitionTo(IState destState) {
1365 mSmHandler.transitionTo(destState);
Wink Savillefc5b4802009-12-08 21:22:24 -08001366 }
1367
1368 /**
1369 * transition to halt state. Upon returning
1370 * from processMessage we will exit all current
Wink Savillebbf30dfd2012-05-29 12:40:46 -07001371 * states, execute the onHalting() method and then
1372 * for all subsequent messages haltedProcessMessage
Wink Savillefc5b4802009-12-08 21:22:24 -08001373 * will be called.
1374 */
1375 protected final void transitionToHaltingState() {
Wink Saville64c42ca2011-04-18 14:55:10 -07001376 mSmHandler.transitionTo(mSmHandler.mHaltingState);
Wink Savillefc5b4802009-12-08 21:22:24 -08001377 }
1378
1379 /**
1380 * Defer this message until next state transition.
1381 * Upon transitioning all deferred messages will be
1382 * placed on the queue and reprocessed in the original
1383 * order. (i.e. The next state the oldest messages will
1384 * be processed first)
1385 *
1386 * @param msg is deferred until the next transition.
1387 */
1388 protected final void deferMessage(Message msg) {
Wink Saville64c42ca2011-04-18 14:55:10 -07001389 mSmHandler.deferMessage(msg);
Wink Savillefc5b4802009-12-08 21:22:24 -08001390 }
1391
Wink Savillefc5b4802009-12-08 21:22:24 -08001392 /**
1393 * Called when message wasn't handled
1394 *
1395 * @param msg that couldn't be handled.
1396 */
1397 protected void unhandledMessage(Message msg) {
Wink Saville58c73c32013-01-28 10:52:34 -08001398 if (mSmHandler.mDbg) loge(" - unhandledMessage: msg.what=" + msg.what);
Wink Savillefc5b4802009-12-08 21:22:24 -08001399 }
1400
1401 /**
1402 * Called for any message that is received after
1403 * transitionToHalting is called.
1404 */
1405 protected void haltedProcessMessage(Message msg) {
1406 }
1407
1408 /**
Wink Savilled3059482011-04-11 11:51:28 -07001409 * This will be called once after handling a message that called
1410 * transitionToHalting. All subsequent messages will invoke
Wink Saville64c42ca2011-04-18 14:55:10 -07001411 * {@link StateMachine#haltedProcessMessage(Message)}
Wink Savillefc5b4802009-12-08 21:22:24 -08001412 */
Wink Savillebbf30dfd2012-05-29 12:40:46 -07001413 protected void onHalting() {
Wink Savillefc5b4802009-12-08 21:22:24 -08001414 }
1415
1416 /**
Wink Savilled3059482011-04-11 11:51:28 -07001417 * This will be called once after a quit message that was NOT handled by
Wink Saville64c42ca2011-04-18 14:55:10 -07001418 * the derived StateMachine. The StateMachine will stop and any subsequent messages will be
1419 * ignored. In addition, if this StateMachine created the thread, the thread will
Wink Savilled3059482011-04-11 11:51:28 -07001420 * be stopped after this method returns.
Wink Saville1b8b98b2010-03-11 11:49:54 -08001421 */
Wink Savillebbf30dfd2012-05-29 12:40:46 -07001422 protected void onQuitting() {
Wink Saville1b8b98b2010-03-11 11:49:54 -08001423 }
1424
1425 /**
Wink Savillefc5b4802009-12-08 21:22:24 -08001426 * @return the name
1427 */
1428 public final String getName() {
1429 return mName;
1430 }
1431
1432 /**
Wink Savillebbf30dfd2012-05-29 12:40:46 -07001433 * Set number of log records to maintain and clears all current records.
Wink Savillefc5b4802009-12-08 21:22:24 -08001434 *
1435 * @param maxSize number of messages to maintain at anyone time.
1436 */
Wink Savillebbf30dfd2012-05-29 12:40:46 -07001437 public final void setLogRecSize(int maxSize) {
1438 mSmHandler.mLogRecords.setSize(maxSize);
Wink Savillefc5b4802009-12-08 21:22:24 -08001439 }
1440
1441 /**
Irfan Sheriffef8da9f2012-11-29 09:42:13 -08001442 * Set to log only messages that cause a state transition
1443 *
1444 * @param enable {@code true} to enable, {@code false} to disable
1445 */
1446 public final void setLogOnlyTransitions(boolean enable) {
1447 mSmHandler.mLogRecords.setLogOnlyTransitions(enable);
1448 }
1449
1450 /**
Wink Savillebbf30dfd2012-05-29 12:40:46 -07001451 * @return number of log records
Wink Savillefc5b4802009-12-08 21:22:24 -08001452 */
Wink Savillebbf30dfd2012-05-29 12:40:46 -07001453 public final int getLogRecSize() {
Wink Savilleefcc3d32013-01-30 11:21:22 -08001454 // mSmHandler can be null if the state machine has quit.
1455 SmHandler smh = mSmHandler;
1456 if (smh == null) return 0;
1457 return smh.mLogRecords.size();
Wink Savillefc5b4802009-12-08 21:22:24 -08001458 }
1459
1460 /**
Wink Savillebbf30dfd2012-05-29 12:40:46 -07001461 * @return the total number of records processed
Wink Savillefc5b4802009-12-08 21:22:24 -08001462 */
Wink Savillebbf30dfd2012-05-29 12:40:46 -07001463 public final int getLogRecCount() {
Wink Savilleefcc3d32013-01-30 11:21:22 -08001464 // mSmHandler can be null if the state machine has quit.
1465 SmHandler smh = mSmHandler;
1466 if (smh == null) return 0;
1467 return smh.mLogRecords.count();
Wink Savillefc5b4802009-12-08 21:22:24 -08001468 }
1469
1470 /**
Wink Savilleefcc3d32013-01-30 11:21:22 -08001471 * @return a log record, or null if index is out of range
Wink Savillefc5b4802009-12-08 21:22:24 -08001472 */
Wink Savillebbf30dfd2012-05-29 12:40:46 -07001473 public final LogRec getLogRec(int index) {
Wink Savilleefcc3d32013-01-30 11:21:22 -08001474 // mSmHandler can be null if the state machine has quit.
1475 SmHandler smh = mSmHandler;
1476 if (smh == null) return null;
1477 return smh.mLogRecords.get(index);
1478 }
1479
1480 /**
1481 * @return a copy of LogRecs as a collection
1482 */
1483 public final Collection<LogRec> copyLogRecs() {
1484 Vector<LogRec> vlr = new Vector<LogRec>();
1485 SmHandler smh = mSmHandler;
1486 if (smh != null) {
1487 for (LogRec lr : smh.mLogRecords.mLogRecVector) {
1488 vlr.add(lr);
1489 }
1490 }
1491 return vlr;
Wink Savillebbf30dfd2012-05-29 12:40:46 -07001492 }
1493
1494 /**
1495 * Add the string to LogRecords.
1496 *
1497 * @param string
1498 */
1499 protected void addLogRec(String string) {
Wink Savilleefcc3d32013-01-30 11:21:22 -08001500 // mSmHandler can be null if the state machine has quit.
1501 SmHandler smh = mSmHandler;
1502 if (smh == null) return;
1503 smh.mLogRecords.add(this, smh.getCurrentMessage(), string, smh.getCurrentState(),
1504 smh.mStateStack[smh.mStateStackTopIndex].state, smh.mDestState);
Wink Savillebbf30dfd2012-05-29 12:40:46 -07001505 }
1506
1507 /**
1508 * @return true if msg should be saved in the log, default is true.
1509 */
1510 protected boolean recordLogRec(Message msg) {
1511 return true;
1512 }
1513
1514 /**
1515 * Return a string to be logged by LogRec, default
1516 * is an empty string. Override if additional information is desired.
1517 *
1518 * @param msg that was processed
1519 * @return information to be logged as a String
1520 */
1521 protected String getLogRecString(Message msg) {
1522 return "";
1523 }
1524
1525 /**
1526 * @return the string for msg.what
1527 */
1528 protected String getWhatToString(int what) {
1529 return null;
Wink Savillefc5b4802009-12-08 21:22:24 -08001530 }
1531
1532 /**
Wink Savilleefcc3d32013-01-30 11:21:22 -08001533 * @return Handler, maybe null if state machine has quit.
Wink Savillefc5b4802009-12-08 21:22:24 -08001534 */
1535 public final Handler getHandler() {
Wink Saville64c42ca2011-04-18 14:55:10 -07001536 return mSmHandler;
Wink Savillefc5b4802009-12-08 21:22:24 -08001537 }
1538
1539 /**
Wink Savilleefcc3d32013-01-30 11:21:22 -08001540 * Get a message and set Message.target state machine handler.
Wink Savillefc5b4802009-12-08 21:22:24 -08001541 *
Wink Savilleefcc3d32013-01-30 11:21:22 -08001542 * Note: The handler can be null if the state machine has quit,
1543 * which means target will be null and may cause a AndroidRuntimeException
1544 * in MessageQueue#enqueMessage if sent directly or if sent using
1545 * StateMachine#sendMessage the message will just be ignored.
1546 *
1547 * @return A Message object from the global pool
Wink Savillefc5b4802009-12-08 21:22:24 -08001548 */
Wink Savilleff4fcdb2013-02-24 07:21:45 -08001549 public final Message obtainMessage() {
Wink Saville64c42ca2011-04-18 14:55:10 -07001550 return Message.obtain(mSmHandler);
Wink Savillefc5b4802009-12-08 21:22:24 -08001551 }
1552
1553 /**
Wink Savilleefcc3d32013-01-30 11:21:22 -08001554 * Get a message and set Message.target state machine handler, what.
1555 *
1556 * Note: The handler can be null if the state machine has quit,
1557 * which means target will be null and may cause a AndroidRuntimeException
1558 * in MessageQueue#enqueMessage if sent directly or if sent using
1559 * StateMachine#sendMessage the message will just be ignored.
Wink Savillefc5b4802009-12-08 21:22:24 -08001560 *
1561 * @param what is the assigned to Message.what.
Wink Savilleefcc3d32013-01-30 11:21:22 -08001562 * @return A Message object from the global pool
Wink Savillefc5b4802009-12-08 21:22:24 -08001563 */
1564 public final Message obtainMessage(int what) {
Wink Saville64c42ca2011-04-18 14:55:10 -07001565 return Message.obtain(mSmHandler, what);
Wink Savillefc5b4802009-12-08 21:22:24 -08001566 }
1567
1568 /**
Wink Savilleefcc3d32013-01-30 11:21:22 -08001569 * Get a message and set Message.target state machine handler,
Wink Savillefc5b4802009-12-08 21:22:24 -08001570 * what and obj.
1571 *
Wink Savilleefcc3d32013-01-30 11:21:22 -08001572 * Note: The handler can be null if the state machine has quit,
1573 * which means target will be null and may cause a AndroidRuntimeException
1574 * in MessageQueue#enqueMessage if sent directly or if sent using
1575 * StateMachine#sendMessage the message will just be ignored.
1576 *
Wink Savillefc5b4802009-12-08 21:22:24 -08001577 * @param what is the assigned to Message.what.
1578 * @param obj is assigned to Message.obj.
Wink Savilleefcc3d32013-01-30 11:21:22 -08001579 * @return A Message object from the global pool
Wink Savillefc5b4802009-12-08 21:22:24 -08001580 */
Wink Savilleff4fcdb2013-02-24 07:21:45 -08001581 public final Message obtainMessage(int what, Object obj) {
Wink Saville64c42ca2011-04-18 14:55:10 -07001582 return Message.obtain(mSmHandler, what, obj);
Wink Savillefc5b4802009-12-08 21:22:24 -08001583 }
1584
Wink Saville91fbd562010-03-17 17:12:43 -07001585 /**
Wink Savilleefcc3d32013-01-30 11:21:22 -08001586 * Get a message and set Message.target state machine handler,
Irfan Sheriff96e6bdb2010-06-25 09:44:27 -07001587 * what, arg1 and arg2
1588 *
Wink Savilleefcc3d32013-01-30 11:21:22 -08001589 * Note: The handler can be null if the state machine has quit,
1590 * which means target will be null and may cause a AndroidRuntimeException
1591 * in MessageQueue#enqueMessage if sent directly or if sent using
1592 * StateMachine#sendMessage the message will just be ignored.
1593 *
Irfan Sheriff96e6bdb2010-06-25 09:44:27 -07001594 * @param what is assigned to Message.what
1595 * @param arg1 is assigned to Message.arg1
Wink Saville8b0db522013-03-14 13:23:19 -07001596 * @return A Message object from the global pool
1597 */
1598 public final Message obtainMessage(int what, int arg1) {
1599 // use this obtain so we don't match the obtain(h, what, Object) method
1600 return Message.obtain(mSmHandler, what, arg1, 0);
1601 }
1602
1603 /**
1604 * Get a message and set Message.target state machine handler,
1605 * what, arg1 and arg2
1606 *
1607 * Note: The handler can be null if the state machine has quit,
1608 * which means target will be null and may cause a AndroidRuntimeException
1609 * in MessageQueue#enqueMessage if sent directly or if sent using
1610 * StateMachine#sendMessage the message will just be ignored.
1611 *
1612 * @param what is assigned to Message.what
1613 * @param arg1 is assigned to Message.arg1
Irfan Sheriff96e6bdb2010-06-25 09:44:27 -07001614 * @param arg2 is assigned to Message.arg2
Wink Savilleefcc3d32013-01-30 11:21:22 -08001615 * @return A Message object from the global pool
Irfan Sheriff96e6bdb2010-06-25 09:44:27 -07001616 */
Wink Savilleff4fcdb2013-02-24 07:21:45 -08001617 public final Message obtainMessage(int what, int arg1, int arg2) {
Wink Saville64c42ca2011-04-18 14:55:10 -07001618 return Message.obtain(mSmHandler, what, arg1, arg2);
Irfan Sheriff96e6bdb2010-06-25 09:44:27 -07001619 }
1620
1621 /**
Wink Savilleefcc3d32013-01-30 11:21:22 -08001622 * Get a message and set Message.target state machine handler,
Irfan Sheriff96e6bdb2010-06-25 09:44:27 -07001623 * what, arg1, arg2 and obj
1624 *
Wink Savilleefcc3d32013-01-30 11:21:22 -08001625 * Note: The handler can be null if the state machine has quit,
1626 * which means target will be null and may cause a AndroidRuntimeException
1627 * in MessageQueue#enqueMessage if sent directly or if sent using
1628 * StateMachine#sendMessage the message will just be ignored.
1629 *
Irfan Sheriff96e6bdb2010-06-25 09:44:27 -07001630 * @param what is assigned to Message.what
1631 * @param arg1 is assigned to Message.arg1
1632 * @param arg2 is assigned to Message.arg2
1633 * @param obj is assigned to Message.obj
Wink Savilleefcc3d32013-01-30 11:21:22 -08001634 * @return A Message object from the global pool
Irfan Sheriff96e6bdb2010-06-25 09:44:27 -07001635 */
Wink Savilleff4fcdb2013-02-24 07:21:45 -08001636 public final Message obtainMessage(int what, int arg1, int arg2, Object obj) {
Wink Saville64c42ca2011-04-18 14:55:10 -07001637 return Message.obtain(mSmHandler, what, arg1, arg2, obj);
Irfan Sheriff96e6bdb2010-06-25 09:44:27 -07001638 }
1639
1640 /**
Wink Saville91fbd562010-03-17 17:12:43 -07001641 * Enqueue a message to this state machine.
Wink Savilleefcc3d32013-01-30 11:21:22 -08001642 *
1643 * Message is ignored if state machine has quit.
Wink Saville91fbd562010-03-17 17:12:43 -07001644 */
Rebecca Silberstein0dbeb9e2016-05-13 13:57:34 -07001645 public void sendMessage(int what) {
Jaikumar Ganesha544d462011-12-07 14:52:01 -08001646 // mSmHandler can be null if the state machine has quit.
Wink Savilleefcc3d32013-01-30 11:21:22 -08001647 SmHandler smh = mSmHandler;
1648 if (smh == null) return;
Jaikumar Ganesha544d462011-12-07 14:52:01 -08001649
Wink Savilleefcc3d32013-01-30 11:21:22 -08001650 smh.sendMessage(obtainMessage(what));
Wink Saville91fbd562010-03-17 17:12:43 -07001651 }
1652
1653 /**
1654 * Enqueue a message to this state machine.
Wink Savilleefcc3d32013-01-30 11:21:22 -08001655 *
1656 * Message is ignored if state machine has quit.
Wink Saville91fbd562010-03-17 17:12:43 -07001657 */
Rebecca Silberstein0dbeb9e2016-05-13 13:57:34 -07001658 public void sendMessage(int what, Object obj) {
Jaikumar Ganesha544d462011-12-07 14:52:01 -08001659 // mSmHandler can be null if the state machine has quit.
Wink Savilleefcc3d32013-01-30 11:21:22 -08001660 SmHandler smh = mSmHandler;
1661 if (smh == null) return;
Jaikumar Ganesha544d462011-12-07 14:52:01 -08001662
Wink Savilleff4fcdb2013-02-24 07:21:45 -08001663 smh.sendMessage(obtainMessage(what, obj));
Wink Saville91fbd562010-03-17 17:12:43 -07001664 }
1665
Wink Savillefc5b4802009-12-08 21:22:24 -08001666 /**
1667 * Enqueue a message to this state machine.
Wink Savilleefcc3d32013-01-30 11:21:22 -08001668 *
1669 * Message is ignored if state machine has quit.
Wink Savillefc5b4802009-12-08 21:22:24 -08001670 */
Rebecca Silberstein0dbeb9e2016-05-13 13:57:34 -07001671 public void sendMessage(int what, int arg1) {
Wink Saville8b0db522013-03-14 13:23:19 -07001672 // mSmHandler can be null if the state machine has quit.
1673 SmHandler smh = mSmHandler;
1674 if (smh == null) return;
1675
1676 smh.sendMessage(obtainMessage(what, arg1));
1677 }
1678
1679 /**
1680 * Enqueue a message to this state machine.
1681 *
1682 * Message is ignored if state machine has quit.
1683 */
Rebecca Silberstein0dbeb9e2016-05-13 13:57:34 -07001684 public void sendMessage(int what, int arg1, int arg2) {
Wink Saville8b0db522013-03-14 13:23:19 -07001685 // mSmHandler can be null if the state machine has quit.
1686 SmHandler smh = mSmHandler;
1687 if (smh == null) return;
1688
1689 smh.sendMessage(obtainMessage(what, arg1, arg2));
1690 }
1691
1692 /**
1693 * Enqueue a message to this state machine.
1694 *
1695 * Message is ignored if state machine has quit.
1696 */
Rebecca Silberstein0dbeb9e2016-05-13 13:57:34 -07001697 public void sendMessage(int what, int arg1, int arg2, Object obj) {
Wink Saville4753cd22013-03-06 13:41:23 -08001698 // mSmHandler can be null if the state machine has quit.
1699 SmHandler smh = mSmHandler;
1700 if (smh == null) return;
1701
1702 smh.sendMessage(obtainMessage(what, arg1, arg2, obj));
1703 }
1704
1705 /**
1706 * Enqueue a message to this state machine.
1707 *
1708 * Message is ignored if state machine has quit.
1709 */
Rebecca Silberstein0dbeb9e2016-05-13 13:57:34 -07001710 public void sendMessage(Message msg) {
Jaikumar Ganesha544d462011-12-07 14:52:01 -08001711 // mSmHandler can be null if the state machine has quit.
Wink Savilleefcc3d32013-01-30 11:21:22 -08001712 SmHandler smh = mSmHandler;
1713 if (smh == null) return;
Jaikumar Ganesha544d462011-12-07 14:52:01 -08001714
Wink Savilleefcc3d32013-01-30 11:21:22 -08001715 smh.sendMessage(msg);
Wink Savillefc5b4802009-12-08 21:22:24 -08001716 }
1717
1718 /**
1719 * Enqueue a message to this state machine after a delay.
Wink Savilleefcc3d32013-01-30 11:21:22 -08001720 *
1721 * Message is ignored if state machine has quit.
Wink Savillefc5b4802009-12-08 21:22:24 -08001722 */
Rebecca Silberstein0dbeb9e2016-05-13 13:57:34 -07001723 public void sendMessageDelayed(int what, long delayMillis) {
Jaikumar Ganesha544d462011-12-07 14:52:01 -08001724 // mSmHandler can be null if the state machine has quit.
Wink Savilleefcc3d32013-01-30 11:21:22 -08001725 SmHandler smh = mSmHandler;
1726 if (smh == null) return;
Jaikumar Ganesha544d462011-12-07 14:52:01 -08001727
Wink Savilleefcc3d32013-01-30 11:21:22 -08001728 smh.sendMessageDelayed(obtainMessage(what), delayMillis);
Wink Saville91fbd562010-03-17 17:12:43 -07001729 }
1730
1731 /**
1732 * Enqueue a message to this state machine after a delay.
Wink Savilleefcc3d32013-01-30 11:21:22 -08001733 *
1734 * Message is ignored if state machine has quit.
Wink Saville91fbd562010-03-17 17:12:43 -07001735 */
Rebecca Silberstein0dbeb9e2016-05-13 13:57:34 -07001736 public void sendMessageDelayed(int what, Object obj, long delayMillis) {
Jaikumar Ganesha544d462011-12-07 14:52:01 -08001737 // mSmHandler can be null if the state machine has quit.
Wink Savilleefcc3d32013-01-30 11:21:22 -08001738 SmHandler smh = mSmHandler;
1739 if (smh == null) return;
Jaikumar Ganesha544d462011-12-07 14:52:01 -08001740
Wink Savilleefcc3d32013-01-30 11:21:22 -08001741 smh.sendMessageDelayed(obtainMessage(what, obj), delayMillis);
Wink Saville91fbd562010-03-17 17:12:43 -07001742 }
1743
1744 /**
1745 * Enqueue a message to this state machine after a delay.
Wink Savilleefcc3d32013-01-30 11:21:22 -08001746 *
1747 * Message is ignored if state machine has quit.
Wink Saville91fbd562010-03-17 17:12:43 -07001748 */
Rebecca Silberstein0dbeb9e2016-05-13 13:57:34 -07001749 public void sendMessageDelayed(int what, int arg1, long delayMillis) {
Wink Saville8b0db522013-03-14 13:23:19 -07001750 // mSmHandler can be null if the state machine has quit.
1751 SmHandler smh = mSmHandler;
1752 if (smh == null) return;
1753
1754 smh.sendMessageDelayed(obtainMessage(what, arg1), delayMillis);
1755 }
1756
1757 /**
1758 * Enqueue a message to this state machine after a delay.
1759 *
1760 * Message is ignored if state machine has quit.
1761 */
Rebecca Silberstein0dbeb9e2016-05-13 13:57:34 -07001762 public void sendMessageDelayed(int what, int arg1, int arg2, long delayMillis) {
Wink Saville8b0db522013-03-14 13:23:19 -07001763 // mSmHandler can be null if the state machine has quit.
1764 SmHandler smh = mSmHandler;
1765 if (smh == null) return;
1766
1767 smh.sendMessageDelayed(obtainMessage(what, arg1, arg2), delayMillis);
1768 }
1769
1770 /**
1771 * Enqueue a message to this state machine after a delay.
1772 *
1773 * Message is ignored if state machine has quit.
1774 */
Rebecca Silberstein0dbeb9e2016-05-13 13:57:34 -07001775 public void sendMessageDelayed(int what, int arg1, int arg2, Object obj,
Wink Saville4753cd22013-03-06 13:41:23 -08001776 long delayMillis) {
1777 // mSmHandler can be null if the state machine has quit.
1778 SmHandler smh = mSmHandler;
1779 if (smh == null) return;
1780
1781 smh.sendMessageDelayed(obtainMessage(what, arg1, arg2, obj), delayMillis);
1782 }
1783
1784 /**
1785 * Enqueue a message to this state machine after a delay.
1786 *
1787 * Message is ignored if state machine has quit.
1788 */
Rebecca Silberstein0dbeb9e2016-05-13 13:57:34 -07001789 public void sendMessageDelayed(Message msg, long delayMillis) {
Jaikumar Ganesha544d462011-12-07 14:52:01 -08001790 // mSmHandler can be null if the state machine has quit.
Wink Savilleefcc3d32013-01-30 11:21:22 -08001791 SmHandler smh = mSmHandler;
1792 if (smh == null) return;
Jaikumar Ganesha544d462011-12-07 14:52:01 -08001793
Wink Savilleefcc3d32013-01-30 11:21:22 -08001794 smh.sendMessageDelayed(msg, delayMillis);
Wink Savillefc5b4802009-12-08 21:22:24 -08001795 }
1796
1797 /**
1798 * Enqueue a message to the front of the queue for this state machine.
Wink Saville64c42ca2011-04-18 14:55:10 -07001799 * Protected, may only be called by instances of StateMachine.
Wink Savilleefcc3d32013-01-30 11:21:22 -08001800 *
1801 * Message is ignored if state machine has quit.
Wink Savillefc5b4802009-12-08 21:22:24 -08001802 */
Wink Saville8b0db522013-03-14 13:23:19 -07001803 protected final void sendMessageAtFrontOfQueue(int what) {
1804 // mSmHandler can be null if the state machine has quit.
1805 SmHandler smh = mSmHandler;
1806 if (smh == null) return;
1807
1808 smh.sendMessageAtFrontOfQueue(obtainMessage(what));
1809 }
1810
1811 /**
1812 * Enqueue a message to the front of the queue for this state machine.
1813 * Protected, may only be called by instances of StateMachine.
1814 *
1815 * Message is ignored if state machine has quit.
1816 */
Wink Saville91fbd562010-03-17 17:12:43 -07001817 protected final void sendMessageAtFrontOfQueue(int what, Object obj) {
Wink Savilleefcc3d32013-01-30 11:21:22 -08001818 // mSmHandler can be null if the state machine has quit.
1819 SmHandler smh = mSmHandler;
1820 if (smh == null) return;
1821
1822 smh.sendMessageAtFrontOfQueue(obtainMessage(what, obj));
Wink Saville91fbd562010-03-17 17:12:43 -07001823 }
1824
1825 /**
1826 * Enqueue a message to the front of the queue for this state machine.
Wink Saville64c42ca2011-04-18 14:55:10 -07001827 * Protected, may only be called by instances of StateMachine.
Wink Savilleefcc3d32013-01-30 11:21:22 -08001828 *
1829 * Message is ignored if state machine has quit.
Wink Saville91fbd562010-03-17 17:12:43 -07001830 */
Wink Saville8b0db522013-03-14 13:23:19 -07001831 protected final void sendMessageAtFrontOfQueue(int what, int arg1) {
Wink Savilleefcc3d32013-01-30 11:21:22 -08001832 // mSmHandler can be null if the state machine has quit.
1833 SmHandler smh = mSmHandler;
1834 if (smh == null) return;
1835
Wink Saville8b0db522013-03-14 13:23:19 -07001836 smh.sendMessageAtFrontOfQueue(obtainMessage(what, arg1));
1837 }
1838
1839
1840 /**
1841 * Enqueue a message to the front of the queue for this state machine.
1842 * Protected, may only be called by instances of StateMachine.
1843 *
1844 * Message is ignored if state machine has quit.
1845 */
1846 protected final void sendMessageAtFrontOfQueue(int what, int arg1, int arg2) {
1847 // mSmHandler can be null if the state machine has quit.
1848 SmHandler smh = mSmHandler;
1849 if (smh == null) return;
1850
1851 smh.sendMessageAtFrontOfQueue(obtainMessage(what, arg1, arg2));
Wink Saville91fbd562010-03-17 17:12:43 -07001852 }
1853
1854 /**
1855 * Enqueue a message to the front of the queue for this state machine.
Wink Saville64c42ca2011-04-18 14:55:10 -07001856 * Protected, may only be called by instances of StateMachine.
Wink Savilleefcc3d32013-01-30 11:21:22 -08001857 *
1858 * Message is ignored if state machine has quit.
Wink Saville91fbd562010-03-17 17:12:43 -07001859 */
Wink Saville4753cd22013-03-06 13:41:23 -08001860 protected final void sendMessageAtFrontOfQueue(int what, int arg1, int arg2, Object obj) {
1861 // mSmHandler can be null if the state machine has quit.
1862 SmHandler smh = mSmHandler;
1863 if (smh == null) return;
1864
1865 smh.sendMessageAtFrontOfQueue(obtainMessage(what, arg1, arg2, obj));
1866 }
1867
1868 /**
1869 * Enqueue a message to the front of the queue for this state machine.
1870 * Protected, may only be called by instances of StateMachine.
1871 *
1872 * Message is ignored if state machine has quit.
1873 */
Wink Savillefc5b4802009-12-08 21:22:24 -08001874 protected final void sendMessageAtFrontOfQueue(Message msg) {
Wink Savilleefcc3d32013-01-30 11:21:22 -08001875 // mSmHandler can be null if the state machine has quit.
1876 SmHandler smh = mSmHandler;
1877 if (smh == null) return;
1878
1879 smh.sendMessageAtFrontOfQueue(msg);
Wink Savillefc5b4802009-12-08 21:22:24 -08001880 }
1881
1882 /**
Jaikumar Ganeshaa4b2352010-10-14 09:36:39 -07001883 * Removes a message from the message queue.
Wink Saville64c42ca2011-04-18 14:55:10 -07001884 * Protected, may only be called by instances of StateMachine.
Jaikumar Ganeshaa4b2352010-10-14 09:36:39 -07001885 */
1886 protected final void removeMessages(int what) {
Wink Savilleefcc3d32013-01-30 11:21:22 -08001887 // mSmHandler can be null if the state machine has quit.
1888 SmHandler smh = mSmHandler;
1889 if (smh == null) return;
1890
1891 smh.removeMessages(what);
1892 }
1893
1894 /**
Ajay Panickerc2119782015-08-26 14:06:34 -07001895 * Removes a message from the deferred messages queue.
1896 */
1897 protected final void removeDeferredMessages(int what) {
1898 SmHandler smh = mSmHandler;
1899 if (smh == null) return;
1900
1901 Iterator<Message> iterator = smh.mDeferredMessages.iterator();
1902 while (iterator.hasNext()) {
1903 Message msg = iterator.next();
1904 if (msg.what == what) iterator.remove();
1905 }
1906 }
1907
1908 /**
Amit Mahajan8ed715e2015-11-03 10:06:03 -08001909 * Check if there are any pending messages with code 'what' in deferred messages queue.
1910 */
1911 protected final boolean hasDeferredMessages(int what) {
1912 SmHandler smh = mSmHandler;
1913 if (smh == null) return false;
1914
1915 Iterator<Message> iterator = smh.mDeferredMessages.iterator();
1916 while (iterator.hasNext()) {
1917 Message msg = iterator.next();
1918 if (msg.what == what) return true;
1919 }
1920
1921 return false;
1922 }
1923
1924 /**
1925 * Check if there are any pending posts of messages with code 'what' in
1926 * the message queue. This does NOT check messages in deferred message queue.
1927 */
1928 protected final boolean hasMessages(int what) {
1929 SmHandler smh = mSmHandler;
1930 if (smh == null) return false;
1931
1932 return smh.hasMessages(what);
1933 }
1934
1935 /**
Wink Savilleefcc3d32013-01-30 11:21:22 -08001936 * Validate that the message was sent by
1937 * {@link StateMachine#quit} or {@link StateMachine#quitNow}.
1938 * */
1939 protected final boolean isQuit(Message msg) {
1940 // mSmHandler can be null if the state machine has quit.
1941 SmHandler smh = mSmHandler;
1942 if (smh == null) return msg.what == SM_QUIT_CMD;
1943
1944 return smh.isQuit(msg);
Jaikumar Ganeshaa4b2352010-10-14 09:36:39 -07001945 }
1946
1947 /**
Wink Savillebbf30dfd2012-05-29 12:40:46 -07001948 * Quit the state machine after all currently queued up messages are processed.
Wink Saville1b8b98b2010-03-11 11:49:54 -08001949 */
Wink Savillebbf30dfd2012-05-29 12:40:46 -07001950 protected final void quit() {
1951 // mSmHandler can be null if the state machine is already stopped.
Wink Savilleefcc3d32013-01-30 11:21:22 -08001952 SmHandler smh = mSmHandler;
1953 if (smh == null) return;
Jaikumar Ganesha544d462011-12-07 14:52:01 -08001954
Wink Savilleefcc3d32013-01-30 11:21:22 -08001955 smh.quit();
Wink Saville1b8b98b2010-03-11 11:49:54 -08001956 }
1957
1958 /**
Wink Savillebbf30dfd2012-05-29 12:40:46 -07001959 * Quit the state machine immediately all currently queued messages will be discarded.
Wink Saville1b8b98b2010-03-11 11:49:54 -08001960 */
Wink Savillebbf30dfd2012-05-29 12:40:46 -07001961 protected final void quitNow() {
1962 // mSmHandler can be null if the state machine is already stopped.
Wink Savilleefcc3d32013-01-30 11:21:22 -08001963 SmHandler smh = mSmHandler;
1964 if (smh == null) return;
Wink Saville1b8b98b2010-03-11 11:49:54 -08001965
Wink Savilleefcc3d32013-01-30 11:21:22 -08001966 smh.quitNow();
Wink Saville583eaaa2012-04-13 16:11:20 -07001967 }
1968
1969 /**
Wink Savillefc5b4802009-12-08 21:22:24 -08001970 * @return if debugging is enabled
1971 */
1972 public boolean isDbg() {
Jaikumar Ganesha544d462011-12-07 14:52:01 -08001973 // mSmHandler can be null if the state machine has quit.
Wink Savilleefcc3d32013-01-30 11:21:22 -08001974 SmHandler smh = mSmHandler;
1975 if (smh == null) return false;
Jaikumar Ganesha544d462011-12-07 14:52:01 -08001976
Wink Savilleefcc3d32013-01-30 11:21:22 -08001977 return smh.isDbg();
Wink Savillefc5b4802009-12-08 21:22:24 -08001978 }
1979
1980 /**
1981 * Set debug enable/disabled.
1982 *
1983 * @param dbg is true to enable debugging.
1984 */
1985 public void setDbg(boolean dbg) {
Jaikumar Ganesha544d462011-12-07 14:52:01 -08001986 // mSmHandler can be null if the state machine has quit.
Wink Savilleefcc3d32013-01-30 11:21:22 -08001987 SmHandler smh = mSmHandler;
1988 if (smh == null) return;
Jaikumar Ganesha544d462011-12-07 14:52:01 -08001989
Wink Savilleefcc3d32013-01-30 11:21:22 -08001990 smh.setDbg(dbg);
Wink Savillefc5b4802009-12-08 21:22:24 -08001991 }
1992
1993 /**
1994 * Start the state machine.
1995 */
1996 public void start() {
Jaikumar Ganesha544d462011-12-07 14:52:01 -08001997 // mSmHandler can be null if the state machine has quit.
Wink Savilleefcc3d32013-01-30 11:21:22 -08001998 SmHandler smh = mSmHandler;
1999 if (smh == null) return;
Jaikumar Ganesha544d462011-12-07 14:52:01 -08002000
Wink Savillefc5b4802009-12-08 21:22:24 -08002001 /** Send the complete construction message */
Wink Savilleefcc3d32013-01-30 11:21:22 -08002002 smh.completeConstruction();
Wink Savillefc5b4802009-12-08 21:22:24 -08002003 }
Wink Saville583eaaa2012-04-13 16:11:20 -07002004
2005 /**
2006 * Dump the current state.
2007 *
2008 * @param fd
2009 * @param pw
2010 * @param args
2011 */
2012 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Wink Saville54b1b1a2015-01-12 11:34:20 -08002013 // Cannot just invoke pw.println(this.toString()) because if the
2014 // resulting string is to long it won't be displayed.
2015 pw.println(getName() + ":");
2016 pw.println(" total records=" + getLogRecCount());
2017 for (int i = 0; i < getLogRecSize(); i++) {
2018 pw.println(" rec[" + i + "]: " + getLogRec(i).toString());
2019 pw.flush();
2020 }
2021 pw.println("curState=" + getCurrentState().getName());
Mike Lockwood726d4de2014-10-28 14:06:28 -07002022 }
2023
2024 @Override
2025 public String toString() {
Wink Saville54b1b1a2015-01-12 11:34:20 -08002026 StringWriter sr = new StringWriter();
2027 PrintWriter pr = new PrintWriter(sr);
2028 dump(null, pr, null);
2029 pr.flush();
2030 pr.close();
2031 return sr.toString();
Wink Saville583eaaa2012-04-13 16:11:20 -07002032 }
Wink Saville58c73c32013-01-28 10:52:34 -08002033
Wink Savilleff4fcdb2013-02-24 07:21:45 -08002034 /**
2035 * Log with debug and add to the LogRecords.
2036 *
2037 * @param s is string log
2038 */
2039 protected void logAndAddLogRec(String s) {
2040 addLogRec(s);
2041 log(s);
2042 }
2043
2044 /**
2045 * Log with debug
2046 *
2047 * @param s is string log
2048 */
Wink Saville58c73c32013-01-28 10:52:34 -08002049 protected void log(String s) {
2050 Log.d(mName, s);
2051 }
2052
Wink Savilleff4fcdb2013-02-24 07:21:45 -08002053 /**
2054 * Log with debug attribute
2055 *
2056 * @param s is string log
2057 */
Wink Saville58c73c32013-01-28 10:52:34 -08002058 protected void logd(String s) {
2059 Log.d(mName, s);
2060 }
2061
Wink Savilleff4fcdb2013-02-24 07:21:45 -08002062 /**
2063 * Log with verbose attribute
2064 *
2065 * @param s is string log
2066 */
2067 protected void logv(String s) {
2068 Log.v(mName, s);
2069 }
2070
2071 /**
2072 * Log with info attribute
2073 *
2074 * @param s is string log
2075 */
2076 protected void logi(String s) {
2077 Log.i(mName, s);
2078 }
2079
2080 /**
2081 * Log with warning attribute
2082 *
2083 * @param s is string log
2084 */
Wink Saville58c73c32013-01-28 10:52:34 -08002085 protected void logw(String s) {
2086 Log.w(mName, s);
2087 }
2088
Wink Savilleff4fcdb2013-02-24 07:21:45 -08002089 /**
2090 * Log with error attribute
2091 *
2092 * @param s is string log
2093 */
Wink Saville58c73c32013-01-28 10:52:34 -08002094 protected void loge(String s) {
2095 Log.e(mName, s);
2096 }
Wink Savilleff4fcdb2013-02-24 07:21:45 -08002097
2098 /**
2099 * Log with error attribute
2100 *
2101 * @param s is string log
2102 * @param e is a Throwable which logs additional information.
2103 */
2104 protected void loge(String s, Throwable e) {
2105 Log.e(mName, s, e);
2106 }
Wink Savillefc5b4802009-12-08 21:22:24 -08002107}