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