blob: d67cef3f0ddaffc32836226e85a688d56d0c4a96 [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
mukesh agrawal8ed82ec2017-02-23 17:13:29 -080026import com.android.internal.annotations.VisibleForTesting;
27
Wink Saville583eaaa2012-04-13 16:11:20 -070028import java.io.FileDescriptor;
29import java.io.PrintWriter;
Wink Saville54b1b1a2015-01-12 11:34:20 -080030import java.io.StringWriter;
Wink Savillefc5b4802009-12-08 21:22:24 -080031import java.util.ArrayList;
Wink Saville583eaaa2012-04-13 16:11:20 -070032import java.util.Calendar;
Wink Savilleefcc3d32013-01-30 11:21:22 -080033import java.util.Collection;
Wink Savillefc5b4802009-12-08 21:22:24 -080034import java.util.HashMap;
Rebecca Silberstein934535b2016-05-13 13:57:34 -070035import java.util.Iterator;
Wink Savilled3059482011-04-11 11:51:28 -070036import java.util.Vector;
Wink Savillefc5b4802009-12-08 21:22:24 -080037
38/**
39 * {@hide}
40 *
Wink Saville64c42ca2011-04-18 14:55:10 -070041 * <p>The state machine defined here is a hierarchical state machine which processes messages
Wink Saville33c54e32010-11-15 10:50:34 -080042 * and can have states arranged hierarchically.</p>
Wink Saville58c73c32013-01-28 10:52:34 -080043 *
Wink Saville64c42ca2011-04-18 14:55:10 -070044 * <p>A state is a <code>State</code> object and must implement
Wink Saville33c54e32010-11-15 10:50:34 -080045 * <code>processMessage</code> and optionally <code>enter/exit/getName</code>.
Wink Savillefc5b4802009-12-08 21:22:24 -080046 * The enter/exit methods are equivalent to the construction and destruction
47 * in Object Oriented programming and are used to perform initialization and
48 * cleanup of the state respectively. The <code>getName</code> method returns the
Kevin Cernekeee318d8c2015-06-18 13:23:24 -070049 * name of the state; the default implementation returns the class name. It may be
50 * desirable to have <code>getName</code> return the the state instance name instead,
51 * in particular if a particular state class has multiple instances.</p>
Wink Savillefc5b4802009-12-08 21:22:24 -080052 *
Kevin Cernekeee318d8c2015-06-18 13:23:24 -070053 * <p>When a state machine is created, <code>addState</code> is used to build the
Wink Savillefc5b4802009-12-08 21:22:24 -080054 * hierarchy and <code>setInitialState</code> is used to identify which of these
55 * is the initial state. After construction the programmer calls <code>start</code>
Wink Savillecea056f2012-03-26 15:03:16 -070056 * which initializes and starts the state machine. The first action the StateMachine
57 * is to the invoke <code>enter</code> for all of the initial state's hierarchy,
58 * starting at its eldest parent. The calls to enter will be done in the context
Kevin Cernekeee318d8c2015-06-18 13:23:24 -070059 * of the StateMachine's Handler, not in the context of the call to start, and they
Wink Savillecea056f2012-03-26 15:03:16 -070060 * will be invoked before any messages are processed. For example, given the simple
Kevin Cernekeee318d8c2015-06-18 13:23:24 -070061 * state machine below, mP1.enter will be invoked and then mS1.enter. Finally,
62 * messages sent to the state machine will be processed by the current state;
Wink Savillecea056f2012-03-26 15:03:16 -070063 * in our simple state machine below that would initially be mS1.processMessage.</p>
Kevin Cernekeee318d8c2015-06-18 13:23:24 -070064<pre>
Wink Savillefc5b4802009-12-08 21:22:24 -080065 mP1
66 / \
Kevin Cernekeee318d8c2015-06-18 13:23:24 -070067 mS2 mS1 ----&gt; initial state
68</pre>
Wink Saville33c54e32010-11-15 10:50:34 -080069 * <p>After the state machine is created and started, messages are sent to a state
Wink Savillea4f3bec2010-05-19 09:11:38 -070070 * machine using <code>sendMessage</code> and the messages are created using
Wink Savillefc5b4802009-12-08 21:22:24 -080071 * <code>obtainMessage</code>. When the state machine receives a message the
72 * current state's <code>processMessage</code> is invoked. In the above example
73 * mS1.processMessage will be invoked first. The state may use <code>transitionTo</code>
Kevin Cernekeee318d8c2015-06-18 13:23:24 -070074 * to change the current state to a new state.</p>
Wink Savillefc5b4802009-12-08 21:22:24 -080075 *
Kevin Cernekeee318d8c2015-06-18 13:23:24 -070076 * <p>Each state in the state machine may have a zero or one parent states. If
Wink Savillefc5b4802009-12-08 21:22:24 -080077 * a child state is unable to handle a message it may have the message processed
Kevin Cernekeee318d8c2015-06-18 13:23:24 -070078 * by its parent by returning false or NOT_HANDLED. If a message is not handled by
79 * a child state or any of its ancestors, <code>unhandledMessage</code> will be invoked
80 * to give one last chance for the state machine to process the message.</p>
Wink Savillefc5b4802009-12-08 21:22:24 -080081 *
Wink Saville33c54e32010-11-15 10:50:34 -080082 * <p>When all processing is completed a state machine may choose to call
Wink Savillefc5b4802009-12-08 21:22:24 -080083 * <code>transitionToHaltingState</code>. When the current <code>processingMessage</code>
84 * returns the state machine will transfer to an internal <code>HaltingState</code>
85 * and invoke <code>halting</code>. Any message subsequently received by the state
Wink Saville33c54e32010-11-15 10:50:34 -080086 * machine will cause <code>haltedProcessMessage</code> to be invoked.</p>
Wink Savillefc5b4802009-12-08 21:22:24 -080087 *
Wink Savillebbf30dfd2012-05-29 12:40:46 -070088 * <p>If it is desirable to completely stop the state machine call <code>quit</code> or
Wink Savilleefcc3d32013-01-30 11:21:22 -080089 * <code>quitNow</code>. These will call <code>exit</code> of the current state and its parents,
Kevin Cernekeee318d8c2015-06-18 13:23:24 -070090 * call <code>onQuitting</code> and then exit Thread/Loopers.</p>
Wink Saville1b8b98b2010-03-11 11:49:54 -080091 *
Wink Saville64c42ca2011-04-18 14:55:10 -070092 * <p>In addition to <code>processMessage</code> each <code>State</code> has
Kevin Cernekeee318d8c2015-06-18 13:23:24 -070093 * an <code>enter</code> method and <code>exit</code> method which may be overridden.</p>
Wink Savillefc5b4802009-12-08 21:22:24 -080094 *
Wink Saville33c54e32010-11-15 10:50:34 -080095 * <p>Since the states are arranged in a hierarchy transitioning to a new state
Wink Savillefc5b4802009-12-08 21:22:24 -080096 * causes current states to be exited and new states to be entered. To determine
97 * the list of states to be entered/exited the common parent closest to
98 * the current state is found. We then exit from the current state and its
99 * parent's up to but not including the common parent state and then enter all
100 * of the new states below the common parent down to the destination state.
101 * If there is no common parent all states are exited and then the new states
Wink Saville33c54e32010-11-15 10:50:34 -0800102 * are entered.</p>
Wink Savillefc5b4802009-12-08 21:22:24 -0800103 *
Wink Saville33c54e32010-11-15 10:50:34 -0800104 * <p>Two other methods that states can use are <code>deferMessage</code> and
Wink Savillefc5b4802009-12-08 21:22:24 -0800105 * <code>sendMessageAtFrontOfQueue</code>. The <code>sendMessageAtFrontOfQueue</code> sends
106 * a message but places it on the front of the queue rather than the back. The
107 * <code>deferMessage</code> causes the message to be saved on a list until a
108 * transition is made to a new state. At which time all of the deferred messages
109 * will be put on the front of the state machine queue with the oldest message
110 * at the front. These will then be processed by the new current state before
111 * any other messages that are on the queue or might be added later. Both of
Wink Saville33c54e32010-11-15 10:50:34 -0800112 * these are protected and may only be invoked from within a state machine.</p>
Wink Savillefc5b4802009-12-08 21:22:24 -0800113 *
Wink Saville33c54e32010-11-15 10:50:34 -0800114 * <p>To illustrate some of these properties we'll use state machine with an 8
115 * state hierarchy:</p>
Kevin Cernekeee318d8c2015-06-18 13:23:24 -0700116<pre>
Wink Savillefc5b4802009-12-08 21:22:24 -0800117 mP0
118 / \
119 mP1 mS0
120 / \
121 mS2 mS1
122 / \ \
Kevin Cernekeee318d8c2015-06-18 13:23:24 -0700123 mS3 mS4 mS5 ---&gt; initial state
124</pre>
Wink Saville33c54e32010-11-15 10:50:34 -0800125 * <p>After starting mS5 the list of active states is mP0, mP1, mS1 and mS5.
Wink Savillefc5b4802009-12-08 21:22:24 -0800126 * So the order of calling processMessage when a message is received is mS5,
Wink Savillea4f3bec2010-05-19 09:11:38 -0700127 * mS1, mP1, mP0 assuming each processMessage indicates it can't handle this
Wink Saville33c54e32010-11-15 10:50:34 -0800128 * message by returning false or NOT_HANDLED.</p>
Wink Savillefc5b4802009-12-08 21:22:24 -0800129 *
Wink Saville33c54e32010-11-15 10:50:34 -0800130 * <p>Now assume mS5.processMessage receives a message it can handle, and during
Wink Savillea4f3bec2010-05-19 09:11:38 -0700131 * the handling determines the machine should change states. It could call
132 * transitionTo(mS4) and return true or HANDLED. Immediately after returning from
Wink Savillefc5b4802009-12-08 21:22:24 -0800133 * processMessage the state machine runtime will find the common parent,
134 * which is mP1. It will then call mS5.exit, mS1.exit, mS2.enter and then
135 * mS4.enter. The new list of active states is mP0, mP1, mS2 and mS4. So
Wink Saville33c54e32010-11-15 10:50:34 -0800136 * when the next message is received mS4.processMessage will be invoked.</p>
Wink Savillefc5b4802009-12-08 21:22:24 -0800137 *
Wink Saville64c42ca2011-04-18 14:55:10 -0700138 * <p>Now for some concrete examples, here is the canonical HelloWorld as a state machine.
Wink Saville33c54e32010-11-15 10:50:34 -0800139 * It responds with "Hello World" being printed to the log for every message.</p>
Kevin Cernekeee318d8c2015-06-18 13:23:24 -0700140<pre>
Wink Saville64c42ca2011-04-18 14:55:10 -0700141class HelloWorld extends StateMachine {
142 HelloWorld(String name) {
Wink Savillefc5b4802009-12-08 21:22:24 -0800143 super(name);
144 addState(mState1);
145 setInitialState(mState1);
146 }
147
148 public static HelloWorld makeHelloWorld() {
149 HelloWorld hw = new HelloWorld("hw");
150 hw.start();
151 return hw;
152 }
153
Wink Saville64c42ca2011-04-18 14:55:10 -0700154 class State1 extends State {
Joe Onorato0b6232d2010-09-16 11:55:35 -0400155 &#64;Override public boolean processMessage(Message message) {
Wink Saville58c73c32013-01-28 10:52:34 -0800156 log("Hello World");
Wink Savillea4f3bec2010-05-19 09:11:38 -0700157 return HANDLED;
Wink Savillefc5b4802009-12-08 21:22:24 -0800158 }
159 }
160 State1 mState1 = new State1();
161}
162
163void testHelloWorld() {
164 HelloWorld hw = makeHelloWorld();
165 hw.sendMessage(hw.obtainMessage());
166}
Kevin Cernekeee318d8c2015-06-18 13:23:24 -0700167</pre>
Wink Saville33c54e32010-11-15 10:50:34 -0800168 * <p>A more interesting state machine is one with four states
169 * with two independent parent states.</p>
Kevin Cernekeee318d8c2015-06-18 13:23:24 -0700170<pre>
Wink Savillefc5b4802009-12-08 21:22:24 -0800171 mP1 mP2
172 / \
173 mS2 mS1
Kevin Cernekeee318d8c2015-06-18 13:23:24 -0700174</pre>
Wink Saville33c54e32010-11-15 10:50:34 -0800175 * <p>Here is a description of this state machine using pseudo code.</p>
Kevin Cernekeee318d8c2015-06-18 13:23:24 -0700176 <pre>
Wink Saville33c54e32010-11-15 10:50:34 -0800177state mP1 {
178 enter { log("mP1.enter"); }
179 exit { log("mP1.exit"); }
180 on msg {
181 CMD_2 {
182 send(CMD_3);
183 defer(msg);
Kevin Cernekeee318d8c2015-06-18 13:23:24 -0700184 transitionTo(mS2);
Wink Saville33c54e32010-11-15 10:50:34 -0800185 return HANDLED;
186 }
187 return NOT_HANDLED;
188 }
189}
190
191INITIAL
192state mS1 parent mP1 {
193 enter { log("mS1.enter"); }
194 exit { log("mS1.exit"); }
195 on msg {
196 CMD_1 {
197 transitionTo(mS1);
198 return HANDLED;
199 }
200 return NOT_HANDLED;
201 }
202}
203
204state mS2 parent mP1 {
205 enter { log("mS2.enter"); }
206 exit { log("mS2.exit"); }
207 on msg {
208 CMD_2 {
209 send(CMD_4);
210 return HANDLED;
211 }
212 CMD_3 {
213 defer(msg);
214 transitionTo(mP2);
215 return HANDLED;
216 }
217 return NOT_HANDLED;
218 }
219}
220
221state mP2 {
222 enter {
223 log("mP2.enter");
224 send(CMD_5);
225 }
226 exit { log("mP2.exit"); }
227 on msg {
228 CMD_3, CMD_4 { return HANDLED; }
229 CMD_5 {
230 transitionTo(HaltingState);
231 return HANDLED;
232 }
233 return NOT_HANDLED;
234 }
235}
Kevin Cernekeee318d8c2015-06-18 13:23:24 -0700236</pre>
Wink Saville64c42ca2011-04-18 14:55:10 -0700237 * <p>The implementation is below and also in StateMachineTest:</p>
Kevin Cernekeee318d8c2015-06-18 13:23:24 -0700238<pre>
Wink Saville64c42ca2011-04-18 14:55:10 -0700239class Hsm1 extends StateMachine {
Wink Savillefc5b4802009-12-08 21:22:24 -0800240 public static final int CMD_1 = 1;
241 public static final int CMD_2 = 2;
242 public static final int CMD_3 = 3;
243 public static final int CMD_4 = 4;
244 public static final int CMD_5 = 5;
245
246 public static Hsm1 makeHsm1() {
Wink Saville58c73c32013-01-28 10:52:34 -0800247 log("makeHsm1 E");
Wink Savillefc5b4802009-12-08 21:22:24 -0800248 Hsm1 sm = new Hsm1("hsm1");
249 sm.start();
Wink Saville58c73c32013-01-28 10:52:34 -0800250 log("makeHsm1 X");
Wink Savillefc5b4802009-12-08 21:22:24 -0800251 return sm;
252 }
253
254 Hsm1(String name) {
255 super(name);
Wink Saville58c73c32013-01-28 10:52:34 -0800256 log("ctor E");
Wink Savillefc5b4802009-12-08 21:22:24 -0800257
258 // Add states, use indentation to show hierarchy
259 addState(mP1);
260 addState(mS1, mP1);
261 addState(mS2, mP1);
262 addState(mP2);
263
264 // Set the initial state
265 setInitialState(mS1);
Wink Saville58c73c32013-01-28 10:52:34 -0800266 log("ctor X");
Wink Savillefc5b4802009-12-08 21:22:24 -0800267 }
268
Wink Saville64c42ca2011-04-18 14:55:10 -0700269 class P1 extends State {
Joe Onorato0b6232d2010-09-16 11:55:35 -0400270 &#64;Override public void enter() {
Wink Saville58c73c32013-01-28 10:52:34 -0800271 log("mP1.enter");
Wink Savillefc5b4802009-12-08 21:22:24 -0800272 }
Joe Onorato0b6232d2010-09-16 11:55:35 -0400273 &#64;Override public boolean processMessage(Message message) {
Wink Savillefc5b4802009-12-08 21:22:24 -0800274 boolean retVal;
Wink Saville58c73c32013-01-28 10:52:34 -0800275 log("mP1.processMessage what=" + message.what);
Wink Savillefc5b4802009-12-08 21:22:24 -0800276 switch(message.what) {
277 case CMD_2:
278 // CMD_2 will arrive in mS2 before CMD_3
279 sendMessage(obtainMessage(CMD_3));
280 deferMessage(message);
281 transitionTo(mS2);
Wink Savillea4f3bec2010-05-19 09:11:38 -0700282 retVal = HANDLED;
Wink Savillefc5b4802009-12-08 21:22:24 -0800283 break;
284 default:
285 // Any message we don't understand in this state invokes unhandledMessage
Wink Savillea4f3bec2010-05-19 09:11:38 -0700286 retVal = NOT_HANDLED;
Wink Savillefc5b4802009-12-08 21:22:24 -0800287 break;
288 }
289 return retVal;
290 }
Joe Onorato0b6232d2010-09-16 11:55:35 -0400291 &#64;Override public void exit() {
Wink Saville58c73c32013-01-28 10:52:34 -0800292 log("mP1.exit");
Wink Savillefc5b4802009-12-08 21:22:24 -0800293 }
294 }
295
Wink Saville64c42ca2011-04-18 14:55:10 -0700296 class S1 extends State {
Joe Onorato0b6232d2010-09-16 11:55:35 -0400297 &#64;Override public void enter() {
Wink Saville58c73c32013-01-28 10:52:34 -0800298 log("mS1.enter");
Wink Savillefc5b4802009-12-08 21:22:24 -0800299 }
Joe Onorato0b6232d2010-09-16 11:55:35 -0400300 &#64;Override public boolean processMessage(Message message) {
Wink Saville58c73c32013-01-28 10:52:34 -0800301 log("S1.processMessage what=" + message.what);
Wink Savillefc5b4802009-12-08 21:22:24 -0800302 if (message.what == CMD_1) {
303 // Transition to ourself to show that enter/exit is called
304 transitionTo(mS1);
Wink Savillea4f3bec2010-05-19 09:11:38 -0700305 return HANDLED;
Wink Savillefc5b4802009-12-08 21:22:24 -0800306 } else {
307 // Let parent process all other messages
Wink Savillea4f3bec2010-05-19 09:11:38 -0700308 return NOT_HANDLED;
Wink Savillefc5b4802009-12-08 21:22:24 -0800309 }
310 }
Joe Onorato0b6232d2010-09-16 11:55:35 -0400311 &#64;Override public void exit() {
Wink Saville58c73c32013-01-28 10:52:34 -0800312 log("mS1.exit");
Wink Savillefc5b4802009-12-08 21:22:24 -0800313 }
314 }
315
Wink Saville64c42ca2011-04-18 14:55:10 -0700316 class S2 extends State {
Joe Onorato0b6232d2010-09-16 11:55:35 -0400317 &#64;Override public void enter() {
Wink Saville58c73c32013-01-28 10:52:34 -0800318 log("mS2.enter");
Wink Savillefc5b4802009-12-08 21:22:24 -0800319 }
Joe Onorato0b6232d2010-09-16 11:55:35 -0400320 &#64;Override public boolean processMessage(Message message) {
Wink Savillefc5b4802009-12-08 21:22:24 -0800321 boolean retVal;
Wink Saville58c73c32013-01-28 10:52:34 -0800322 log("mS2.processMessage what=" + message.what);
Wink Savillefc5b4802009-12-08 21:22:24 -0800323 switch(message.what) {
324 case(CMD_2):
325 sendMessage(obtainMessage(CMD_4));
Wink Savillea4f3bec2010-05-19 09:11:38 -0700326 retVal = HANDLED;
Wink Savillefc5b4802009-12-08 21:22:24 -0800327 break;
328 case(CMD_3):
329 deferMessage(message);
330 transitionTo(mP2);
Wink Savillea4f3bec2010-05-19 09:11:38 -0700331 retVal = HANDLED;
Wink Savillefc5b4802009-12-08 21:22:24 -0800332 break;
333 default:
Wink Savillea4f3bec2010-05-19 09:11:38 -0700334 retVal = NOT_HANDLED;
Wink Savillefc5b4802009-12-08 21:22:24 -0800335 break;
336 }
337 return retVal;
338 }
Joe Onorato0b6232d2010-09-16 11:55:35 -0400339 &#64;Override public void exit() {
Wink Saville58c73c32013-01-28 10:52:34 -0800340 log("mS2.exit");
Wink Savillefc5b4802009-12-08 21:22:24 -0800341 }
342 }
343
Wink Saville64c42ca2011-04-18 14:55:10 -0700344 class P2 extends State {
Joe Onorato0b6232d2010-09-16 11:55:35 -0400345 &#64;Override public void enter() {
Wink Saville58c73c32013-01-28 10:52:34 -0800346 log("mP2.enter");
Wink Savillefc5b4802009-12-08 21:22:24 -0800347 sendMessage(obtainMessage(CMD_5));
348 }
Joe Onorato0b6232d2010-09-16 11:55:35 -0400349 &#64;Override public boolean processMessage(Message message) {
Wink Saville58c73c32013-01-28 10:52:34 -0800350 log("P2.processMessage what=" + message.what);
Wink Savillefc5b4802009-12-08 21:22:24 -0800351 switch(message.what) {
352 case(CMD_3):
353 break;
354 case(CMD_4):
355 break;
356 case(CMD_5):
357 transitionToHaltingState();
358 break;
359 }
Wink Savillea4f3bec2010-05-19 09:11:38 -0700360 return HANDLED;
Wink Savillefc5b4802009-12-08 21:22:24 -0800361 }
Joe Onorato0b6232d2010-09-16 11:55:35 -0400362 &#64;Override public void exit() {
Wink Saville58c73c32013-01-28 10:52:34 -0800363 log("mP2.exit");
Wink Savillefc5b4802009-12-08 21:22:24 -0800364 }
365 }
366
Joe Onorato0b6232d2010-09-16 11:55:35 -0400367 &#64;Override
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700368 void onHalting() {
Wink Saville58c73c32013-01-28 10:52:34 -0800369 log("halting");
Wink Savillefc5b4802009-12-08 21:22:24 -0800370 synchronized (this) {
371 this.notifyAll();
372 }
373 }
374
375 P1 mP1 = new P1();
376 S1 mS1 = new S1();
377 S2 mS2 = new S2();
378 P2 mP2 = new P2();
379}
Kevin Cernekeee318d8c2015-06-18 13:23:24 -0700380</pre>
Wink Saville33c54e32010-11-15 10:50:34 -0800381 * <p>If this is executed by sending two messages CMD_1 and CMD_2
382 * (Note the synchronize is only needed because we use hsm.wait())</p>
Kevin Cernekeee318d8c2015-06-18 13:23:24 -0700383<pre>
Wink Saville33c54e32010-11-15 10:50:34 -0800384Hsm1 hsm = makeHsm1();
385synchronize(hsm) {
386 hsm.sendMessage(obtainMessage(hsm.CMD_1));
387 hsm.sendMessage(obtainMessage(hsm.CMD_2));
388 try {
389 // wait for the messages to be handled
390 hsm.wait();
391 } catch (InterruptedException e) {
Wink Saville58c73c32013-01-28 10:52:34 -0800392 loge("exception while waiting " + e.getMessage());
Wink Saville33c54e32010-11-15 10:50:34 -0800393 }
394}
Kevin Cernekeee318d8c2015-06-18 13:23:24 -0700395</pre>
Wink Saville33c54e32010-11-15 10:50:34 -0800396 * <p>The output is:</p>
Kevin Cernekeee318d8c2015-06-18 13:23:24 -0700397<pre>
Wink Saville33c54e32010-11-15 10:50:34 -0800398D/hsm1 ( 1999): makeHsm1 E
399D/hsm1 ( 1999): ctor E
400D/hsm1 ( 1999): ctor X
401D/hsm1 ( 1999): mP1.enter
402D/hsm1 ( 1999): mS1.enter
403D/hsm1 ( 1999): makeHsm1 X
404D/hsm1 ( 1999): mS1.processMessage what=1
405D/hsm1 ( 1999): mS1.exit
406D/hsm1 ( 1999): mS1.enter
407D/hsm1 ( 1999): mS1.processMessage what=2
408D/hsm1 ( 1999): mP1.processMessage what=2
409D/hsm1 ( 1999): mS1.exit
410D/hsm1 ( 1999): mS2.enter
411D/hsm1 ( 1999): mS2.processMessage what=2
412D/hsm1 ( 1999): mS2.processMessage what=3
413D/hsm1 ( 1999): mS2.exit
414D/hsm1 ( 1999): mP1.exit
415D/hsm1 ( 1999): mP2.enter
416D/hsm1 ( 1999): mP2.processMessage what=3
417D/hsm1 ( 1999): mP2.processMessage what=4
418D/hsm1 ( 1999): mP2.processMessage what=5
419D/hsm1 ( 1999): mP2.exit
420D/hsm1 ( 1999): halting
Kevin Cernekeee318d8c2015-06-18 13:23:24 -0700421</pre>
Wink Savillefc5b4802009-12-08 21:22:24 -0800422 */
Wink Saville64c42ca2011-04-18 14:55:10 -0700423public class StateMachine {
Wink Saville58c73c32013-01-28 10:52:34 -0800424 // Name of the state machine and used as logging tag
Wink Savillefc5b4802009-12-08 21:22:24 -0800425 private String mName;
426
Wink Savillea4f3bec2010-05-19 09:11:38 -0700427 /** Message.what value when quitting */
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700428 private static final int SM_QUIT_CMD = -1;
Wink Saville1b8b98b2010-03-11 11:49:54 -0800429
Wink Savillea4f3bec2010-05-19 09:11:38 -0700430 /** Message.what value when initializing */
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700431 private static final int SM_INIT_CMD = -2;
Wink Savillea4f3bec2010-05-19 09:11:38 -0700432
433 /**
434 * Convenience constant that maybe returned by processMessage
435 * to indicate the the message was processed and is not to be
436 * processed by parent states
437 */
438 public static final boolean HANDLED = true;
439
440 /**
441 * Convenience constant that maybe returned by processMessage
442 * to indicate the the message was NOT processed and is to be
443 * processed by parent states
444 */
445 public static final boolean NOT_HANDLED = false;
446
Wink Savilled3059482011-04-11 11:51:28 -0700447 /**
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700448 * StateMachine logging record.
Wink Savilled3059482011-04-11 11:51:28 -0700449 * {@hide}
Wink Savilled3059482011-04-11 11:51:28 -0700450 */
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700451 public static class LogRec {
Wink Savilleefcc3d32013-01-30 11:21:22 -0800452 private StateMachine mSm;
Wink Saville583eaaa2012-04-13 16:11:20 -0700453 private long mTime;
454 private int mWhat;
455 private String mInfo;
Wink Savilleefcc3d32013-01-30 11:21:22 -0800456 private IState mState;
457 private IState mOrgState;
458 private IState mDstState;
Wink Savilled3059482011-04-11 11:51:28 -0700459
460 /**
461 * Constructor
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700462 *
463 * @param msg
Irfan Sheriffef8da9f2012-11-29 09:42:13 -0800464 * @param state the state which handled the message
Wink Savilled3059482011-04-11 11:51:28 -0700465 * @param orgState is the first state the received the message but
466 * did not processes the message.
Irfan Sheriffef8da9f2012-11-29 09:42:13 -0800467 * @param transToState is the state that was transitioned to after the message was
468 * processed.
Wink Savilled3059482011-04-11 11:51:28 -0700469 */
Wink Savilleefcc3d32013-01-30 11:21:22 -0800470 LogRec(StateMachine sm, Message msg, String info, IState state, IState orgState,
471 IState transToState) {
472 update(sm, msg, info, state, orgState, transToState);
Wink Savilled3059482011-04-11 11:51:28 -0700473 }
474
475 /**
476 * Update the information in the record.
477 * @param state that handled the message
Wink Savilleefcc3d32013-01-30 11:21:22 -0800478 * @param orgState is the first state the received the message
479 * @param dstState is the state that was the transition target when logging
Wink Savilled3059482011-04-11 11:51:28 -0700480 */
Wink Savilleefcc3d32013-01-30 11:21:22 -0800481 public void update(StateMachine sm, Message msg, String info, IState state, IState orgState,
482 IState dstState) {
483 mSm = sm;
Wink Saville583eaaa2012-04-13 16:11:20 -0700484 mTime = System.currentTimeMillis();
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700485 mWhat = (msg != null) ? msg.what : 0;
Wink Saville583eaaa2012-04-13 16:11:20 -0700486 mInfo = info;
487 mState = state;
488 mOrgState = orgState;
Wink Savilleefcc3d32013-01-30 11:21:22 -0800489 mDstState = dstState;
Wink Saville583eaaa2012-04-13 16:11:20 -0700490 }
491
492 /**
493 * @return time stamp
494 */
495 public long getTime() {
496 return mTime;
497 }
498
499 /**
500 * @return msg.what
501 */
502 public long getWhat() {
503 return mWhat;
Wink Savilled3059482011-04-11 11:51:28 -0700504 }
505
506 /**
507 * @return the command that was executing
508 */
Wink Saville583eaaa2012-04-13 16:11:20 -0700509 public String getInfo() {
510 return mInfo;
Wink Savilled3059482011-04-11 11:51:28 -0700511 }
512
513 /**
514 * @return the state that handled this message
515 */
Wink Savilleefcc3d32013-01-30 11:21:22 -0800516 public IState getState() {
Wink Saville583eaaa2012-04-13 16:11:20 -0700517 return mState;
Wink Savilled3059482011-04-11 11:51:28 -0700518 }
519
520 /**
Wink Savilleefcc3d32013-01-30 11:21:22 -0800521 * @return the state destination state if a transition is occurring or null if none.
522 */
523 public IState getDestState() {
524 return mDstState;
525 }
526
Wink Savilleefcc3d32013-01-30 11:21:22 -0800527 /**
Wink Savilled3059482011-04-11 11:51:28 -0700528 * @return the original state that received the message.
529 */
Wink Savilleefcc3d32013-01-30 11:21:22 -0800530 public IState getOriginalState() {
Wink Saville583eaaa2012-04-13 16:11:20 -0700531 return mOrgState;
Wink Savilled3059482011-04-11 11:51:28 -0700532 }
533
Wink Savilleefcc3d32013-01-30 11:21:22 -0800534 @Override
535 public String toString() {
Wink Savilled3059482011-04-11 11:51:28 -0700536 StringBuilder sb = new StringBuilder();
Wink Saville583eaaa2012-04-13 16:11:20 -0700537 sb.append("time=");
538 Calendar c = Calendar.getInstance();
539 c.setTimeInMillis(mTime);
540 sb.append(String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c));
Irfan Sheriffef8da9f2012-11-29 09:42:13 -0800541 sb.append(" processed=");
Wink Saville583eaaa2012-04-13 16:11:20 -0700542 sb.append(mState == null ? "<null>" : mState.getName());
Irfan Sheriffef8da9f2012-11-29 09:42:13 -0800543 sb.append(" org=");
Wink Saville583eaaa2012-04-13 16:11:20 -0700544 sb.append(mOrgState == null ? "<null>" : mOrgState.getName());
Irfan Sheriffef8da9f2012-11-29 09:42:13 -0800545 sb.append(" dest=");
Wink Savilleefcc3d32013-01-30 11:21:22 -0800546 sb.append(mDstState == null ? "<null>" : mDstState.getName());
Wink Saville583eaaa2012-04-13 16:11:20 -0700547 sb.append(" what=");
Wink Savilleefcc3d32013-01-30 11:21:22 -0800548 String what = mSm != null ? mSm.getWhatToString(mWhat) : "";
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700549 if (TextUtils.isEmpty(what)) {
550 sb.append(mWhat);
551 sb.append("(0x");
552 sb.append(Integer.toHexString(mWhat));
553 sb.append(")");
554 } else {
555 sb.append(what);
556 }
Wink Savilleff4fcdb2013-02-24 07:21:45 -0800557 if (!TextUtils.isEmpty(mInfo)) {
Wink Saville583eaaa2012-04-13 16:11:20 -0700558 sb.append(" ");
559 sb.append(mInfo);
Wink Savilled3059482011-04-11 11:51:28 -0700560 }
Wink Saville583eaaa2012-04-13 16:11:20 -0700561 return sb.toString();
Wink Savilled3059482011-04-11 11:51:28 -0700562 }
563 }
564
565 /**
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700566 * A list of log records including messages recently processed by the state machine.
Wink Savilled3059482011-04-11 11:51:28 -0700567 *
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700568 * The class maintains a list of log records including messages
Wink Savilled3059482011-04-11 11:51:28 -0700569 * recently processed. The list is finite and may be set in the
570 * constructor or by calling setSize. The public interface also
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700571 * includes size which returns the number of recent records,
572 * count which is the number of records processed since the
573 * the last setSize, get which returns a record and
574 * add which adds a record.
Wink Savilled3059482011-04-11 11:51:28 -0700575 */
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700576 private static class LogRecords {
Wink Savilled3059482011-04-11 11:51:28 -0700577
578 private static final int DEFAULT_SIZE = 20;
579
Irfan Sheriffef8da9f2012-11-29 09:42:13 -0800580 private Vector<LogRec> mLogRecVector = new Vector<LogRec>();
Wink Savilled3059482011-04-11 11:51:28 -0700581 private int mMaxSize = DEFAULT_SIZE;
582 private int mOldestIndex = 0;
583 private int mCount = 0;
Irfan Sheriffef8da9f2012-11-29 09:42:13 -0800584 private boolean mLogOnlyTransitions = false;
Wink Savilled3059482011-04-11 11:51:28 -0700585
586 /**
Wink Saville583eaaa2012-04-13 16:11:20 -0700587 * private constructor use add
Wink Savilled3059482011-04-11 11:51:28 -0700588 */
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700589 private LogRecords() {
Wink Savilled3059482011-04-11 11:51:28 -0700590 }
591
592 /**
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700593 * Set size of messages to maintain and clears all current records.
Wink Savilled3059482011-04-11 11:51:28 -0700594 *
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700595 * @param maxSize number of records to maintain at anyone time.
Wink Savilled3059482011-04-11 11:51:28 -0700596 */
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700597 synchronized void setSize(int maxSize) {
Samuel Tan2326e9b2016-04-15 13:06:12 -0700598 // TODO: once b/28217358 is fixed, add unit tests to verify that these variables are
599 // cleared after calling this method, and that subsequent calls to get() function as
600 // expected.
Wink Savilled3059482011-04-11 11:51:28 -0700601 mMaxSize = maxSize;
Samuel Tan2326e9b2016-04-15 13:06:12 -0700602 mOldestIndex = 0;
Wink Savilled3059482011-04-11 11:51:28 -0700603 mCount = 0;
Irfan Sheriffef8da9f2012-11-29 09:42:13 -0800604 mLogRecVector.clear();
605 }
606
607 synchronized void setLogOnlyTransitions(boolean enable) {
608 mLogOnlyTransitions = enable;
609 }
610
611 synchronized boolean logOnlyTransitions() {
612 return mLogOnlyTransitions;
Wink Savilled3059482011-04-11 11:51:28 -0700613 }
614
615 /**
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700616 * @return the number of recent records.
Wink Savilled3059482011-04-11 11:51:28 -0700617 */
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700618 synchronized int size() {
Irfan Sheriffef8da9f2012-11-29 09:42:13 -0800619 return mLogRecVector.size();
Wink Savilled3059482011-04-11 11:51:28 -0700620 }
621
622 /**
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700623 * @return the total number of records processed since size was set.
Wink Savilled3059482011-04-11 11:51:28 -0700624 */
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700625 synchronized int count() {
Wink Savilled3059482011-04-11 11:51:28 -0700626 return mCount;
627 }
628
629 /**
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700630 * Clear the list of records.
Jaikumar Ganesh6f9a6162011-11-28 13:13:02 -0800631 */
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700632 synchronized void cleanup() {
Irfan Sheriffef8da9f2012-11-29 09:42:13 -0800633 mLogRecVector.clear();
Jaikumar Ganesh6f9a6162011-11-28 13:13:02 -0800634 }
635
636 /**
Wink Savilled3059482011-04-11 11:51:28 -0700637 * @return the information on a particular record. 0 is the oldest
638 * record and size()-1 is the newest record. If the index is to
639 * large null is returned.
640 */
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700641 synchronized LogRec get(int index) {
Wink Savilled3059482011-04-11 11:51:28 -0700642 int nextIndex = mOldestIndex + index;
643 if (nextIndex >= mMaxSize) {
644 nextIndex -= mMaxSize;
645 }
646 if (nextIndex >= size()) {
647 return null;
648 } else {
Irfan Sheriffef8da9f2012-11-29 09:42:13 -0800649 return mLogRecVector.get(nextIndex);
Wink Savilled3059482011-04-11 11:51:28 -0700650 }
651 }
652
653 /**
654 * Add a processed message.
655 *
Wink Saville583eaaa2012-04-13 16:11:20 -0700656 * @param msg
657 * @param messageInfo to be stored
Wink Savilled3059482011-04-11 11:51:28 -0700658 * @param state that handled the message
659 * @param orgState is the first state the received the message but
660 * did not processes the message.
Irfan Sheriffef8da9f2012-11-29 09:42:13 -0800661 * @param transToState is the state that was transitioned to after the message was
662 * processed.
663 *
Wink Savilled3059482011-04-11 11:51:28 -0700664 */
Wink Savilleefcc3d32013-01-30 11:21:22 -0800665 synchronized void add(StateMachine sm, Message msg, String messageInfo, IState state,
Wink Savilleff4fcdb2013-02-24 07:21:45 -0800666 IState orgState, IState transToState) {
Wink Savilled3059482011-04-11 11:51:28 -0700667 mCount += 1;
Irfan Sheriffef8da9f2012-11-29 09:42:13 -0800668 if (mLogRecVector.size() < mMaxSize) {
Wink Savilleefcc3d32013-01-30 11:21:22 -0800669 mLogRecVector.add(new LogRec(sm, msg, messageInfo, state, orgState, transToState));
Wink Savilled3059482011-04-11 11:51:28 -0700670 } else {
Irfan Sheriffef8da9f2012-11-29 09:42:13 -0800671 LogRec pmi = mLogRecVector.get(mOldestIndex);
Wink Savilled3059482011-04-11 11:51:28 -0700672 mOldestIndex += 1;
673 if (mOldestIndex >= mMaxSize) {
674 mOldestIndex = 0;
675 }
Wink Savilleefcc3d32013-01-30 11:21:22 -0800676 pmi.update(sm, msg, messageInfo, state, orgState, transToState);
Wink Savilled3059482011-04-11 11:51:28 -0700677 }
678 }
679 }
680
Wink Saville64c42ca2011-04-18 14:55:10 -0700681 private static class SmHandler extends Handler {
Wink Savillefc5b4802009-12-08 21:22:24 -0800682
Wink Saville03812c72013-04-16 13:21:00 -0700683 /** true if StateMachine has quit */
684 private boolean mHasQuit = false;
685
Wink Savillefc5b4802009-12-08 21:22:24 -0800686 /** The debug flag */
687 private boolean mDbg = false;
688
Wink Savillecea056f2012-03-26 15:03:16 -0700689 /** The SmHandler object, identifies that message is internal */
690 private static final Object mSmHandlerObj = new Object();
Wink Saville1b8b98b2010-03-11 11:49:54 -0800691
Wink Savillea4f3bec2010-05-19 09:11:38 -0700692 /** The current message */
693 private Message mMsg;
694
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700695 /** A list of log records including messages this state machine has processed */
696 private LogRecords mLogRecords = new LogRecords();
Wink Savillefc5b4802009-12-08 21:22:24 -0800697
698 /** true if construction of the state machine has not been completed */
699 private boolean mIsConstructionCompleted;
700
701 /** Stack used to manage the current hierarchy of states */
702 private StateInfo mStateStack[];
703
704 /** Top of mStateStack */
705 private int mStateStackTopIndex = -1;
706
707 /** A temporary stack used to manage the state stack */
708 private StateInfo mTempStateStack[];
709
710 /** The top of the mTempStateStack */
711 private int mTempStateStackCount;
712
713 /** State used when state machine is halted */
714 private HaltingState mHaltingState = new HaltingState();
715
Wink Saville1b8b98b2010-03-11 11:49:54 -0800716 /** State used when state machine is quitting */
717 private QuittingState mQuittingState = new QuittingState();
718
Wink Saville64c42ca2011-04-18 14:55:10 -0700719 /** Reference to the StateMachine */
720 private StateMachine mSm;
Wink Savillefc5b4802009-12-08 21:22:24 -0800721
722 /**
723 * Information about a state.
724 * Used to maintain the hierarchy.
725 */
726 private class StateInfo {
727 /** The state */
Wink Saville64c42ca2011-04-18 14:55:10 -0700728 State state;
Wink Savillefc5b4802009-12-08 21:22:24 -0800729
730 /** The parent of this state, null if there is no parent */
731 StateInfo parentStateInfo;
732
733 /** True when the state has been entered and on the stack */
734 boolean active;
735
736 /**
737 * Convert StateInfo to string
738 */
739 @Override
740 public String toString() {
Wink Savilleff4fcdb2013-02-24 07:21:45 -0800741 return "state=" + state.getName() + ",active=" + active + ",parent="
742 + ((parentStateInfo == null) ? "null" : parentStateInfo.state.getName());
Wink Savillefc5b4802009-12-08 21:22:24 -0800743 }
744 }
745
746 /** The map of all of the states in the state machine */
Wink Savilleff4fcdb2013-02-24 07:21:45 -0800747 private HashMap<State, StateInfo> mStateInfo = new HashMap<State, StateInfo>();
Wink Savillefc5b4802009-12-08 21:22:24 -0800748
749 /** The initial state that will process the first message */
Wink Saville64c42ca2011-04-18 14:55:10 -0700750 private State mInitialState;
Wink Savillefc5b4802009-12-08 21:22:24 -0800751
752 /** The destination state when transitionTo has been invoked */
Wink Saville64c42ca2011-04-18 14:55:10 -0700753 private State mDestState;
Wink Savillefc5b4802009-12-08 21:22:24 -0800754
Mitchell Wills07e317c2016-08-11 11:05:03 -0700755 /**
756 * Indicates if a transition is in progress
757 *
758 * This will be true for all calls of State.exit and all calls of State.enter except for the
759 * last enter call for the current destination state.
760 */
761 private boolean mTransitionInProgress = false;
762
Wink Savillefc5b4802009-12-08 21:22:24 -0800763 /** The list of deferred messages */
764 private ArrayList<Message> mDeferredMessages = new ArrayList<Message>();
765
766 /**
767 * State entered when transitionToHaltingState is called.
768 */
Wink Saville64c42ca2011-04-18 14:55:10 -0700769 private class HaltingState extends State {
Wink Savillefc5b4802009-12-08 21:22:24 -0800770 @Override
771 public boolean processMessage(Message msg) {
Wink Saville64c42ca2011-04-18 14:55:10 -0700772 mSm.haltedProcessMessage(msg);
Wink Savillefc5b4802009-12-08 21:22:24 -0800773 return true;
774 }
775 }
776
777 /**
Wink Saville1b8b98b2010-03-11 11:49:54 -0800778 * State entered when a valid quit message is handled.
779 */
Wink Saville64c42ca2011-04-18 14:55:10 -0700780 private class QuittingState extends State {
Wink Saville1b8b98b2010-03-11 11:49:54 -0800781 @Override
782 public boolean processMessage(Message msg) {
Wink Savillea4f3bec2010-05-19 09:11:38 -0700783 return NOT_HANDLED;
Wink Saville1b8b98b2010-03-11 11:49:54 -0800784 }
785 }
786
787 /**
Wink Savillefc5b4802009-12-08 21:22:24 -0800788 * Handle messages sent to the state machine by calling
789 * the current state's processMessage. It also handles
790 * the enter/exit calls and placing any deferred messages
791 * back onto the queue when transitioning to a new state.
792 */
793 @Override
794 public final void handleMessage(Message msg) {
Wink Saville03812c72013-04-16 13:21:00 -0700795 if (!mHasQuit) {
Hall Liu74d7d0f2016-01-04 15:17:53 -0800796 if (mSm != null && msg.what != SM_INIT_CMD && msg.what != SM_QUIT_CMD) {
Brad Ebinger355f1102015-12-14 08:46:49 -0800797 mSm.onPreHandleMessage(msg);
798 }
799
Wink Saville03812c72013-04-16 13:21:00 -0700800 if (mDbg) mSm.log("handleMessage: E msg.what=" + msg.what);
Wink Savillefc5b4802009-12-08 21:22:24 -0800801
Wink Saville03812c72013-04-16 13:21:00 -0700802 /** Save the current message */
803 mMsg = msg;
Wink Savillea4f3bec2010-05-19 09:11:38 -0700804
Wink Saville03812c72013-04-16 13:21:00 -0700805 /** State that processed the message */
806 State msgProcessedState = null;
807 if (mIsConstructionCompleted) {
808 /** Normal path */
809 msgProcessedState = processMsg(msg);
810 } else if (!mIsConstructionCompleted && (mMsg.what == SM_INIT_CMD)
811 && (mMsg.obj == mSmHandlerObj)) {
812 /** Initial one time path. */
813 mIsConstructionCompleted = true;
814 invokeEnterMethods(0);
815 } else {
816 throw new RuntimeException("StateMachine.handleMessage: "
817 + "The start method not called, received msg: " + msg);
818 }
819 performTransitions(msgProcessedState, msg);
820
821 // We need to check if mSm == null here as we could be quitting.
822 if (mDbg && mSm != null) mSm.log("handleMessage: X");
Brad Ebinger355f1102015-12-14 08:46:49 -0800823
Hall Liu74d7d0f2016-01-04 15:17:53 -0800824 if (mSm != null && msg.what != SM_INIT_CMD && msg.what != SM_QUIT_CMD) {
Brad Ebinger355f1102015-12-14 08:46:49 -0800825 mSm.onPostHandleMessage(msg);
826 }
Wink Savillefc5b4802009-12-08 21:22:24 -0800827 }
Wink Savillee7be6a82010-03-18 17:03:30 -0700828 }
829
830 /**
831 * Do any transitions
Irfan Sheriffef8da9f2012-11-29 09:42:13 -0800832 * @param msgProcessedState is the state that processed the message
Wink Savillee7be6a82010-03-18 17:03:30 -0700833 */
Wink Savilleefcc3d32013-01-30 11:21:22 -0800834 private void performTransitions(State msgProcessedState, Message msg) {
Wink Savillefc5b4802009-12-08 21:22:24 -0800835 /**
836 * If transitionTo has been called, exit and then enter
Wink Savillee7be6a82010-03-18 17:03:30 -0700837 * the appropriate states. We loop on this to allow
838 * enter and exit methods to use transitionTo.
Wink Savillefc5b4802009-12-08 21:22:24 -0800839 */
Irfan Sheriffef8da9f2012-11-29 09:42:13 -0800840 State orgState = mStateStack[mStateStackTopIndex].state;
841
Wink Savilleefcc3d32013-01-30 11:21:22 -0800842 /**
843 * Record whether message needs to be logged before we transition and
844 * and we won't log special messages SM_INIT_CMD or SM_QUIT_CMD which
845 * always set msg.obj to the handler.
846 */
Wink Savillef6430692013-02-11 16:16:02 -0800847 boolean recordLogMsg = mSm.recordLogRec(mMsg) && (msg.obj != mSmHandlerObj);
Irfan Sheriffef8da9f2012-11-29 09:42:13 -0800848
Wink Savilleefcc3d32013-01-30 11:21:22 -0800849 if (mLogRecords.logOnlyTransitions()) {
850 /** Record only if there is a transition */
851 if (mDestState != null) {
852 mLogRecords.add(mSm, mMsg, mSm.getLogRecString(mMsg), msgProcessedState,
853 orgState, mDestState);
854 }
855 } else if (recordLogMsg) {
856 /** Record message */
Wink Savilleff4fcdb2013-02-24 07:21:45 -0800857 mLogRecords.add(mSm, mMsg, mSm.getLogRecString(mMsg), msgProcessedState, orgState,
858 mDestState);
Wink Savilleefcc3d32013-01-30 11:21:22 -0800859 }
Wink Savillefc5b4802009-12-08 21:22:24 -0800860
Wink Savilleefcc3d32013-01-30 11:21:22 -0800861 State destState = mDestState;
862 if (destState != null) {
Wink Savillefc5b4802009-12-08 21:22:24 -0800863 /**
Wink Savilleefcc3d32013-01-30 11:21:22 -0800864 * Process the transitions including transitions in the enter/exit methods
Wink Savillee7be6a82010-03-18 17:03:30 -0700865 */
Wink Savilleefcc3d32013-01-30 11:21:22 -0800866 while (true) {
867 if (mDbg) mSm.log("handleMessage: new destination call exit/enter");
868
869 /**
870 * Determine the states to exit and enter and return the
871 * common ancestor state of the enter/exit states. Then
872 * invoke the exit methods then the enter methods.
873 */
874 StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState);
Mitchell Wills07e317c2016-08-11 11:05:03 -0700875 // flag is cleared in invokeEnterMethods before entering the target state
876 mTransitionInProgress = true;
Wink Savilleefcc3d32013-01-30 11:21:22 -0800877 invokeExitMethods(commonStateInfo);
878 int stateStackEnteringIndex = moveTempStateStackToStateStack();
879 invokeEnterMethods(stateStackEnteringIndex);
880
Wink Savilleefcc3d32013-01-30 11:21:22 -0800881 /**
882 * Since we have transitioned to a new state we need to have
883 * any deferred messages moved to the front of the message queue
884 * so they will be processed before any other messages in the
885 * message queue.
886 */
887 moveDeferredMessageAtFrontOfQueue();
888
889 if (destState != mDestState) {
890 // A new mDestState so continue looping
891 destState = mDestState;
892 } else {
893 // No change in mDestState so we're done
894 break;
895 }
896 }
Wink Savillee7be6a82010-03-18 17:03:30 -0700897 mDestState = null;
Wink Savillee7be6a82010-03-18 17:03:30 -0700898 }
Wink Savillefc5b4802009-12-08 21:22:24 -0800899
Wink Savillee7be6a82010-03-18 17:03:30 -0700900 /**
901 * After processing all transitions check and
902 * see if the last transition was to quit or halt.
903 */
904 if (destState != null) {
905 if (destState == mQuittingState) {
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700906 /**
907 * Call onQuitting to let subclasses cleanup.
908 */
909 mSm.onQuitting();
Jaikumar Ganesh6f9a6162011-11-28 13:13:02 -0800910 cleanupAfterQuitting();
Wink Savillee7be6a82010-03-18 17:03:30 -0700911 } else if (destState == mHaltingState) {
912 /**
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700913 * Call onHalting() if we've transitioned to the halting
Wink Savillee7be6a82010-03-18 17:03:30 -0700914 * state. All subsequent messages will be processed in
915 * in the halting state which invokes haltedProcessMessage(msg);
916 */
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700917 mSm.onHalting();
Wink Savillefc5b4802009-12-08 21:22:24 -0800918 }
Wink Savillefc5b4802009-12-08 21:22:24 -0800919 }
Wink Savillefc5b4802009-12-08 21:22:24 -0800920 }
921
922 /**
Jaikumar Ganesh6f9a6162011-11-28 13:13:02 -0800923 * Cleanup all the static variables and the looper after the SM has been quit.
924 */
925 private final void cleanupAfterQuitting() {
Jaikumar Ganesh6f9a6162011-11-28 13:13:02 -0800926 if (mSm.mSmThread != null) {
927 // If we made the thread then quit looper which stops the thread.
928 getLooper().quit();
929 mSm.mSmThread = null;
930 }
931
932 mSm.mSmHandler = null;
933 mSm = null;
934 mMsg = null;
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700935 mLogRecords.cleanup();
Jaikumar Ganesh6f9a6162011-11-28 13:13:02 -0800936 mStateStack = null;
937 mTempStateStack = null;
938 mStateInfo.clear();
939 mInitialState = null;
940 mDestState = null;
941 mDeferredMessages.clear();
Wink Saville03812c72013-04-16 13:21:00 -0700942 mHasQuit = true;
Jaikumar Ganesh6f9a6162011-11-28 13:13:02 -0800943 }
944
945 /**
Wink Savillefc5b4802009-12-08 21:22:24 -0800946 * Complete the construction of the state machine.
947 */
948 private final void completeConstruction() {
Wink Saville58c73c32013-01-28 10:52:34 -0800949 if (mDbg) mSm.log("completeConstruction: E");
Wink Savillefc5b4802009-12-08 21:22:24 -0800950
951 /**
952 * Determine the maximum depth of the state hierarchy
953 * so we can allocate the state stacks.
954 */
955 int maxDepth = 0;
956 for (StateInfo si : mStateInfo.values()) {
957 int depth = 0;
958 for (StateInfo i = si; i != null; depth++) {
959 i = i.parentStateInfo;
960 }
961 if (maxDepth < depth) {
962 maxDepth = depth;
963 }
964 }
Wink Saville58c73c32013-01-28 10:52:34 -0800965 if (mDbg) mSm.log("completeConstruction: maxDepth=" + maxDepth);
Wink Savillefc5b4802009-12-08 21:22:24 -0800966
967 mStateStack = new StateInfo[maxDepth];
968 mTempStateStack = new StateInfo[maxDepth];
969 setupInitialStateStack();
970
Wink Savillecea056f2012-03-26 15:03:16 -0700971 /** Sending SM_INIT_CMD message to invoke enter methods asynchronously */
972 sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj));
Wink Savillee7be6a82010-03-18 17:03:30 -0700973
Wink Saville58c73c32013-01-28 10:52:34 -0800974 if (mDbg) mSm.log("completeConstruction: X");
Wink Savillefc5b4802009-12-08 21:22:24 -0800975 }
976
977 /**
978 * Process the message. If the current state doesn't handle
979 * it, call the states parent and so on. If it is never handled then
980 * call the state machines unhandledMessage method.
Irfan Sheriffef8da9f2012-11-29 09:42:13 -0800981 * @return the state that processed the message
Wink Savillefc5b4802009-12-08 21:22:24 -0800982 */
Irfan Sheriffef8da9f2012-11-29 09:42:13 -0800983 private final State processMsg(Message msg) {
Wink Savillefc5b4802009-12-08 21:22:24 -0800984 StateInfo curStateInfo = mStateStack[mStateStackTopIndex];
985 if (mDbg) {
Wink Saville58c73c32013-01-28 10:52:34 -0800986 mSm.log("processMsg: " + curStateInfo.state.getName());
Wink Savillefc5b4802009-12-08 21:22:24 -0800987 }
Wink Savillefc5b4802009-12-08 21:22:24 -0800988
Wink Savillebbf30dfd2012-05-29 12:40:46 -0700989 if (isQuit(msg)) {
990 transitionTo(mQuittingState);
991 } else {
992 while (!curStateInfo.state.processMessage(msg)) {
993 /**
994 * Not processed
995 */
996 curStateInfo = curStateInfo.parentStateInfo;
997 if (curStateInfo == null) {
998 /**
999 * No parents left so it's not handled
1000 */
1001 mSm.unhandledMessage(msg);
1002 break;
1003 }
1004 if (mDbg) {
Wink Saville58c73c32013-01-28 10:52:34 -08001005 mSm.log("processMsg: " + curStateInfo.state.getName());
Wink Savillebbf30dfd2012-05-29 12:40:46 -07001006 }
1007 }
Wink Savilleff4fcdb2013-02-24 07:21:45 -08001008 }
Irfan Sheriffef8da9f2012-11-29 09:42:13 -08001009 return (curStateInfo != null) ? curStateInfo.state : null;
Wink Savillefc5b4802009-12-08 21:22:24 -08001010 }
1011
1012 /**
1013 * Call the exit method for each state from the top of stack
1014 * up to the common ancestor state.
1015 */
1016 private final void invokeExitMethods(StateInfo commonStateInfo) {
Wink Savilleff4fcdb2013-02-24 07:21:45 -08001017 while ((mStateStackTopIndex >= 0)
1018 && (mStateStack[mStateStackTopIndex] != commonStateInfo)) {
Wink Saville64c42ca2011-04-18 14:55:10 -07001019 State curState = mStateStack[mStateStackTopIndex].state;
Wink Saville58c73c32013-01-28 10:52:34 -08001020 if (mDbg) mSm.log("invokeExitMethods: " + curState.getName());
Wink Savillefc5b4802009-12-08 21:22:24 -08001021 curState.exit();
1022 mStateStack[mStateStackTopIndex].active = false;
1023 mStateStackTopIndex -= 1;
1024 }
1025 }
1026
1027 /**
1028 * Invoke the enter method starting at the entering index to top of state stack
1029 */
1030 private final void invokeEnterMethods(int stateStackEnteringIndex) {
1031 for (int i = stateStackEnteringIndex; i <= mStateStackTopIndex; i++) {
Mitchell Wills07e317c2016-08-11 11:05:03 -07001032 if (stateStackEnteringIndex == mStateStackTopIndex) {
1033 // Last enter state for transition
1034 mTransitionInProgress = false;
1035 }
Wink Saville58c73c32013-01-28 10:52:34 -08001036 if (mDbg) mSm.log("invokeEnterMethods: " + mStateStack[i].state.getName());
Wink Savillefc5b4802009-12-08 21:22:24 -08001037 mStateStack[i].state.enter();
1038 mStateStack[i].active = true;
1039 }
Mitchell Wills07e317c2016-08-11 11:05:03 -07001040 mTransitionInProgress = false; // ensure flag set to false if no methods called
Wink Savillefc5b4802009-12-08 21:22:24 -08001041 }
1042
1043 /**
1044 * Move the deferred message to the front of the message queue.
1045 */
1046 private final void moveDeferredMessageAtFrontOfQueue() {
1047 /**
1048 * The oldest messages on the deferred list must be at
1049 * the front of the queue so start at the back, which
1050 * as the most resent message and end with the oldest
1051 * messages at the front of the queue.
1052 */
Wink Savilleff4fcdb2013-02-24 07:21:45 -08001053 for (int i = mDeferredMessages.size() - 1; i >= 0; i--) {
Wink Savillefc5b4802009-12-08 21:22:24 -08001054 Message curMsg = mDeferredMessages.get(i);
Wink Saville58c73c32013-01-28 10:52:34 -08001055 if (mDbg) mSm.log("moveDeferredMessageAtFrontOfQueue; what=" + curMsg.what);
Wink Savillefc5b4802009-12-08 21:22:24 -08001056 sendMessageAtFrontOfQueue(curMsg);
1057 }
1058 mDeferredMessages.clear();
1059 }
1060
1061 /**
1062 * Move the contents of the temporary stack to the state stack
1063 * reversing the order of the items on the temporary stack as
1064 * they are moved.
1065 *
Wink Saville64c42ca2011-04-18 14:55:10 -07001066 * @return index into mStateStack where entering needs to start
Wink Savillefc5b4802009-12-08 21:22:24 -08001067 */
1068 private final int moveTempStateStackToStateStack() {
1069 int startingIndex = mStateStackTopIndex + 1;
1070 int i = mTempStateStackCount - 1;
1071 int j = startingIndex;
1072 while (i >= 0) {
Wink Saville58c73c32013-01-28 10:52:34 -08001073 if (mDbg) mSm.log("moveTempStackToStateStack: i=" + i + ",j=" + j);
Wink Savillefc5b4802009-12-08 21:22:24 -08001074 mStateStack[j] = mTempStateStack[i];
1075 j += 1;
1076 i -= 1;
1077 }
1078
1079 mStateStackTopIndex = j - 1;
1080 if (mDbg) {
Wink Savilleff4fcdb2013-02-24 07:21:45 -08001081 mSm.log("moveTempStackToStateStack: X mStateStackTop=" + mStateStackTopIndex
1082 + ",startingIndex=" + startingIndex + ",Top="
1083 + mStateStack[mStateStackTopIndex].state.getName());
Wink Savillefc5b4802009-12-08 21:22:24 -08001084 }
1085 return startingIndex;
1086 }
1087
1088 /**
1089 * Setup the mTempStateStack with the states we are going to enter.
1090 *
1091 * This is found by searching up the destState's ancestors for a
1092 * state that is already active i.e. StateInfo.active == true.
1093 * The destStae and all of its inactive parents will be on the
1094 * TempStateStack as the list of states to enter.
1095 *
1096 * @return StateInfo of the common ancestor for the destState and
1097 * current state or null if there is no common parent.
1098 */
Wink Saville64c42ca2011-04-18 14:55:10 -07001099 private final StateInfo setupTempStateStackWithStatesToEnter(State destState) {
Wink Savillefc5b4802009-12-08 21:22:24 -08001100 /**
1101 * Search up the parent list of the destination state for an active
1102 * state. Use a do while() loop as the destState must always be entered
1103 * even if it is active. This can happen if we are exiting/entering
1104 * the current state.
1105 */
1106 mTempStateStackCount = 0;
1107 StateInfo curStateInfo = mStateInfo.get(destState);
1108 do {
1109 mTempStateStack[mTempStateStackCount++] = curStateInfo;
1110 curStateInfo = curStateInfo.parentStateInfo;
1111 } while ((curStateInfo != null) && !curStateInfo.active);
1112
1113 if (mDbg) {
Wink Saville58c73c32013-01-28 10:52:34 -08001114 mSm.log("setupTempStateStackWithStatesToEnter: X mTempStateStackCount="
Wink Savilleff4fcdb2013-02-24 07:21:45 -08001115 + mTempStateStackCount + ",curStateInfo: " + curStateInfo);
Wink Savillefc5b4802009-12-08 21:22:24 -08001116 }
1117 return curStateInfo;
1118 }
1119
1120 /**
1121 * Initialize StateStack to mInitialState.
1122 */
1123 private final void setupInitialStateStack() {
1124 if (mDbg) {
Wink Savilleff4fcdb2013-02-24 07:21:45 -08001125 mSm.log("setupInitialStateStack: E mInitialState=" + mInitialState.getName());
Wink Savillefc5b4802009-12-08 21:22:24 -08001126 }
1127
1128 StateInfo curStateInfo = mStateInfo.get(mInitialState);
1129 for (mTempStateStackCount = 0; curStateInfo != null; mTempStateStackCount++) {
1130 mTempStateStack[mTempStateStackCount] = curStateInfo;
1131 curStateInfo = curStateInfo.parentStateInfo;
1132 }
1133
1134 // Empty the StateStack
1135 mStateStackTopIndex = -1;
1136
1137 moveTempStateStackToStateStack();
1138 }
1139
1140 /**
Wink Savillea4f3bec2010-05-19 09:11:38 -07001141 * @return current message
1142 */
1143 private final Message getCurrentMessage() {
1144 return mMsg;
1145 }
1146
1147 /**
Wink Savillefc5b4802009-12-08 21:22:24 -08001148 * @return current state
1149 */
Wink Saville64c42ca2011-04-18 14:55:10 -07001150 private final IState getCurrentState() {
Wink Savillefc5b4802009-12-08 21:22:24 -08001151 return mStateStack[mStateStackTopIndex].state;
1152 }
1153
1154 /**
1155 * Add a new state to the state machine. Bottom up addition
1156 * of states is allowed but the same state may only exist
1157 * in one hierarchy.
1158 *
1159 * @param state the state to add
1160 * @param parent the parent of state
1161 * @return stateInfo for this state
1162 */
Wink Saville64c42ca2011-04-18 14:55:10 -07001163 private final StateInfo addState(State state, State parent) {
Wink Savillefc5b4802009-12-08 21:22:24 -08001164 if (mDbg) {
Wink Savilleff4fcdb2013-02-24 07:21:45 -08001165 mSm.log("addStateInternal: E state=" + state.getName() + ",parent="
1166 + ((parent == null) ? "" : parent.getName()));
Wink Savillefc5b4802009-12-08 21:22:24 -08001167 }
1168 StateInfo parentStateInfo = null;
1169 if (parent != null) {
1170 parentStateInfo = mStateInfo.get(parent);
1171 if (parentStateInfo == null) {
1172 // Recursively add our parent as it's not been added yet.
1173 parentStateInfo = addState(parent, null);
1174 }
1175 }
1176 StateInfo stateInfo = mStateInfo.get(state);
1177 if (stateInfo == null) {
1178 stateInfo = new StateInfo();
1179 mStateInfo.put(state, stateInfo);
1180 }
1181
1182 // Validate that we aren't adding the same state in two different hierarchies.
Wink Savilleff4fcdb2013-02-24 07:21:45 -08001183 if ((stateInfo.parentStateInfo != null)
1184 && (stateInfo.parentStateInfo != parentStateInfo)) {
1185 throw new RuntimeException("state already added");
Wink Savillefc5b4802009-12-08 21:22:24 -08001186 }
1187 stateInfo.state = state;
1188 stateInfo.parentStateInfo = parentStateInfo;
1189 stateInfo.active = false;
Wink Saville58c73c32013-01-28 10:52:34 -08001190 if (mDbg) mSm.log("addStateInternal: X stateInfo: " + stateInfo);
Wink Savillefc5b4802009-12-08 21:22:24 -08001191 return stateInfo;
1192 }
1193
1194 /**
Hall Liub222d202016-11-21 17:29:02 -08001195 * Remove a state from the state machine. Will not remove the state if it is currently
1196 * active or if it has any children in the hierarchy.
1197 * @param state the state to remove
1198 */
1199 private void removeState(State state) {
1200 StateInfo stateInfo = mStateInfo.get(state);
1201 if (stateInfo == null || stateInfo.active) {
1202 return;
1203 }
1204 boolean isParent = mStateInfo.values().stream()
1205 .filter(si -> si.parentStateInfo == stateInfo)
1206 .findAny()
1207 .isPresent();
1208 if (isParent) {
1209 return;
1210 }
1211 mStateInfo.remove(state);
1212 }
1213
1214 /**
Wink Savillefc5b4802009-12-08 21:22:24 -08001215 * Constructor
1216 *
1217 * @param looper for dispatching messages
Wink Saville64c42ca2011-04-18 14:55:10 -07001218 * @param sm the hierarchical state machine
Wink Savillefc5b4802009-12-08 21:22:24 -08001219 */
Wink Saville64c42ca2011-04-18 14:55:10 -07001220 private SmHandler(Looper looper, StateMachine sm) {
Wink Savillefc5b4802009-12-08 21:22:24 -08001221 super(looper);
Wink Saville64c42ca2011-04-18 14:55:10 -07001222 mSm = sm;
Wink Savillefc5b4802009-12-08 21:22:24 -08001223
1224 addState(mHaltingState, null);
Wink Saville1b8b98b2010-03-11 11:49:54 -08001225 addState(mQuittingState, null);
Wink Savillefc5b4802009-12-08 21:22:24 -08001226 }
1227
Wink Saville64c42ca2011-04-18 14:55:10 -07001228 /** @see StateMachine#setInitialState(State) */
1229 private final void setInitialState(State initialState) {
Wink Saville58c73c32013-01-28 10:52:34 -08001230 if (mDbg) mSm.log("setInitialState: initialState=" + initialState.getName());
Wink Savillefc5b4802009-12-08 21:22:24 -08001231 mInitialState = initialState;
1232 }
1233
Wink Saville64c42ca2011-04-18 14:55:10 -07001234 /** @see StateMachine#transitionTo(IState) */
1235 private final void transitionTo(IState destState) {
Mitchell Wills07e317c2016-08-11 11:05:03 -07001236 if (mTransitionInProgress) {
1237 Log.wtf(mSm.mName, "transitionTo called while transition already in progress to " +
1238 mDestState + ", new target state=" + destState);
1239 }
Wink Saville64c42ca2011-04-18 14:55:10 -07001240 mDestState = (State) destState;
Wink Saville58c73c32013-01-28 10:52:34 -08001241 if (mDbg) mSm.log("transitionTo: destState=" + mDestState.getName());
Wink Savillefc5b4802009-12-08 21:22:24 -08001242 }
1243
Wink Saville64c42ca2011-04-18 14:55:10 -07001244 /** @see StateMachine#deferMessage(Message) */
Wink Savillefc5b4802009-12-08 21:22:24 -08001245 private final void deferMessage(Message msg) {
Wink Saville58c73c32013-01-28 10:52:34 -08001246 if (mDbg) mSm.log("deferMessage: msg=" + msg.what);
Wink Savillefc5b4802009-12-08 21:22:24 -08001247
1248 /* Copy the "msg" to "newMsg" as "msg" will be recycled */
1249 Message newMsg = obtainMessage();
1250 newMsg.copyFrom(msg);
1251
1252 mDeferredMessages.add(newMsg);
1253 }
1254
Wink Savillebbf30dfd2012-05-29 12:40:46 -07001255 /** @see StateMachine#quit() */
Wink Saville1b8b98b2010-03-11 11:49:54 -08001256 private final void quit() {
Wink Saville58c73c32013-01-28 10:52:34 -08001257 if (mDbg) mSm.log("quit:");
Wink Savillecea056f2012-03-26 15:03:16 -07001258 sendMessage(obtainMessage(SM_QUIT_CMD, mSmHandlerObj));
Wink Saville1b8b98b2010-03-11 11:49:54 -08001259 }
1260
Wink Savillebbf30dfd2012-05-29 12:40:46 -07001261 /** @see StateMachine#quitNow() */
1262 private final void quitNow() {
Wink Saville58c73c32013-01-28 10:52:34 -08001263 if (mDbg) mSm.log("quitNow:");
Wink Savillebbf30dfd2012-05-29 12:40:46 -07001264 sendMessageAtFrontOfQueue(obtainMessage(SM_QUIT_CMD, mSmHandlerObj));
1265 }
1266
Wink Saville58c73c32013-01-28 10:52:34 -08001267 /** Validate that the message was sent by quit or quitNow. */
Wink Saville1b8b98b2010-03-11 11:49:54 -08001268 private final boolean isQuit(Message msg) {
Wink Savillecea056f2012-03-26 15:03:16 -07001269 return (msg.what == SM_QUIT_CMD) && (msg.obj == mSmHandlerObj);
Wink Saville1b8b98b2010-03-11 11:49:54 -08001270 }
1271
Wink Saville64c42ca2011-04-18 14:55:10 -07001272 /** @see StateMachine#isDbg() */
Wink Savillefc5b4802009-12-08 21:22:24 -08001273 private final boolean isDbg() {
1274 return mDbg;
1275 }
1276
Wink Saville64c42ca2011-04-18 14:55:10 -07001277 /** @see StateMachine#setDbg(boolean) */
Wink Savillefc5b4802009-12-08 21:22:24 -08001278 private final void setDbg(boolean dbg) {
1279 mDbg = dbg;
1280 }
1281
Wink Savillefc5b4802009-12-08 21:22:24 -08001282 }
1283
Wink Saville64c42ca2011-04-18 14:55:10 -07001284 private SmHandler mSmHandler;
1285 private HandlerThread mSmThread;
Wink Savillefc5b4802009-12-08 21:22:24 -08001286
1287 /**
1288 * Initialize.
1289 *
1290 * @param looper for this state machine
1291 * @param name of the state machine
1292 */
Wink Savillef0f566e2010-03-11 14:03:50 -08001293 private void initStateMachine(String name, Looper looper) {
Wink Savillefc5b4802009-12-08 21:22:24 -08001294 mName = name;
Wink Saville64c42ca2011-04-18 14:55:10 -07001295 mSmHandler = new SmHandler(looper, this);
Wink Savillefc5b4802009-12-08 21:22:24 -08001296 }
1297
1298 /**
Wink Saville64c42ca2011-04-18 14:55:10 -07001299 * Constructor creates a StateMachine with its own thread.
Wink Savillefc5b4802009-12-08 21:22:24 -08001300 *
1301 * @param name of the state machine
1302 */
Wink Saville64c42ca2011-04-18 14:55:10 -07001303 protected StateMachine(String name) {
1304 mSmThread = new HandlerThread(name);
1305 mSmThread.start();
1306 Looper looper = mSmThread.getLooper();
Wink Savillefc5b4802009-12-08 21:22:24 -08001307
Wink Savillef0f566e2010-03-11 14:03:50 -08001308 initStateMachine(name, looper);
Wink Savillefc5b4802009-12-08 21:22:24 -08001309 }
1310
1311 /**
Ken Wakasaf76a50c2012-03-09 19:56:35 +09001312 * Constructor creates a StateMachine using the looper.
Wink Savillefc5b4802009-12-08 21:22:24 -08001313 *
1314 * @param name of the state machine
1315 */
Wink Saville64c42ca2011-04-18 14:55:10 -07001316 protected StateMachine(String name, Looper looper) {
Wink Savillef0f566e2010-03-11 14:03:50 -08001317 initStateMachine(name, looper);
Wink Savillefc5b4802009-12-08 21:22:24 -08001318 }
1319
1320 /**
Wink Saville24d248a2013-03-17 14:26:21 -07001321 * Constructor creates a StateMachine using the handler.
1322 *
1323 * @param name of the state machine
1324 */
1325 protected StateMachine(String name, Handler handler) {
1326 initStateMachine(name, handler.getLooper());
1327 }
1328
1329 /**
Brad Ebinger0c714042015-12-02 17:41:45 -08001330 * Notifies subclass that the StateMachine handler is about to process the Message msg
1331 * @param msg The message that is being handled
1332 */
1333 protected void onPreHandleMessage(Message msg) {
1334 }
1335
1336 /**
1337 * Notifies subclass that the StateMachine handler has finished processing the Message msg and
1338 * has possibly transitioned to a new state.
1339 * @param msg The message that is being handled
1340 */
1341 protected void onPostHandleMessage(Message msg) {
1342 }
1343
1344 /**
Wink Savillefc5b4802009-12-08 21:22:24 -08001345 * Add a new state to the state machine
1346 * @param state the state to add
1347 * @param parent the parent of state
1348 */
Mitchell Wills009ae992016-08-09 13:33:20 -07001349 public final void addState(State state, State parent) {
Wink Saville64c42ca2011-04-18 14:55:10 -07001350 mSmHandler.addState(state, parent);
Wink Savillefc5b4802009-12-08 21:22:24 -08001351 }
Wink Savillea4f3bec2010-05-19 09:11:38 -07001352
1353 /**
Wink Savillefc5b4802009-12-08 21:22:24 -08001354 * Add a new state to the state machine, parent will be null
1355 * @param state to add
1356 */
Mitchell Wills009ae992016-08-09 13:33:20 -07001357 public final void addState(State state) {
Wink Saville64c42ca2011-04-18 14:55:10 -07001358 mSmHandler.addState(state, null);
Wink Savillefc5b4802009-12-08 21:22:24 -08001359 }
1360
1361 /**
Hall Liub222d202016-11-21 17:29:02 -08001362 * Removes a state from the state machine, unless it is currently active or if it has children.
1363 * @param state state to remove
1364 */
1365 public final void removeState(State state) {
1366 mSmHandler.removeState(state);
1367 }
1368
1369 /**
Wink Savillefc5b4802009-12-08 21:22:24 -08001370 * Set the initial state. This must be invoked before
1371 * and messages are sent to the state machine.
1372 *
1373 * @param initialState is the state which will receive the first message.
1374 */
Mitchell Wills009ae992016-08-09 13:33:20 -07001375 public final void setInitialState(State initialState) {
Wink Saville64c42ca2011-04-18 14:55:10 -07001376 mSmHandler.setInitialState(initialState);
Wink Savillefc5b4802009-12-08 21:22:24 -08001377 }
1378
1379 /**
Wink Savilleefcc3d32013-01-30 11:21:22 -08001380 * @return current message
1381 */
Mitchell Wills009ae992016-08-09 13:33:20 -07001382 public final Message getCurrentMessage() {
Wink Savilleefcc3d32013-01-30 11:21:22 -08001383 // mSmHandler can be null if the state machine has quit.
1384 SmHandler smh = mSmHandler;
1385 if (smh == null) return null;
1386 return smh.getCurrentMessage();
1387 }
1388
1389 /**
1390 * @return current state
1391 */
Mitchell Wills009ae992016-08-09 13:33:20 -07001392 public final IState getCurrentState() {
Wink Savilleefcc3d32013-01-30 11:21:22 -08001393 // mSmHandler can be null if the state machine has quit.
1394 SmHandler smh = mSmHandler;
1395 if (smh == null) return null;
1396 return smh.getCurrentState();
1397 }
1398
1399 /**
Wink Savillefc5b4802009-12-08 21:22:24 -08001400 * transition to destination state. Upon returning
1401 * from processMessage the current state's exit will
1402 * be executed and upon the next message arriving
1403 * destState.enter will be invoked.
1404 *
Isaac Levyd2fe04b2011-07-22 08:48:26 -07001405 * this function can also be called inside the enter function of the
1406 * previous transition target, but the behavior is undefined when it is
1407 * called mid-way through a previous transition (for example, calling this
1408 * in the enter() routine of a intermediate node when the current transition
1409 * target is one of the nodes descendants).
1410 *
Wink Savillefc5b4802009-12-08 21:22:24 -08001411 * @param destState will be the state that receives the next message.
1412 */
Mitchell Wills009ae992016-08-09 13:33:20 -07001413 public final void transitionTo(IState destState) {
Wink Saville64c42ca2011-04-18 14:55:10 -07001414 mSmHandler.transitionTo(destState);
Wink Savillefc5b4802009-12-08 21:22:24 -08001415 }
1416
1417 /**
1418 * transition to halt state. Upon returning
1419 * from processMessage we will exit all current
Wink Savillebbf30dfd2012-05-29 12:40:46 -07001420 * states, execute the onHalting() method and then
1421 * for all subsequent messages haltedProcessMessage
Wink Savillefc5b4802009-12-08 21:22:24 -08001422 * will be called.
1423 */
Mitchell Wills009ae992016-08-09 13:33:20 -07001424 public final void transitionToHaltingState() {
Wink Saville64c42ca2011-04-18 14:55:10 -07001425 mSmHandler.transitionTo(mSmHandler.mHaltingState);
Wink Savillefc5b4802009-12-08 21:22:24 -08001426 }
1427
1428 /**
1429 * Defer this message until next state transition.
1430 * Upon transitioning all deferred messages will be
1431 * placed on the queue and reprocessed in the original
1432 * order. (i.e. The next state the oldest messages will
1433 * be processed first)
1434 *
1435 * @param msg is deferred until the next transition.
1436 */
Mitchell Wills009ae992016-08-09 13:33:20 -07001437 public final void deferMessage(Message msg) {
Wink Saville64c42ca2011-04-18 14:55:10 -07001438 mSmHandler.deferMessage(msg);
Wink Savillefc5b4802009-12-08 21:22:24 -08001439 }
1440
Wink Savillefc5b4802009-12-08 21:22:24 -08001441 /**
1442 * Called when message wasn't handled
1443 *
1444 * @param msg that couldn't be handled.
1445 */
1446 protected void unhandledMessage(Message msg) {
Wink Saville58c73c32013-01-28 10:52:34 -08001447 if (mSmHandler.mDbg) loge(" - unhandledMessage: msg.what=" + msg.what);
Wink Savillefc5b4802009-12-08 21:22:24 -08001448 }
1449
1450 /**
1451 * Called for any message that is received after
1452 * transitionToHalting is called.
1453 */
1454 protected void haltedProcessMessage(Message msg) {
1455 }
1456
1457 /**
Wink Savilled3059482011-04-11 11:51:28 -07001458 * This will be called once after handling a message that called
1459 * transitionToHalting. All subsequent messages will invoke
Wink Saville64c42ca2011-04-18 14:55:10 -07001460 * {@link StateMachine#haltedProcessMessage(Message)}
Wink Savillefc5b4802009-12-08 21:22:24 -08001461 */
Wink Savillebbf30dfd2012-05-29 12:40:46 -07001462 protected void onHalting() {
Wink Savillefc5b4802009-12-08 21:22:24 -08001463 }
1464
1465 /**
Wink Savilled3059482011-04-11 11:51:28 -07001466 * This will be called once after a quit message that was NOT handled by
Wink Saville64c42ca2011-04-18 14:55:10 -07001467 * the derived StateMachine. The StateMachine will stop and any subsequent messages will be
1468 * ignored. In addition, if this StateMachine created the thread, the thread will
Wink Savilled3059482011-04-11 11:51:28 -07001469 * be stopped after this method returns.
Wink Saville1b8b98b2010-03-11 11:49:54 -08001470 */
Wink Savillebbf30dfd2012-05-29 12:40:46 -07001471 protected void onQuitting() {
Wink Saville1b8b98b2010-03-11 11:49:54 -08001472 }
1473
1474 /**
Wink Savillefc5b4802009-12-08 21:22:24 -08001475 * @return the name
1476 */
1477 public final String getName() {
1478 return mName;
1479 }
1480
1481 /**
Wink Savillebbf30dfd2012-05-29 12:40:46 -07001482 * Set number of log records to maintain and clears all current records.
Wink Savillefc5b4802009-12-08 21:22:24 -08001483 *
1484 * @param maxSize number of messages to maintain at anyone time.
1485 */
Wink Savillebbf30dfd2012-05-29 12:40:46 -07001486 public final void setLogRecSize(int maxSize) {
1487 mSmHandler.mLogRecords.setSize(maxSize);
Wink Savillefc5b4802009-12-08 21:22:24 -08001488 }
1489
1490 /**
Irfan Sheriffef8da9f2012-11-29 09:42:13 -08001491 * Set to log only messages that cause a state transition
1492 *
1493 * @param enable {@code true} to enable, {@code false} to disable
1494 */
1495 public final void setLogOnlyTransitions(boolean enable) {
1496 mSmHandler.mLogRecords.setLogOnlyTransitions(enable);
1497 }
1498
1499 /**
mukesh agrawal8ed82ec2017-02-23 17:13:29 -08001500 * @return the number of log records currently readable
Wink Savillefc5b4802009-12-08 21:22:24 -08001501 */
Wink Savillebbf30dfd2012-05-29 12:40:46 -07001502 public final int getLogRecSize() {
Wink Savilleefcc3d32013-01-30 11:21:22 -08001503 // mSmHandler can be null if the state machine has quit.
1504 SmHandler smh = mSmHandler;
1505 if (smh == null) return 0;
1506 return smh.mLogRecords.size();
Wink Savillefc5b4802009-12-08 21:22:24 -08001507 }
1508
1509 /**
mukesh agrawal8ed82ec2017-02-23 17:13:29 -08001510 * @return the number of log records we can store
1511 */
1512 @VisibleForTesting
1513 public final int getLogRecMaxSize() {
1514 // mSmHandler can be null if the state machine has quit.
1515 SmHandler smh = mSmHandler;
1516 if (smh == null) return 0;
1517 return smh.mLogRecords.mMaxSize;
1518 }
1519
1520 /**
Wink Savillebbf30dfd2012-05-29 12:40:46 -07001521 * @return the total number of records processed
Wink Savillefc5b4802009-12-08 21:22:24 -08001522 */
Wink Savillebbf30dfd2012-05-29 12:40:46 -07001523 public final int getLogRecCount() {
Wink Savilleefcc3d32013-01-30 11:21:22 -08001524 // mSmHandler can be null if the state machine has quit.
1525 SmHandler smh = mSmHandler;
1526 if (smh == null) return 0;
1527 return smh.mLogRecords.count();
Wink Savillefc5b4802009-12-08 21:22:24 -08001528 }
1529
1530 /**
Wink Savilleefcc3d32013-01-30 11:21:22 -08001531 * @return a log record, or null if index is out of range
Wink Savillefc5b4802009-12-08 21:22:24 -08001532 */
Wink Savillebbf30dfd2012-05-29 12:40:46 -07001533 public final LogRec getLogRec(int index) {
Wink Savilleefcc3d32013-01-30 11:21:22 -08001534 // mSmHandler can be null if the state machine has quit.
1535 SmHandler smh = mSmHandler;
1536 if (smh == null) return null;
1537 return smh.mLogRecords.get(index);
1538 }
1539
1540 /**
1541 * @return a copy of LogRecs as a collection
1542 */
1543 public final Collection<LogRec> copyLogRecs() {
1544 Vector<LogRec> vlr = new Vector<LogRec>();
1545 SmHandler smh = mSmHandler;
1546 if (smh != null) {
1547 for (LogRec lr : smh.mLogRecords.mLogRecVector) {
1548 vlr.add(lr);
1549 }
1550 }
1551 return vlr;
Wink Savillebbf30dfd2012-05-29 12:40:46 -07001552 }
1553
1554 /**
1555 * Add the string to LogRecords.
1556 *
1557 * @param string
1558 */
Mitchell Wills009ae992016-08-09 13:33:20 -07001559 public void addLogRec(String string) {
Wink Savilleefcc3d32013-01-30 11:21:22 -08001560 // mSmHandler can be null if the state machine has quit.
1561 SmHandler smh = mSmHandler;
1562 if (smh == null) return;
1563 smh.mLogRecords.add(this, smh.getCurrentMessage(), string, smh.getCurrentState(),
1564 smh.mStateStack[smh.mStateStackTopIndex].state, smh.mDestState);
Wink Savillebbf30dfd2012-05-29 12:40:46 -07001565 }
1566
1567 /**
1568 * @return true if msg should be saved in the log, default is true.
1569 */
1570 protected boolean recordLogRec(Message msg) {
1571 return true;
1572 }
1573
1574 /**
1575 * Return a string to be logged by LogRec, default
1576 * is an empty string. Override if additional information is desired.
1577 *
1578 * @param msg that was processed
1579 * @return information to be logged as a String
1580 */
1581 protected String getLogRecString(Message msg) {
1582 return "";
1583 }
1584
1585 /**
1586 * @return the string for msg.what
1587 */
1588 protected String getWhatToString(int what) {
1589 return null;
Wink Savillefc5b4802009-12-08 21:22:24 -08001590 }
1591
1592 /**
Wink Savilleefcc3d32013-01-30 11:21:22 -08001593 * @return Handler, maybe null if state machine has quit.
Wink Savillefc5b4802009-12-08 21:22:24 -08001594 */
1595 public final Handler getHandler() {
Wink Saville64c42ca2011-04-18 14:55:10 -07001596 return mSmHandler;
Wink Savillefc5b4802009-12-08 21:22:24 -08001597 }
1598
1599 /**
Wink Savilleefcc3d32013-01-30 11:21:22 -08001600 * Get a message and set Message.target state machine handler.
Wink Savillefc5b4802009-12-08 21:22:24 -08001601 *
Wink Savilleefcc3d32013-01-30 11:21:22 -08001602 * Note: The handler can be null if the state machine has quit,
1603 * which means target will be null and may cause a AndroidRuntimeException
1604 * in MessageQueue#enqueMessage if sent directly or if sent using
1605 * StateMachine#sendMessage the message will just be ignored.
1606 *
1607 * @return A Message object from the global pool
Wink Savillefc5b4802009-12-08 21:22:24 -08001608 */
Wink Savilleff4fcdb2013-02-24 07:21:45 -08001609 public final Message obtainMessage() {
Wink Saville64c42ca2011-04-18 14:55:10 -07001610 return Message.obtain(mSmHandler);
Wink Savillefc5b4802009-12-08 21:22:24 -08001611 }
1612
1613 /**
Wink Savilleefcc3d32013-01-30 11:21:22 -08001614 * Get a message and set Message.target state machine handler, what.
1615 *
1616 * Note: The handler can be null if the state machine has quit,
1617 * which means target will be null and may cause a AndroidRuntimeException
1618 * in MessageQueue#enqueMessage if sent directly or if sent using
1619 * StateMachine#sendMessage the message will just be ignored.
Wink Savillefc5b4802009-12-08 21:22:24 -08001620 *
1621 * @param what is the assigned to Message.what.
Wink Savilleefcc3d32013-01-30 11:21:22 -08001622 * @return A Message object from the global pool
Wink Savillefc5b4802009-12-08 21:22:24 -08001623 */
1624 public final Message obtainMessage(int what) {
Wink Saville64c42ca2011-04-18 14:55:10 -07001625 return Message.obtain(mSmHandler, what);
Wink Savillefc5b4802009-12-08 21:22:24 -08001626 }
1627
1628 /**
Wink Savilleefcc3d32013-01-30 11:21:22 -08001629 * Get a message and set Message.target state machine handler,
Wink Savillefc5b4802009-12-08 21:22:24 -08001630 * what and obj.
1631 *
Wink Savilleefcc3d32013-01-30 11:21:22 -08001632 * Note: The handler can be null if the state machine has quit,
1633 * which means target will be null and may cause a AndroidRuntimeException
1634 * in MessageQueue#enqueMessage if sent directly or if sent using
1635 * StateMachine#sendMessage the message will just be ignored.
1636 *
Wink Savillefc5b4802009-12-08 21:22:24 -08001637 * @param what is the assigned to Message.what.
1638 * @param obj is assigned to Message.obj.
Wink Savilleefcc3d32013-01-30 11:21:22 -08001639 * @return A Message object from the global pool
Wink Savillefc5b4802009-12-08 21:22:24 -08001640 */
Wink Savilleff4fcdb2013-02-24 07:21:45 -08001641 public final Message obtainMessage(int what, Object obj) {
Wink Saville64c42ca2011-04-18 14:55:10 -07001642 return Message.obtain(mSmHandler, what, obj);
Wink Savillefc5b4802009-12-08 21:22:24 -08001643 }
1644
Wink Saville91fbd562010-03-17 17:12:43 -07001645 /**
Wink Savilleefcc3d32013-01-30 11:21:22 -08001646 * Get a message and set Message.target state machine handler,
Irfan Sheriff96e6bdb2010-06-25 09:44:27 -07001647 * what, arg1 and arg2
1648 *
Wink Savilleefcc3d32013-01-30 11:21:22 -08001649 * Note: The handler can be null if the state machine has quit,
1650 * which means target will be null and may cause a AndroidRuntimeException
1651 * in MessageQueue#enqueMessage if sent directly or if sent using
1652 * StateMachine#sendMessage the message will just be ignored.
1653 *
Irfan Sheriff96e6bdb2010-06-25 09:44:27 -07001654 * @param what is assigned to Message.what
1655 * @param arg1 is assigned to Message.arg1
Wink Saville8b0db522013-03-14 13:23:19 -07001656 * @return A Message object from the global pool
1657 */
1658 public final Message obtainMessage(int what, int arg1) {
1659 // use this obtain so we don't match the obtain(h, what, Object) method
1660 return Message.obtain(mSmHandler, what, arg1, 0);
1661 }
1662
1663 /**
1664 * Get a message and set Message.target state machine handler,
1665 * what, arg1 and arg2
1666 *
1667 * Note: The handler can be null if the state machine has quit,
1668 * which means target will be null and may cause a AndroidRuntimeException
1669 * in MessageQueue#enqueMessage if sent directly or if sent using
1670 * StateMachine#sendMessage the message will just be ignored.
1671 *
1672 * @param what is assigned to Message.what
1673 * @param arg1 is assigned to Message.arg1
Irfan Sheriff96e6bdb2010-06-25 09:44:27 -07001674 * @param arg2 is assigned to Message.arg2
Wink Savilleefcc3d32013-01-30 11:21:22 -08001675 * @return A Message object from the global pool
Irfan Sheriff96e6bdb2010-06-25 09:44:27 -07001676 */
Wink Savilleff4fcdb2013-02-24 07:21:45 -08001677 public final Message obtainMessage(int what, int arg1, int arg2) {
Wink Saville64c42ca2011-04-18 14:55:10 -07001678 return Message.obtain(mSmHandler, what, arg1, arg2);
Irfan Sheriff96e6bdb2010-06-25 09:44:27 -07001679 }
1680
1681 /**
Wink Savilleefcc3d32013-01-30 11:21:22 -08001682 * Get a message and set Message.target state machine handler,
Irfan Sheriff96e6bdb2010-06-25 09:44:27 -07001683 * what, arg1, arg2 and obj
1684 *
Wink Savilleefcc3d32013-01-30 11:21:22 -08001685 * Note: The handler can be null if the state machine has quit,
1686 * which means target will be null and may cause a AndroidRuntimeException
1687 * in MessageQueue#enqueMessage if sent directly or if sent using
1688 * StateMachine#sendMessage the message will just be ignored.
1689 *
Irfan Sheriff96e6bdb2010-06-25 09:44:27 -07001690 * @param what is assigned to Message.what
1691 * @param arg1 is assigned to Message.arg1
1692 * @param arg2 is assigned to Message.arg2
1693 * @param obj is assigned to Message.obj
Wink Savilleefcc3d32013-01-30 11:21:22 -08001694 * @return A Message object from the global pool
Irfan Sheriff96e6bdb2010-06-25 09:44:27 -07001695 */
Wink Savilleff4fcdb2013-02-24 07:21:45 -08001696 public final Message obtainMessage(int what, int arg1, int arg2, Object obj) {
Wink Saville64c42ca2011-04-18 14:55:10 -07001697 return Message.obtain(mSmHandler, what, arg1, arg2, obj);
Irfan Sheriff96e6bdb2010-06-25 09:44:27 -07001698 }
1699
1700 /**
Wink Saville91fbd562010-03-17 17:12:43 -07001701 * Enqueue a message to this state machine.
Wink Savilleefcc3d32013-01-30 11:21:22 -08001702 *
1703 * Message is ignored if state machine has quit.
Wink Saville91fbd562010-03-17 17:12:43 -07001704 */
Rebecca Silberstein934535b2016-05-13 13:57:34 -07001705 public void sendMessage(int what) {
Jaikumar Ganesha544d462011-12-07 14:52:01 -08001706 // mSmHandler can be null if the state machine has quit.
Wink Savilleefcc3d32013-01-30 11:21:22 -08001707 SmHandler smh = mSmHandler;
1708 if (smh == null) return;
Jaikumar Ganesha544d462011-12-07 14:52:01 -08001709
Wink Savilleefcc3d32013-01-30 11:21:22 -08001710 smh.sendMessage(obtainMessage(what));
Wink Saville91fbd562010-03-17 17:12:43 -07001711 }
1712
1713 /**
1714 * Enqueue a message to this state machine.
Wink Savilleefcc3d32013-01-30 11:21:22 -08001715 *
1716 * Message is ignored if state machine has quit.
Wink Saville91fbd562010-03-17 17:12:43 -07001717 */
Rebecca Silberstein934535b2016-05-13 13:57:34 -07001718 public void sendMessage(int what, Object obj) {
Jaikumar Ganesha544d462011-12-07 14:52:01 -08001719 // mSmHandler can be null if the state machine has quit.
Wink Savilleefcc3d32013-01-30 11:21:22 -08001720 SmHandler smh = mSmHandler;
1721 if (smh == null) return;
Jaikumar Ganesha544d462011-12-07 14:52:01 -08001722
Wink Savilleff4fcdb2013-02-24 07:21:45 -08001723 smh.sendMessage(obtainMessage(what, obj));
Wink Saville91fbd562010-03-17 17:12:43 -07001724 }
1725
Wink Savillefc5b4802009-12-08 21:22:24 -08001726 /**
1727 * Enqueue a message to this state machine.
Wink Savilleefcc3d32013-01-30 11:21:22 -08001728 *
1729 * Message is ignored if state machine has quit.
Wink Savillefc5b4802009-12-08 21:22:24 -08001730 */
Rebecca Silberstein934535b2016-05-13 13:57:34 -07001731 public void sendMessage(int what, int arg1) {
Wink Saville8b0db522013-03-14 13:23:19 -07001732 // mSmHandler can be null if the state machine has quit.
1733 SmHandler smh = mSmHandler;
1734 if (smh == null) return;
1735
1736 smh.sendMessage(obtainMessage(what, arg1));
1737 }
1738
1739 /**
1740 * Enqueue a message to this state machine.
1741 *
1742 * Message is ignored if state machine has quit.
1743 */
Rebecca Silberstein934535b2016-05-13 13:57:34 -07001744 public void sendMessage(int what, int arg1, int arg2) {
Wink Saville8b0db522013-03-14 13:23:19 -07001745 // mSmHandler can be null if the state machine has quit.
1746 SmHandler smh = mSmHandler;
1747 if (smh == null) return;
1748
1749 smh.sendMessage(obtainMessage(what, arg1, arg2));
1750 }
1751
1752 /**
1753 * Enqueue a message to this state machine.
1754 *
1755 * Message is ignored if state machine has quit.
1756 */
Rebecca Silberstein934535b2016-05-13 13:57:34 -07001757 public void sendMessage(int what, int arg1, int arg2, Object obj) {
Wink Saville4753cd22013-03-06 13:41:23 -08001758 // mSmHandler can be null if the state machine has quit.
1759 SmHandler smh = mSmHandler;
1760 if (smh == null) return;
1761
1762 smh.sendMessage(obtainMessage(what, arg1, arg2, obj));
1763 }
1764
1765 /**
1766 * Enqueue a message to this state machine.
1767 *
1768 * Message is ignored if state machine has quit.
1769 */
Rebecca Silberstein934535b2016-05-13 13:57:34 -07001770 public void sendMessage(Message msg) {
Jaikumar Ganesha544d462011-12-07 14:52:01 -08001771 // mSmHandler can be null if the state machine has quit.
Wink Savilleefcc3d32013-01-30 11:21:22 -08001772 SmHandler smh = mSmHandler;
1773 if (smh == null) return;
Jaikumar Ganesha544d462011-12-07 14:52:01 -08001774
Wink Savilleefcc3d32013-01-30 11:21:22 -08001775 smh.sendMessage(msg);
Wink Savillefc5b4802009-12-08 21:22:24 -08001776 }
1777
1778 /**
1779 * Enqueue a message to this state machine after a delay.
Wink Savilleefcc3d32013-01-30 11:21:22 -08001780 *
1781 * Message is ignored if state machine has quit.
Wink Savillefc5b4802009-12-08 21:22:24 -08001782 */
Rebecca Silberstein934535b2016-05-13 13:57:34 -07001783 public void sendMessageDelayed(int what, long delayMillis) {
Jaikumar Ganesha544d462011-12-07 14:52:01 -08001784 // mSmHandler can be null if the state machine has quit.
Wink Savilleefcc3d32013-01-30 11:21:22 -08001785 SmHandler smh = mSmHandler;
1786 if (smh == null) return;
Jaikumar Ganesha544d462011-12-07 14:52:01 -08001787
Wink Savilleefcc3d32013-01-30 11:21:22 -08001788 smh.sendMessageDelayed(obtainMessage(what), delayMillis);
Wink Saville91fbd562010-03-17 17:12:43 -07001789 }
1790
1791 /**
1792 * Enqueue a message to this state machine after a delay.
Wink Savilleefcc3d32013-01-30 11:21:22 -08001793 *
1794 * Message is ignored if state machine has quit.
Wink Saville91fbd562010-03-17 17:12:43 -07001795 */
Rebecca Silberstein934535b2016-05-13 13:57:34 -07001796 public void sendMessageDelayed(int what, Object obj, long delayMillis) {
Jaikumar Ganesha544d462011-12-07 14:52:01 -08001797 // mSmHandler can be null if the state machine has quit.
Wink Savilleefcc3d32013-01-30 11:21:22 -08001798 SmHandler smh = mSmHandler;
1799 if (smh == null) return;
Jaikumar Ganesha544d462011-12-07 14:52:01 -08001800
Wink Savilleefcc3d32013-01-30 11:21:22 -08001801 smh.sendMessageDelayed(obtainMessage(what, obj), delayMillis);
Wink Saville91fbd562010-03-17 17:12:43 -07001802 }
1803
1804 /**
1805 * Enqueue a message to this state machine after a delay.
Wink Savilleefcc3d32013-01-30 11:21:22 -08001806 *
1807 * Message is ignored if state machine has quit.
Wink Saville91fbd562010-03-17 17:12:43 -07001808 */
Rebecca Silberstein934535b2016-05-13 13:57:34 -07001809 public void sendMessageDelayed(int what, int arg1, long delayMillis) {
Wink Saville8b0db522013-03-14 13:23:19 -07001810 // mSmHandler can be null if the state machine has quit.
1811 SmHandler smh = mSmHandler;
1812 if (smh == null) return;
1813
1814 smh.sendMessageDelayed(obtainMessage(what, arg1), delayMillis);
1815 }
1816
1817 /**
1818 * Enqueue a message to this state machine after a delay.
1819 *
1820 * Message is ignored if state machine has quit.
1821 */
Rebecca Silberstein934535b2016-05-13 13:57:34 -07001822 public void sendMessageDelayed(int what, int arg1, int arg2, long delayMillis) {
Wink Saville8b0db522013-03-14 13:23:19 -07001823 // mSmHandler can be null if the state machine has quit.
1824 SmHandler smh = mSmHandler;
1825 if (smh == null) return;
1826
1827 smh.sendMessageDelayed(obtainMessage(what, arg1, arg2), delayMillis);
1828 }
1829
1830 /**
1831 * Enqueue a message to this state machine after a delay.
1832 *
1833 * Message is ignored if state machine has quit.
1834 */
Rebecca Silberstein934535b2016-05-13 13:57:34 -07001835 public void sendMessageDelayed(int what, int arg1, int arg2, Object obj,
Wink Saville4753cd22013-03-06 13:41:23 -08001836 long delayMillis) {
1837 // mSmHandler can be null if the state machine has quit.
1838 SmHandler smh = mSmHandler;
1839 if (smh == null) return;
1840
1841 smh.sendMessageDelayed(obtainMessage(what, arg1, arg2, obj), delayMillis);
1842 }
1843
1844 /**
1845 * Enqueue a message to this state machine after a delay.
1846 *
1847 * Message is ignored if state machine has quit.
1848 */
Rebecca Silberstein934535b2016-05-13 13:57:34 -07001849 public void sendMessageDelayed(Message msg, long delayMillis) {
Jaikumar Ganesha544d462011-12-07 14:52:01 -08001850 // mSmHandler can be null if the state machine has quit.
Wink Savilleefcc3d32013-01-30 11:21:22 -08001851 SmHandler smh = mSmHandler;
1852 if (smh == null) return;
Jaikumar Ganesha544d462011-12-07 14:52:01 -08001853
Wink Savilleefcc3d32013-01-30 11:21:22 -08001854 smh.sendMessageDelayed(msg, delayMillis);
Wink Savillefc5b4802009-12-08 21:22:24 -08001855 }
1856
1857 /**
1858 * Enqueue a message to the front of the queue for this state machine.
Wink Saville64c42ca2011-04-18 14:55:10 -07001859 * Protected, may only be called by instances of StateMachine.
Wink Savilleefcc3d32013-01-30 11:21:22 -08001860 *
1861 * Message is ignored if state machine has quit.
Wink Savillefc5b4802009-12-08 21:22:24 -08001862 */
Wink Saville8b0db522013-03-14 13:23:19 -07001863 protected final void sendMessageAtFrontOfQueue(int what) {
1864 // mSmHandler can be null if the state machine has quit.
1865 SmHandler smh = mSmHandler;
1866 if (smh == null) return;
1867
1868 smh.sendMessageAtFrontOfQueue(obtainMessage(what));
1869 }
1870
1871 /**
1872 * Enqueue a message to the front of the queue for this state machine.
1873 * Protected, may only be called by instances of StateMachine.
1874 *
1875 * Message is ignored if state machine has quit.
1876 */
Wink Saville91fbd562010-03-17 17:12:43 -07001877 protected final void sendMessageAtFrontOfQueue(int what, Object obj) {
Wink Savilleefcc3d32013-01-30 11:21:22 -08001878 // mSmHandler can be null if the state machine has quit.
1879 SmHandler smh = mSmHandler;
1880 if (smh == null) return;
1881
1882 smh.sendMessageAtFrontOfQueue(obtainMessage(what, obj));
Wink Saville91fbd562010-03-17 17:12:43 -07001883 }
1884
1885 /**
1886 * Enqueue a message to the front of the queue for this state machine.
Wink Saville64c42ca2011-04-18 14:55:10 -07001887 * Protected, may only be called by instances of StateMachine.
Wink Savilleefcc3d32013-01-30 11:21:22 -08001888 *
1889 * Message is ignored if state machine has quit.
Wink Saville91fbd562010-03-17 17:12:43 -07001890 */
Wink Saville8b0db522013-03-14 13:23:19 -07001891 protected final void sendMessageAtFrontOfQueue(int what, int arg1) {
Wink Savilleefcc3d32013-01-30 11:21:22 -08001892 // mSmHandler can be null if the state machine has quit.
1893 SmHandler smh = mSmHandler;
1894 if (smh == null) return;
1895
Wink Saville8b0db522013-03-14 13:23:19 -07001896 smh.sendMessageAtFrontOfQueue(obtainMessage(what, arg1));
1897 }
1898
1899
1900 /**
1901 * Enqueue a message to the front of the queue for this state machine.
1902 * Protected, may only be called by instances of StateMachine.
1903 *
1904 * Message is ignored if state machine has quit.
1905 */
1906 protected final void sendMessageAtFrontOfQueue(int what, int arg1, int arg2) {
1907 // mSmHandler can be null if the state machine has quit.
1908 SmHandler smh = mSmHandler;
1909 if (smh == null) return;
1910
1911 smh.sendMessageAtFrontOfQueue(obtainMessage(what, arg1, arg2));
Wink Saville91fbd562010-03-17 17:12:43 -07001912 }
1913
1914 /**
1915 * Enqueue a message to the front of the queue for this state machine.
Wink Saville64c42ca2011-04-18 14:55:10 -07001916 * Protected, may only be called by instances of StateMachine.
Wink Savilleefcc3d32013-01-30 11:21:22 -08001917 *
1918 * Message is ignored if state machine has quit.
Wink Saville91fbd562010-03-17 17:12:43 -07001919 */
Wink Saville4753cd22013-03-06 13:41:23 -08001920 protected final void sendMessageAtFrontOfQueue(int what, int arg1, int arg2, Object obj) {
1921 // mSmHandler can be null if the state machine has quit.
1922 SmHandler smh = mSmHandler;
1923 if (smh == null) return;
1924
1925 smh.sendMessageAtFrontOfQueue(obtainMessage(what, arg1, arg2, obj));
1926 }
1927
1928 /**
1929 * Enqueue a message to the front of the queue for this state machine.
1930 * Protected, may only be called by instances of StateMachine.
1931 *
1932 * Message is ignored if state machine has quit.
1933 */
Wink Savillefc5b4802009-12-08 21:22:24 -08001934 protected final void sendMessageAtFrontOfQueue(Message msg) {
Wink Savilleefcc3d32013-01-30 11:21:22 -08001935 // mSmHandler can be null if the state machine has quit.
1936 SmHandler smh = mSmHandler;
1937 if (smh == null) return;
1938
1939 smh.sendMessageAtFrontOfQueue(msg);
Wink Savillefc5b4802009-12-08 21:22:24 -08001940 }
1941
1942 /**
Jaikumar Ganeshaa4b2352010-10-14 09:36:39 -07001943 * Removes a message from the message queue.
Wink Saville64c42ca2011-04-18 14:55:10 -07001944 * Protected, may only be called by instances of StateMachine.
Jaikumar Ganeshaa4b2352010-10-14 09:36:39 -07001945 */
1946 protected final void removeMessages(int what) {
Wink Savilleefcc3d32013-01-30 11:21:22 -08001947 // mSmHandler can be null if the state machine has quit.
1948 SmHandler smh = mSmHandler;
1949 if (smh == null) return;
1950
1951 smh.removeMessages(what);
1952 }
1953
1954 /**
Ajay Panickerc2119782015-08-26 14:06:34 -07001955 * Removes a message from the deferred messages queue.
1956 */
1957 protected final void removeDeferredMessages(int what) {
1958 SmHandler smh = mSmHandler;
1959 if (smh == null) return;
1960
1961 Iterator<Message> iterator = smh.mDeferredMessages.iterator();
1962 while (iterator.hasNext()) {
1963 Message msg = iterator.next();
1964 if (msg.what == what) iterator.remove();
1965 }
1966 }
1967
1968 /**
Amit Mahajan8ed715e2015-11-03 10:06:03 -08001969 * Check if there are any pending messages with code 'what' in deferred messages queue.
1970 */
1971 protected final boolean hasDeferredMessages(int what) {
1972 SmHandler smh = mSmHandler;
1973 if (smh == null) return false;
1974
1975 Iterator<Message> iterator = smh.mDeferredMessages.iterator();
1976 while (iterator.hasNext()) {
1977 Message msg = iterator.next();
1978 if (msg.what == what) return true;
1979 }
1980
1981 return false;
1982 }
1983
1984 /**
1985 * Check if there are any pending posts of messages with code 'what' in
1986 * the message queue. This does NOT check messages in deferred message queue.
1987 */
1988 protected final boolean hasMessages(int what) {
1989 SmHandler smh = mSmHandler;
1990 if (smh == null) return false;
1991
1992 return smh.hasMessages(what);
1993 }
1994
1995 /**
Wink Savilleefcc3d32013-01-30 11:21:22 -08001996 * Validate that the message was sent by
1997 * {@link StateMachine#quit} or {@link StateMachine#quitNow}.
1998 * */
1999 protected final boolean isQuit(Message msg) {
2000 // mSmHandler can be null if the state machine has quit.
2001 SmHandler smh = mSmHandler;
2002 if (smh == null) return msg.what == SM_QUIT_CMD;
2003
2004 return smh.isQuit(msg);
Jaikumar Ganeshaa4b2352010-10-14 09:36:39 -07002005 }
2006
2007 /**
Wink Savillebbf30dfd2012-05-29 12:40:46 -07002008 * Quit the state machine after all currently queued up messages are processed.
Wink Saville1b8b98b2010-03-11 11:49:54 -08002009 */
Mitchell Wills009ae992016-08-09 13:33:20 -07002010 public final void quit() {
Wink Savillebbf30dfd2012-05-29 12:40:46 -07002011 // mSmHandler can be null if the state machine is already stopped.
Wink Savilleefcc3d32013-01-30 11:21:22 -08002012 SmHandler smh = mSmHandler;
2013 if (smh == null) return;
Jaikumar Ganesha544d462011-12-07 14:52:01 -08002014
Wink Savilleefcc3d32013-01-30 11:21:22 -08002015 smh.quit();
Wink Saville1b8b98b2010-03-11 11:49:54 -08002016 }
2017
2018 /**
Wink Savillebbf30dfd2012-05-29 12:40:46 -07002019 * Quit the state machine immediately all currently queued messages will be discarded.
Wink Saville1b8b98b2010-03-11 11:49:54 -08002020 */
Mitchell Wills009ae992016-08-09 13:33:20 -07002021 public final void quitNow() {
Wink Savillebbf30dfd2012-05-29 12:40:46 -07002022 // mSmHandler can be null if the state machine is already stopped.
Wink Savilleefcc3d32013-01-30 11:21:22 -08002023 SmHandler smh = mSmHandler;
2024 if (smh == null) return;
Wink Saville1b8b98b2010-03-11 11:49:54 -08002025
Wink Savilleefcc3d32013-01-30 11:21:22 -08002026 smh.quitNow();
Wink Saville583eaaa2012-04-13 16:11:20 -07002027 }
2028
2029 /**
Wink Savillefc5b4802009-12-08 21:22:24 -08002030 * @return if debugging is enabled
2031 */
2032 public boolean isDbg() {
Jaikumar Ganesha544d462011-12-07 14:52:01 -08002033 // mSmHandler can be null if the state machine has quit.
Wink Savilleefcc3d32013-01-30 11:21:22 -08002034 SmHandler smh = mSmHandler;
2035 if (smh == null) return false;
Jaikumar Ganesha544d462011-12-07 14:52:01 -08002036
Wink Savilleefcc3d32013-01-30 11:21:22 -08002037 return smh.isDbg();
Wink Savillefc5b4802009-12-08 21:22:24 -08002038 }
2039
2040 /**
2041 * Set debug enable/disabled.
2042 *
2043 * @param dbg is true to enable debugging.
2044 */
2045 public void setDbg(boolean dbg) {
Jaikumar Ganesha544d462011-12-07 14:52:01 -08002046 // mSmHandler can be null if the state machine has quit.
Wink Savilleefcc3d32013-01-30 11:21:22 -08002047 SmHandler smh = mSmHandler;
2048 if (smh == null) return;
Jaikumar Ganesha544d462011-12-07 14:52:01 -08002049
Wink Savilleefcc3d32013-01-30 11:21:22 -08002050 smh.setDbg(dbg);
Wink Savillefc5b4802009-12-08 21:22:24 -08002051 }
2052
2053 /**
2054 * Start the state machine.
2055 */
2056 public void start() {
Jaikumar Ganesha544d462011-12-07 14:52:01 -08002057 // mSmHandler can be null if the state machine has quit.
Wink Savilleefcc3d32013-01-30 11:21:22 -08002058 SmHandler smh = mSmHandler;
2059 if (smh == null) return;
Jaikumar Ganesha544d462011-12-07 14:52:01 -08002060
Wink Savillefc5b4802009-12-08 21:22:24 -08002061 /** Send the complete construction message */
Wink Savilleefcc3d32013-01-30 11:21:22 -08002062 smh.completeConstruction();
Wink Savillefc5b4802009-12-08 21:22:24 -08002063 }
Wink Saville583eaaa2012-04-13 16:11:20 -07002064
2065 /**
2066 * Dump the current state.
2067 *
2068 * @param fd
2069 * @param pw
2070 * @param args
2071 */
2072 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Wink Saville54b1b1a2015-01-12 11:34:20 -08002073 // Cannot just invoke pw.println(this.toString()) because if the
2074 // resulting string is to long it won't be displayed.
2075 pw.println(getName() + ":");
2076 pw.println(" total records=" + getLogRecCount());
2077 for (int i = 0; i < getLogRecSize(); i++) {
2078 pw.println(" rec[" + i + "]: " + getLogRec(i).toString());
2079 pw.flush();
2080 }
2081 pw.println("curState=" + getCurrentState().getName());
Mike Lockwood726d4de2014-10-28 14:06:28 -07002082 }
2083
2084 @Override
2085 public String toString() {
Wink Saville54b1b1a2015-01-12 11:34:20 -08002086 StringWriter sr = new StringWriter();
2087 PrintWriter pr = new PrintWriter(sr);
2088 dump(null, pr, null);
2089 pr.flush();
2090 pr.close();
2091 return sr.toString();
Wink Saville583eaaa2012-04-13 16:11:20 -07002092 }
Wink Saville58c73c32013-01-28 10:52:34 -08002093
Wink Savilleff4fcdb2013-02-24 07:21:45 -08002094 /**
2095 * Log with debug and add to the LogRecords.
2096 *
2097 * @param s is string log
2098 */
2099 protected void logAndAddLogRec(String s) {
2100 addLogRec(s);
2101 log(s);
2102 }
2103
2104 /**
2105 * Log with debug
2106 *
2107 * @param s is string log
2108 */
Wink Saville58c73c32013-01-28 10:52:34 -08002109 protected void log(String s) {
2110 Log.d(mName, s);
2111 }
2112
Wink Savilleff4fcdb2013-02-24 07:21:45 -08002113 /**
2114 * Log with debug attribute
2115 *
2116 * @param s is string log
2117 */
Wink Saville58c73c32013-01-28 10:52:34 -08002118 protected void logd(String s) {
2119 Log.d(mName, s);
2120 }
2121
Wink Savilleff4fcdb2013-02-24 07:21:45 -08002122 /**
2123 * Log with verbose attribute
2124 *
2125 * @param s is string log
2126 */
2127 protected void logv(String s) {
2128 Log.v(mName, s);
2129 }
2130
2131 /**
2132 * Log with info attribute
2133 *
2134 * @param s is string log
2135 */
2136 protected void logi(String s) {
2137 Log.i(mName, s);
2138 }
2139
2140 /**
2141 * Log with warning attribute
2142 *
2143 * @param s is string log
2144 */
Wink Saville58c73c32013-01-28 10:52:34 -08002145 protected void logw(String s) {
2146 Log.w(mName, s);
2147 }
2148
Wink Savilleff4fcdb2013-02-24 07:21:45 -08002149 /**
2150 * Log with error attribute
2151 *
2152 * @param s is string log
2153 */
Wink Saville58c73c32013-01-28 10:52:34 -08002154 protected void loge(String s) {
2155 Log.e(mName, s);
2156 }
Wink Savilleff4fcdb2013-02-24 07:21:45 -08002157
2158 /**
2159 * Log with error attribute
2160 *
2161 * @param s is string log
2162 * @param e is a Throwable which logs additional information.
2163 */
2164 protected void loge(String s, Throwable e) {
2165 Log.e(mName, s, e);
2166 }
Wink Savillefc5b4802009-12-08 21:22:24 -08002167}