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