PageRenderTime 26ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

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

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