PageRenderTime 68ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/core/java/com/android/internal/util/StateMachine.java

https://gitlab.com/AvayKumar/android_frameworks_base
Java | 1437 lines | 511 code | 127 blank | 799 comment | 112 complexity | 8441e8e13ee4ae705e4a82569feff30c MD5 | raw file
  1. /**
  2. * Copyright (C) 2009 The Android Open Source Project
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package com.android.internal.util;
  17. import android.os.Handler;
  18. import android.os.HandlerThread;
  19. import android.os.Looper;
  20. import android.os.Message;
  21. import android.text.TextUtils;
  22. import android.util.Log;
  23. import java.io.FileDescriptor;
  24. import java.io.PrintWriter;
  25. import java.io.StringWriter;
  26. import java.util.ArrayList;
  27. import java.util.Calendar;
  28. import java.util.Collection;
  29. import java.util.HashMap;
  30. import java.util.Vector;
  31. /**
  32. * {@hide}
  33. *
  34. * <p>The state machine defined here is a hierarchical state machine which processes messages
  35. * and can have states arranged hierarchically.</p>
  36. *
  37. * <p>A state is a <code>State</code> object and must implement
  38. * <code>processMessage</code> and optionally <code>enter/exit/getName</code>.
  39. * The enter/exit methods are equivalent to the construction and destruction
  40. * in Object Oriented programming and are used to perform initialization and
  41. * cleanup of the state respectively. The <code>getName</code> method returns the
  42. * name of the state the default implementation returns the class name it may be
  43. * desirable to have this return the name of the state instance name instead.
  44. * In particular if a particular state class has multiple instances.</p>
  45. *
  46. * <p>When a state machine is created <code>addState</code> is used to build the
  47. * hierarchy and <code>setInitialState</code> is used to identify which of these
  48. * is the initial state. After construction the programmer calls <code>start</code>
  49. * which initializes and starts the state machine. The first action the StateMachine
  50. * is to the invoke <code>enter</code> for all of the initial state's hierarchy,
  51. * starting at its eldest parent. The calls to enter will be done in the context
  52. * of the StateMachines Handler not in the context of the call to start and they
  53. * will be invoked before any messages are processed. For example, given the simple
  54. * state machine below mP1.enter will be invoked and then mS1.enter. Finally,
  55. * messages sent to the state machine will be processed by the current state,
  56. * in our simple state machine below that would initially be mS1.processMessage.</p>
  57. <code>
  58. mP1
  59. / \
  60. mS2 mS1 ----> initial state
  61. </code>
  62. * <p>After the state machine is created and started, messages are sent to a state
  63. * machine using <code>sendMessage</code> and the messages are created using
  64. * <code>obtainMessage</code>. When the state machine receives a message the
  65. * current state's <code>processMessage</code> is invoked. In the above example
  66. * mS1.processMessage will be invoked first. The state may use <code>transitionTo</code>
  67. * to change the current state to a new state</p>
  68. *
  69. * <p>Each state in the state machine may have a zero or one parent states and if
  70. * a child state is unable to handle a message it may have the message processed
  71. * by its parent by returning false or NOT_HANDLED. If a message is never processed
  72. * <code>unhandledMessage</code> will be invoked to give one last chance for the state machine
  73. * to process the message.</p>
  74. *
  75. * <p>When all processing is completed a state machine may choose to call
  76. * <code>transitionToHaltingState</code>. When the current <code>processingMessage</code>
  77. * returns the state machine will transfer to an internal <code>HaltingState</code>
  78. * and invoke <code>halting</code>. Any message subsequently received by the state
  79. * machine will cause <code>haltedProcessMessage</code> to be invoked.</p>
  80. *
  81. * <p>If it is desirable to completely stop the state machine call <code>quit</code> or
  82. * <code>quitNow</code>. These will call <code>exit</code> of the current state and its parents,
  83. * call <code>onQuiting</code> and then exit Thread/Loopers.</p>
  84. *
  85. * <p>In addition to <code>processMessage</code> each <code>State</code> has
  86. * an <code>enter</code> method and <code>exit</exit> method which may be overridden.</p>
  87. *
  88. * <p>Since the states are arranged in a hierarchy transitioning to a new state
  89. * causes current states to be exited and new states to be entered. To determine
  90. * the list of states to be entered/exited the common parent closest to
  91. * the current state is found. We then exit from the current state and its
  92. * parent's up to but not including the common parent state and then enter all
  93. * of the new states below the common parent down to the destination state.
  94. * If there is no common parent all states are exited and then the new states
  95. * are entered.</p>
  96. *
  97. * <p>Two other methods that states can use are <code>deferMessage</code> and
  98. * <code>sendMessageAtFrontOfQueue</code>. The <code>sendMessageAtFrontOfQueue</code> sends
  99. * a message but places it on the front of the queue rather than the back. The
  100. * <code>deferMessage</code> causes the message to be saved on a list until a
  101. * transition is made to a new state. At which time all of the deferred messages
  102. * will be put on the front of the state machine queue with the oldest message
  103. * at the front. These will then be processed by the new current state before
  104. * any other messages that are on the queue or might be added later. Both of
  105. * these are protected and may only be invoked from within a state machine.</p>
  106. *
  107. * <p>To illustrate some of these properties we'll use state machine with an 8
  108. * state hierarchy:</p>
  109. <code>
  110. mP0
  111. / \
  112. mP1 mS0
  113. / \
  114. mS2 mS1
  115. / \ \
  116. mS3 mS4 mS5 ---> initial state
  117. </code>
  118. * <p>After starting mS5 the list of active states is mP0, mP1, mS1 and mS5.
  119. * So the order of calling processMessage when a message is received is mS5,
  120. * mS1, mP1, mP0 assuming each processMessage indicates it can't handle this
  121. * message by returning false or NOT_HANDLED.</p>
  122. *
  123. * <p>Now assume mS5.processMessage receives a message it can handle, and during
  124. * the handling determines the machine should change states. It could call
  125. * transitionTo(mS4) and return true or HANDLED. Immediately after returning from
  126. * processMessage the state machine runtime will find the common parent,
  127. * which is mP1. It will then call mS5.exit, mS1.exit, mS2.enter and then
  128. * mS4.enter. The new list of active states is mP0, mP1, mS2 and mS4. So
  129. * when the next message is received mS4.processMessage will be invoked.</p>
  130. *
  131. * <p>Now for some concrete examples, here is the canonical HelloWorld as a state machine.
  132. * It responds with "Hello World" being printed to the log for every message.</p>
  133. <code>
  134. class HelloWorld extends StateMachine {
  135. HelloWorld(String name) {
  136. super(name);
  137. addState(mState1);
  138. setInitialState(mState1);
  139. }
  140. public static HelloWorld makeHelloWorld() {
  141. HelloWorld hw = new HelloWorld("hw");
  142. hw.start();
  143. return hw;
  144. }
  145. class State1 extends State {
  146. &#64;Override public boolean processMessage(Message message) {
  147. log("Hello World");
  148. return HANDLED;
  149. }
  150. }
  151. State1 mState1 = new State1();
  152. }
  153. void testHelloWorld() {
  154. HelloWorld hw = makeHelloWorld();
  155. hw.sendMessage(hw.obtainMessage());
  156. }
  157. </code>
  158. * <p>A more interesting state machine is one with four states
  159. * with two independent parent states.</p>
  160. <code>
  161. mP1 mP2
  162. / \
  163. mS2 mS1
  164. </code>
  165. * <p>Here is a description of this state machine using pseudo code.</p>
  166. <code>
  167. state mP1 {
  168. enter { log("mP1.enter"); }
  169. exit { log("mP1.exit"); }
  170. on msg {
  171. CMD_2 {
  172. send(CMD_3);
  173. defer(msg);
  174. transitonTo(mS2);
  175. return HANDLED;
  176. }
  177. return NOT_HANDLED;
  178. }
  179. }
  180. INITIAL
  181. state mS1 parent mP1 {
  182. enter { log("mS1.enter"); }
  183. exit { log("mS1.exit"); }
  184. on msg {
  185. CMD_1 {
  186. transitionTo(mS1);
  187. return HANDLED;
  188. }
  189. return NOT_HANDLED;
  190. }
  191. }
  192. state mS2 parent mP1 {
  193. enter { log("mS2.enter"); }
  194. exit { log("mS2.exit"); }
  195. on msg {
  196. CMD_2 {
  197. send(CMD_4);
  198. return HANDLED;
  199. }
  200. CMD_3 {
  201. defer(msg);
  202. transitionTo(mP2);
  203. return HANDLED;
  204. }
  205. return NOT_HANDLED;
  206. }
  207. }
  208. state mP2 {
  209. enter {
  210. log("mP2.enter");
  211. send(CMD_5);
  212. }
  213. exit { log("mP2.exit"); }
  214. on msg {
  215. CMD_3, CMD_4 { return HANDLED; }
  216. CMD_5 {
  217. transitionTo(HaltingState);
  218. return HANDLED;
  219. }
  220. return NOT_HANDLED;
  221. }
  222. }
  223. </code>
  224. * <p>The implementation is below and also in StateMachineTest:</p>
  225. <code>
  226. class Hsm1 extends StateMachine {
  227. public static final int CMD_1 = 1;
  228. public static final int CMD_2 = 2;
  229. public static final int CMD_3 = 3;
  230. public static final int CMD_4 = 4;
  231. public static final int CMD_5 = 5;
  232. public static Hsm1 makeHsm1() {
  233. log("makeHsm1 E");
  234. Hsm1 sm = new Hsm1("hsm1");
  235. sm.start();
  236. log("makeHsm1 X");
  237. return sm;
  238. }
  239. Hsm1(String name) {
  240. super(name);
  241. log("ctor E");
  242. // Add states, use indentation to show hierarchy
  243. addState(mP1);
  244. addState(mS1, mP1);
  245. addState(mS2, mP1);
  246. addState(mP2);
  247. // Set the initial state
  248. setInitialState(mS1);
  249. log("ctor X");
  250. }
  251. class P1 extends State {
  252. &#64;Override public void enter() {
  253. log("mP1.enter");
  254. }
  255. &#64;Override public boolean processMessage(Message message) {
  256. boolean retVal;
  257. log("mP1.processMessage what=" + message.what);
  258. switch(message.what) {
  259. case CMD_2:
  260. // CMD_2 will arrive in mS2 before CMD_3
  261. sendMessage(obtainMessage(CMD_3));
  262. deferMessage(message);
  263. transitionTo(mS2);
  264. retVal = HANDLED;
  265. break;
  266. default:
  267. // Any message we don't understand in this state invokes unhandledMessage
  268. retVal = NOT_HANDLED;
  269. break;
  270. }
  271. return retVal;
  272. }
  273. &#64;Override public void exit() {
  274. log("mP1.exit");
  275. }
  276. }
  277. class S1 extends State {
  278. &#64;Override public void enter() {
  279. log("mS1.enter");
  280. }
  281. &#64;Override public boolean processMessage(Message message) {
  282. log("S1.processMessage what=" + message.what);
  283. if (message.what == CMD_1) {
  284. // Transition to ourself to show that enter/exit is called
  285. transitionTo(mS1);
  286. return HANDLED;
  287. } else {
  288. // Let parent process all other messages
  289. return NOT_HANDLED;
  290. }
  291. }
  292. &#64;Override public void exit() {
  293. log("mS1.exit");
  294. }
  295. }
  296. class S2 extends State {
  297. &#64;Override public void enter() {
  298. log("mS2.enter");
  299. }
  300. &#64;Override public boolean processMessage(Message message) {
  301. boolean retVal;
  302. log("mS2.processMessage what=" + message.what);
  303. switch(message.what) {
  304. case(CMD_2):
  305. sendMessage(obtainMessage(CMD_4));
  306. retVal = HANDLED;
  307. break;
  308. case(CMD_3):
  309. deferMessage(message);
  310. transitionTo(mP2);
  311. retVal = HANDLED;
  312. break;
  313. default:
  314. retVal = NOT_HANDLED;
  315. break;
  316. }
  317. return retVal;
  318. }
  319. &#64;Override public void exit() {
  320. log("mS2.exit");
  321. }
  322. }
  323. class P2 extends State {
  324. &#64;Override public void enter() {
  325. log("mP2.enter");
  326. sendMessage(obtainMessage(CMD_5));
  327. }
  328. &#64;Override public boolean processMessage(Message message) {
  329. log("P2.processMessage what=" + message.what);
  330. switch(message.what) {
  331. case(CMD_3):
  332. break;
  333. case(CMD_4):
  334. break;
  335. case(CMD_5):
  336. transitionToHaltingState();
  337. break;
  338. }
  339. return HANDLED;
  340. }
  341. &#64;Override public void exit() {
  342. log("mP2.exit");
  343. }
  344. }
  345. &#64;Override
  346. void onHalting() {
  347. log("halting");
  348. synchronized (this) {
  349. this.notifyAll();
  350. }
  351. }
  352. P1 mP1 = new P1();
  353. S1 mS1 = new S1();
  354. S2 mS2 = new S2();
  355. P2 mP2 = new P2();
  356. }
  357. </code>
  358. * <p>If this is executed by sending two messages CMD_1 and CMD_2
  359. * (Note the synchronize is only needed because we use hsm.wait())</p>
  360. <code>
  361. Hsm1 hsm = makeHsm1();
  362. synchronize(hsm) {
  363. hsm.sendMessage(obtainMessage(hsm.CMD_1));
  364. hsm.sendMessage(obtainMessage(hsm.CMD_2));
  365. try {
  366. // wait for the messages to be handled
  367. hsm.wait();
  368. } catch (InterruptedException e) {
  369. loge("exception while waiting " + e.getMessage());
  370. }
  371. }
  372. </code>
  373. * <p>The output is:</p>
  374. <code>
  375. D/hsm1 ( 1999): makeHsm1 E
  376. D/hsm1 ( 1999): ctor E
  377. D/hsm1 ( 1999): ctor X
  378. D/hsm1 ( 1999): mP1.enter
  379. D/hsm1 ( 1999): mS1.enter
  380. D/hsm1 ( 1999): makeHsm1 X
  381. D/hsm1 ( 1999): mS1.processMessage what=1
  382. D/hsm1 ( 1999): mS1.exit
  383. D/hsm1 ( 1999): mS1.enter
  384. D/hsm1 ( 1999): mS1.processMessage what=2
  385. D/hsm1 ( 1999): mP1.processMessage what=2
  386. D/hsm1 ( 1999): mS1.exit
  387. D/hsm1 ( 1999): mS2.enter
  388. D/hsm1 ( 1999): mS2.processMessage what=2
  389. D/hsm1 ( 1999): mS2.processMessage what=3
  390. D/hsm1 ( 1999): mS2.exit
  391. D/hsm1 ( 1999): mP1.exit
  392. D/hsm1 ( 1999): mP2.enter
  393. D/hsm1 ( 1999): mP2.processMessage what=3
  394. D/hsm1 ( 1999): mP2.processMessage what=4
  395. D/hsm1 ( 1999): mP2.processMessage what=5
  396. D/hsm1 ( 1999): mP2.exit
  397. D/hsm1 ( 1999): halting
  398. </code>
  399. */
  400. public class StateMachine {
  401. // Name of the state machine and used as logging tag
  402. private String mName;
  403. /** Message.what value when quitting */
  404. private static final int SM_QUIT_CMD = -1;
  405. /** Message.what value when initializing */
  406. private static final int SM_INIT_CMD = -2;
  407. /**
  408. * Convenience constant that maybe returned by processMessage
  409. * to indicate the the message was processed and is not to be
  410. * processed by parent states
  411. */
  412. public static final boolean HANDLED = true;
  413. /**
  414. * Convenience constant that maybe returned by processMessage
  415. * to indicate the the message was NOT processed and is to be
  416. * processed by parent states
  417. */
  418. public static final boolean NOT_HANDLED = false;
  419. /**
  420. * StateMachine logging record.
  421. * {@hide}
  422. */
  423. public static class LogRec {
  424. private StateMachine mSm;
  425. private long mTime;
  426. private int mWhat;
  427. private String mInfo;
  428. private IState mState;
  429. private IState mOrgState;
  430. private IState mDstState;
  431. /**
  432. * Constructor
  433. *
  434. * @param msg
  435. * @param state the state which handled the message
  436. * @param orgState is the first state the received the message but
  437. * did not processes the message.
  438. * @param transToState is the state that was transitioned to after the message was
  439. * processed.
  440. */
  441. LogRec(StateMachine sm, Message msg, String info, IState state, IState orgState,
  442. IState transToState) {
  443. update(sm, msg, info, state, orgState, transToState);
  444. }
  445. /**
  446. * Update the information in the record.
  447. * @param state that handled the message
  448. * @param orgState is the first state the received the message
  449. * @param dstState is the state that was the transition target when logging
  450. */
  451. public void update(StateMachine sm, Message msg, String info, IState state, IState orgState,
  452. IState dstState) {
  453. mSm = sm;
  454. mTime = System.currentTimeMillis();
  455. mWhat = (msg != null) ? msg.what : 0;
  456. mInfo = info;
  457. mState = state;
  458. mOrgState = orgState;
  459. mDstState = dstState;
  460. }
  461. /**
  462. * @return time stamp
  463. */
  464. public long getTime() {
  465. return mTime;
  466. }
  467. /**
  468. * @return msg.what
  469. */
  470. public long getWhat() {
  471. return mWhat;
  472. }
  473. /**
  474. * @return the command that was executing
  475. */
  476. public String getInfo() {
  477. return mInfo;
  478. }
  479. /**
  480. * @return the state that handled this message
  481. */
  482. public IState getState() {
  483. return mState;
  484. }
  485. /**
  486. * @return the state destination state if a transition is occurring or null if none.
  487. */
  488. public IState getDestState() {
  489. return mDstState;
  490. }
  491. /**
  492. * @return the original state that received the message.
  493. */
  494. public IState getOriginalState() {
  495. return mOrgState;
  496. }
  497. @Override
  498. public String toString() {
  499. StringBuilder sb = new StringBuilder();
  500. sb.append("time=");
  501. Calendar c = Calendar.getInstance();
  502. c.setTimeInMillis(mTime);
  503. sb.append(String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c));
  504. sb.append(" processed=");
  505. sb.append(mState == null ? "<null>" : mState.getName());
  506. sb.append(" org=");
  507. sb.append(mOrgState == null ? "<null>" : mOrgState.getName());
  508. sb.append(" dest=");
  509. sb.append(mDstState == null ? "<null>" : mDstState.getName());
  510. sb.append(" what=");
  511. String what = mSm != null ? mSm.getWhatToString(mWhat) : "";
  512. if (TextUtils.isEmpty(what)) {
  513. sb.append(mWhat);
  514. sb.append("(0x");
  515. sb.append(Integer.toHexString(mWhat));
  516. sb.append(")");
  517. } else {
  518. sb.append(what);
  519. }
  520. if (!TextUtils.isEmpty(mInfo)) {
  521. sb.append(" ");
  522. sb.append(mInfo);
  523. }
  524. return sb.toString();
  525. }
  526. }
  527. /**
  528. * A list of log records including messages recently processed by the state machine.
  529. *
  530. * The class maintains a list of log records including messages
  531. * recently processed. The list is finite and may be set in the
  532. * constructor or by calling setSize. The public interface also
  533. * includes size which returns the number of recent records,
  534. * count which is the number of records processed since the
  535. * the last setSize, get which returns a record and
  536. * add which adds a record.
  537. */
  538. private static class LogRecords {
  539. private static final int DEFAULT_SIZE = 20;
  540. private Vector<LogRec> mLogRecVector = new Vector<LogRec>();
  541. private int mMaxSize = DEFAULT_SIZE;
  542. private int mOldestIndex = 0;
  543. private int mCount = 0;
  544. private boolean mLogOnlyTransitions = false;
  545. /**
  546. * private constructor use add
  547. */
  548. private LogRecords() {
  549. }
  550. /**
  551. * Set size of messages to maintain and clears all current records.
  552. *
  553. * @param maxSize number of records to maintain at anyone time.
  554. */
  555. synchronized void setSize(int maxSize) {
  556. mMaxSize = maxSize;
  557. mCount = 0;
  558. mLogRecVector.clear();
  559. }
  560. synchronized void setLogOnlyTransitions(boolean enable) {
  561. mLogOnlyTransitions = enable;
  562. }
  563. synchronized boolean logOnlyTransitions() {
  564. return mLogOnlyTransitions;
  565. }
  566. /**
  567. * @return the number of recent records.
  568. */
  569. synchronized int size() {
  570. return mLogRecVector.size();
  571. }
  572. /**
  573. * @return the total number of records processed since size was set.
  574. */
  575. synchronized int count() {
  576. return mCount;
  577. }
  578. /**
  579. * Clear the list of records.
  580. */
  581. synchronized void cleanup() {
  582. mLogRecVector.clear();
  583. }
  584. /**
  585. * @return the information on a particular record. 0 is the oldest
  586. * record and size()-1 is the newest record. If the index is to
  587. * large null is returned.
  588. */
  589. synchronized LogRec get(int index) {
  590. int nextIndex = mOldestIndex + index;
  591. if (nextIndex >= mMaxSize) {
  592. nextIndex -= mMaxSize;
  593. }
  594. if (nextIndex >= size()) {
  595. return null;
  596. } else {
  597. return mLogRecVector.get(nextIndex);
  598. }
  599. }
  600. /**
  601. * Add a processed message.
  602. *
  603. * @param msg
  604. * @param messageInfo to be stored
  605. * @param state that handled the message
  606. * @param orgState is the first state the received the message but
  607. * did not processes the message.
  608. * @param transToState is the state that was transitioned to after the message was
  609. * processed.
  610. *
  611. */
  612. synchronized void add(StateMachine sm, Message msg, String messageInfo, IState state,
  613. IState orgState, IState transToState) {
  614. mCount += 1;
  615. if (mLogRecVector.size() < mMaxSize) {
  616. mLogRecVector.add(new LogRec(sm, msg, messageInfo, state, orgState, transToState));
  617. } else {
  618. LogRec pmi = mLogRecVector.get(mOldestIndex);
  619. mOldestIndex += 1;
  620. if (mOldestIndex >= mMaxSize) {
  621. mOldestIndex = 0;
  622. }
  623. pmi.update(sm, msg, messageInfo, state, orgState, transToState);
  624. }
  625. }
  626. }
  627. private static class SmHandler extends Handler {
  628. /** true if StateMachine has quit */
  629. private boolean mHasQuit = false;
  630. /** The debug flag */
  631. private boolean mDbg = false;
  632. /** The SmHandler object, identifies that message is internal */
  633. private static final Object mSmHandlerObj = new Object();
  634. /** The current message */
  635. private Message mMsg;
  636. /** A list of log records including messages this state machine has processed */
  637. private LogRecords mLogRecords = new LogRecords();
  638. /** true if construction of the state machine has not been completed */
  639. private boolean mIsConstructionCompleted;
  640. /** Stack used to manage the current hierarchy of states */
  641. private StateInfo mStateStack[];
  642. /** Top of mStateStack */
  643. private int mStateStackTopIndex = -1;
  644. /** A temporary stack used to manage the state stack */
  645. private StateInfo mTempStateStack[];
  646. /** The top of the mTempStateStack */
  647. private int mTempStateStackCount;
  648. /** State used when state machine is halted */
  649. private HaltingState mHaltingState = new HaltingState();
  650. /** State used when state machine is quitting */
  651. private QuittingState mQuittingState = new QuittingState();
  652. /** Reference to the StateMachine */
  653. private StateMachine mSm;
  654. /**
  655. * Information about a state.
  656. * Used to maintain the hierarchy.
  657. */
  658. private class StateInfo {
  659. /** The state */
  660. State state;
  661. /** The parent of this state, null if there is no parent */
  662. StateInfo parentStateInfo;
  663. /** True when the state has been entered and on the stack */
  664. boolean active;
  665. /**
  666. * Convert StateInfo to string
  667. */
  668. @Override
  669. public String toString() {
  670. return "state=" + state.getName() + ",active=" + active + ",parent="
  671. + ((parentStateInfo == null) ? "null" : parentStateInfo.state.getName());
  672. }
  673. }
  674. /** The map of all of the states in the state machine */
  675. private HashMap<State, StateInfo> mStateInfo = new HashMap<State, StateInfo>();
  676. /** The initial state that will process the first message */
  677. private State mInitialState;
  678. /** The destination state when transitionTo has been invoked */
  679. private State mDestState;
  680. /** The list of deferred messages */
  681. private ArrayList<Message> mDeferredMessages = new ArrayList<Message>();
  682. /**
  683. * State entered when transitionToHaltingState is called.
  684. */
  685. private class HaltingState extends State {
  686. @Override
  687. public boolean processMessage(Message msg) {
  688. mSm.haltedProcessMessage(msg);
  689. return true;
  690. }
  691. }
  692. /**
  693. * State entered when a valid quit message is handled.
  694. */
  695. private class QuittingState extends State {
  696. @Override
  697. public boolean processMessage(Message msg) {
  698. return NOT_HANDLED;
  699. }
  700. }
  701. /**
  702. * Handle messages sent to the state machine by calling
  703. * the current state's processMessage. It also handles
  704. * the enter/exit calls and placing any deferred messages
  705. * back onto the queue when transitioning to a new state.
  706. */
  707. @Override
  708. public final void handleMessage(Message msg) {
  709. if (!mHasQuit) {
  710. if (mDbg) mSm.log("handleMessage: E msg.what=" + msg.what);
  711. /** Save the current message */
  712. mMsg = msg;
  713. /** State that processed the message */
  714. State msgProcessedState = null;
  715. if (mIsConstructionCompleted) {
  716. /** Normal path */
  717. msgProcessedState = processMsg(msg);
  718. } else if (!mIsConstructionCompleted && (mMsg.what == SM_INIT_CMD)
  719. && (mMsg.obj == mSmHandlerObj)) {
  720. /** Initial one time path. */
  721. mIsConstructionCompleted = true;
  722. invokeEnterMethods(0);
  723. } else {
  724. throw new RuntimeException("StateMachine.handleMessage: "
  725. + "The start method not called, received msg: " + msg);
  726. }
  727. performTransitions(msgProcessedState, msg);
  728. // We need to check if mSm == null here as we could be quitting.
  729. if (mDbg && mSm != null) mSm.log("handleMessage: X");
  730. }
  731. }
  732. /**
  733. * Do any transitions
  734. * @param msgProcessedState is the state that processed the message
  735. */
  736. private void performTransitions(State msgProcessedState, Message msg) {
  737. /**
  738. * If transitionTo has been called, exit and then enter
  739. * the appropriate states. We loop on this to allow
  740. * enter and exit methods to use transitionTo.
  741. */
  742. State orgState = mStateStack[mStateStackTopIndex].state;
  743. /**
  744. * Record whether message needs to be logged before we transition and
  745. * and we won't log special messages SM_INIT_CMD or SM_QUIT_CMD which
  746. * always set msg.obj to the handler.
  747. */
  748. boolean recordLogMsg = mSm.recordLogRec(mMsg) && (msg.obj != mSmHandlerObj);
  749. if (mLogRecords.logOnlyTransitions()) {
  750. /** Record only if there is a transition */
  751. if (mDestState != null) {
  752. mLogRecords.add(mSm, mMsg, mSm.getLogRecString(mMsg), msgProcessedState,
  753. orgState, mDestState);
  754. }
  755. } else if (recordLogMsg) {
  756. /** Record message */
  757. mLogRecords.add(mSm, mMsg, mSm.getLogRecString(mMsg), msgProcessedState, orgState,
  758. mDestState);
  759. }
  760. State destState = mDestState;
  761. if (destState != null) {
  762. /**
  763. * Process the transitions including transitions in the enter/exit methods
  764. */
  765. while (true) {
  766. if (mDbg) mSm.log("handleMessage: new destination call exit/enter");
  767. /**
  768. * Determine the states to exit and enter and return the
  769. * common ancestor state of the enter/exit states. Then
  770. * invoke the exit methods then the enter methods.
  771. */
  772. StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState);
  773. invokeExitMethods(commonStateInfo);
  774. int stateStackEnteringIndex = moveTempStateStackToStateStack();
  775. invokeEnterMethods(stateStackEnteringIndex);
  776. /**
  777. * Since we have transitioned to a new state we need to have
  778. * any deferred messages moved to the front of the message queue
  779. * so they will be processed before any other messages in the
  780. * message queue.
  781. */
  782. moveDeferredMessageAtFrontOfQueue();
  783. if (destState != mDestState) {
  784. // A new mDestState so continue looping
  785. destState = mDestState;
  786. } else {
  787. // No change in mDestState so we're done
  788. break;
  789. }
  790. }
  791. mDestState = null;
  792. }
  793. /**
  794. * After processing all transitions check and
  795. * see if the last transition was to quit or halt.
  796. */
  797. if (destState != null) {
  798. if (destState == mQuittingState) {
  799. /**
  800. * Call onQuitting to let subclasses cleanup.
  801. */
  802. mSm.onQuitting();
  803. cleanupAfterQuitting();
  804. } else if (destState == mHaltingState) {
  805. /**
  806. * Call onHalting() if we've transitioned to the halting
  807. * state. All subsequent messages will be processed in
  808. * in the halting state which invokes haltedProcessMessage(msg);
  809. */
  810. mSm.onHalting();
  811. }
  812. }
  813. }
  814. /**
  815. * Cleanup all the static variables and the looper after the SM has been quit.
  816. */
  817. private final void cleanupAfterQuitting() {
  818. if (mSm.mSmThread != null) {
  819. // If we made the thread then quit looper which stops the thread.
  820. getLooper().quit();
  821. mSm.mSmThread = null;
  822. }
  823. mSm.mSmHandler = null;
  824. mSm = null;
  825. mMsg = null;
  826. mLogRecords.cleanup();
  827. mStateStack = null;
  828. mTempStateStack = null;
  829. mStateInfo.clear();
  830. mInitialState = null;
  831. mDestState = null;
  832. mDeferredMessages.clear();
  833. mHasQuit = true;
  834. }
  835. /**
  836. * Complete the construction of the state machine.
  837. */
  838. private final void completeConstruction() {
  839. if (mDbg) mSm.log("completeConstruction: E");
  840. /**
  841. * Determine the maximum depth of the state hierarchy
  842. * so we can allocate the state stacks.
  843. */
  844. int maxDepth = 0;
  845. for (StateInfo si : mStateInfo.values()) {
  846. int depth = 0;
  847. for (StateInfo i = si; i != null; depth++) {
  848. i = i.parentStateInfo;
  849. }
  850. if (maxDepth < depth) {
  851. maxDepth = depth;
  852. }
  853. }
  854. if (mDbg) mSm.log("completeConstruction: maxDepth=" + maxDepth);
  855. mStateStack = new StateInfo[maxDepth];
  856. mTempStateStack = new StateInfo[maxDepth];
  857. setupInitialStateStack();
  858. /** Sending SM_INIT_CMD message to invoke enter methods asynchronously */
  859. sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj));
  860. if (mDbg) mSm.log("completeConstruction: X");
  861. }
  862. /**
  863. * Process the message. If the current state doesn't handle
  864. * it, call the states parent and so on. If it is never handled then
  865. * call the state machines unhandledMessage method.
  866. * @return the state that processed the message
  867. */
  868. private final State processMsg(Message msg) {
  869. StateInfo curStateInfo = mStateStack[mStateStackTopIndex];
  870. if (mDbg) {
  871. mSm.log("processMsg: " + curStateInfo.state.getName());
  872. }
  873. if (isQuit(msg)) {
  874. transitionTo(mQuittingState);
  875. } else {
  876. while (!curStateInfo.state.processMessage(msg)) {
  877. /**
  878. * Not processed
  879. */
  880. curStateInfo = curStateInfo.parentStateInfo;
  881. if (curStateInfo == null) {
  882. /**
  883. * No parents left so it's not handled
  884. */
  885. mSm.unhandledMessage(msg);
  886. break;
  887. }
  888. if (mDbg) {
  889. mSm.log("processMsg: " + curStateInfo.state.getName());
  890. }
  891. }
  892. }
  893. return (curStateInfo != null) ? curStateInfo.state : null;
  894. }
  895. /**
  896. * Call the exit method for each state from the top of stack
  897. * up to the common ancestor state.
  898. */
  899. private final void invokeExitMethods(StateInfo commonStateInfo) {
  900. while ((mStateStackTopIndex >= 0)
  901. && (mStateStack[mStateStackTopIndex] != commonStateInfo)) {
  902. State curState = mStateStack[mStateStackTopIndex].state;
  903. if (mDbg) mSm.log("invokeExitMethods: " + curState.getName());
  904. curState.exit();
  905. mStateStack[mStateStackTopIndex].active = false;
  906. mStateStackTopIndex -= 1;
  907. }
  908. }
  909. /**
  910. * Invoke the enter method starting at the entering index to top of state stack
  911. */
  912. private final void invokeEnterMethods(int stateStackEnteringIndex) {
  913. for (int i = stateStackEnteringIndex; i <= mStateStackTopIndex; i++) {
  914. if (mDbg) mSm.log("invokeEnterMethods: " + mStateStack[i].state.getName());
  915. mStateStack[i].state.enter();
  916. mStateStack[i].active = true;
  917. }
  918. }
  919. /**
  920. * Move the deferred message to the front of the message queue.
  921. */
  922. private final void moveDeferredMessageAtFrontOfQueue() {
  923. /**
  924. * The oldest messages on the deferred list must be at
  925. * the front of the queue so start at the back, which
  926. * as the most resent message and end with the oldest
  927. * messages at the front of the queue.
  928. */
  929. for (int i = mDeferredMessages.size() - 1; i >= 0; i--) {
  930. Message curMsg = mDeferredMessages.get(i);
  931. if (mDbg) mSm.log("moveDeferredMessageAtFrontOfQueue; what=" + curMsg.what);
  932. sendMessageAtFrontOfQueue(curMsg);
  933. }
  934. mDeferredMessages.clear();
  935. }
  936. /**
  937. * Move the contents of the temporary stack to the state stack
  938. * reversing the order of the items on the temporary stack as
  939. * they are moved.
  940. *
  941. * @return index into mStateStack where entering needs to start
  942. */
  943. private final int moveTempStateStackToStateStack() {
  944. int startingIndex = mStateStackTopIndex + 1;
  945. int i = mTempStateStackCount - 1;
  946. int j = startingIndex;
  947. while (i >= 0) {
  948. if (mDbg) mSm.log("moveTempStackToStateStack: i=" + i + ",j=" + j);
  949. mStateStack[j] = mTempStateStack[i];
  950. j += 1;
  951. i -= 1;
  952. }
  953. mStateStackTopIndex = j - 1;
  954. if (mDbg) {
  955. mSm.log("moveTempStackToStateStack: X mStateStackTop=" + mStateStackTopIndex
  956. + ",startingIndex=" + startingIndex + ",Top="
  957. + mStateStack[mStateStackTopIndex].state.getName());
  958. }
  959. return startingIndex;
  960. }
  961. /**
  962. * Setup the mTempStateStack with the states we are going to enter.
  963. *
  964. * This is found by searching up the destState's ancestors for a
  965. * state that is already active i.e. StateInfo.active == true.
  966. * The destStae and all of its inactive parents will be on the
  967. * TempStateStack as the list of states to enter.
  968. *
  969. * @return StateInfo of the common ancestor for the destState and
  970. * current state or null if there is no common parent.
  971. */
  972. private final StateInfo setupTempStateStackWithStatesToEnter(State destState) {
  973. /**
  974. * Search up the parent list of the destination state for an active
  975. * state. Use a do while() loop as the destState must always be entered
  976. * even if it is active. This can happen if we are exiting/entering
  977. * the current state.
  978. */
  979. mTempStateStackCount = 0;
  980. StateInfo curStateInfo = mStateInfo.get(destState);
  981. do {
  982. mTempStateStack[mTempStateStackCount++] = curStateInfo;
  983. curStateInfo = curStateInfo.parentStateInfo;
  984. } while ((curStateInfo != null) && !curStateInfo.active);
  985. if (mDbg) {
  986. mSm.log("setupTempStateStackWithStatesToEnter: X mTempStateStackCount="
  987. + mTempStateStackCount + ",curStateInfo: " + curStateInfo);
  988. }
  989. return curStateInfo;
  990. }
  991. /**
  992. * Initialize StateStack to mInitialState.
  993. */
  994. private final void setupInitialStateStack() {
  995. if (mDbg) {
  996. mSm.log("setupInitialStateStack: E mInitialState=" + mInitialState.getName());
  997. }
  998. StateInfo curStateInfo = mStateInfo.get(mInitialState);
  999. for (mTempStateStackCount = 0; curStateInfo != null; mTempStateStackCount++) {
  1000. mTempStateStack[mTempStateStackCount] = curStateInfo;
  1001. curStateInfo = curStateInfo.parentStateInfo;
  1002. }
  1003. // Empty the StateStack
  1004. mStateStackTopIndex = -1;
  1005. moveTempStateStackToStateStack();
  1006. }
  1007. /**
  1008. * @return current message
  1009. */
  1010. private final Message getCurrentMessage() {
  1011. return mMsg;
  1012. }
  1013. /**
  1014. * @return current state
  1015. */
  1016. private final IState getCurrentState() {
  1017. return mStateStack[mStateStackTopIndex].state;
  1018. }
  1019. /**
  1020. * Add a new state to the state machine. Bottom up addition
  1021. * of states is allowed but the same state may only exist
  1022. * in one hierarchy.
  1023. *
  1024. * @param state the state to add
  1025. * @param parent the parent of state
  1026. * @return stateInfo for this state
  1027. */
  1028. private final StateInfo addState(State state, State parent) {
  1029. if (mDbg) {
  1030. mSm.log("addStateInternal: E state=" + state.getName() + ",parent="
  1031. + ((parent == null) ? "" : parent.getName()));
  1032. }
  1033. StateInfo parentStateInfo = null;
  1034. if (parent != null) {
  1035. parentStateInfo = mStateInfo.get(parent);
  1036. if (parentStateInfo == null) {
  1037. // Recursively add our parent as it's not been added yet.
  1038. parentStateInfo = addState(parent, null);
  1039. }
  1040. }
  1041. StateInfo stateInfo = mStateInfo.get(state);
  1042. if (stateInfo == null) {
  1043. stateInfo = new StateInfo();
  1044. mStateInfo.put(state, stateInfo);
  1045. }
  1046. // Validate that we aren't adding the same state in two different hierarchies.
  1047. if ((stateInfo.parentStateInfo != null)
  1048. && (stateInfo.parentStateInfo != parentStateInfo)) {
  1049. throw new RuntimeException("state already added");
  1050. }
  1051. stateInfo.state = state;
  1052. stateInfo.parentStateInfo = parentStateInfo;
  1053. stateInfo.active = false;
  1054. if (mDbg) mSm.log("addStateInternal: X stateInfo: " + stateInfo);
  1055. return stateInfo;
  1056. }
  1057. /**
  1058. * Constructor
  1059. *
  1060. * @param looper for dispatching messages
  1061. * @param sm the hierarchical state machine
  1062. */
  1063. private SmHandler(Looper looper, StateMachine sm) {
  1064. super(looper);
  1065. mSm = sm;
  1066. addState(mHaltingState, null);
  1067. addState(mQuittingState, null);
  1068. }
  1069. /** @see StateMachine#setInitialState(State) */
  1070. private final void setInitialState(State initialState) {
  1071. if (mDbg) mSm.log("setInitialState: initialState=" + initialState.getName());
  1072. mInitialState = initialState;
  1073. }
  1074. /** @see StateMachine#transitionTo(IState) */
  1075. private final void transitionTo(IState destState) {
  1076. mDestState = (State) destState;
  1077. if (mDbg) mSm.log("transitionTo: destState=" + mDestState.getName());
  1078. }
  1079. /** @see StateMachine#deferMessage(Message) */
  1080. private final void deferMessage(Message msg) {
  1081. if (mDbg) mSm.log("deferMessage: msg=" + msg.what);
  1082. /* Copy the "msg" to "newMsg" as "msg" will be recycled */
  1083. Message newMsg = obtainMessage();
  1084. newMsg.copyFrom(msg);
  1085. mDeferredMessages.add(newMsg);
  1086. }
  1087. /** @see StateMachine#quit() */
  1088. private final void quit() {
  1089. if (mDbg) mSm.log("quit:");
  1090. sendMessage(obtainMessage(SM_QUIT_CMD, mSmHandlerObj));
  1091. }
  1092. /** @see StateMachine#quitNow() */
  1093. private final void quitNow() {
  1094. if (mDbg) mSm.log("quitNow:");
  1095. sendMessageAtFrontOfQueue(obtainMessage(SM_QUIT_CMD, mSmHandlerObj));
  1096. }
  1097. /** Validate that the message was sent by quit or quitNow. */
  1098. private final boolean isQuit(Message msg) {
  1099. return (msg.what == SM_QUIT_CMD) && (msg.obj == mSmHandlerObj);
  1100. }
  1101. /** @see StateMachine#isDbg() */
  1102. private final boolean isDbg() {
  1103. return mDbg;
  1104. }
  1105. /** @see StateMachine#setDbg(boolean) */
  1106. private final void setDbg(boolean dbg) {
  1107. mDbg = dbg;
  1108. }
  1109. }
  1110. private SmHandler mSmHandler;
  1111. private HandlerThread mSmThread;
  1112. /**
  1113. * Initialize.
  1114. *
  1115. * @param looper for this state machine
  1116. * @param name of the state machine
  1117. */
  1118. private void initStateMachine(String name, Looper looper) {
  1119. mName = name;
  1120. mSmHandler = new SmHandler(looper, this);
  1121. }
  1122. /**
  1123. * Constructor creates a StateMachine with its own thread.
  1124. *
  1125. * @param name of the state machine
  1126. */
  1127. protected StateMachine(String name) {
  1128. mSmThread = new HandlerThread(name);
  1129. mSmThread.start();
  1130. Looper looper = mSmThread.getLooper();
  1131. initStateMachine(name, looper);
  1132. }
  1133. /**
  1134. * Constructor creates a StateMachine using the looper.
  1135. *
  1136. * @param name of the state machine
  1137. */
  1138. protected StateMachine(String name, Looper looper) {
  1139. initStateMachine(name, looper);
  1140. }
  1141. /**
  1142. * Constructor creates a StateMachine using the handler.
  1143. *
  1144. * @param name of the state machine
  1145. */
  1146. protected StateMachine(String name, Handler handler) {
  1147. initStateMachine(name, handler.getLooper());
  1148. }
  1149. /**
  1150. * Add a new state to the state machine
  1151. * @param state the state to add
  1152. * @param parent the parent of state
  1153. */
  1154. protected final void addState(State state, State parent) {
  1155. mSmHandler.addState(state, parent);
  1156. }
  1157. /**
  1158. * Add a new state to the state machine, parent will be null
  1159. * @param state to add
  1160. */
  1161. protected final void addState(State state) {
  1162. mSmHandler.addState(state, null);
  1163. }
  1164. /**
  1165. * Set the initial state. This must be invoked before
  1166. * and messages are sent to the state machine.
  1167. *
  1168. * @param initialState is the state which will receive the first message.
  1169. */
  1170. protected final void setInitialState(State initialState) {
  1171. mSmHandler.setInitialState(initialState);
  1172. }
  1173. /**
  1174. * @return current message
  1175. */
  1176. protected final Message getCurrentMessage() {
  1177. // mSmHandler can be null if the state machine has quit.
  1178. SmHandler smh = mSmHandler;
  1179. if (smh == null) return null;
  1180. return smh.getCurrentMessage();
  1181. }
  1182. /**
  1183. * @return current state
  1184. */
  1185. protected final IState getCurrentState() {
  1186. // mSmHandler can be null if the state machine has quit.
  1187. SmHandler smh = mSmHandler;
  1188. if (smh == null) return null;
  1189. return smh.getCurrentState();
  1190. }
  1191. /**
  1192. * transition to destination state. Upon returning
  1193. * from processMessage the current state's exit will
  1194. * be executed and upon the next message arriving
  1195. * destState.enter will be invoked.
  1196. *
  1197. * this function can also be called inside the enter function of the
  1198. * previous transition target, but the behavior is undefined when it is
  1199. * called mid-way through a previous transition (for example, calling this
  1200. * in the enter() routine of a intermediate node when the current transition
  1201. * target is one of the nodes descendants).
  1202. *
  1203. * @param destState will be the state that receives the next message.
  1204. */
  1205. protected final void transitionTo(IState destState) {
  1206. mSmHandler.transitionTo(destState);
  1207. }
  1208. /**
  1209. * transition to halt state. Upon returning
  1210. * from processMessage we will exit all current
  1211. * states, execute the onHalting() method and then
  1212. * for all subsequent messages haltedProcessMessage
  1213. * will be called.
  1214. */
  1215. protected final void transitionToHaltingState() {
  1216. mSmHandler.transitionTo(mSmHandler.mHaltingState);
  1217. }
  1218. /**
  1219. * Defer this message until next state transition.
  1220. * Upon transitioning all deferred messages will be
  1221. * placed on the queue and reprocessed in the original
  1222. * order. (i.e. The next state the oldest messages will
  1223. * be processed first)
  1224. *
  1225. * @param msg is deferred until the next transition.
  1226. */
  1227. protected final void deferMessage(Message msg) {
  1228. mSmHandler.deferMessage(msg);
  1229. }
  1230. /**
  1231. * Called when message wasn't handled
  1232. *
  1233. * @param msg that couldn't be handled.
  1234. */
  1235. protected void unhandledMessage(Message msg) {
  1236. if (mSmHandler.mDbg) loge(" - unhandledMessage: msg.what=" + msg.what);
  1237. }
  1238. /**
  1239. * Called for any message that is received after
  1240. * transitionToHalting is called.
  1241. */
  1242. protected void haltedProcessMessage(Message msg) {
  1243. }
  1244. /**
  1245. * This will be called once after handling a message that called
  1246. * transitionToHalting. All subsequent messages will invoke
  1247. * {@link StateMachine#haltedProcessMessage(Message)}
  1248. */
  1249. protected void onHalting() {
  1250. }
  1251. /**
  1252. * This will be called once after a quit message that was NOT handled by
  1253. * the derived StateMachine. The StateMachine will stop and any subsequent messages will be
  1254. * ignored. In addition, if this StateMachine created the thread, the thread will
  1255. * be stopped after this method returns.
  1256. */
  1257. protected void onQuitting() {
  1258. }
  1259. /**
  1260. * @return the name
  1261. */
  1262. public final String getName() {
  1263. return mName;
  1264. }
  1265. /**
  1266. * Set number of log records to maintain and clears all current records.
  1267. *
  1268. * @param maxSize number of messages to maintain at anyone time.
  1269. */
  1270. public final void setLogRecSize(int maxSize) {
  1271. mSmHandler.mLogRecords.setSize(maxSize);
  1272. }
  1273. /**
  1274. * Set to log only messages that cause a state transition
  1275. *
  1276. * @param enable {@code true} to enable, {@code false} to disable
  1277. */
  1278. public final void setLogOnlyTransitions(boolean enable) {
  1279. mSmHandler.mLogRecords.setLogOnlyTransitions(enable);
  1280. }
  1281. /**
  1282. * @return number of log records
  1283. */
  1284. public final int getLogRecSize() {
  1285. // mSmHandler can be null if the state machine has quit.
  1286. SmHandler smh = mSmHandler;
  1287. if (smh == null) return 0;
  1288. return smh.mLogRecords.size();
  1289. }
  1290. /**
  1291. * @return the total number of records processed
  1292. */
  1293. public final int getLogRecCount() {
  1294. // mSmHandler can be null if the state machine has quit.