blob: 0ea7b832183bd39397823ee67839dcb385759e65 [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 Savillefc5b4802009-12-08 21:22:24 -080028import java.util.ArrayList;
Wink Saville583eaaa2012-04-13 16:11:20 -070029import java.util.Calendar;
Wink Savillefc5b4802009-12-08 21:22:24 -080030import java.util.HashMap;
Wink Savilled3059482011-04-11 11:51:28 -070031import java.util.Vector;
Wink Savillefc5b4802009-12-08 21:22:24 -080032
33/**
34 * {@hide}
35 *
Wink Saville64c42ca2011-04-18 14:55:10 -070036 * <p>The state machine defined here is a hierarchical state machine which processes messages
Wink Saville33c54e32010-11-15 10:50:34 -080037 * and can have states arranged hierarchically.</p>
38 *
Wink Saville64c42ca2011-04-18 14:55:10 -070039 * <p>A state is a <code>State</code> object and must implement
Wink Saville33c54e32010-11-15 10:50:34 -080040 * <code>processMessage</code> and optionally <code>enter/exit/getName</code>.
Wink Savillefc5b4802009-12-08 21:22:24 -080041 * The enter/exit methods are equivalent to the construction and destruction
42 * in Object Oriented programming and are used to perform initialization and
43 * cleanup of the state respectively. The <code>getName</code> method returns the
44 * name of the state the default implementation returns the class name it may be
45 * desirable to have this return the name of the state instance name instead.
Wink Saville33c54e32010-11-15 10:50:34 -080046 * In particular if a particular state class has multiple instances.</p>
Wink Savillefc5b4802009-12-08 21:22:24 -080047 *
Wink Saville33c54e32010-11-15 10:50:34 -080048 * <p>When a state machine is created <code>addState</code> is used to build the
Wink Savillefc5b4802009-12-08 21:22:24 -080049 * hierarchy and <code>setInitialState</code> is used to identify which of these
50 * is the initial state. After construction the programmer calls <code>start</code>
Wink Savillecea056f2012-03-26 15:03:16 -070051 * which initializes and starts the state machine. The first action the StateMachine
52 * is to the invoke <code>enter</code> for all of the initial state's hierarchy,
53 * starting at its eldest parent. The calls to enter will be done in the context
54 * of the StateMachines Handler not in the context of the call to start and they
55 * will be invoked before any messages are processed. For example, given the simple
56 * state machine below mP1.enter will be invoked and then mS1.enter. Finally,
57 * messages sent to the state machine will be processed by the current state,
58 * in our simple state machine below that would initially be mS1.processMessage.</p>
Wink Savillefc5b4802009-12-08 21:22:24 -080059<code>
60 mP1
61 / \
62 mS2 mS1 ----> initial state
63</code>
Wink Saville33c54e32010-11-15 10:50:34 -080064 * <p>After the state machine is created and started, messages are sent to a state
Wink Savillea4f3bec2010-05-19 09:11:38 -070065 * machine using <code>sendMessage</code> and the messages are created using
Wink Savillefc5b4802009-12-08 21:22:24 -080066 * <code>obtainMessage</code>. When the state machine receives a message the
67 * current state's <code>processMessage</code> is invoked. In the above example
68 * mS1.processMessage will be invoked first. The state may use <code>transitionTo</code>
Wink Saville33c54e32010-11-15 10:50:34 -080069 * to change the current state to a new state</p>
Wink Savillefc5b4802009-12-08 21:22:24 -080070 *
Wink Saville33c54e32010-11-15 10:50:34 -080071 * <p>Each state in the state machine may have a zero or one parent states and if
Wink Savillefc5b4802009-12-08 21:22:24 -080072 * a child state is unable to handle a message it may have the message processed
Wink Savillea4f3bec2010-05-19 09:11:38 -070073 * by its parent by returning false or NOT_HANDLED. If a message is never processed
74 * <code>unhandledMessage</code> will be invoked to give one last chance for the state machine
Wink Saville33c54e32010-11-15 10:50:34 -080075 * to process the message.</p>
Wink Savillefc5b4802009-12-08 21:22:24 -080076 *
Wink Saville33c54e32010-11-15 10:50:34 -080077 * <p>When all processing is completed a state machine may choose to call
Wink Savillefc5b4802009-12-08 21:22:24 -080078 * <code>transitionToHaltingState</code>. When the current <code>processingMessage</code>
79 * returns the state machine will transfer to an internal <code>HaltingState</code>
80 * and invoke <code>halting</code>. Any message subsequently received by the state
Wink Saville33c54e32010-11-15 10:50:34 -080081 * machine will cause <code>haltedProcessMessage</code> to be invoked.</p>
Wink Savillefc5b4802009-12-08 21:22:24 -080082 *
Wink Saville095c58b2012-05-29 12:40:46 -070083 * <p>If it is desirable to completely stop the state machine call <code>quit</code> or
84 * <code>abort</code>. These will call <code>exit</code> of the current state and its parents, call
85 * <code>onQuiting</code> and then exit Thread/Loopers.</p>
Wink Saville1b8b98b2010-03-11 11:49:54 -080086 *
Wink Saville64c42ca2011-04-18 14:55:10 -070087 * <p>In addition to <code>processMessage</code> each <code>State</code> has
Wink Saville33c54e32010-11-15 10:50:34 -080088 * an <code>enter</code> method and <code>exit</exit> method which may be overridden.</p>
Wink Savillefc5b4802009-12-08 21:22:24 -080089 *
Wink Saville33c54e32010-11-15 10:50:34 -080090 * <p>Since the states are arranged in a hierarchy transitioning to a new state
Wink Savillefc5b4802009-12-08 21:22:24 -080091 * causes current states to be exited and new states to be entered. To determine
92 * the list of states to be entered/exited the common parent closest to
93 * the current state is found. We then exit from the current state and its
94 * parent's up to but not including the common parent state and then enter all
95 * of the new states below the common parent down to the destination state.
96 * If there is no common parent all states are exited and then the new states
Wink Saville33c54e32010-11-15 10:50:34 -080097 * are entered.</p>
Wink Savillefc5b4802009-12-08 21:22:24 -080098 *
Wink Saville33c54e32010-11-15 10:50:34 -080099 * <p>Two other methods that states can use are <code>deferMessage</code> and
Wink Savillefc5b4802009-12-08 21:22:24 -0800100 * <code>sendMessageAtFrontOfQueue</code>. The <code>sendMessageAtFrontOfQueue</code> sends
101 * a message but places it on the front of the queue rather than the back. The
102 * <code>deferMessage</code> causes the message to be saved on a list until a
103 * transition is made to a new state. At which time all of the deferred messages
104 * will be put on the front of the state machine queue with the oldest message
105 * at the front. These will then be processed by the new current state before
106 * any other messages that are on the queue or might be added later. Both of
Wink Saville33c54e32010-11-15 10:50:34 -0800107 * these are protected and may only be invoked from within a state machine.</p>
Wink Savillefc5b4802009-12-08 21:22:24 -0800108 *
Wink Saville33c54e32010-11-15 10:50:34 -0800109 * <p>To illustrate some of these properties we'll use state machine with an 8
110 * state hierarchy:</p>
Wink Savillefc5b4802009-12-08 21:22:24 -0800111<code>
112 mP0
113 / \
114 mP1 mS0
115 / \
116 mS2 mS1
117 / \ \
118 mS3 mS4 mS5 ---> initial state
119</code>
Wink Saville33c54e32010-11-15 10:50:34 -0800120 * <p>After starting mS5 the list of active states is mP0, mP1, mS1 and mS5.
Wink Savillefc5b4802009-12-08 21:22:24 -0800121 * So the order of calling processMessage when a message is received is mS5,
Wink Savillea4f3bec2010-05-19 09:11:38 -0700122 * mS1, mP1, mP0 assuming each processMessage indicates it can't handle this
Wink Saville33c54e32010-11-15 10:50:34 -0800123 * message by returning false or NOT_HANDLED.</p>
Wink Savillefc5b4802009-12-08 21:22:24 -0800124 *
Wink Saville33c54e32010-11-15 10:50:34 -0800125 * <p>Now assume mS5.processMessage receives a message it can handle, and during
Wink Savillea4f3bec2010-05-19 09:11:38 -0700126 * the handling determines the machine should change states. It could call
127 * transitionTo(mS4) and return true or HANDLED. Immediately after returning from
Wink Savillefc5b4802009-12-08 21:22:24 -0800128 * processMessage the state machine runtime will find the common parent,
129 * which is mP1. It will then call mS5.exit, mS1.exit, mS2.enter and then
130 * mS4.enter. The new list of active states is mP0, mP1, mS2 and mS4. So
Wink Saville33c54e32010-11-15 10:50:34 -0800131 * when the next message is received mS4.processMessage will be invoked.</p>
Wink Savillefc5b4802009-12-08 21:22:24 -0800132 *
Wink Saville64c42ca2011-04-18 14:55:10 -0700133 * <p>Now for some concrete examples, here is the canonical HelloWorld as a state machine.
Wink Saville33c54e32010-11-15 10:50:34 -0800134 * It responds with "Hello World" being printed to the log for every message.</p>
Wink Savillefc5b4802009-12-08 21:22:24 -0800135<code>
Wink Saville64c42ca2011-04-18 14:55:10 -0700136class HelloWorld extends StateMachine {
137 HelloWorld(String name) {
Wink Savillefc5b4802009-12-08 21:22:24 -0800138 super(name);
139 addState(mState1);
140 setInitialState(mState1);
141 }
142
143 public static HelloWorld makeHelloWorld() {
144 HelloWorld hw = new HelloWorld("hw");
145 hw.start();
146 return hw;
147 }
148
Wink Saville64c42ca2011-04-18 14:55:10 -0700149 class State1 extends State {
Joe Onorato0b6232d2010-09-16 11:55:35 -0400150 &#64;Override public boolean processMessage(Message message) {
Wink Savillefc5b4802009-12-08 21:22:24 -0800151 Log.d(TAG, "Hello World");
Wink Savillea4f3bec2010-05-19 09:11:38 -0700152 return HANDLED;
Wink Savillefc5b4802009-12-08 21:22:24 -0800153 }
154 }
155 State1 mState1 = new State1();
156}
157
158void testHelloWorld() {
159 HelloWorld hw = makeHelloWorld();
160 hw.sendMessage(hw.obtainMessage());
161}
162</code>
Wink Saville33c54e32010-11-15 10:50:34 -0800163 * <p>A more interesting state machine is one with four states
164 * with two independent parent states.</p>
Wink Savillefc5b4802009-12-08 21:22:24 -0800165<code>
166 mP1 mP2
167 / \
168 mS2 mS1
169</code>
Wink Saville33c54e32010-11-15 10:50:34 -0800170 * <p>Here is a description of this state machine using pseudo code.</p>
171 <code>
172state mP1 {
173 enter { log("mP1.enter"); }
174 exit { log("mP1.exit"); }
175 on msg {
176 CMD_2 {
177 send(CMD_3);
178 defer(msg);
179 transitonTo(mS2);
180 return HANDLED;
181 }
182 return NOT_HANDLED;
183 }
184}
185
186INITIAL
187state mS1 parent mP1 {
188 enter { log("mS1.enter"); }
189 exit { log("mS1.exit"); }
190 on msg {
191 CMD_1 {
192 transitionTo(mS1);
193 return HANDLED;
194 }
195 return NOT_HANDLED;
196 }
197}
198
199state mS2 parent mP1 {
200 enter { log("mS2.enter"); }
201 exit { log("mS2.exit"); }
202 on msg {
203 CMD_2 {
204 send(CMD_4);
205 return HANDLED;
206 }
207 CMD_3 {
208 defer(msg);
209 transitionTo(mP2);
210 return HANDLED;
211 }
212 return NOT_HANDLED;
213 }
214}
215
216state mP2 {
217 enter {
218 log("mP2.enter");
219 send(CMD_5);
220 }
221 exit { log("mP2.exit"); }
222 on msg {
223 CMD_3, CMD_4 { return HANDLED; }
224 CMD_5 {
225 transitionTo(HaltingState);
226 return HANDLED;
227 }
228 return NOT_HANDLED;
229 }
230}
231</code>
Wink Saville64c42ca2011-04-18 14:55:10 -0700232 * <p>The implementation is below and also in StateMachineTest:</p>
Wink Savillefc5b4802009-12-08 21:22:24 -0800233<code>
Wink Saville64c42ca2011-04-18 14:55:10 -0700234class Hsm1 extends StateMachine {
Wink Savillefc5b4802009-12-08 21:22:24 -0800235 private static final String TAG = "hsm1";
236
237 public static final int CMD_1 = 1;
238 public static final int CMD_2 = 2;
239 public static final int CMD_3 = 3;
240 public static final int CMD_4 = 4;
241 public static final int CMD_5 = 5;
242
243 public static Hsm1 makeHsm1() {
244 Log.d(TAG, "makeHsm1 E");
245 Hsm1 sm = new Hsm1("hsm1");
246 sm.start();
247 Log.d(TAG, "makeHsm1 X");
248 return sm;
249 }
250
251 Hsm1(String name) {
252 super(name);
253 Log.d(TAG, "ctor E");
254
255 // Add states, use indentation to show hierarchy
256 addState(mP1);
257 addState(mS1, mP1);
258 addState(mS2, mP1);
259 addState(mP2);
260
261 // Set the initial state
262 setInitialState(mS1);
263 Log.d(TAG, "ctor X");
264 }
265
Wink Saville64c42ca2011-04-18 14:55:10 -0700266 class P1 extends State {
Joe Onorato0b6232d2010-09-16 11:55:35 -0400267 &#64;Override public void enter() {
Wink Savillefc5b4802009-12-08 21:22:24 -0800268 Log.d(TAG, "mP1.enter");
269 }
Joe Onorato0b6232d2010-09-16 11:55:35 -0400270 &#64;Override public boolean processMessage(Message message) {
Wink Savillefc5b4802009-12-08 21:22:24 -0800271 boolean retVal;
272 Log.d(TAG, "mP1.processMessage what=" + message.what);
273 switch(message.what) {
274 case CMD_2:
275 // CMD_2 will arrive in mS2 before CMD_3
276 sendMessage(obtainMessage(CMD_3));
277 deferMessage(message);
278 transitionTo(mS2);
Wink Savillea4f3bec2010-05-19 09:11:38 -0700279 retVal = HANDLED;
Wink Savillefc5b4802009-12-08 21:22:24 -0800280 break;
281 default:
282 // Any message we don't understand in this state invokes unhandledMessage
Wink Savillea4f3bec2010-05-19 09:11:38 -0700283 retVal = NOT_HANDLED;
Wink Savillefc5b4802009-12-08 21:22:24 -0800284 break;
285 }
286 return retVal;
287 }
Joe Onorato0b6232d2010-09-16 11:55:35 -0400288 &#64;Override public void exit() {
Wink Savillefc5b4802009-12-08 21:22:24 -0800289 Log.d(TAG, "mP1.exit");
290 }
291 }
292
Wink Saville64c42ca2011-04-18 14:55:10 -0700293 class S1 extends State {
Joe Onorato0b6232d2010-09-16 11:55:35 -0400294 &#64;Override public void enter() {
Wink Savillefc5b4802009-12-08 21:22:24 -0800295 Log.d(TAG, "mS1.enter");
296 }
Joe Onorato0b6232d2010-09-16 11:55:35 -0400297 &#64;Override public boolean processMessage(Message message) {
Wink Savillefc5b4802009-12-08 21:22:24 -0800298 Log.d(TAG, "S1.processMessage what=" + message.what);
299 if (message.what == CMD_1) {
300 // Transition to ourself to show that enter/exit is called
301 transitionTo(mS1);
Wink Savillea4f3bec2010-05-19 09:11:38 -0700302 return HANDLED;
Wink Savillefc5b4802009-12-08 21:22:24 -0800303 } else {
304 // Let parent process all other messages
Wink Savillea4f3bec2010-05-19 09:11:38 -0700305 return NOT_HANDLED;
Wink Savillefc5b4802009-12-08 21:22:24 -0800306 }
307 }
Joe Onorato0b6232d2010-09-16 11:55:35 -0400308 &#64;Override public void exit() {
Wink Savillefc5b4802009-12-08 21:22:24 -0800309 Log.d(TAG, "mS1.exit");
310 }
311 }
312
Wink Saville64c42ca2011-04-18 14:55:10 -0700313 class S2 extends State {
Joe Onorato0b6232d2010-09-16 11:55:35 -0400314 &#64;Override public void enter() {
Wink Savillefc5b4802009-12-08 21:22:24 -0800315 Log.d(TAG, "mS2.enter");
316 }
Joe Onorato0b6232d2010-09-16 11:55:35 -0400317 &#64;Override public boolean processMessage(Message message) {
Wink Savillefc5b4802009-12-08 21:22:24 -0800318 boolean retVal;
319 Log.d(TAG, "mS2.processMessage what=" + message.what);
320 switch(message.what) {
321 case(CMD_2):
322 sendMessage(obtainMessage(CMD_4));
Wink Savillea4f3bec2010-05-19 09:11:38 -0700323 retVal = HANDLED;
Wink Savillefc5b4802009-12-08 21:22:24 -0800324 break;
325 case(CMD_3):
326 deferMessage(message);
327 transitionTo(mP2);
Wink Savillea4f3bec2010-05-19 09:11:38 -0700328 retVal = HANDLED;
Wink Savillefc5b4802009-12-08 21:22:24 -0800329 break;
330 default:
Wink Savillea4f3bec2010-05-19 09:11:38 -0700331 retVal = NOT_HANDLED;
Wink Savillefc5b4802009-12-08 21:22:24 -0800332 break;
333 }
334 return retVal;
335 }
Joe Onorato0b6232d2010-09-16 11:55:35 -0400336 &#64;Override public void exit() {
Wink Savillefc5b4802009-12-08 21:22:24 -0800337 Log.d(TAG, "mS2.exit");
338 }
339 }
340
Wink Saville64c42ca2011-04-18 14:55:10 -0700341 class P2 extends State {
Joe Onorato0b6232d2010-09-16 11:55:35 -0400342 &#64;Override public void enter() {
Wink Savillefc5b4802009-12-08 21:22:24 -0800343 Log.d(TAG, "mP2.enter");
344 sendMessage(obtainMessage(CMD_5));
345 }
Joe Onorato0b6232d2010-09-16 11:55:35 -0400346 &#64;Override public boolean processMessage(Message message) {
Wink Savillefc5b4802009-12-08 21:22:24 -0800347 Log.d(TAG, "P2.processMessage what=" + message.what);
348 switch(message.what) {
349 case(CMD_3):
350 break;
351 case(CMD_4):
352 break;
353 case(CMD_5):
354 transitionToHaltingState();
355 break;
356 }
Wink Savillea4f3bec2010-05-19 09:11:38 -0700357 return HANDLED;
Wink Savillefc5b4802009-12-08 21:22:24 -0800358 }
Joe Onorato0b6232d2010-09-16 11:55:35 -0400359 &#64;Override public void exit() {
Wink Savillefc5b4802009-12-08 21:22:24 -0800360 Log.d(TAG, "mP2.exit");
361 }
362 }
363
Joe Onorato0b6232d2010-09-16 11:55:35 -0400364 &#64;Override
Wink Saville095c58b2012-05-29 12:40:46 -0700365 void onHalting() {
Wink Savillefc5b4802009-12-08 21:22:24 -0800366 Log.d(TAG, "halting");
367 synchronized (this) {
368 this.notifyAll();
369 }
370 }
371
372 P1 mP1 = new P1();
373 S1 mS1 = new S1();
374 S2 mS2 = new S2();
375 P2 mP2 = new P2();
376}
377</code>
Wink Saville33c54e32010-11-15 10:50:34 -0800378 * <p>If this is executed by sending two messages CMD_1 and CMD_2
379 * (Note the synchronize is only needed because we use hsm.wait())</p>
380<code>
381Hsm1 hsm = makeHsm1();
382synchronize(hsm) {
383 hsm.sendMessage(obtainMessage(hsm.CMD_1));
384 hsm.sendMessage(obtainMessage(hsm.CMD_2));
385 try {
386 // wait for the messages to be handled
387 hsm.wait();
388 } catch (InterruptedException e) {
389 Log.e(TAG, "exception while waiting " + e.getMessage());
390 }
391}
392</code>
393 * <p>The output is:</p>
394<code>
395D/hsm1 ( 1999): makeHsm1 E
396D/hsm1 ( 1999): ctor E
397D/hsm1 ( 1999): ctor X
398D/hsm1 ( 1999): mP1.enter
399D/hsm1 ( 1999): mS1.enter
400D/hsm1 ( 1999): makeHsm1 X
401D/hsm1 ( 1999): mS1.processMessage what=1
402D/hsm1 ( 1999): mS1.exit
403D/hsm1 ( 1999): mS1.enter
404D/hsm1 ( 1999): mS1.processMessage what=2
405D/hsm1 ( 1999): mP1.processMessage what=2
406D/hsm1 ( 1999): mS1.exit
407D/hsm1 ( 1999): mS2.enter
408D/hsm1 ( 1999): mS2.processMessage what=2
409D/hsm1 ( 1999): mS2.processMessage what=3
410D/hsm1 ( 1999): mS2.exit
411D/hsm1 ( 1999): mP1.exit
412D/hsm1 ( 1999): mP2.enter
413D/hsm1 ( 1999): mP2.processMessage what=3
414D/hsm1 ( 1999): mP2.processMessage what=4
415D/hsm1 ( 1999): mP2.processMessage what=5
416D/hsm1 ( 1999): mP2.exit
417D/hsm1 ( 1999): halting
418</code>
Wink Savillefc5b4802009-12-08 21:22:24 -0800419 */
Wink Saville64c42ca2011-04-18 14:55:10 -0700420public class StateMachine {
Wink Savillefc5b4802009-12-08 21:22:24 -0800421
Wink Saville64c42ca2011-04-18 14:55:10 -0700422 private static final String TAG = "StateMachine";
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 Saville095c58b2012-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 Saville095c58b2012-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 Saville095c58b2012-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 Saville095c58b2012-05-29 12:40:46 -0700449 public static class LogRec {
Wink Saville583eaaa2012-04-13 16:11:20 -0700450 private long mTime;
451 private int mWhat;
452 private String mInfo;
453 private State mState;
454 private State mOrgState;
Wink Savilled3059482011-04-11 11:51:28 -0700455
456 /**
457 * Constructor
Wink Saville095c58b2012-05-29 12:40:46 -0700458 *
459 * @param msg
Wink Savilled3059482011-04-11 11:51:28 -0700460 * @param state that handled the message
461 * @param orgState is the first state the received the message but
462 * did not processes the message.
463 */
Wink Saville095c58b2012-05-29 12:40:46 -0700464 LogRec(Message msg, String info, State state, State orgState) {
Wink Saville583eaaa2012-04-13 16:11:20 -0700465 update(msg, info, state, orgState);
Wink Savilled3059482011-04-11 11:51:28 -0700466 }
467
468 /**
469 * Update the information in the record.
470 * @param state that handled the message
471 * @param orgState is the first state the received the message but
472 * did not processes the message.
473 */
Wink Saville583eaaa2012-04-13 16:11:20 -0700474 public void update(Message msg, String info, State state, State orgState) {
475 mTime = System.currentTimeMillis();
Wink Saville095c58b2012-05-29 12:40:46 -0700476 mWhat = (msg != null) ? msg.what : 0;
Wink Saville583eaaa2012-04-13 16:11:20 -0700477 mInfo = info;
478 mState = state;
479 mOrgState = orgState;
480 }
481
482 /**
483 * @return time stamp
484 */
485 public long getTime() {
486 return mTime;
487 }
488
489 /**
490 * @return msg.what
491 */
492 public long getWhat() {
493 return mWhat;
Wink Savilled3059482011-04-11 11:51:28 -0700494 }
495
496 /**
497 * @return the command that was executing
498 */
Wink Saville583eaaa2012-04-13 16:11:20 -0700499 public String getInfo() {
500 return mInfo;
Wink Savilled3059482011-04-11 11:51:28 -0700501 }
502
503 /**
504 * @return the state that handled this message
505 */
Wink Saville64c42ca2011-04-18 14:55:10 -0700506 public State getState() {
Wink Saville583eaaa2012-04-13 16:11:20 -0700507 return mState;
Wink Savilled3059482011-04-11 11:51:28 -0700508 }
509
510 /**
511 * @return the original state that received the message.
512 */
Wink Saville64c42ca2011-04-18 14:55:10 -0700513 public State getOriginalState() {
Wink Saville583eaaa2012-04-13 16:11:20 -0700514 return mOrgState;
Wink Savilled3059482011-04-11 11:51:28 -0700515 }
516
517 /**
518 * @return as string
519 */
Wink Saville095c58b2012-05-29 12:40:46 -0700520 public String toString(StateMachine sm) {
Wink Savilled3059482011-04-11 11:51:28 -0700521 StringBuilder sb = new StringBuilder();
Wink Saville583eaaa2012-04-13 16:11:20 -0700522 sb.append("time=");
523 Calendar c = Calendar.getInstance();
524 c.setTimeInMillis(mTime);
525 sb.append(String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c));
Wink Savilled3059482011-04-11 11:51:28 -0700526 sb.append(" state=");
Wink Saville583eaaa2012-04-13 16:11:20 -0700527 sb.append(mState == null ? "<null>" : mState.getName());
Wink Savilled3059482011-04-11 11:51:28 -0700528 sb.append(" orgState=");
Wink Saville583eaaa2012-04-13 16:11:20 -0700529 sb.append(mOrgState == null ? "<null>" : mOrgState.getName());
530 sb.append(" what=");
Wink Saville095c58b2012-05-29 12:40:46 -0700531 String what = sm.getWhatToString(mWhat);
532 if (TextUtils.isEmpty(what)) {
533 sb.append(mWhat);
534 sb.append("(0x");
535 sb.append(Integer.toHexString(mWhat));
536 sb.append(")");
537 } else {
538 sb.append(what);
539 }
Wink Saville583eaaa2012-04-13 16:11:20 -0700540 if ( ! TextUtils.isEmpty(mInfo)) {
541 sb.append(" ");
542 sb.append(mInfo);
Wink Savilled3059482011-04-11 11:51:28 -0700543 }
Wink Saville583eaaa2012-04-13 16:11:20 -0700544 return sb.toString();
Wink Savilled3059482011-04-11 11:51:28 -0700545 }
546 }
547
548 /**
Wink Saville095c58b2012-05-29 12:40:46 -0700549 * A list of log records including messages recently processed by the state machine.
Wink Savilled3059482011-04-11 11:51:28 -0700550 *
Wink Saville095c58b2012-05-29 12:40:46 -0700551 * The class maintains a list of log records including messages
Wink Savilled3059482011-04-11 11:51:28 -0700552 * recently processed. The list is finite and may be set in the
553 * constructor or by calling setSize. The public interface also
Wink Saville095c58b2012-05-29 12:40:46 -0700554 * includes size which returns the number of recent records,
555 * count which is the number of records processed since the
556 * the last setSize, get which returns a record and
557 * add which adds a record.
Wink Savilled3059482011-04-11 11:51:28 -0700558 */
Wink Saville095c58b2012-05-29 12:40:46 -0700559 private static class LogRecords {
Wink Savilled3059482011-04-11 11:51:28 -0700560
561 private static final int DEFAULT_SIZE = 20;
562
Wink Saville095c58b2012-05-29 12:40:46 -0700563 private Vector<LogRec> mLogRecords = new Vector<LogRec>();
Wink Savilled3059482011-04-11 11:51:28 -0700564 private int mMaxSize = DEFAULT_SIZE;
565 private int mOldestIndex = 0;
566 private int mCount = 0;
567
568 /**
Wink Saville583eaaa2012-04-13 16:11:20 -0700569 * private constructor use add
Wink Savilled3059482011-04-11 11:51:28 -0700570 */
Wink Saville095c58b2012-05-29 12:40:46 -0700571 private LogRecords() {
Wink Savilled3059482011-04-11 11:51:28 -0700572 }
573
574 /**
Wink Saville095c58b2012-05-29 12:40:46 -0700575 * Set size of messages to maintain and clears all current records.
Wink Savilled3059482011-04-11 11:51:28 -0700576 *
Wink Saville095c58b2012-05-29 12:40:46 -0700577 * @param maxSize number of records to maintain at anyone time.
Wink Savilled3059482011-04-11 11:51:28 -0700578 */
Wink Saville095c58b2012-05-29 12:40:46 -0700579 synchronized void setSize(int maxSize) {
Wink Savilled3059482011-04-11 11:51:28 -0700580 mMaxSize = maxSize;
581 mCount = 0;
Wink Saville095c58b2012-05-29 12:40:46 -0700582 mLogRecords.clear();
Wink Savilled3059482011-04-11 11:51:28 -0700583 }
584
585 /**
Wink Saville095c58b2012-05-29 12:40:46 -0700586 * @return the number of recent records.
Wink Savilled3059482011-04-11 11:51:28 -0700587 */
Wink Saville095c58b2012-05-29 12:40:46 -0700588 synchronized int size() {
589 return mLogRecords.size();
Wink Savilled3059482011-04-11 11:51:28 -0700590 }
591
592 /**
Wink Saville095c58b2012-05-29 12:40:46 -0700593 * @return the total number of records processed since size was set.
Wink Savilled3059482011-04-11 11:51:28 -0700594 */
Wink Saville095c58b2012-05-29 12:40:46 -0700595 synchronized int count() {
Wink Savilled3059482011-04-11 11:51:28 -0700596 return mCount;
597 }
598
599 /**
Wink Saville095c58b2012-05-29 12:40:46 -0700600 * Clear the list of records.
Jaikumar Ganesh6f9a6162011-11-28 13:13:02 -0800601 */
Wink Saville095c58b2012-05-29 12:40:46 -0700602 synchronized void cleanup() {
603 mLogRecords.clear();
Jaikumar Ganesh6f9a6162011-11-28 13:13:02 -0800604 }
605
606 /**
Wink Savilled3059482011-04-11 11:51:28 -0700607 * @return the information on a particular record. 0 is the oldest
608 * record and size()-1 is the newest record. If the index is to
609 * large null is returned.
610 */
Wink Saville095c58b2012-05-29 12:40:46 -0700611 synchronized LogRec get(int index) {
Wink Savilled3059482011-04-11 11:51:28 -0700612 int nextIndex = mOldestIndex + index;
613 if (nextIndex >= mMaxSize) {
614 nextIndex -= mMaxSize;
615 }
616 if (nextIndex >= size()) {
617 return null;
618 } else {
Wink Saville095c58b2012-05-29 12:40:46 -0700619 return mLogRecords.get(nextIndex);
Wink Savilled3059482011-04-11 11:51:28 -0700620 }
621 }
622
623 /**
624 * Add a processed message.
625 *
Wink Saville583eaaa2012-04-13 16:11:20 -0700626 * @param msg
627 * @param messageInfo to be stored
Wink Savilled3059482011-04-11 11:51:28 -0700628 * @param state that handled the message
629 * @param orgState is the first state the received the message but
630 * did not processes the message.
631 */
Wink Saville095c58b2012-05-29 12:40:46 -0700632 synchronized void add(Message msg, String messageInfo, State state, State orgState) {
Wink Savilled3059482011-04-11 11:51:28 -0700633 mCount += 1;
Wink Saville095c58b2012-05-29 12:40:46 -0700634 if (mLogRecords.size() < mMaxSize) {
635 mLogRecords.add(new LogRec(msg, messageInfo, state, orgState));
Wink Savilled3059482011-04-11 11:51:28 -0700636 } else {
Wink Saville095c58b2012-05-29 12:40:46 -0700637 LogRec pmi = mLogRecords.get(mOldestIndex);
Wink Savilled3059482011-04-11 11:51:28 -0700638 mOldestIndex += 1;
639 if (mOldestIndex >= mMaxSize) {
640 mOldestIndex = 0;
641 }
Wink Saville583eaaa2012-04-13 16:11:20 -0700642 pmi.update(msg, messageInfo, state, orgState);
Wink Savilled3059482011-04-11 11:51:28 -0700643 }
644 }
645 }
646
Jaikumar Ganesh6f9a6162011-11-28 13:13:02 -0800647
Wink Saville64c42ca2011-04-18 14:55:10 -0700648 private static class SmHandler extends Handler {
Wink Savillefc5b4802009-12-08 21:22:24 -0800649
650 /** The debug flag */
651 private boolean mDbg = false;
652
Wink Savillecea056f2012-03-26 15:03:16 -0700653 /** The SmHandler object, identifies that message is internal */
654 private static final Object mSmHandlerObj = new Object();
Wink Saville1b8b98b2010-03-11 11:49:54 -0800655
Wink Savillea4f3bec2010-05-19 09:11:38 -0700656 /** The current message */
657 private Message mMsg;
658
Wink Saville095c58b2012-05-29 12:40:46 -0700659 /** A list of log records including messages this state machine has processed */
660 private LogRecords mLogRecords = new LogRecords();
Wink Savillefc5b4802009-12-08 21:22:24 -0800661
662 /** true if construction of the state machine has not been completed */
663 private boolean mIsConstructionCompleted;
664
665 /** Stack used to manage the current hierarchy of states */
666 private StateInfo mStateStack[];
667
668 /** Top of mStateStack */
669 private int mStateStackTopIndex = -1;
670
671 /** A temporary stack used to manage the state stack */
672 private StateInfo mTempStateStack[];
673
674 /** The top of the mTempStateStack */
675 private int mTempStateStackCount;
676
677 /** State used when state machine is halted */
678 private HaltingState mHaltingState = new HaltingState();
679
Wink Saville1b8b98b2010-03-11 11:49:54 -0800680 /** State used when state machine is quitting */
681 private QuittingState mQuittingState = new QuittingState();
682
Wink Saville64c42ca2011-04-18 14:55:10 -0700683 /** Reference to the StateMachine */
684 private StateMachine mSm;
Wink Savillefc5b4802009-12-08 21:22:24 -0800685
686 /**
687 * Information about a state.
688 * Used to maintain the hierarchy.
689 */
690 private class StateInfo {
691 /** The state */
Wink Saville64c42ca2011-04-18 14:55:10 -0700692 State state;
Wink Savillefc5b4802009-12-08 21:22:24 -0800693
694 /** The parent of this state, null if there is no parent */
695 StateInfo parentStateInfo;
696
697 /** True when the state has been entered and on the stack */
698 boolean active;
699
700 /**
701 * Convert StateInfo to string
702 */
703 @Override
704 public String toString() {
705 return "state=" + state.getName() + ",active=" + active
706 + ",parent=" + ((parentStateInfo == null) ?
707 "null" : parentStateInfo.state.getName());
708 }
709 }
710
711 /** The map of all of the states in the state machine */
Wink Saville64c42ca2011-04-18 14:55:10 -0700712 private HashMap<State, StateInfo> mStateInfo =
713 new HashMap<State, StateInfo>();
Wink Savillefc5b4802009-12-08 21:22:24 -0800714
715 /** The initial state that will process the first message */
Wink Saville64c42ca2011-04-18 14:55:10 -0700716 private State mInitialState;
Wink Savillefc5b4802009-12-08 21:22:24 -0800717
718 /** The destination state when transitionTo has been invoked */
Wink Saville64c42ca2011-04-18 14:55:10 -0700719 private State mDestState;
Wink Savillefc5b4802009-12-08 21:22:24 -0800720
721 /** The list of deferred messages */
722 private ArrayList<Message> mDeferredMessages = new ArrayList<Message>();
723
724 /**
725 * State entered when transitionToHaltingState is called.
726 */
Wink Saville64c42ca2011-04-18 14:55:10 -0700727 private class HaltingState extends State {
Wink Savillefc5b4802009-12-08 21:22:24 -0800728 @Override
729 public boolean processMessage(Message msg) {
Wink Saville64c42ca2011-04-18 14:55:10 -0700730 mSm.haltedProcessMessage(msg);
Wink Savillefc5b4802009-12-08 21:22:24 -0800731 return true;
732 }
733 }
734
735 /**
Wink Saville1b8b98b2010-03-11 11:49:54 -0800736 * State entered when a valid quit message is handled.
737 */
Wink Saville64c42ca2011-04-18 14:55:10 -0700738 private class QuittingState extends State {
Wink Saville1b8b98b2010-03-11 11:49:54 -0800739 @Override
740 public boolean processMessage(Message msg) {
Wink Savillea4f3bec2010-05-19 09:11:38 -0700741 return NOT_HANDLED;
Wink Saville1b8b98b2010-03-11 11:49:54 -0800742 }
743 }
744
745 /**
Wink Savillefc5b4802009-12-08 21:22:24 -0800746 * Handle messages sent to the state machine by calling
747 * the current state's processMessage. It also handles
748 * the enter/exit calls and placing any deferred messages
749 * back onto the queue when transitioning to a new state.
750 */
751 @Override
752 public final void handleMessage(Message msg) {
753 if (mDbg) Log.d(TAG, "handleMessage: E msg.what=" + msg.what);
754
Wink Savillea4f3bec2010-05-19 09:11:38 -0700755 /** Save the current message */
756 mMsg = msg;
757
Wink Savillecea056f2012-03-26 15:03:16 -0700758 if (mIsConstructionCompleted) {
759 /** Normal path */
760 processMsg(msg);
761 } else if (!mIsConstructionCompleted &&
762 (mMsg.what == SM_INIT_CMD) && (mMsg.obj == mSmHandlerObj)) {
763 /** Initial one time path. */
764 mIsConstructionCompleted = true;
765 invokeEnterMethods(0);
766 } else {
767 throw new RuntimeException("StateMachine.handleMessage: " +
768 "The start method not called, received msg: " + msg);
Wink Savillefc5b4802009-12-08 21:22:24 -0800769 }
Wink Savillee7be6a82010-03-18 17:03:30 -0700770 performTransitions();
Wink Savillefc5b4802009-12-08 21:22:24 -0800771
Wink Savillee7be6a82010-03-18 17:03:30 -0700772 if (mDbg) Log.d(TAG, "handleMessage: X");
773 }
774
775 /**
776 * Do any transitions
777 */
778 private void performTransitions() {
Wink Savillefc5b4802009-12-08 21:22:24 -0800779 /**
780 * If transitionTo has been called, exit and then enter
Wink Savillee7be6a82010-03-18 17:03:30 -0700781 * the appropriate states. We loop on this to allow
782 * enter and exit methods to use transitionTo.
Wink Savillefc5b4802009-12-08 21:22:24 -0800783 */
Wink Saville64c42ca2011-04-18 14:55:10 -0700784 State destState = null;
Wink Savillee7be6a82010-03-18 17:03:30 -0700785 while (mDestState != null) {
Wink Savillefc5b4802009-12-08 21:22:24 -0800786 if (mDbg) Log.d(TAG, "handleMessage: new destination call exit");
787
788 /**
Wink Savillee7be6a82010-03-18 17:03:30 -0700789 * Save mDestState locally and set to null
790 * to know if enter/exit use transitionTo.
791 */
792 destState = mDestState;
793 mDestState = null;
794
795 /**
Wink Savillefc5b4802009-12-08 21:22:24 -0800796 * Determine the states to exit and enter and return the
797 * common ancestor state of the enter/exit states. Then
798 * invoke the exit methods then the enter methods.
799 */
Wink Savillee7be6a82010-03-18 17:03:30 -0700800 StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState);
Wink Savillefc5b4802009-12-08 21:22:24 -0800801 invokeExitMethods(commonStateInfo);
802 int stateStackEnteringIndex = moveTempStateStackToStateStack();
803 invokeEnterMethods(stateStackEnteringIndex);
804
805
806 /**
807 * Since we have transitioned to a new state we need to have
808 * any deferred messages moved to the front of the message queue
809 * so they will be processed before any other messages in the
810 * message queue.
811 */
812 moveDeferredMessageAtFrontOfQueue();
Wink Savillee7be6a82010-03-18 17:03:30 -0700813 }
Wink Savillefc5b4802009-12-08 21:22:24 -0800814
Wink Savillee7be6a82010-03-18 17:03:30 -0700815 /**
816 * After processing all transitions check and
817 * see if the last transition was to quit or halt.
818 */
819 if (destState != null) {
820 if (destState == mQuittingState) {
Wink Saville095c58b2012-05-29 12:40:46 -0700821 /**
822 * Call onQuitting to let subclasses cleanup.
823 */
824 mSm.onQuitting();
Jaikumar Ganesh6f9a6162011-11-28 13:13:02 -0800825 cleanupAfterQuitting();
Wink Savillee7be6a82010-03-18 17:03:30 -0700826 } else if (destState == mHaltingState) {
827 /**
Wink Saville095c58b2012-05-29 12:40:46 -0700828 * Call onHalting() if we've transitioned to the halting
Wink Savillee7be6a82010-03-18 17:03:30 -0700829 * state. All subsequent messages will be processed in
830 * in the halting state which invokes haltedProcessMessage(msg);
831 */
Wink Saville095c58b2012-05-29 12:40:46 -0700832 mSm.onHalting();
Wink Savillefc5b4802009-12-08 21:22:24 -0800833 }
Wink Savillefc5b4802009-12-08 21:22:24 -0800834 }
Wink Savillefc5b4802009-12-08 21:22:24 -0800835 }
836
837 /**
Jaikumar Ganesh6f9a6162011-11-28 13:13:02 -0800838 * Cleanup all the static variables and the looper after the SM has been quit.
839 */
840 private final void cleanupAfterQuitting() {
Jaikumar Ganesh6f9a6162011-11-28 13:13:02 -0800841 if (mSm.mSmThread != null) {
842 // If we made the thread then quit looper which stops the thread.
843 getLooper().quit();
844 mSm.mSmThread = null;
845 }
846
847 mSm.mSmHandler = null;
848 mSm = null;
849 mMsg = null;
Wink Saville095c58b2012-05-29 12:40:46 -0700850 mLogRecords.cleanup();
Jaikumar Ganesh6f9a6162011-11-28 13:13:02 -0800851 mStateStack = null;
852 mTempStateStack = null;
853 mStateInfo.clear();
854 mInitialState = null;
855 mDestState = null;
856 mDeferredMessages.clear();
857 }
858
859 /**
Wink Savillefc5b4802009-12-08 21:22:24 -0800860 * Complete the construction of the state machine.
861 */
862 private final void completeConstruction() {
863 if (mDbg) Log.d(TAG, "completeConstruction: E");
864
865 /**
866 * Determine the maximum depth of the state hierarchy
867 * so we can allocate the state stacks.
868 */
869 int maxDepth = 0;
870 for (StateInfo si : mStateInfo.values()) {
871 int depth = 0;
872 for (StateInfo i = si; i != null; depth++) {
873 i = i.parentStateInfo;
874 }
875 if (maxDepth < depth) {
876 maxDepth = depth;
877 }
878 }
879 if (mDbg) Log.d(TAG, "completeConstruction: maxDepth=" + maxDepth);
880
881 mStateStack = new StateInfo[maxDepth];
882 mTempStateStack = new StateInfo[maxDepth];
883 setupInitialStateStack();
884
Wink Savillecea056f2012-03-26 15:03:16 -0700885 /** Sending SM_INIT_CMD message to invoke enter methods asynchronously */
886 sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj));
Wink Savillee7be6a82010-03-18 17:03:30 -0700887
Wink Savillefc5b4802009-12-08 21:22:24 -0800888 if (mDbg) Log.d(TAG, "completeConstruction: X");
889 }
890
891 /**
892 * Process the message. If the current state doesn't handle
893 * it, call the states parent and so on. If it is never handled then
894 * call the state machines unhandledMessage method.
895 */
896 private final void processMsg(Message msg) {
897 StateInfo curStateInfo = mStateStack[mStateStackTopIndex];
898 if (mDbg) {
899 Log.d(TAG, "processMsg: " + curStateInfo.state.getName());
900 }
Wink Savillefc5b4802009-12-08 21:22:24 -0800901
Wink Saville095c58b2012-05-29 12:40:46 -0700902 if (isQuit(msg)) {
903 transitionTo(mQuittingState);
904 } else {
905 while (!curStateInfo.state.processMessage(msg)) {
906 /**
907 * Not processed
908 */
909 curStateInfo = curStateInfo.parentStateInfo;
910 if (curStateInfo == null) {
911 /**
912 * No parents left so it's not handled
913 */
914 mSm.unhandledMessage(msg);
915 break;
916 }
917 if (mDbg) {
918 Log.d(TAG, "processMsg: " + curStateInfo.state.getName());
919 }
920 }
921
922 /**
923 * Record that we processed the message
924 */
925 if (mSm.recordLogRec(msg)) {
926 if (curStateInfo != null) {
927 State orgState = mStateStack[mStateStackTopIndex].state;
928 mLogRecords.add(msg, mSm.getLogRecString(msg), curStateInfo.state,
929 orgState);
930 } else {
931 mLogRecords.add(msg, mSm.getLogRecString(msg), null, null);
932 }
Wink Saville583eaaa2012-04-13 16:11:20 -0700933 }
Wink Savillefc5b4802009-12-08 21:22:24 -0800934 }
935 }
936
937 /**
938 * Call the exit method for each state from the top of stack
939 * up to the common ancestor state.
940 */
941 private final void invokeExitMethods(StateInfo commonStateInfo) {
942 while ((mStateStackTopIndex >= 0) &&
943 (mStateStack[mStateStackTopIndex] != commonStateInfo)) {
Wink Saville64c42ca2011-04-18 14:55:10 -0700944 State curState = mStateStack[mStateStackTopIndex].state;
Wink Savillefc5b4802009-12-08 21:22:24 -0800945 if (mDbg) Log.d(TAG, "invokeExitMethods: " + curState.getName());
946 curState.exit();
947 mStateStack[mStateStackTopIndex].active = false;
948 mStateStackTopIndex -= 1;
949 }
950 }
951
952 /**
953 * Invoke the enter method starting at the entering index to top of state stack
954 */
955 private final void invokeEnterMethods(int stateStackEnteringIndex) {
956 for (int i = stateStackEnteringIndex; i <= mStateStackTopIndex; i++) {
957 if (mDbg) Log.d(TAG, "invokeEnterMethods: " + mStateStack[i].state.getName());
958 mStateStack[i].state.enter();
959 mStateStack[i].active = true;
960 }
961 }
962
963 /**
964 * Move the deferred message to the front of the message queue.
965 */
966 private final void moveDeferredMessageAtFrontOfQueue() {
967 /**
968 * The oldest messages on the deferred list must be at
969 * the front of the queue so start at the back, which
970 * as the most resent message and end with the oldest
971 * messages at the front of the queue.
972 */
973 for (int i = mDeferredMessages.size() - 1; i >= 0; i-- ) {
974 Message curMsg = mDeferredMessages.get(i);
975 if (mDbg) Log.d(TAG, "moveDeferredMessageAtFrontOfQueue; what=" + curMsg.what);
976 sendMessageAtFrontOfQueue(curMsg);
977 }
978 mDeferredMessages.clear();
979 }
980
981 /**
982 * Move the contents of the temporary stack to the state stack
983 * reversing the order of the items on the temporary stack as
984 * they are moved.
985 *
Wink Saville64c42ca2011-04-18 14:55:10 -0700986 * @return index into mStateStack where entering needs to start
Wink Savillefc5b4802009-12-08 21:22:24 -0800987 */
988 private final int moveTempStateStackToStateStack() {
989 int startingIndex = mStateStackTopIndex + 1;
990 int i = mTempStateStackCount - 1;
991 int j = startingIndex;
992 while (i >= 0) {
993 if (mDbg) Log.d(TAG, "moveTempStackToStateStack: i=" + i + ",j=" + j);
994 mStateStack[j] = mTempStateStack[i];
995 j += 1;
996 i -= 1;
997 }
998
999 mStateStackTopIndex = j - 1;
1000 if (mDbg) {
1001 Log.d(TAG, "moveTempStackToStateStack: X mStateStackTop="
1002 + mStateStackTopIndex + ",startingIndex=" + startingIndex
1003 + ",Top=" + mStateStack[mStateStackTopIndex].state.getName());
1004 }
1005 return startingIndex;
1006 }
1007
1008 /**
1009 * Setup the mTempStateStack with the states we are going to enter.
1010 *
1011 * This is found by searching up the destState's ancestors for a
1012 * state that is already active i.e. StateInfo.active == true.
1013 * The destStae and all of its inactive parents will be on the
1014 * TempStateStack as the list of states to enter.
1015 *
1016 * @return StateInfo of the common ancestor for the destState and
1017 * current state or null if there is no common parent.
1018 */
Wink Saville64c42ca2011-04-18 14:55:10 -07001019 private final StateInfo setupTempStateStackWithStatesToEnter(State destState) {
Wink Savillefc5b4802009-12-08 21:22:24 -08001020 /**
1021 * Search up the parent list of the destination state for an active
1022 * state. Use a do while() loop as the destState must always be entered
1023 * even if it is active. This can happen if we are exiting/entering
1024 * the current state.
1025 */
1026 mTempStateStackCount = 0;
1027 StateInfo curStateInfo = mStateInfo.get(destState);
1028 do {
1029 mTempStateStack[mTempStateStackCount++] = curStateInfo;
1030 curStateInfo = curStateInfo.parentStateInfo;
1031 } while ((curStateInfo != null) && !curStateInfo.active);
1032
1033 if (mDbg) {
1034 Log.d(TAG, "setupTempStateStackWithStatesToEnter: X mTempStateStackCount="
1035 + mTempStateStackCount + ",curStateInfo: " + curStateInfo);
1036 }
1037 return curStateInfo;
1038 }
1039
1040 /**
1041 * Initialize StateStack to mInitialState.
1042 */
1043 private final void setupInitialStateStack() {
1044 if (mDbg) {
1045 Log.d(TAG, "setupInitialStateStack: E mInitialState="
1046 + mInitialState.getName());
1047 }
1048
1049 StateInfo curStateInfo = mStateInfo.get(mInitialState);
1050 for (mTempStateStackCount = 0; curStateInfo != null; mTempStateStackCount++) {
1051 mTempStateStack[mTempStateStackCount] = curStateInfo;
1052 curStateInfo = curStateInfo.parentStateInfo;
1053 }
1054
1055 // Empty the StateStack
1056 mStateStackTopIndex = -1;
1057
1058 moveTempStateStackToStateStack();
1059 }
1060
1061 /**
Wink Savillea4f3bec2010-05-19 09:11:38 -07001062 * @return current message
1063 */
1064 private final Message getCurrentMessage() {
1065 return mMsg;
1066 }
1067
1068 /**
Wink Savillefc5b4802009-12-08 21:22:24 -08001069 * @return current state
1070 */
Wink Saville64c42ca2011-04-18 14:55:10 -07001071 private final IState getCurrentState() {
Wink Savillefc5b4802009-12-08 21:22:24 -08001072 return mStateStack[mStateStackTopIndex].state;
1073 }
1074
1075 /**
1076 * Add a new state to the state machine. Bottom up addition
1077 * of states is allowed but the same state may only exist
1078 * in one hierarchy.
1079 *
1080 * @param state the state to add
1081 * @param parent the parent of state
1082 * @return stateInfo for this state
1083 */
Wink Saville64c42ca2011-04-18 14:55:10 -07001084 private final StateInfo addState(State state, State parent) {
Wink Savillefc5b4802009-12-08 21:22:24 -08001085 if (mDbg) {
1086 Log.d(TAG, "addStateInternal: E state=" + state.getName()
1087 + ",parent=" + ((parent == null) ? "" : parent.getName()));
1088 }
1089 StateInfo parentStateInfo = null;
1090 if (parent != null) {
1091 parentStateInfo = mStateInfo.get(parent);
1092 if (parentStateInfo == null) {
1093 // Recursively add our parent as it's not been added yet.
1094 parentStateInfo = addState(parent, null);
1095 }
1096 }
1097 StateInfo stateInfo = mStateInfo.get(state);
1098 if (stateInfo == null) {
1099 stateInfo = new StateInfo();
1100 mStateInfo.put(state, stateInfo);
1101 }
1102
1103 // Validate that we aren't adding the same state in two different hierarchies.
1104 if ((stateInfo.parentStateInfo != null) &&
1105 (stateInfo.parentStateInfo != parentStateInfo)) {
1106 throw new RuntimeException("state already added");
1107 }
1108 stateInfo.state = state;
1109 stateInfo.parentStateInfo = parentStateInfo;
1110 stateInfo.active = false;
1111 if (mDbg) Log.d(TAG, "addStateInternal: X stateInfo: " + stateInfo);
1112 return stateInfo;
1113 }
1114
1115 /**
1116 * Constructor
1117 *
1118 * @param looper for dispatching messages
Wink Saville64c42ca2011-04-18 14:55:10 -07001119 * @param sm the hierarchical state machine
Wink Savillefc5b4802009-12-08 21:22:24 -08001120 */
Wink Saville64c42ca2011-04-18 14:55:10 -07001121 private SmHandler(Looper looper, StateMachine sm) {
Wink Savillefc5b4802009-12-08 21:22:24 -08001122 super(looper);
Wink Saville64c42ca2011-04-18 14:55:10 -07001123 mSm = sm;
Wink Savillefc5b4802009-12-08 21:22:24 -08001124
1125 addState(mHaltingState, null);
Wink Saville1b8b98b2010-03-11 11:49:54 -08001126 addState(mQuittingState, null);
Wink Savillefc5b4802009-12-08 21:22:24 -08001127 }
1128
Wink Saville64c42ca2011-04-18 14:55:10 -07001129 /** @see StateMachine#setInitialState(State) */
1130 private final void setInitialState(State initialState) {
Wink Savillecea056f2012-03-26 15:03:16 -07001131 if (mDbg) Log.d(TAG, "setInitialState: initialState=" + initialState.getName());
Wink Savillefc5b4802009-12-08 21:22:24 -08001132 mInitialState = initialState;
1133 }
1134
Wink Saville64c42ca2011-04-18 14:55:10 -07001135 /** @see StateMachine#transitionTo(IState) */
1136 private final void transitionTo(IState destState) {
1137 mDestState = (State) destState;
Wink Savillecea056f2012-03-26 15:03:16 -07001138 if (mDbg) Log.d(TAG, "transitionTo: destState=" + mDestState.getName());
Wink Savillefc5b4802009-12-08 21:22:24 -08001139 }
1140
Wink Saville64c42ca2011-04-18 14:55:10 -07001141 /** @see StateMachine#deferMessage(Message) */
Wink Savillefc5b4802009-12-08 21:22:24 -08001142 private final void deferMessage(Message msg) {
1143 if (mDbg) Log.d(TAG, "deferMessage: msg=" + msg.what);
1144
1145 /* Copy the "msg" to "newMsg" as "msg" will be recycled */
1146 Message newMsg = obtainMessage();
1147 newMsg.copyFrom(msg);
1148
1149 mDeferredMessages.add(newMsg);
1150 }
1151
Wink Saville095c58b2012-05-29 12:40:46 -07001152 /** @see StateMachine#quit() */
Wink Saville1b8b98b2010-03-11 11:49:54 -08001153 private final void quit() {
1154 if (mDbg) Log.d(TAG, "quit:");
Wink Savillecea056f2012-03-26 15:03:16 -07001155 sendMessage(obtainMessage(SM_QUIT_CMD, mSmHandlerObj));
Wink Saville1b8b98b2010-03-11 11:49:54 -08001156 }
1157
Wink Saville095c58b2012-05-29 12:40:46 -07001158 /** @see StateMachine#quitNow() */
1159 private final void quitNow() {
1160 if (mDbg) Log.d(TAG, "abort:");
1161 sendMessageAtFrontOfQueue(obtainMessage(SM_QUIT_CMD, mSmHandlerObj));
1162 }
1163
1164 /** Validate that the message was sent by quit or abort. */
Wink Saville1b8b98b2010-03-11 11:49:54 -08001165 private final boolean isQuit(Message msg) {
Wink Savillecea056f2012-03-26 15:03:16 -07001166 return (msg.what == SM_QUIT_CMD) && (msg.obj == mSmHandlerObj);
Wink Saville1b8b98b2010-03-11 11:49:54 -08001167 }
1168
Wink Saville64c42ca2011-04-18 14:55:10 -07001169 /** @see StateMachine#isDbg() */
Wink Savillefc5b4802009-12-08 21:22:24 -08001170 private final boolean isDbg() {
1171 return mDbg;
1172 }
1173
Wink Saville64c42ca2011-04-18 14:55:10 -07001174 /** @see StateMachine#setDbg(boolean) */
Wink Savillefc5b4802009-12-08 21:22:24 -08001175 private final void setDbg(boolean dbg) {
1176 mDbg = dbg;
1177 }
1178
Wink Savillefc5b4802009-12-08 21:22:24 -08001179 }
1180
Wink Saville64c42ca2011-04-18 14:55:10 -07001181 private SmHandler mSmHandler;
1182 private HandlerThread mSmThread;
Wink Savillefc5b4802009-12-08 21:22:24 -08001183
1184 /**
1185 * Initialize.
1186 *
1187 * @param looper for this state machine
1188 * @param name of the state machine
1189 */
Wink Savillef0f566e2010-03-11 14:03:50 -08001190 private void initStateMachine(String name, Looper looper) {
Wink Savillefc5b4802009-12-08 21:22:24 -08001191 mName = name;
Wink Saville64c42ca2011-04-18 14:55:10 -07001192 mSmHandler = new SmHandler(looper, this);
Wink Savillefc5b4802009-12-08 21:22:24 -08001193 }
1194
1195 /**
Wink Saville64c42ca2011-04-18 14:55:10 -07001196 * Constructor creates a StateMachine with its own thread.
Wink Savillefc5b4802009-12-08 21:22:24 -08001197 *
1198 * @param name of the state machine
1199 */
Wink Saville64c42ca2011-04-18 14:55:10 -07001200 protected StateMachine(String name) {
1201 mSmThread = new HandlerThread(name);
1202 mSmThread.start();
1203 Looper looper = mSmThread.getLooper();
Wink Savillefc5b4802009-12-08 21:22:24 -08001204
Wink Savillef0f566e2010-03-11 14:03:50 -08001205 initStateMachine(name, looper);
Wink Savillefc5b4802009-12-08 21:22:24 -08001206 }
1207
1208 /**
Ken Wakasaf76a50c2012-03-09 19:56:35 +09001209 * Constructor creates a StateMachine using the looper.
Wink Savillefc5b4802009-12-08 21:22:24 -08001210 *
1211 * @param name of the state machine
1212 */
Wink Saville64c42ca2011-04-18 14:55:10 -07001213 protected StateMachine(String name, Looper looper) {
Wink Savillef0f566e2010-03-11 14:03:50 -08001214 initStateMachine(name, looper);
Wink Savillefc5b4802009-12-08 21:22:24 -08001215 }
1216
1217 /**
1218 * Add a new state to the state machine
1219 * @param state the state to add
1220 * @param parent the parent of state
1221 */
Wink Saville64c42ca2011-04-18 14:55:10 -07001222 protected final void addState(State state, State parent) {
1223 mSmHandler.addState(state, parent);
Wink Savillefc5b4802009-12-08 21:22:24 -08001224 }
Wink Savillea4f3bec2010-05-19 09:11:38 -07001225
1226 /**
1227 * @return current message
1228 */
1229 protected final Message getCurrentMessage() {
Wink Saville64c42ca2011-04-18 14:55:10 -07001230 return mSmHandler.getCurrentMessage();
Wink Savillea4f3bec2010-05-19 09:11:38 -07001231 }
1232
Wink Savillefc5b4802009-12-08 21:22:24 -08001233 /**
1234 * @return current state
1235 */
Wink Saville64c42ca2011-04-18 14:55:10 -07001236 protected final IState getCurrentState() {
1237 return mSmHandler.getCurrentState();
Wink Savillefc5b4802009-12-08 21:22:24 -08001238 }
1239
Wink Savillefc5b4802009-12-08 21:22:24 -08001240 /**
1241 * Add a new state to the state machine, parent will be null
1242 * @param state to add
1243 */
Wink Saville64c42ca2011-04-18 14:55:10 -07001244 protected final void addState(State state) {
1245 mSmHandler.addState(state, null);
Wink Savillefc5b4802009-12-08 21:22:24 -08001246 }
1247
1248 /**
1249 * Set the initial state. This must be invoked before
1250 * and messages are sent to the state machine.
1251 *
1252 * @param initialState is the state which will receive the first message.
1253 */
Wink Saville64c42ca2011-04-18 14:55:10 -07001254 protected final void setInitialState(State initialState) {
1255 mSmHandler.setInitialState(initialState);
Wink Savillefc5b4802009-12-08 21:22:24 -08001256 }
1257
1258 /**
1259 * transition to destination state. Upon returning
1260 * from processMessage the current state's exit will
1261 * be executed and upon the next message arriving
1262 * destState.enter will be invoked.
1263 *
Isaac Levyd2fe04b2011-07-22 08:48:26 -07001264 * this function can also be called inside the enter function of the
1265 * previous transition target, but the behavior is undefined when it is
1266 * called mid-way through a previous transition (for example, calling this
1267 * in the enter() routine of a intermediate node when the current transition
1268 * target is one of the nodes descendants).
1269 *
Wink Savillefc5b4802009-12-08 21:22:24 -08001270 * @param destState will be the state that receives the next message.
1271 */
Wink Saville64c42ca2011-04-18 14:55:10 -07001272 protected final void transitionTo(IState destState) {
1273 mSmHandler.transitionTo(destState);
Wink Savillefc5b4802009-12-08 21:22:24 -08001274 }
1275
1276 /**
1277 * transition to halt state. Upon returning
1278 * from processMessage we will exit all current
Wink Saville095c58b2012-05-29 12:40:46 -07001279 * states, execute the onHalting() method and then
1280 * for all subsequent messages haltedProcessMessage
Wink Savillefc5b4802009-12-08 21:22:24 -08001281 * will be called.
1282 */
1283 protected final void transitionToHaltingState() {
Wink Saville64c42ca2011-04-18 14:55:10 -07001284 mSmHandler.transitionTo(mSmHandler.mHaltingState);
Wink Savillefc5b4802009-12-08 21:22:24 -08001285 }
1286
1287 /**
1288 * Defer this message until next state transition.
1289 * Upon transitioning all deferred messages will be
1290 * placed on the queue and reprocessed in the original
1291 * order. (i.e. The next state the oldest messages will
1292 * be processed first)
1293 *
1294 * @param msg is deferred until the next transition.
1295 */
1296 protected final void deferMessage(Message msg) {
Wink Saville64c42ca2011-04-18 14:55:10 -07001297 mSmHandler.deferMessage(msg);
Wink Savillefc5b4802009-12-08 21:22:24 -08001298 }
1299
Wink Savillefc5b4802009-12-08 21:22:24 -08001300 /**
1301 * Called when message wasn't handled
1302 *
1303 * @param msg that couldn't be handled.
1304 */
1305 protected void unhandledMessage(Message msg) {
Wink Saville64c42ca2011-04-18 14:55:10 -07001306 if (mSmHandler.mDbg) Log.e(TAG, mName + " - unhandledMessage: msg.what=" + msg.what);
Wink Savillefc5b4802009-12-08 21:22:24 -08001307 }
1308
1309 /**
1310 * Called for any message that is received after
1311 * transitionToHalting is called.
1312 */
1313 protected void haltedProcessMessage(Message msg) {
1314 }
1315
1316 /**
Wink Savilled3059482011-04-11 11:51:28 -07001317 * This will be called once after handling a message that called
1318 * transitionToHalting. All subsequent messages will invoke
Wink Saville64c42ca2011-04-18 14:55:10 -07001319 * {@link StateMachine#haltedProcessMessage(Message)}
Wink Savillefc5b4802009-12-08 21:22:24 -08001320 */
Wink Saville095c58b2012-05-29 12:40:46 -07001321 protected void onHalting() {
Wink Savillefc5b4802009-12-08 21:22:24 -08001322 }
1323
1324 /**
Wink Savilled3059482011-04-11 11:51:28 -07001325 * This will be called once after a quit message that was NOT handled by
Wink Saville64c42ca2011-04-18 14:55:10 -07001326 * the derived StateMachine. The StateMachine will stop and any subsequent messages will be
1327 * ignored. In addition, if this StateMachine created the thread, the thread will
Wink Savilled3059482011-04-11 11:51:28 -07001328 * be stopped after this method returns.
Wink Saville1b8b98b2010-03-11 11:49:54 -08001329 */
Wink Saville095c58b2012-05-29 12:40:46 -07001330 protected void onQuitting() {
Wink Saville1b8b98b2010-03-11 11:49:54 -08001331 }
1332
1333 /**
Wink Savillefc5b4802009-12-08 21:22:24 -08001334 * @return the name
1335 */
1336 public final String getName() {
1337 return mName;
1338 }
1339
1340 /**
Wink Saville095c58b2012-05-29 12:40:46 -07001341 * Set number of log records to maintain and clears all current records.
Wink Savillefc5b4802009-12-08 21:22:24 -08001342 *
1343 * @param maxSize number of messages to maintain at anyone time.
1344 */
Wink Saville095c58b2012-05-29 12:40:46 -07001345 public final void setLogRecSize(int maxSize) {
1346 mSmHandler.mLogRecords.setSize(maxSize);
Wink Savillefc5b4802009-12-08 21:22:24 -08001347 }
1348
1349 /**
Wink Saville095c58b2012-05-29 12:40:46 -07001350 * @return number of log records
Wink Savillefc5b4802009-12-08 21:22:24 -08001351 */
Wink Saville095c58b2012-05-29 12:40:46 -07001352 public final int getLogRecSize() {
1353 return mSmHandler.mLogRecords.size();
Wink Savillefc5b4802009-12-08 21:22:24 -08001354 }
1355
1356 /**
Wink Saville095c58b2012-05-29 12:40:46 -07001357 * @return the total number of records processed
Wink Savillefc5b4802009-12-08 21:22:24 -08001358 */
Wink Saville095c58b2012-05-29 12:40:46 -07001359 public final int getLogRecCount() {
1360 return mSmHandler.mLogRecords.count();
Wink Savillefc5b4802009-12-08 21:22:24 -08001361 }
1362
1363 /**
Wink Saville095c58b2012-05-29 12:40:46 -07001364 * @return a log record
Wink Savillefc5b4802009-12-08 21:22:24 -08001365 */
Wink Saville095c58b2012-05-29 12:40:46 -07001366 public final LogRec getLogRec(int index) {
1367 return mSmHandler.mLogRecords.get(index);
1368 }
1369
1370 /**
1371 * Add the string to LogRecords.
1372 *
1373 * @param string
1374 */
1375 protected void addLogRec(String string) {
1376 mSmHandler.mLogRecords.add(null, string, null, null);
1377 }
1378
1379 /**
1380 * Add the string and state to LogRecords
1381 *
1382 * @param string
1383 * @param state current state
1384 */
1385 protected void addLogRec(String string, State state) {
1386 mSmHandler.mLogRecords.add(null, string, state, null);
1387 }
1388
1389 /**
1390 * @return true if msg should be saved in the log, default is true.
1391 */
1392 protected boolean recordLogRec(Message msg) {
1393 return true;
1394 }
1395
1396 /**
1397 * Return a string to be logged by LogRec, default
1398 * is an empty string. Override if additional information is desired.
1399 *
1400 * @param msg that was processed
1401 * @return information to be logged as a String
1402 */
1403 protected String getLogRecString(Message msg) {
1404 return "";
1405 }
1406
1407 /**
1408 * @return the string for msg.what
1409 */
1410 protected String getWhatToString(int what) {
1411 return null;
Wink Savillefc5b4802009-12-08 21:22:24 -08001412 }
1413
1414 /**
1415 * @return Handler
1416 */
1417 public final Handler getHandler() {
Wink Saville64c42ca2011-04-18 14:55:10 -07001418 return mSmHandler;
Wink Savillefc5b4802009-12-08 21:22:24 -08001419 }
1420
1421 /**
1422 * Get a message and set Message.target = this.
1423 *
Jaikumar Ganesha544d462011-12-07 14:52:01 -08001424 * @return message or null if SM has quit
Wink Savillefc5b4802009-12-08 21:22:24 -08001425 */
1426 public final Message obtainMessage()
1427 {
Jaikumar Ganesha544d462011-12-07 14:52:01 -08001428 if (mSmHandler == null) return null;
1429
Wink Saville64c42ca2011-04-18 14:55:10 -07001430 return Message.obtain(mSmHandler);
Wink Savillefc5b4802009-12-08 21:22:24 -08001431 }
1432
1433 /**
1434 * Get a message and set Message.target = this and what
1435 *
1436 * @param what is the assigned to Message.what.
Jaikumar Ganesha544d462011-12-07 14:52:01 -08001437 * @return message or null if SM has quit
Wink Savillefc5b4802009-12-08 21:22:24 -08001438 */
1439 public final Message obtainMessage(int what) {
Jaikumar Ganesha544d462011-12-07 14:52:01 -08001440 if (mSmHandler == null) return null;
1441
Wink Saville64c42ca2011-04-18 14:55:10 -07001442 return Message.obtain(mSmHandler, what);
Wink Savillefc5b4802009-12-08 21:22:24 -08001443 }
1444
1445 /**
1446 * Get a message and set Message.target = this,
1447 * what and obj.
1448 *
1449 * @param what is the assigned to Message.what.
1450 * @param obj is assigned to Message.obj.
Jaikumar Ganesha544d462011-12-07 14:52:01 -08001451 * @return message or null if SM has quit
Wink Savillefc5b4802009-12-08 21:22:24 -08001452 */
1453 public final Message obtainMessage(int what, Object obj)
1454 {
Jaikumar Ganesha544d462011-12-07 14:52:01 -08001455 if (mSmHandler == null) return null;
1456
Wink Saville64c42ca2011-04-18 14:55:10 -07001457 return Message.obtain(mSmHandler, what, obj);
Wink Savillefc5b4802009-12-08 21:22:24 -08001458 }
1459
Wink Saville91fbd562010-03-17 17:12:43 -07001460 /**
Irfan Sheriff96e6bdb2010-06-25 09:44:27 -07001461 * Get a message and set Message.target = this,
1462 * what, arg1 and arg2
1463 *
1464 * @param what is assigned to Message.what
1465 * @param arg1 is assigned to Message.arg1
1466 * @param arg2 is assigned to Message.arg2
Jaikumar Ganesha544d462011-12-07 14:52:01 -08001467 * @return A Message object from the global pool or null if
1468 * SM has quit
Irfan Sheriff96e6bdb2010-06-25 09:44:27 -07001469 */
1470 public final Message obtainMessage(int what, int arg1, int arg2)
1471 {
Jaikumar Ganesha544d462011-12-07 14:52:01 -08001472 if (mSmHandler == null) return null;
1473
Wink Saville64c42ca2011-04-18 14:55:10 -07001474 return Message.obtain(mSmHandler, what, arg1, arg2);
Irfan Sheriff96e6bdb2010-06-25 09:44:27 -07001475 }
1476
1477 /**
1478 * Get a message and set Message.target = this,
1479 * what, arg1, arg2 and obj
1480 *
1481 * @param what is assigned to Message.what
1482 * @param arg1 is assigned to Message.arg1
1483 * @param arg2 is assigned to Message.arg2
1484 * @param obj is assigned to Message.obj
Jaikumar Ganesha544d462011-12-07 14:52:01 -08001485 * @return A Message object from the global pool or null if
1486 * SM has quit
Irfan Sheriff96e6bdb2010-06-25 09:44:27 -07001487 */
1488 public final Message obtainMessage(int what, int arg1, int arg2, Object obj)
1489 {
Jaikumar Ganesha544d462011-12-07 14:52:01 -08001490 if (mSmHandler == null) return null;
1491
Wink Saville64c42ca2011-04-18 14:55:10 -07001492 return Message.obtain(mSmHandler, what, arg1, arg2, obj);
Irfan Sheriff96e6bdb2010-06-25 09:44:27 -07001493 }
1494
1495 /**
Wink Saville91fbd562010-03-17 17:12:43 -07001496 * Enqueue a message to this state machine.
1497 */
1498 public final void sendMessage(int what) {
Jaikumar Ganesha544d462011-12-07 14:52:01 -08001499 // mSmHandler can be null if the state machine has quit.
1500 if (mSmHandler == null) return;
1501
Wink Saville64c42ca2011-04-18 14:55:10 -07001502 mSmHandler.sendMessage(obtainMessage(what));
Wink Saville91fbd562010-03-17 17:12:43 -07001503 }
1504
1505 /**
1506 * Enqueue a message to this state machine.
1507 */
1508 public final void sendMessage(int what, Object obj) {
Jaikumar Ganesha544d462011-12-07 14:52:01 -08001509 // mSmHandler can be null if the state machine has quit.
1510 if (mSmHandler == null) return;
1511
Wink Saville64c42ca2011-04-18 14:55:10 -07001512 mSmHandler.sendMessage(obtainMessage(what,obj));
Wink Saville91fbd562010-03-17 17:12:43 -07001513 }
1514
Wink Savillefc5b4802009-12-08 21:22:24 -08001515 /**
1516 * Enqueue a message to this state machine.
1517 */
1518 public final void sendMessage(Message msg) {
Jaikumar Ganesha544d462011-12-07 14:52:01 -08001519 // mSmHandler can be null if the state machine has quit.
1520 if (mSmHandler == null) return;
1521
Wink Saville64c42ca2011-04-18 14:55:10 -07001522 mSmHandler.sendMessage(msg);
Wink Savillefc5b4802009-12-08 21:22:24 -08001523 }
1524
1525 /**
1526 * Enqueue a message to this state machine after a delay.
1527 */
Wink Saville91fbd562010-03-17 17:12:43 -07001528 public final void sendMessageDelayed(int what, long delayMillis) {
Jaikumar Ganesha544d462011-12-07 14:52:01 -08001529 // mSmHandler can be null if the state machine has quit.
1530 if (mSmHandler == null) return;
1531
Wink Saville64c42ca2011-04-18 14:55:10 -07001532 mSmHandler.sendMessageDelayed(obtainMessage(what), delayMillis);
Wink Saville91fbd562010-03-17 17:12:43 -07001533 }
1534
1535 /**
1536 * Enqueue a message to this state machine after a delay.
1537 */
1538 public final void sendMessageDelayed(int what, Object obj, long delayMillis) {
Jaikumar Ganesha544d462011-12-07 14:52:01 -08001539 // mSmHandler can be null if the state machine has quit.
1540 if (mSmHandler == null) return;
1541
Wink Saville64c42ca2011-04-18 14:55:10 -07001542 mSmHandler.sendMessageDelayed(obtainMessage(what, obj), delayMillis);
Wink Saville91fbd562010-03-17 17:12:43 -07001543 }
1544
1545 /**
1546 * Enqueue a message to this state machine after a delay.
1547 */
Wink Savillefc5b4802009-12-08 21:22:24 -08001548 public final void sendMessageDelayed(Message msg, long delayMillis) {
Jaikumar Ganesha544d462011-12-07 14:52:01 -08001549 // mSmHandler can be null if the state machine has quit.
1550 if (mSmHandler == null) return;
1551
Wink Saville64c42ca2011-04-18 14:55:10 -07001552 mSmHandler.sendMessageDelayed(msg, delayMillis);
Wink Savillefc5b4802009-12-08 21:22:24 -08001553 }
1554
1555 /**
1556 * Enqueue a message to the front of the queue for this state machine.
Wink Saville64c42ca2011-04-18 14:55:10 -07001557 * Protected, may only be called by instances of StateMachine.
Wink Savillefc5b4802009-12-08 21:22:24 -08001558 */
Wink Saville91fbd562010-03-17 17:12:43 -07001559 protected final void sendMessageAtFrontOfQueue(int what, Object obj) {
Wink Saville64c42ca2011-04-18 14:55:10 -07001560 mSmHandler.sendMessageAtFrontOfQueue(obtainMessage(what, obj));
Wink Saville91fbd562010-03-17 17:12:43 -07001561 }
1562
1563 /**
1564 * Enqueue a message to the front of the queue for this state machine.
Wink Saville64c42ca2011-04-18 14:55:10 -07001565 * Protected, may only be called by instances of StateMachine.
Wink Saville91fbd562010-03-17 17:12:43 -07001566 */
1567 protected final void sendMessageAtFrontOfQueue(int what) {
Wink Saville64c42ca2011-04-18 14:55:10 -07001568 mSmHandler.sendMessageAtFrontOfQueue(obtainMessage(what));
Wink Saville91fbd562010-03-17 17:12:43 -07001569 }
1570
1571 /**
1572 * Enqueue a message to the front of the queue for this state machine.
Wink Saville64c42ca2011-04-18 14:55:10 -07001573 * Protected, may only be called by instances of StateMachine.
Wink Saville91fbd562010-03-17 17:12:43 -07001574 */
Wink Savillefc5b4802009-12-08 21:22:24 -08001575 protected final void sendMessageAtFrontOfQueue(Message msg) {
Wink Saville64c42ca2011-04-18 14:55:10 -07001576 mSmHandler.sendMessageAtFrontOfQueue(msg);
Wink Savillefc5b4802009-12-08 21:22:24 -08001577 }
1578
1579 /**
Jaikumar Ganeshaa4b2352010-10-14 09:36:39 -07001580 * Removes a message from the message queue.
Wink Saville64c42ca2011-04-18 14:55:10 -07001581 * Protected, may only be called by instances of StateMachine.
Jaikumar Ganeshaa4b2352010-10-14 09:36:39 -07001582 */
1583 protected final void removeMessages(int what) {
Wink Saville64c42ca2011-04-18 14:55:10 -07001584 mSmHandler.removeMessages(what);
Jaikumar Ganeshaa4b2352010-10-14 09:36:39 -07001585 }
1586
1587 /**
Wink Saville095c58b2012-05-29 12:40:46 -07001588 * Quit the state machine after all currently queued up messages are processed.
Wink Saville1b8b98b2010-03-11 11:49:54 -08001589 */
Wink Saville095c58b2012-05-29 12:40:46 -07001590 protected final void quit() {
1591 // mSmHandler can be null if the state machine is already stopped.
Jaikumar Ganesha544d462011-12-07 14:52:01 -08001592 if (mSmHandler == null) return;
1593
Wink Saville64c42ca2011-04-18 14:55:10 -07001594 mSmHandler.quit();
Wink Saville1b8b98b2010-03-11 11:49:54 -08001595 }
1596
1597 /**
Wink Saville095c58b2012-05-29 12:40:46 -07001598 * Quit the state machine immediately all currently queued messages will be discarded.
Wink Saville1b8b98b2010-03-11 11:49:54 -08001599 */
Wink Saville095c58b2012-05-29 12:40:46 -07001600 protected final void quitNow() {
1601 // mSmHandler can be null if the state machine is already stopped.
1602 if (mSmHandler == null) return;
Wink Saville1b8b98b2010-03-11 11:49:54 -08001603
Wink Saville095c58b2012-05-29 12:40:46 -07001604 mSmHandler.quitNow();
Wink Saville583eaaa2012-04-13 16:11:20 -07001605 }
1606
1607 /**
Wink Savillefc5b4802009-12-08 21:22:24 -08001608 * @return if debugging is enabled
1609 */
1610 public boolean isDbg() {
Jaikumar Ganesha544d462011-12-07 14:52:01 -08001611 // mSmHandler can be null if the state machine has quit.
1612 if (mSmHandler == null) return false;
1613
Wink Saville64c42ca2011-04-18 14:55:10 -07001614 return mSmHandler.isDbg();
Wink Savillefc5b4802009-12-08 21:22:24 -08001615 }
1616
1617 /**
1618 * Set debug enable/disabled.
1619 *
1620 * @param dbg is true to enable debugging.
1621 */
1622 public void setDbg(boolean dbg) {
Jaikumar Ganesha544d462011-12-07 14:52:01 -08001623 // mSmHandler can be null if the state machine has quit.
1624 if (mSmHandler == null) return;
1625
Wink Saville64c42ca2011-04-18 14:55:10 -07001626 mSmHandler.setDbg(dbg);
Wink Savillefc5b4802009-12-08 21:22:24 -08001627 }
1628
1629 /**
1630 * Start the state machine.
1631 */
1632 public void start() {
Jaikumar Ganesha544d462011-12-07 14:52:01 -08001633 // mSmHandler can be null if the state machine has quit.
1634 if (mSmHandler == null) return;
1635
Wink Savillefc5b4802009-12-08 21:22:24 -08001636 /** Send the complete construction message */
Wink Saville64c42ca2011-04-18 14:55:10 -07001637 mSmHandler.completeConstruction();
Wink Savillefc5b4802009-12-08 21:22:24 -08001638 }
Wink Saville583eaaa2012-04-13 16:11:20 -07001639
1640 /**
1641 * Dump the current state.
1642 *
1643 * @param fd
1644 * @param pw
1645 * @param args
1646 */
1647 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1648 pw.println(getName() + ":");
Wink Saville095c58b2012-05-29 12:40:46 -07001649 pw.println(" total records=" + getLogRecCount());
1650 for (int i=0; i < getLogRecSize(); i++) {
1651 pw.printf(" rec[%d]: %s\n", i, getLogRec(i).toString(this));
Wink Saville583eaaa2012-04-13 16:11:20 -07001652 pw.flush();
1653 }
1654 pw.println("curState=" + getCurrentState().getName());
1655 }
Wink Savillefc5b4802009-12-08 21:22:24 -08001656}