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