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.text.TextUtils; 24 import android.util.Log; 25 26 import java.io.FileDescriptor; 27 import java.io.PrintWriter; 28 import java.io.StringWriter; 29 import java.util.ArrayList; 30 import java.util.Calendar; 31 import java.util.Collection; 32 import java.util.HashMap; 33 import java.util.Vector; 34 35 /** 36 * {@hide} 37 * 38 * <p>The state machine defined here is a hierarchical state machine which processes messages 39 * and can have states arranged hierarchically.</p> 40 * 41 * <p>A state is a <code>State</code> object and must implement 42 * <code>processMessage</code> and optionally <code>enter/exit/getName</code>. 43 * The enter/exit methods are equivalent to the construction and destruction 44 * in Object Oriented programming and are used to perform initialization and 45 * cleanup of the state respectively. The <code>getName</code> method returns the 46 * name of the state the default implementation returns the class name it may be 47 * desirable to have this return the name of the state instance name instead. 48 * In particular if a particular state class has multiple instances.</p> 49 * 50 * <p>When a state machine is created <code>addState</code> is used to build the 51 * hierarchy and <code>setInitialState</code> is used to identify which of these 52 * is the initial state. After construction the programmer calls <code>start</code> 53 * which initializes and starts the state machine. The first action the StateMachine 54 * is to the invoke <code>enter</code> for all of the initial state's hierarchy, 55 * starting at its eldest parent. The calls to enter will be done in the context 56 * of the StateMachines Handler not in the context of the call to start and they 57 * will be invoked before any messages are processed. For example, given the simple 58 * state machine below mP1.enter will be invoked and then mS1.enter. Finally, 59 * messages sent to the state machine will be processed by the current state, 60 * in our simple state machine below that would initially be mS1.processMessage.</p> 61 <code> 62 mP1 63 / \ 64 mS2 mS1 ----> initial state 65 </code> 66 * <p>After the state machine is created and started, messages are sent to a state 67 * machine using <code>sendMessage</code> and the messages are created using 68 * <code>obtainMessage</code>. When the state machine receives a message the 69 * current state's <code>processMessage</code> is invoked. In the above example 70 * mS1.processMessage will be invoked first. The state may use <code>transitionTo</code> 71 * to change the current state to a new state</p> 72 * 73 * <p>Each state in the state machine may have a zero or one parent states and if 74 * a child state is unable to handle a message it may have the message processed 75 * by its parent by returning false or NOT_HANDLED. If a message is never processed 76 * <code>unhandledMessage</code> will be invoked to give one last chance for the state machine 77 * to process the message.</p> 78 * 79 * <p>When all processing is completed a state machine may choose to call 80 * <code>transitionToHaltingState</code>. When the current <code>processingMessage</code> 81 * returns the state machine will transfer to an internal <code>HaltingState</code> 82 * and invoke <code>halting</code>. Any message subsequently received by the state 83 * machine will cause <code>haltedProcessMessage</code> to be invoked.</p> 84 * 85 * <p>If it is desirable to completely stop the state machine call <code>quit</code> or 86 * <code>quitNow</code>. These will call <code>exit</code> of the current state and its parents, 87 * call <code>onQuiting</code> and then exit Thread/Loopers.</p> 88 * 89 * <p>In addition to <code>processMessage</code> each <code>State</code> has 90 * an <code>enter</code> method and <code>exit</exit> method which may be overridden.</p> 91 * 92 * <p>Since the states are arranged in a hierarchy transitioning to a new state 93 * causes current states to be exited and new states to be entered. To determine 94 * the list of states to be entered/exited the common parent closest to 95 * the current state is found. We then exit from the current state and its 96 * parent's up to but not including the common parent state and then enter all 97 * of the new states below the common parent down to the destination state. 98 * If there is no common parent all states are exited and then the new states 99 * are entered.</p> 100 * 101 * <p>Two other methods that states can use are <code>deferMessage</code> and 102 * <code>sendMessageAtFrontOfQueue</code>. The <code>sendMessageAtFrontOfQueue</code> sends 103 * a message but places it on the front of the queue rather than the back. The 104 * <code>deferMessage</code> causes the message to be saved on a list until a 105 * transition is made to a new state. At which time all of the deferred messages 106 * will be put on the front of the state machine queue with the oldest message 107 * at the front. These will then be processed by the new current state before 108 * any other messages that are on the queue or might be added later. Both of 109 * these are protected and may only be invoked from within a state machine.</p> 110 * 111 * <p>To illustrate some of these properties we'll use state machine with an 8 112 * state hierarchy:</p> 113 <code> 114 mP0 115 / \ 116 mP1 mS0 117 / \ 118 mS2 mS1 119 / \ \ 120 mS3 mS4 mS5 ---> initial state 121 </code> 122 * <p>After starting mS5 the list of active states is mP0, mP1, mS1 and mS5. 123 * So the order of calling processMessage when a message is received is mS5, 124 * mS1, mP1, mP0 assuming each processMessage indicates it can't handle this 125 * message by returning false or NOT_HANDLED.</p> 126 * 127 * <p>Now assume mS5.processMessage receives a message it can handle, and during 128 * the handling determines the machine should change states. It could call 129 * transitionTo(mS4) and return true or HANDLED. Immediately after returning from 130 * processMessage the state machine runtime will find the common parent, 131 * which is mP1. It will then call mS5.exit, mS1.exit, mS2.enter and then 132 * mS4.enter. The new list of active states is mP0, mP1, mS2 and mS4. So 133 * when the next message is received mS4.processMessage will be invoked.</p> 134 * 135 * <p>Now for some concrete examples, here is the canonical HelloWorld as a state machine. 136 * It responds with "Hello World" being printed to the log for every message.</p> 137 <code> 138 class HelloWorld extends StateMachine { 139 HelloWorld(String name) { 140 super(name); 141 addState(mState1); 142 setInitialState(mState1); 143 } 144 145 public static HelloWorld makeHelloWorld() { 146 HelloWorld hw = new HelloWorld("hw"); 147 hw.start(); 148 return hw; 149 } 150 151 class State1 extends State { 152 @Override public boolean processMessage(Message message) { 153 log("Hello World"); 154 return HANDLED; 155 } 156 } 157 State1 mState1 = new State1(); 158 } 159 160 void testHelloWorld() { 161 HelloWorld hw = makeHelloWorld(); 162 hw.sendMessage(hw.obtainMessage()); 163 } 164 </code> 165 * <p>A more interesting state machine is one with four states 166 * with two independent parent states.</p> 167 <code> 168 mP1 mP2 169 / \ 170 mS2 mS1 171 </code> 172 * <p>Here is a description of this state machine using pseudo code.</p> 173 <code> 174 state mP1 { 175 enter { log("mP1.enter"); } 176 exit { log("mP1.exit"); } 177 on msg { 178 CMD_2 { 179 send(CMD_3); 180 defer(msg); 181 transitonTo(mS2); 182 return HANDLED; 183 } 184 return NOT_HANDLED; 185 } 186 } 187 188 INITIAL 189 state mS1 parent mP1 { 190 enter { log("mS1.enter"); } 191 exit { log("mS1.exit"); } 192 on msg { 193 CMD_1 { 194 transitionTo(mS1); 195 return HANDLED; 196 } 197 return NOT_HANDLED; 198 } 199 } 200 201 state mS2 parent mP1 { 202 enter { log("mS2.enter"); } 203 exit { log("mS2.exit"); } 204 on msg { 205 CMD_2 { 206 send(CMD_4); 207 return HANDLED; 208 } 209 CMD_3 { 210 defer(msg); 211 transitionTo(mP2); 212 return HANDLED; 213 } 214 return NOT_HANDLED; 215 } 216 } 217 218 state mP2 { 219 enter { 220 log("mP2.enter"); 221 send(CMD_5); 222 } 223 exit { log("mP2.exit"); } 224 on msg { 225 CMD_3, CMD_4 { return HANDLED; } 226 CMD_5 { 227 transitionTo(HaltingState); 228 return HANDLED; 229 } 230 return NOT_HANDLED; 231 } 232 } 233 </code> 234 * <p>The implementation is below and also in StateMachineTest:</p> 235 <code> 236 class Hsm1 extends StateMachine { 237 public static final int CMD_1 = 1; 238 public static final int CMD_2 = 2; 239 public static final int CMD_3 = 3; 240 public static final int CMD_4 = 4; 241 public static final int CMD_5 = 5; 242 243 public static Hsm1 makeHsm1() { 244 log("makeHsm1 E"); 245 Hsm1 sm = new Hsm1("hsm1"); 246 sm.start(); 247 log("makeHsm1 X"); 248 return sm; 249 } 250 251 Hsm1(String name) { 252 super(name); 253 log("ctor E"); 254 255 // Add states, use indentation to show hierarchy 256 addState(mP1); 257 addState(mS1, mP1); 258 addState(mS2, mP1); 259 addState(mP2); 260 261 // Set the initial state 262 setInitialState(mS1); 263 log("ctor X"); 264 } 265 266 class P1 extends State { 267 @Override public void enter() { 268 log("mP1.enter"); 269 } 270 @Override public boolean processMessage(Message message) { 271 boolean retVal; 272 log("mP1.processMessage what=" + message.what); 273 switch(message.what) { 274 case CMD_2: 275 // CMD_2 will arrive in mS2 before CMD_3 276 sendMessage(obtainMessage(CMD_3)); 277 deferMessage(message); 278 transitionTo(mS2); 279 retVal = HANDLED; 280 break; 281 default: 282 // Any message we don't understand in this state invokes unhandledMessage 283 retVal = NOT_HANDLED; 284 break; 285 } 286 return retVal; 287 } 288 @Override public void exit() { 289 log("mP1.exit"); 290 } 291 } 292 293 class S1 extends State { 294 @Override public void enter() { 295 log("mS1.enter"); 296 } 297 @Override public boolean processMessage(Message message) { 298 log("S1.processMessage what=" + message.what); 299 if (message.what == CMD_1) { 300 // Transition to ourself to show that enter/exit is called 301 transitionTo(mS1); 302 return HANDLED; 303 } else { 304 // Let parent process all other messages 305 return NOT_HANDLED; 306 } 307 } 308 @Override public void exit() { 309 log("mS1.exit"); 310 } 311 } 312 313 class S2 extends State { 314 @Override public void enter() { 315 log("mS2.enter"); 316 } 317 @Override public boolean processMessage(Message message) { 318 boolean retVal; 319 log("mS2.processMessage what=" + message.what); 320 switch(message.what) { 321 case(CMD_2): 322 sendMessage(obtainMessage(CMD_4)); 323 retVal = HANDLED; 324 break; 325 case(CMD_3): 326 deferMessage(message); 327 transitionTo(mP2); 328 retVal = HANDLED; 329 break; 330 default: 331 retVal = NOT_HANDLED; 332 break; 333 } 334 return retVal; 335 } 336 @Override public void exit() { 337 log("mS2.exit"); 338 } 339 } 340 341 class P2 extends State { 342 @Override public void enter() { 343 log("mP2.enter"); 344 sendMessage(obtainMessage(CMD_5)); 345 } 346 @Override public boolean processMessage(Message message) { 347 log("P2.processMessage what=" + message.what); 348 switch(message.what) { 349 case(CMD_3): 350 break; 351 case(CMD_4): 352 break; 353 case(CMD_5): 354 transitionToHaltingState(); 355 break; 356 } 357 return HANDLED; 358 } 359 @Override public void exit() { 360 log("mP2.exit"); 361 } 362 } 363 364 @Override 365 void onHalting() { 366 log("halting"); 367 synchronized (this) { 368 this.notifyAll(); 369 } 370 } 371 372 P1 mP1 = new P1(); 373 S1 mS1 = new S1(); 374 S2 mS2 = new S2(); 375 P2 mP2 = new P2(); 376 } 377 </code> 378 * <p>If this is executed by sending two messages CMD_1 and CMD_2 379 * (Note the synchronize is only needed because we use hsm.wait())</p> 380 <code> 381 Hsm1 hsm = makeHsm1(); 382 synchronize(hsm) { 383 hsm.sendMessage(obtainMessage(hsm.CMD_1)); 384 hsm.sendMessage(obtainMessage(hsm.CMD_2)); 385 try { 386 // wait for the messages to be handled 387 hsm.wait(); 388 } catch (InterruptedException e) { 389 loge("exception while waiting " + e.getMessage()); 390 } 391 } 392 </code> 393 * <p>The output is:</p> 394 <code> 395 D/hsm1 ( 1999): makeHsm1 E 396 D/hsm1 ( 1999): ctor E 397 D/hsm1 ( 1999): ctor X 398 D/hsm1 ( 1999): mP1.enter 399 D/hsm1 ( 1999): mS1.enter 400 D/hsm1 ( 1999): makeHsm1 X 401 D/hsm1 ( 1999): mS1.processMessage what=1 402 D/hsm1 ( 1999): mS1.exit 403 D/hsm1 ( 1999): mS1.enter 404 D/hsm1 ( 1999): mS1.processMessage what=2 405 D/hsm1 ( 1999): mP1.processMessage what=2 406 D/hsm1 ( 1999): mS1.exit 407 D/hsm1 ( 1999): mS2.enter 408 D/hsm1 ( 1999): mS2.processMessage what=2 409 D/hsm1 ( 1999): mS2.processMessage what=3 410 D/hsm1 ( 1999): mS2.exit 411 D/hsm1 ( 1999): mP1.exit 412 D/hsm1 ( 1999): mP2.enter 413 D/hsm1 ( 1999): mP2.processMessage what=3 414 D/hsm1 ( 1999): mP2.processMessage what=4 415 D/hsm1 ( 1999): mP2.processMessage what=5 416 D/hsm1 ( 1999): mP2.exit 417 D/hsm1 ( 1999): halting 418 </code> 419 */ 420 public class StateMachine { 421 // Name of the state machine and used as logging tag 422 private String mName; 423 424 /** Message.what value when quitting */ 425 private static final int SM_QUIT_CMD = -1; 426 427 /** Message.what value when initializing */ 428 private static final int SM_INIT_CMD = -2; 429 430 /** 431 * Convenience constant that maybe returned by processMessage 432 * to indicate the the message was processed and is not to be 433 * processed by parent states 434 */ 435 public static final boolean HANDLED = true; 436 437 /** 438 * Convenience constant that maybe returned by processMessage 439 * to indicate the the message was NOT processed and is to be 440 * processed by parent states 441 */ 442 public static final boolean NOT_HANDLED = false; 443 444 /** 445 * StateMachine logging record. 446 * {@hide} 447 */ 448 public static class LogRec { 449 private StateMachine mSm; 450 private long mTime; 451 private int mWhat; 452 private String mInfo; 453 private IState mState; 454 private IState mOrgState; 455 private IState mDstState; 456 457 /** 458 * Constructor 459 * 460 * @param msg 461 * @param state the state which handled the message 462 * @param orgState is the first state the received the message but 463 * did not processes the message. 464 * @param transToState is the state that was transitioned to after the message was 465 * processed. 466 */ LogRec(StateMachine sm, Message msg, String info, IState state, IState orgState, IState transToState)467 LogRec(StateMachine sm, Message msg, String info, IState state, IState orgState, 468 IState transToState) { 469 update(sm, msg, info, state, orgState, transToState); 470 } 471 472 /** 473 * Update the information in the record. 474 * @param state that handled the message 475 * @param orgState is the first state the received the message 476 * @param dstState is the state that was the transition target when logging 477 */ update(StateMachine sm, Message msg, String info, IState state, IState orgState, IState dstState)478 public void update(StateMachine sm, Message msg, String info, IState state, IState orgState, 479 IState dstState) { 480 mSm = sm; 481 mTime = System.currentTimeMillis(); 482 mWhat = (msg != null) ? msg.what : 0; 483 mInfo = info; 484 mState = state; 485 mOrgState = orgState; 486 mDstState = dstState; 487 } 488 489 /** 490 * @return time stamp 491 */ getTime()492 public long getTime() { 493 return mTime; 494 } 495 496 /** 497 * @return msg.what 498 */ getWhat()499 public long getWhat() { 500 return mWhat; 501 } 502 503 /** 504 * @return the command that was executing 505 */ getInfo()506 public String getInfo() { 507 return mInfo; 508 } 509 510 /** 511 * @return the state that handled this message 512 */ getState()513 public IState getState() { 514 return mState; 515 } 516 517 /** 518 * @return the state destination state if a transition is occurring or null if none. 519 */ getDestState()520 public IState getDestState() { 521 return mDstState; 522 } 523 524 /** 525 * @return the original state that received the message. 526 */ getOriginalState()527 public IState getOriginalState() { 528 return mOrgState; 529 } 530 531 @Override toString()532 public String toString() { 533 StringBuilder sb = new StringBuilder(); 534 sb.append("time="); 535 Calendar c = Calendar.getInstance(); 536 c.setTimeInMillis(mTime); 537 sb.append(String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c)); 538 sb.append(" processed="); 539 sb.append(mState == null ? "<null>" : mState.getName()); 540 sb.append(" org="); 541 sb.append(mOrgState == null ? "<null>" : mOrgState.getName()); 542 sb.append(" dest="); 543 sb.append(mDstState == null ? "<null>" : mDstState.getName()); 544 sb.append(" what="); 545 String what = mSm != null ? mSm.getWhatToString(mWhat) : ""; 546 if (TextUtils.isEmpty(what)) { 547 sb.append(mWhat); 548 sb.append("(0x"); 549 sb.append(Integer.toHexString(mWhat)); 550 sb.append(")"); 551 } else { 552 sb.append(what); 553 } 554 if (!TextUtils.isEmpty(mInfo)) { 555 sb.append(" "); 556 sb.append(mInfo); 557 } 558 return sb.toString(); 559 } 560 } 561 562 /** 563 * A list of log records including messages recently processed by the state machine. 564 * 565 * The class maintains a list of log records including messages 566 * recently processed. The list is finite and may be set in the 567 * constructor or by calling setSize. The public interface also 568 * includes size which returns the number of recent records, 569 * count which is the number of records processed since the 570 * the last setSize, get which returns a record and 571 * add which adds a record. 572 */ 573 private static class LogRecords { 574 575 private static final int DEFAULT_SIZE = 20; 576 577 private Vector<LogRec> mLogRecVector = new Vector<LogRec>(); 578 private int mMaxSize = DEFAULT_SIZE; 579 private int mOldestIndex = 0; 580 private int mCount = 0; 581 private boolean mLogOnlyTransitions = false; 582 583 /** 584 * private constructor use add 585 */ LogRecords()586 private LogRecords() { 587 } 588 589 /** 590 * Set size of messages to maintain and clears all current records. 591 * 592 * @param maxSize number of records to maintain at anyone time. 593 */ setSize(int maxSize)594 synchronized void setSize(int maxSize) { 595 mMaxSize = maxSize; 596 mCount = 0; 597 mLogRecVector.clear(); 598 } 599 setLogOnlyTransitions(boolean enable)600 synchronized void setLogOnlyTransitions(boolean enable) { 601 mLogOnlyTransitions = enable; 602 } 603 logOnlyTransitions()604 synchronized boolean logOnlyTransitions() { 605 return mLogOnlyTransitions; 606 } 607 608 /** 609 * @return the number of recent records. 610 */ size()611 synchronized int size() { 612 return mLogRecVector.size(); 613 } 614 615 /** 616 * @return the total number of records processed since size was set. 617 */ count()618 synchronized int count() { 619 return mCount; 620 } 621 622 /** 623 * Clear the list of records. 624 */ cleanup()625 synchronized void cleanup() { 626 mLogRecVector.clear(); 627 } 628 629 /** 630 * @return the information on a particular record. 0 is the oldest 631 * record and size()-1 is the newest record. If the index is to 632 * large null is returned. 633 */ get(int index)634 synchronized LogRec get(int index) { 635 int nextIndex = mOldestIndex + index; 636 if (nextIndex >= mMaxSize) { 637 nextIndex -= mMaxSize; 638 } 639 if (nextIndex >= size()) { 640 return null; 641 } else { 642 return mLogRecVector.get(nextIndex); 643 } 644 } 645 646 /** 647 * Add a processed message. 648 * 649 * @param msg 650 * @param messageInfo to be stored 651 * @param state that handled the message 652 * @param orgState is the first state the received the message but 653 * did not processes the message. 654 * @param transToState is the state that was transitioned to after the message was 655 * processed. 656 * 657 */ add(StateMachine sm, Message msg, String messageInfo, IState state, IState orgState, IState transToState)658 synchronized void add(StateMachine sm, Message msg, String messageInfo, IState state, 659 IState orgState, IState transToState) { 660 mCount += 1; 661 if (mLogRecVector.size() < mMaxSize) { 662 mLogRecVector.add(new LogRec(sm, msg, messageInfo, state, orgState, transToState)); 663 } else { 664 LogRec pmi = mLogRecVector.get(mOldestIndex); 665 mOldestIndex += 1; 666 if (mOldestIndex >= mMaxSize) { 667 mOldestIndex = 0; 668 } 669 pmi.update(sm, msg, messageInfo, state, orgState, transToState); 670 } 671 } 672 } 673 674 private static class SmHandler extends Handler { 675 676 /** true if StateMachine has quit */ 677 private boolean mHasQuit = false; 678 679 /** The debug flag */ 680 private boolean mDbg = false; 681 682 /** The SmHandler object, identifies that message is internal */ 683 private static final Object mSmHandlerObj = new Object(); 684 685 /** The current message */ 686 private Message mMsg; 687 688 /** A list of log records including messages this state machine has processed */ 689 private LogRecords mLogRecords = new LogRecords(); 690 691 /** true if construction of the state machine has not been completed */ 692 private boolean mIsConstructionCompleted; 693 694 /** Stack used to manage the current hierarchy of states */ 695 private StateInfo mStateStack[]; 696 697 /** Top of mStateStack */ 698 private int mStateStackTopIndex = -1; 699 700 /** A temporary stack used to manage the state stack */ 701 private StateInfo mTempStateStack[]; 702 703 /** The top of the mTempStateStack */ 704 private int mTempStateStackCount; 705 706 /** State used when state machine is halted */ 707 private HaltingState mHaltingState = new HaltingState(); 708 709 /** State used when state machine is quitting */ 710 private QuittingState mQuittingState = new QuittingState(); 711 712 /** Reference to the StateMachine */ 713 private StateMachine mSm; 714 715 /** 716 * Information about a state. 717 * Used to maintain the hierarchy. 718 */ 719 private class StateInfo { 720 /** The state */ 721 State state; 722 723 /** The parent of this state, null if there is no parent */ 724 StateInfo parentStateInfo; 725 726 /** True when the state has been entered and on the stack */ 727 boolean active; 728 729 /** 730 * Convert StateInfo to string 731 */ 732 @Override toString()733 public String toString() { 734 return "state=" + state.getName() + ",active=" + active + ",parent=" 735 + ((parentStateInfo == null) ? "null" : parentStateInfo.state.getName()); 736 } 737 } 738 739 /** The map of all of the states in the state machine */ 740 private HashMap<State, StateInfo> mStateInfo = new HashMap<State, StateInfo>(); 741 742 /** The initial state that will process the first message */ 743 private State mInitialState; 744 745 /** The destination state when transitionTo has been invoked */ 746 private State mDestState; 747 748 /** The list of deferred messages */ 749 private ArrayList<Message> mDeferredMessages = new ArrayList<Message>(); 750 751 /** 752 * State entered when transitionToHaltingState is called. 753 */ 754 private class HaltingState extends State { 755 @Override processMessage(Message msg)756 public boolean processMessage(Message msg) { 757 mSm.haltedProcessMessage(msg); 758 return true; 759 } 760 } 761 762 /** 763 * State entered when a valid quit message is handled. 764 */ 765 private class QuittingState extends State { 766 @Override processMessage(Message msg)767 public boolean processMessage(Message msg) { 768 return NOT_HANDLED; 769 } 770 } 771 772 /** 773 * Handle messages sent to the state machine by calling 774 * the current state's processMessage. It also handles 775 * the enter/exit calls and placing any deferred messages 776 * back onto the queue when transitioning to a new state. 777 */ 778 @Override handleMessage(Message msg)779 public final void handleMessage(Message msg) { 780 if (!mHasQuit) { 781 if (mDbg) mSm.log("handleMessage: E msg.what=" + msg.what); 782 783 /** Save the current message */ 784 mMsg = msg; 785 786 /** State that processed the message */ 787 State msgProcessedState = null; 788 if (mIsConstructionCompleted) { 789 /** Normal path */ 790 msgProcessedState = processMsg(msg); 791 } else if (!mIsConstructionCompleted && (mMsg.what == SM_INIT_CMD) 792 && (mMsg.obj == mSmHandlerObj)) { 793 /** Initial one time path. */ 794 mIsConstructionCompleted = true; 795 invokeEnterMethods(0); 796 } else { 797 throw new RuntimeException("StateMachine.handleMessage: " 798 + "The start method not called, received msg: " + msg); 799 } 800 performTransitions(msgProcessedState, msg); 801 802 // We need to check if mSm == null here as we could be quitting. 803 if (mDbg && mSm != null) mSm.log("handleMessage: X"); 804 } 805 } 806 807 /** 808 * Do any transitions 809 * @param msgProcessedState is the state that processed the message 810 */ performTransitions(State msgProcessedState, Message msg)811 private void performTransitions(State msgProcessedState, Message msg) { 812 /** 813 * If transitionTo has been called, exit and then enter 814 * the appropriate states. We loop on this to allow 815 * enter and exit methods to use transitionTo. 816 */ 817 State orgState = mStateStack[mStateStackTopIndex].state; 818 819 /** 820 * Record whether message needs to be logged before we transition and 821 * and we won't log special messages SM_INIT_CMD or SM_QUIT_CMD which 822 * always set msg.obj to the handler. 823 */ 824 boolean recordLogMsg = mSm.recordLogRec(mMsg) && (msg.obj != mSmHandlerObj); 825 826 if (mLogRecords.logOnlyTransitions()) { 827 /** Record only if there is a transition */ 828 if (mDestState != null) { 829 mLogRecords.add(mSm, mMsg, mSm.getLogRecString(mMsg), msgProcessedState, 830 orgState, mDestState); 831 } 832 } else if (recordLogMsg) { 833 /** Record message */ 834 mLogRecords.add(mSm, mMsg, mSm.getLogRecString(mMsg), msgProcessedState, orgState, 835 mDestState); 836 } 837 838 State destState = mDestState; 839 if (destState != null) { 840 /** 841 * Process the transitions including transitions in the enter/exit methods 842 */ 843 while (true) { 844 if (mDbg) mSm.log("handleMessage: new destination call exit/enter"); 845 846 /** 847 * Determine the states to exit and enter and return the 848 * common ancestor state of the enter/exit states. Then 849 * invoke the exit methods then the enter methods. 850 */ 851 StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState); 852 invokeExitMethods(commonStateInfo); 853 int stateStackEnteringIndex = moveTempStateStackToStateStack(); 854 invokeEnterMethods(stateStackEnteringIndex); 855 856 /** 857 * Since we have transitioned to a new state we need to have 858 * any deferred messages moved to the front of the message queue 859 * so they will be processed before any other messages in the 860 * message queue. 861 */ 862 moveDeferredMessageAtFrontOfQueue(); 863 864 if (destState != mDestState) { 865 // A new mDestState so continue looping 866 destState = mDestState; 867 } else { 868 // No change in mDestState so we're done 869 break; 870 } 871 } 872 mDestState = null; 873 } 874 875 /** 876 * After processing all transitions check and 877 * see if the last transition was to quit or halt. 878 */ 879 if (destState != null) { 880 if (destState == mQuittingState) { 881 /** 882 * Call onQuitting to let subclasses cleanup. 883 */ 884 mSm.onQuitting(); 885 cleanupAfterQuitting(); 886 } else if (destState == mHaltingState) { 887 /** 888 * Call onHalting() if we've transitioned to the halting 889 * state. All subsequent messages will be processed in 890 * in the halting state which invokes haltedProcessMessage(msg); 891 */ 892 mSm.onHalting(); 893 } 894 } 895 } 896 897 /** 898 * Cleanup all the static variables and the looper after the SM has been quit. 899 */ cleanupAfterQuitting()900 private final void cleanupAfterQuitting() { 901 if (mSm.mSmThread != null) { 902 // If we made the thread then quit looper which stops the thread. 903 getLooper().quit(); 904 mSm.mSmThread = null; 905 } 906 907 mSm.mSmHandler = null; 908 mSm = null; 909 mMsg = null; 910 mLogRecords.cleanup(); 911 mStateStack = null; 912 mTempStateStack = null; 913 mStateInfo.clear(); 914 mInitialState = null; 915 mDestState = null; 916 mDeferredMessages.clear(); 917 mHasQuit = true; 918 } 919 920 /** 921 * Complete the construction of the state machine. 922 */ completeConstruction()923 private final void completeConstruction() { 924 if (mDbg) mSm.log("completeConstruction: E"); 925 926 /** 927 * Determine the maximum depth of the state hierarchy 928 * so we can allocate the state stacks. 929 */ 930 int maxDepth = 0; 931 for (StateInfo si : mStateInfo.values()) { 932 int depth = 0; 933 for (StateInfo i = si; i != null; depth++) { 934 i = i.parentStateInfo; 935 } 936 if (maxDepth < depth) { 937 maxDepth = depth; 938 } 939 } 940 if (mDbg) mSm.log("completeConstruction: maxDepth=" + maxDepth); 941 942 mStateStack = new StateInfo[maxDepth]; 943 mTempStateStack = new StateInfo[maxDepth]; 944 setupInitialStateStack(); 945 946 /** Sending SM_INIT_CMD message to invoke enter methods asynchronously */ 947 sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj)); 948 949 if (mDbg) mSm.log("completeConstruction: X"); 950 } 951 952 /** 953 * Process the message. If the current state doesn't handle 954 * it, call the states parent and so on. If it is never handled then 955 * call the state machines unhandledMessage method. 956 * @return the state that processed the message 957 */ processMsg(Message msg)958 private final State processMsg(Message msg) { 959 StateInfo curStateInfo = mStateStack[mStateStackTopIndex]; 960 if (mDbg) { 961 mSm.log("processMsg: " + curStateInfo.state.getName()); 962 } 963 964 if (isQuit(msg)) { 965 transitionTo(mQuittingState); 966 } else { 967 while (!curStateInfo.state.processMessage(msg)) { 968 /** 969 * Not processed 970 */ 971 curStateInfo = curStateInfo.parentStateInfo; 972 if (curStateInfo == null) { 973 /** 974 * No parents left so it's not handled 975 */ 976 mSm.unhandledMessage(msg); 977 break; 978 } 979 if (mDbg) { 980 mSm.log("processMsg: " + curStateInfo.state.getName()); 981 } 982 } 983 } 984 return (curStateInfo != null) ? curStateInfo.state : null; 985 } 986 987 /** 988 * Call the exit method for each state from the top of stack 989 * up to the common ancestor state. 990 */ invokeExitMethods(StateInfo commonStateInfo)991 private final void invokeExitMethods(StateInfo commonStateInfo) { 992 while ((mStateStackTopIndex >= 0) 993 && (mStateStack[mStateStackTopIndex] != commonStateInfo)) { 994 State curState = mStateStack[mStateStackTopIndex].state; 995 if (mDbg) mSm.log("invokeExitMethods: " + curState.getName()); 996 curState.exit(); 997 mStateStack[mStateStackTopIndex].active = false; 998 mStateStackTopIndex -= 1; 999 } 1000 } 1001 1002 /** 1003 * Invoke the enter method starting at the entering index to top of state stack 1004 */ invokeEnterMethods(int stateStackEnteringIndex)1005 private final void invokeEnterMethods(int stateStackEnteringIndex) { 1006 for (int i = stateStackEnteringIndex; i <= mStateStackTopIndex; i++) { 1007 if (mDbg) mSm.log("invokeEnterMethods: " + mStateStack[i].state.getName()); 1008 mStateStack[i].state.enter(); 1009 mStateStack[i].active = true; 1010 } 1011 } 1012 1013 /** 1014 * Move the deferred message to the front of the message queue. 1015 */ moveDeferredMessageAtFrontOfQueue()1016 private final void moveDeferredMessageAtFrontOfQueue() { 1017 /** 1018 * The oldest messages on the deferred list must be at 1019 * the front of the queue so start at the back, which 1020 * as the most resent message and end with the oldest 1021 * messages at the front of the queue. 1022 */ 1023 for (int i = mDeferredMessages.size() - 1; i >= 0; i--) { 1024 Message curMsg = mDeferredMessages.get(i); 1025 if (mDbg) mSm.log("moveDeferredMessageAtFrontOfQueue; what=" + curMsg.what); 1026 sendMessageAtFrontOfQueue(curMsg); 1027 } 1028 mDeferredMessages.clear(); 1029 } 1030 1031 /** 1032 * Move the contents of the temporary stack to the state stack 1033 * reversing the order of the items on the temporary stack as 1034 * they are moved. 1035 * 1036 * @return index into mStateStack where entering needs to start 1037 */ moveTempStateStackToStateStack()1038 private final int moveTempStateStackToStateStack() { 1039 int startingIndex = mStateStackTopIndex + 1; 1040 int i = mTempStateStackCount - 1; 1041 int j = startingIndex; 1042 while (i >= 0) { 1043 if (mDbg) mSm.log("moveTempStackToStateStack: i=" + i + ",j=" + j); 1044 mStateStack[j] = mTempStateStack[i]; 1045 j += 1; 1046 i -= 1; 1047 } 1048 1049 mStateStackTopIndex = j - 1; 1050 if (mDbg) { 1051 mSm.log("moveTempStackToStateStack: X mStateStackTop=" + mStateStackTopIndex 1052 + ",startingIndex=" + startingIndex + ",Top=" 1053 + mStateStack[mStateStackTopIndex].state.getName()); 1054 } 1055 return startingIndex; 1056 } 1057 1058 /** 1059 * Setup the mTempStateStack with the states we are going to enter. 1060 * 1061 * This is found by searching up the destState's ancestors for a 1062 * state that is already active i.e. StateInfo.active == true. 1063 * The destStae and all of its inactive parents will be on the 1064 * TempStateStack as the list of states to enter. 1065 * 1066 * @return StateInfo of the common ancestor for the destState and 1067 * current state or null if there is no common parent. 1068 */ setupTempStateStackWithStatesToEnter(State destState)1069 private final StateInfo setupTempStateStackWithStatesToEnter(State destState) { 1070 /** 1071 * Search up the parent list of the destination state for an active 1072 * state. Use a do while() loop as the destState must always be entered 1073 * even if it is active. This can happen if we are exiting/entering 1074 * the current state. 1075 */ 1076 mTempStateStackCount = 0; 1077 StateInfo curStateInfo = mStateInfo.get(destState); 1078 do { 1079 mTempStateStack[mTempStateStackCount++] = curStateInfo; 1080 curStateInfo = curStateInfo.parentStateInfo; 1081 } while ((curStateInfo != null) && !curStateInfo.active); 1082 1083 if (mDbg) { 1084 mSm.log("setupTempStateStackWithStatesToEnter: X mTempStateStackCount=" 1085 + mTempStateStackCount + ",curStateInfo: " + curStateInfo); 1086 } 1087 return curStateInfo; 1088 } 1089 1090 /** 1091 * Initialize StateStack to mInitialState. 1092 */ setupInitialStateStack()1093 private final void setupInitialStateStack() { 1094 if (mDbg) { 1095 mSm.log("setupInitialStateStack: E mInitialState=" + mInitialState.getName()); 1096 } 1097 1098 StateInfo curStateInfo = mStateInfo.get(mInitialState); 1099 for (mTempStateStackCount = 0; curStateInfo != null; mTempStateStackCount++) { 1100 mTempStateStack[mTempStateStackCount] = curStateInfo; 1101 curStateInfo = curStateInfo.parentStateInfo; 1102 } 1103 1104 // Empty the StateStack 1105 mStateStackTopIndex = -1; 1106 1107 moveTempStateStackToStateStack(); 1108 } 1109 1110 /** 1111 * @return current message 1112 */ getCurrentMessage()1113 private final Message getCurrentMessage() { 1114 return mMsg; 1115 } 1116 1117 /** 1118 * @return current state 1119 */ getCurrentState()1120 private final IState getCurrentState() { 1121 return mStateStack[mStateStackTopIndex].state; 1122 } 1123 1124 /** 1125 * Add a new state to the state machine. Bottom up addition 1126 * of states is allowed but the same state may only exist 1127 * in one hierarchy. 1128 * 1129 * @param state the state to add 1130 * @param parent the parent of state 1131 * @return stateInfo for this state 1132 */ addState(State state, State parent)1133 private final StateInfo addState(State state, State parent) { 1134 if (mDbg) { 1135 mSm.log("addStateInternal: E state=" + state.getName() + ",parent=" 1136 + ((parent == null) ? "" : parent.getName())); 1137 } 1138 StateInfo parentStateInfo = null; 1139 if (parent != null) { 1140 parentStateInfo = mStateInfo.get(parent); 1141 if (parentStateInfo == null) { 1142 // Recursively add our parent as it's not been added yet. 1143 parentStateInfo = addState(parent, null); 1144 } 1145 } 1146 StateInfo stateInfo = mStateInfo.get(state); 1147 if (stateInfo == null) { 1148 stateInfo = new StateInfo(); 1149 mStateInfo.put(state, stateInfo); 1150 } 1151 1152 // Validate that we aren't adding the same state in two different hierarchies. 1153 if ((stateInfo.parentStateInfo != null) 1154 && (stateInfo.parentStateInfo != parentStateInfo)) { 1155 throw new RuntimeException("state already added"); 1156 } 1157 stateInfo.state = state; 1158 stateInfo.parentStateInfo = parentStateInfo; 1159 stateInfo.active = false; 1160 if (mDbg) mSm.log("addStateInternal: X stateInfo: " + stateInfo); 1161 return stateInfo; 1162 } 1163 1164 /** 1165 * Constructor 1166 * 1167 * @param looper for dispatching messages 1168 * @param sm the hierarchical state machine 1169 */ SmHandler(Looper looper, StateMachine sm)1170 private SmHandler(Looper looper, StateMachine sm) { 1171 super(looper); 1172 mSm = sm; 1173 1174 addState(mHaltingState, null); 1175 addState(mQuittingState, null); 1176 } 1177 1178 /** @see StateMachine#setInitialState(State) */ setInitialState(State initialState)1179 private final void setInitialState(State initialState) { 1180 if (mDbg) mSm.log("setInitialState: initialState=" + initialState.getName()); 1181 mInitialState = initialState; 1182 } 1183 1184 /** @see StateMachine#transitionTo(IState) */ transitionTo(IState destState)1185 private final void transitionTo(IState destState) { 1186 mDestState = (State) destState; 1187 if (mDbg) mSm.log("transitionTo: destState=" + mDestState.getName()); 1188 } 1189 1190 /** @see StateMachine#deferMessage(Message) */ deferMessage(Message msg)1191 private final void deferMessage(Message msg) { 1192 if (mDbg) mSm.log("deferMessage: msg=" + msg.what); 1193 1194 /* Copy the "msg" to "newMsg" as "msg" will be recycled */ 1195 Message newMsg = obtainMessage(); 1196 newMsg.copyFrom(msg); 1197 1198 mDeferredMessages.add(newMsg); 1199 } 1200 1201 /** @see StateMachine#quit() */ quit()1202 private final void quit() { 1203 if (mDbg) mSm.log("quit:"); 1204 sendMessage(obtainMessage(SM_QUIT_CMD, mSmHandlerObj)); 1205 } 1206 1207 /** @see StateMachine#quitNow() */ quitNow()1208 private final void quitNow() { 1209 if (mDbg) mSm.log("quitNow:"); 1210 sendMessageAtFrontOfQueue(obtainMessage(SM_QUIT_CMD, mSmHandlerObj)); 1211 } 1212 1213 /** Validate that the message was sent by quit or quitNow. */ isQuit(Message msg)1214 private final boolean isQuit(Message msg) { 1215 return (msg.what == SM_QUIT_CMD) && (msg.obj == mSmHandlerObj); 1216 } 1217 1218 /** @see StateMachine#isDbg() */ isDbg()1219 private final boolean isDbg() { 1220 return mDbg; 1221 } 1222 1223 /** @see StateMachine#setDbg(boolean) */ setDbg(boolean dbg)1224 private final void setDbg(boolean dbg) { 1225 mDbg = dbg; 1226 } 1227 1228 } 1229 1230 private SmHandler mSmHandler; 1231 private HandlerThread mSmThread; 1232 1233 /** 1234 * Initialize. 1235 * 1236 * @param looper for this state machine 1237 * @param name of the state machine 1238 */ initStateMachine(String name, Looper looper)1239 private void initStateMachine(String name, Looper looper) { 1240 mName = name; 1241 mSmHandler = new SmHandler(looper, this); 1242 } 1243 1244 /** 1245 * Constructor creates a StateMachine with its own thread. 1246 * 1247 * @param name of the state machine 1248 */ StateMachine(String name)1249 protected StateMachine(String name) { 1250 mSmThread = new HandlerThread(name); 1251 mSmThread.start(); 1252 Looper looper = mSmThread.getLooper(); 1253 1254 initStateMachine(name, looper); 1255 } 1256 1257 /** 1258 * Constructor creates a StateMachine using the looper. 1259 * 1260 * @param name of the state machine 1261 */ StateMachine(String name, Looper looper)1262 protected StateMachine(String name, Looper looper) { 1263 initStateMachine(name, looper); 1264 } 1265 1266 /** 1267 * Constructor creates a StateMachine using the handler. 1268 * 1269 * @param name of the state machine 1270 */ StateMachine(String name, Handler handler)1271 protected StateMachine(String name, Handler handler) { 1272 initStateMachine(name, handler.getLooper()); 1273 } 1274 1275 /** 1276 * Add a new state to the state machine 1277 * @param state the state to add 1278 * @param parent the parent of state 1279 */ addState(State state, State parent)1280 protected final void addState(State state, State parent) { 1281 mSmHandler.addState(state, parent); 1282 } 1283 1284 /** 1285 * Add a new state to the state machine, parent will be null 1286 * @param state to add 1287 */ addState(State state)1288 protected final void addState(State state) { 1289 mSmHandler.addState(state, null); 1290 } 1291 1292 /** 1293 * Set the initial state. This must be invoked before 1294 * and messages are sent to the state machine. 1295 * 1296 * @param initialState is the state which will receive the first message. 1297 */ setInitialState(State initialState)1298 protected final void setInitialState(State initialState) { 1299 mSmHandler.setInitialState(initialState); 1300 } 1301 1302 /** 1303 * @return current message 1304 */ getCurrentMessage()1305 protected final Message getCurrentMessage() { 1306 // mSmHandler can be null if the state machine has quit. 1307 SmHandler smh = mSmHandler; 1308 if (smh == null) return null; 1309 return smh.getCurrentMessage(); 1310 } 1311 1312 /** 1313 * @return current state 1314 */ getCurrentState()1315 protected final IState getCurrentState() { 1316 // mSmHandler can be null if the state machine has quit. 1317 SmHandler smh = mSmHandler; 1318 if (smh == null) return null; 1319 return smh.getCurrentState(); 1320 } 1321 1322 /** 1323 * transition to destination state. Upon returning 1324 * from processMessage the current state's exit will 1325 * be executed and upon the next message arriving 1326 * destState.enter will be invoked. 1327 * 1328 * this function can also be called inside the enter function of the 1329 * previous transition target, but the behavior is undefined when it is 1330 * called mid-way through a previous transition (for example, calling this 1331 * in the enter() routine of a intermediate node when the current transition 1332 * target is one of the nodes descendants). 1333 * 1334 * @param destState will be the state that receives the next message. 1335 */ transitionTo(IState destState)1336 protected final void transitionTo(IState destState) { 1337 mSmHandler.transitionTo(destState); 1338 } 1339 1340 /** 1341 * transition to halt state. Upon returning 1342 * from processMessage we will exit all current 1343 * states, execute the onHalting() method and then 1344 * for all subsequent messages haltedProcessMessage 1345 * will be called. 1346 */ transitionToHaltingState()1347 protected final void transitionToHaltingState() { 1348 mSmHandler.transitionTo(mSmHandler.mHaltingState); 1349 } 1350 1351 /** 1352 * Defer this message until next state transition. 1353 * Upon transitioning all deferred messages will be 1354 * placed on the queue and reprocessed in the original 1355 * order. (i.e. The next state the oldest messages will 1356 * be processed first) 1357 * 1358 * @param msg is deferred until the next transition. 1359 */ deferMessage(Message msg)1360 protected final void deferMessage(Message msg) { 1361 mSmHandler.deferMessage(msg); 1362 } 1363 1364 /** 1365 * Called when message wasn't handled 1366 * 1367 * @param msg that couldn't be handled. 1368 */ unhandledMessage(Message msg)1369 protected void unhandledMessage(Message msg) { 1370 if (mSmHandler.mDbg) loge(" - unhandledMessage: msg.what=" + msg.what); 1371 } 1372 1373 /** 1374 * Called for any message that is received after 1375 * transitionToHalting is called. 1376 */ haltedProcessMessage(Message msg)1377 protected void haltedProcessMessage(Message msg) { 1378 } 1379 1380 /** 1381 * This will be called once after handling a message that called 1382 * transitionToHalting. All subsequent messages will invoke 1383 * {@link StateMachine#haltedProcessMessage(Message)} 1384 */ onHalting()1385 protected void onHalting() { 1386 } 1387 1388 /** 1389 * This will be called once after a quit message that was NOT handled by 1390 * the derived StateMachine. The StateMachine will stop and any subsequent messages will be 1391 * ignored. In addition, if this StateMachine created the thread, the thread will 1392 * be stopped after this method returns. 1393 */ onQuitting()1394 protected void onQuitting() { 1395 } 1396 1397 /** 1398 * @return the name 1399 */ getName()1400 public final String getName() { 1401 return mName; 1402 } 1403 1404 /** 1405 * Set number of log records to maintain and clears all current records. 1406 * 1407 * @param maxSize number of messages to maintain at anyone time. 1408 */ setLogRecSize(int maxSize)1409 public final void setLogRecSize(int maxSize) { 1410 mSmHandler.mLogRecords.setSize(maxSize); 1411 } 1412 1413 /** 1414 * Set to log only messages that cause a state transition 1415 * 1416 * @param enable {@code true} to enable, {@code false} to disable 1417 */ setLogOnlyTransitions(boolean enable)1418 public final void setLogOnlyTransitions(boolean enable) { 1419 mSmHandler.mLogRecords.setLogOnlyTransitions(enable); 1420 } 1421 1422 /** 1423 * @return number of log records 1424 */ getLogRecSize()1425 public final int getLogRecSize() { 1426 // mSmHandler can be null if the state machine has quit. 1427 SmHandler smh = mSmHandler; 1428 if (smh == null) return 0; 1429 return smh.mLogRecords.size(); 1430 } 1431 1432 /** 1433 * @return the total number of records processed 1434 */ getLogRecCount()1435 public final int getLogRecCount() { 1436 // mSmHandler can be null if the state machine has quit. 1437 SmHandler smh = mSmHandler; 1438 if (smh == null) return 0; 1439 return smh.mLogRecords.count(); 1440 } 1441 1442 /** 1443 * @return a log record, or null if index is out of range 1444 */ getLogRec(int index)1445 public final LogRec getLogRec(int index) { 1446 // mSmHandler can be null if the state machine has quit. 1447 SmHandler smh = mSmHandler; 1448 if (smh == null) return null; 1449 return smh.mLogRecords.get(index); 1450 } 1451 1452 /** 1453 * @return a copy of LogRecs as a collection 1454 */ copyLogRecs()1455 public final Collection<LogRec> copyLogRecs() { 1456 Vector<LogRec> vlr = new Vector<LogRec>(); 1457 SmHandler smh = mSmHandler; 1458 if (smh != null) { 1459 for (LogRec lr : smh.mLogRecords.mLogRecVector) { 1460 vlr.add(lr); 1461 } 1462 } 1463 return vlr; 1464 } 1465 1466 /** 1467 * Add the string to LogRecords. 1468 * 1469 * @param string 1470 */ addLogRec(String string)1471 protected void addLogRec(String string) { 1472 // mSmHandler can be null if the state machine has quit. 1473 SmHandler smh = mSmHandler; 1474 if (smh == null) return; 1475 smh.mLogRecords.add(this, smh.getCurrentMessage(), string, smh.getCurrentState(), 1476 smh.mStateStack[smh.mStateStackTopIndex].state, smh.mDestState); 1477 } 1478 1479 /** 1480 * @return true if msg should be saved in the log, default is true. 1481 */ recordLogRec(Message msg)1482 protected boolean recordLogRec(Message msg) { 1483 return true; 1484 } 1485 1486 /** 1487 * Return a string to be logged by LogRec, default 1488 * is an empty string. Override if additional information is desired. 1489 * 1490 * @param msg that was processed 1491 * @return information to be logged as a String 1492 */ getLogRecString(Message msg)1493 protected String getLogRecString(Message msg) { 1494 return ""; 1495 } 1496 1497 /** 1498 * @return the string for msg.what 1499 */ getWhatToString(int what)1500 protected String getWhatToString(int what) { 1501 return null; 1502 } 1503 1504 /** 1505 * @return Handler, maybe null if state machine has quit. 1506 */ getHandler()1507 public final Handler getHandler() { 1508 return mSmHandler; 1509 } 1510 1511 /** 1512 * Get a message and set Message.target state machine handler. 1513 * 1514 * Note: The handler can be null if the state machine has quit, 1515 * which means target will be null and may cause a AndroidRuntimeException 1516 * in MessageQueue#enqueMessage if sent directly or if sent using 1517 * StateMachine#sendMessage the message will just be ignored. 1518 * 1519 * @return A Message object from the global pool 1520 */ obtainMessage()1521 public final Message obtainMessage() { 1522 return Message.obtain(mSmHandler); 1523 } 1524 1525 /** 1526 * Get a message and set Message.target state machine handler, what. 1527 * 1528 * Note: The handler can be null if the state machine has quit, 1529 * which means target will be null and may cause a AndroidRuntimeException 1530 * in MessageQueue#enqueMessage if sent directly or if sent using 1531 * StateMachine#sendMessage the message will just be ignored. 1532 * 1533 * @param what is the assigned to Message.what. 1534 * @return A Message object from the global pool 1535 */ obtainMessage(int what)1536 public final Message obtainMessage(int what) { 1537 return Message.obtain(mSmHandler, what); 1538 } 1539 1540 /** 1541 * Get a message and set Message.target state machine handler, 1542 * what and obj. 1543 * 1544 * Note: The handler can be null if the state machine has quit, 1545 * which means target will be null and may cause a AndroidRuntimeException 1546 * in MessageQueue#enqueMessage if sent directly or if sent using 1547 * StateMachine#sendMessage the message will just be ignored. 1548 * 1549 * @param what is the assigned to Message.what. 1550 * @param obj is assigned to Message.obj. 1551 * @return A Message object from the global pool 1552 */ obtainMessage(int what, Object obj)1553 public final Message obtainMessage(int what, Object obj) { 1554 return Message.obtain(mSmHandler, what, obj); 1555 } 1556 1557 /** 1558 * Get a message and set Message.target state machine handler, 1559 * what, arg1 and arg2 1560 * 1561 * Note: The handler can be null if the state machine has quit, 1562 * which means target will be null and may cause a AndroidRuntimeException 1563 * in MessageQueue#enqueMessage if sent directly or if sent using 1564 * StateMachine#sendMessage the message will just be ignored. 1565 * 1566 * @param what is assigned to Message.what 1567 * @param arg1 is assigned to Message.arg1 1568 * @return A Message object from the global pool 1569 */ obtainMessage(int what, int arg1)1570 public final Message obtainMessage(int what, int arg1) { 1571 // use this obtain so we don't match the obtain(h, what, Object) method 1572 return Message.obtain(mSmHandler, what, arg1, 0); 1573 } 1574 1575 /** 1576 * Get a message and set Message.target state machine handler, 1577 * what, arg1 and arg2 1578 * 1579 * Note: The handler can be null if the state machine has quit, 1580 * which means target will be null and may cause a AndroidRuntimeException 1581 * in MessageQueue#enqueMessage if sent directly or if sent using 1582 * StateMachine#sendMessage the message will just be ignored. 1583 * 1584 * @param what is assigned to Message.what 1585 * @param arg1 is assigned to Message.arg1 1586 * @param arg2 is assigned to Message.arg2 1587 * @return A Message object from the global pool 1588 */ obtainMessage(int what, int arg1, int arg2)1589 public final Message obtainMessage(int what, int arg1, int arg2) { 1590 return Message.obtain(mSmHandler, what, arg1, arg2); 1591 } 1592 1593 /** 1594 * Get a message and set Message.target state machine handler, 1595 * what, arg1, arg2 and obj 1596 * 1597 * Note: The handler can be null if the state machine has quit, 1598 * which means target will be null and may cause a AndroidRuntimeException 1599 * in MessageQueue#enqueMessage if sent directly or if sent using 1600 * StateMachine#sendMessage the message will just be ignored. 1601 * 1602 * @param what is assigned to Message.what 1603 * @param arg1 is assigned to Message.arg1 1604 * @param arg2 is assigned to Message.arg2 1605 * @param obj is assigned to Message.obj 1606 * @return A Message object from the global pool 1607 */ obtainMessage(int what, int arg1, int arg2, Object obj)1608 public final Message obtainMessage(int what, int arg1, int arg2, Object obj) { 1609 return Message.obtain(mSmHandler, what, arg1, arg2, obj); 1610 } 1611 1612 /** 1613 * Enqueue a message to this state machine. 1614 * 1615 * Message is ignored if state machine has quit. 1616 */ sendMessage(int what)1617 public final void sendMessage(int what) { 1618 // mSmHandler can be null if the state machine has quit. 1619 SmHandler smh = mSmHandler; 1620 if (smh == null) return; 1621 1622 smh.sendMessage(obtainMessage(what)); 1623 } 1624 1625 /** 1626 * Enqueue a message to this state machine. 1627 * 1628 * Message is ignored if state machine has quit. 1629 */ sendMessage(int what, Object obj)1630 public final void sendMessage(int what, Object obj) { 1631 // mSmHandler can be null if the state machine has quit. 1632 SmHandler smh = mSmHandler; 1633 if (smh == null) return; 1634 1635 smh.sendMessage(obtainMessage(what, obj)); 1636 } 1637 1638 /** 1639 * Enqueue a message to this state machine. 1640 * 1641 * Message is ignored if state machine has quit. 1642 */ sendMessage(int what, int arg1)1643 public final void sendMessage(int what, int arg1) { 1644 // mSmHandler can be null if the state machine has quit. 1645 SmHandler smh = mSmHandler; 1646 if (smh == null) return; 1647 1648 smh.sendMessage(obtainMessage(what, arg1)); 1649 } 1650 1651 /** 1652 * Enqueue a message to this state machine. 1653 * 1654 * Message is ignored if state machine has quit. 1655 */ sendMessage(int what, int arg1, int arg2)1656 public final void sendMessage(int what, int arg1, int arg2) { 1657 // mSmHandler can be null if the state machine has quit. 1658 SmHandler smh = mSmHandler; 1659 if (smh == null) return; 1660 1661 smh.sendMessage(obtainMessage(what, arg1, arg2)); 1662 } 1663 1664 /** 1665 * Enqueue a message to this state machine. 1666 * 1667 * Message is ignored if state machine has quit. 1668 */ sendMessage(int what, int arg1, int arg2, Object obj)1669 public final void sendMessage(int what, int arg1, int arg2, Object obj) { 1670 // mSmHandler can be null if the state machine has quit. 1671 SmHandler smh = mSmHandler; 1672 if (smh == null) return; 1673 1674 smh.sendMessage(obtainMessage(what, arg1, arg2, obj)); 1675 } 1676 1677 /** 1678 * Enqueue a message to this state machine. 1679 * 1680 * Message is ignored if state machine has quit. 1681 */ sendMessage(Message msg)1682 public final void sendMessage(Message msg) { 1683 // mSmHandler can be null if the state machine has quit. 1684 SmHandler smh = mSmHandler; 1685 if (smh == null) return; 1686 1687 smh.sendMessage(msg); 1688 } 1689 1690 /** 1691 * Enqueue a message to this state machine after a delay. 1692 * 1693 * Message is ignored if state machine has quit. 1694 */ sendMessageDelayed(int what, long delayMillis)1695 public final void sendMessageDelayed(int what, long delayMillis) { 1696 // mSmHandler can be null if the state machine has quit. 1697 SmHandler smh = mSmHandler; 1698 if (smh == null) return; 1699 1700 smh.sendMessageDelayed(obtainMessage(what), delayMillis); 1701 } 1702 1703 /** 1704 * Enqueue a message to this state machine after a delay. 1705 * 1706 * Message is ignored if state machine has quit. 1707 */ sendMessageDelayed(int what, Object obj, long delayMillis)1708 public final void sendMessageDelayed(int what, Object obj, long delayMillis) { 1709 // mSmHandler can be null if the state machine has quit. 1710 SmHandler smh = mSmHandler; 1711 if (smh == null) return; 1712 1713 smh.sendMessageDelayed(obtainMessage(what, obj), delayMillis); 1714 } 1715 1716 /** 1717 * Enqueue a message to this state machine after a delay. 1718 * 1719 * Message is ignored if state machine has quit. 1720 */ sendMessageDelayed(int what, int arg1, long delayMillis)1721 public final void sendMessageDelayed(int what, int arg1, long delayMillis) { 1722 // mSmHandler can be null if the state machine has quit. 1723 SmHandler smh = mSmHandler; 1724 if (smh == null) return; 1725 1726 smh.sendMessageDelayed(obtainMessage(what, arg1), delayMillis); 1727 } 1728 1729 /** 1730 * Enqueue a message to this state machine after a delay. 1731 * 1732 * Message is ignored if state machine has quit. 1733 */ sendMessageDelayed(int what, int arg1, int arg2, long delayMillis)1734 public final void sendMessageDelayed(int what, int arg1, int arg2, long delayMillis) { 1735 // mSmHandler can be null if the state machine has quit. 1736 SmHandler smh = mSmHandler; 1737 if (smh == null) return; 1738 1739 smh.sendMessageDelayed(obtainMessage(what, arg1, arg2), delayMillis); 1740 } 1741 1742 /** 1743 * Enqueue a message to this state machine after a delay. 1744 * 1745 * Message is ignored if state machine has quit. 1746 */ sendMessageDelayed(int what, int arg1, int arg2, Object obj, long delayMillis)1747 public final void sendMessageDelayed(int what, int arg1, int arg2, Object obj, 1748 long delayMillis) { 1749 // mSmHandler can be null if the state machine has quit. 1750 SmHandler smh = mSmHandler; 1751 if (smh == null) return; 1752 1753 smh.sendMessageDelayed(obtainMessage(what, arg1, arg2, obj), delayMillis); 1754 } 1755 1756 /** 1757 * Enqueue a message to this state machine after a delay. 1758 * 1759 * Message is ignored if state machine has quit. 1760 */ sendMessageDelayed(Message msg, long delayMillis)1761 public final void sendMessageDelayed(Message msg, long delayMillis) { 1762 // mSmHandler can be null if the state machine has quit. 1763 SmHandler smh = mSmHandler; 1764 if (smh == null) return; 1765 1766 smh.sendMessageDelayed(msg, delayMillis); 1767 } 1768 1769 /** 1770 * Enqueue a message to the front of the queue for this state machine. 1771 * Protected, may only be called by instances of StateMachine. 1772 * 1773 * Message is ignored if state machine has quit. 1774 */ sendMessageAtFrontOfQueue(int what)1775 protected final void sendMessageAtFrontOfQueue(int what) { 1776 // mSmHandler can be null if the state machine has quit. 1777 SmHandler smh = mSmHandler; 1778 if (smh == null) return; 1779 1780 smh.sendMessageAtFrontOfQueue(obtainMessage(what)); 1781 } 1782 1783 /** 1784 * Enqueue a message to the front of the queue for this state machine. 1785 * Protected, may only be called by instances of StateMachine. 1786 * 1787 * Message is ignored if state machine has quit. 1788 */ sendMessageAtFrontOfQueue(int what, Object obj)1789 protected final void sendMessageAtFrontOfQueue(int what, Object obj) { 1790 // mSmHandler can be null if the state machine has quit. 1791 SmHandler smh = mSmHandler; 1792 if (smh == null) return; 1793 1794 smh.sendMessageAtFrontOfQueue(obtainMessage(what, obj)); 1795 } 1796 1797 /** 1798 * Enqueue a message to the front of the queue for this state machine. 1799 * Protected, may only be called by instances of StateMachine. 1800 * 1801 * Message is ignored if state machine has quit. 1802 */ sendMessageAtFrontOfQueue(int what, int arg1)1803 protected final void sendMessageAtFrontOfQueue(int what, int arg1) { 1804 // mSmHandler can be null if the state machine has quit. 1805 SmHandler smh = mSmHandler; 1806 if (smh == null) return; 1807 1808 smh.sendMessageAtFrontOfQueue(obtainMessage(what, arg1)); 1809 } 1810 1811 1812 /** 1813 * Enqueue a message to the front of the queue for this state machine. 1814 * Protected, may only be called by instances of StateMachine. 1815 * 1816 * Message is ignored if state machine has quit. 1817 */ sendMessageAtFrontOfQueue(int what, int arg1, int arg2)1818 protected final void sendMessageAtFrontOfQueue(int what, int arg1, int arg2) { 1819 // mSmHandler can be null if the state machine has quit. 1820 SmHandler smh = mSmHandler; 1821 if (smh == null) return; 1822 1823 smh.sendMessageAtFrontOfQueue(obtainMessage(what, arg1, arg2)); 1824 } 1825 1826 /** 1827 * Enqueue a message to the front of the queue for this state machine. 1828 * Protected, may only be called by instances of StateMachine. 1829 * 1830 * Message is ignored if state machine has quit. 1831 */ sendMessageAtFrontOfQueue(int what, int arg1, int arg2, Object obj)1832 protected final void sendMessageAtFrontOfQueue(int what, int arg1, int arg2, Object obj) { 1833 // mSmHandler can be null if the state machine has quit. 1834 SmHandler smh = mSmHandler; 1835 if (smh == null) return; 1836 1837 smh.sendMessageAtFrontOfQueue(obtainMessage(what, arg1, arg2, obj)); 1838 } 1839 1840 /** 1841 * Enqueue a message to the front of the queue for this state machine. 1842 * Protected, may only be called by instances of StateMachine. 1843 * 1844 * Message is ignored if state machine has quit. 1845 */ sendMessageAtFrontOfQueue(Message msg)1846 protected final void sendMessageAtFrontOfQueue(Message msg) { 1847 // mSmHandler can be null if the state machine has quit. 1848 SmHandler smh = mSmHandler; 1849 if (smh == null) return; 1850 1851 smh.sendMessageAtFrontOfQueue(msg); 1852 } 1853 1854 /** 1855 * Removes a message from the message queue. 1856 * Protected, may only be called by instances of StateMachine. 1857 */ removeMessages(int what)1858 protected final void removeMessages(int what) { 1859 // mSmHandler can be null if the state machine has quit. 1860 SmHandler smh = mSmHandler; 1861 if (smh == null) return; 1862 1863 smh.removeMessages(what); 1864 } 1865 1866 /** 1867 * Validate that the message was sent by 1868 * {@link StateMachine#quit} or {@link StateMachine#quitNow}. 1869 * */ isQuit(Message msg)1870 protected final boolean isQuit(Message msg) { 1871 // mSmHandler can be null if the state machine has quit. 1872 SmHandler smh = mSmHandler; 1873 if (smh == null) return msg.what == SM_QUIT_CMD; 1874 1875 return smh.isQuit(msg); 1876 } 1877 1878 /** 1879 * Quit the state machine after all currently queued up messages are processed. 1880 */ quit()1881 protected final void quit() { 1882 // mSmHandler can be null if the state machine is already stopped. 1883 SmHandler smh = mSmHandler; 1884 if (smh == null) return; 1885 1886 smh.quit(); 1887 } 1888 1889 /** 1890 * Quit the state machine immediately all currently queued messages will be discarded. 1891 */ quitNow()1892 protected final void quitNow() { 1893 // mSmHandler can be null if the state machine is already stopped. 1894 SmHandler smh = mSmHandler; 1895 if (smh == null) return; 1896 1897 smh.quitNow(); 1898 } 1899 1900 /** 1901 * @return if debugging is enabled 1902 */ isDbg()1903 public boolean isDbg() { 1904 // mSmHandler can be null if the state machine has quit. 1905 SmHandler smh = mSmHandler; 1906 if (smh == null) return false; 1907 1908 return smh.isDbg(); 1909 } 1910 1911 /** 1912 * Set debug enable/disabled. 1913 * 1914 * @param dbg is true to enable debugging. 1915 */ setDbg(boolean dbg)1916 public void setDbg(boolean dbg) { 1917 // mSmHandler can be null if the state machine has quit. 1918 SmHandler smh = mSmHandler; 1919 if (smh == null) return; 1920 1921 smh.setDbg(dbg); 1922 } 1923 1924 /** 1925 * Start the state machine. 1926 */ start()1927 public void start() { 1928 // mSmHandler can be null if the state machine has quit. 1929 SmHandler smh = mSmHandler; 1930 if (smh == null) return; 1931 1932 /** Send the complete construction message */ 1933 smh.completeConstruction(); 1934 } 1935 1936 /** 1937 * Dump the current state. 1938 * 1939 * @param fd 1940 * @param pw 1941 * @param args 1942 */ dump(FileDescriptor fd, PrintWriter pw, String[] args)1943 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1944 // Cannot just invoke pw.println(this.toString()) because if the 1945 // resulting string is to long it won't be displayed. 1946 pw.println(getName() + ":"); 1947 pw.println(" total records=" + getLogRecCount()); 1948 for (int i = 0; i < getLogRecSize(); i++) { 1949 pw.println(" rec[" + i + "]: " + getLogRec(i).toString()); 1950 pw.flush(); 1951 } 1952 pw.println("curState=" + getCurrentState().getName()); 1953 } 1954 1955 @Override toString()1956 public String toString() { 1957 StringWriter sr = new StringWriter(); 1958 PrintWriter pr = new PrintWriter(sr); 1959 dump(null, pr, null); 1960 pr.flush(); 1961 pr.close(); 1962 return sr.toString(); 1963 } 1964 1965 /** 1966 * Log with debug and add to the LogRecords. 1967 * 1968 * @param s is string log 1969 */ logAndAddLogRec(String s)1970 protected void logAndAddLogRec(String s) { 1971 addLogRec(s); 1972 log(s); 1973 } 1974 1975 /** 1976 * Log with debug 1977 * 1978 * @param s is string log 1979 */ log(String s)1980 protected void log(String s) { 1981 Log.d(mName, s); 1982 } 1983 1984 /** 1985 * Log with debug attribute 1986 * 1987 * @param s is string log 1988 */ logd(String s)1989 protected void logd(String s) { 1990 Log.d(mName, s); 1991 } 1992 1993 /** 1994 * Log with verbose attribute 1995 * 1996 * @param s is string log 1997 */ logv(String s)1998 protected void logv(String s) { 1999 Log.v(mName, s); 2000 } 2001 2002 /** 2003 * Log with info attribute 2004 * 2005 * @param s is string log 2006 */ logi(String s)2007 protected void logi(String s) { 2008 Log.i(mName, s); 2009 } 2010 2011 /** 2012 * Log with warning attribute 2013 * 2014 * @param s is string log 2015 */ logw(String s)2016 protected void logw(String s) { 2017 Log.w(mName, s); 2018 } 2019 2020 /** 2021 * Log with error attribute 2022 * 2023 * @param s is string log 2024 */ loge(String s)2025 protected void loge(String s) { 2026 Log.e(mName, s); 2027 } 2028 2029 /** 2030 * Log with error attribute 2031 * 2032 * @param s is string log 2033 * @param e is a Throwable which logs additional information. 2034 */ loge(String s, Throwable e)2035 protected void loge(String s, Throwable e) { 2036 Log.e(mName, s, e); 2037 } 2038 } 2039