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 17package com.android.internal.util; 18 19import android.os.Debug; 20import android.os.HandlerThread; 21import android.os.Looper; 22import android.os.Message; 23import android.os.SystemClock; 24 25import com.android.internal.util.State; 26import com.android.internal.util.StateMachine; 27import com.android.internal.util.StateMachine.LogRec; 28 29import android.test.suitebuilder.annotation.MediumTest; 30import android.test.suitebuilder.annotation.SmallTest; 31import android.util.Log; 32 33import junit.framework.TestCase; 34 35/** 36 * Test for StateMachine. 37 */ 38public class StateMachineTest extends TestCase { 39 private static final String ENTER = "enter"; 40 private static final String EXIT = "exit"; 41 private static final String ON_QUITTING = "ON_QUITTING"; 42 43 private static final int TEST_CMD_1 = 1; 44 private static final int TEST_CMD_2 = 2; 45 private static final int TEST_CMD_3 = 3; 46 private static final int TEST_CMD_4 = 4; 47 private static final int TEST_CMD_5 = 5; 48 private static final int TEST_CMD_6 = 6; 49 50 private static final boolean DBG = true; 51 private static final boolean WAIT_FOR_DEBUGGER = false; 52 private static final String TAG = "StateMachineTest"; 53 54 private void sleep(int millis) { 55 try { 56 Thread.sleep(millis); 57 } catch(InterruptedException e) { 58 } 59 } 60 61 /** 62 * Tests {@link StateMachine#quit()}. 63 */ 64 class StateMachineQuitTest extends StateMachine { 65 Object mWaitUntilTestDone = new Object(); 66 67 StateMachineQuitTest(String name) { 68 super(name); 69 mThisSm = this; 70 setDbg(DBG); 71 72 // Setup state machine with 1 state 73 addState(mS1); 74 75 // Set the initial state 76 setInitialState(mS1); 77 } 78 79 @Override 80 public void onQuitting() { 81 Log.d(TAG, "onQuitting"); 82 addLogRec(ON_QUITTING); 83 synchronized (mThisSm) { 84 mThisSm.notifyAll(); 85 } 86 87 // Don't leave onQuitting before the test is done as everything is cleared 88 // including the log records. 89 synchronized (mWaitUntilTestDone) { 90 try { 91 mWaitUntilTestDone.wait(); 92 } catch(InterruptedException e) { 93 } 94 } 95 } 96 97 class S1 extends State { 98 public void exit() { 99 Log.d(TAG, "S1.exit"); 100 addLogRec(EXIT, mS1); 101 } 102 @Override 103 public boolean processMessage(Message message) { 104 switch(message.what) { 105 // Sleep and assume the other messages will be queued up. 106 case TEST_CMD_1: { 107 Log.d(TAG, "TEST_CMD_1"); 108 sleep(500); 109 quit(); 110 break; 111 } 112 default: { 113 Log.d(TAG, "default what=" + message.what); 114 break; 115 } 116 } 117 return HANDLED; 118 } 119 } 120 121 private StateMachineQuitTest mThisSm; 122 private S1 mS1 = new S1(); 123 } 124 125 @SmallTest 126 public void testStateMachineQuit() throws Exception { 127 if (WAIT_FOR_DEBUGGER) Debug.waitForDebugger(); 128 129 StateMachineQuitTest smQuitTest = new StateMachineQuitTest("smQuitTest"); 130 smQuitTest.start(); 131 if (smQuitTest.isDbg()) Log.d(TAG, "testStateMachineQuit E"); 132 133 synchronized (smQuitTest) { 134 135 // Send 6 message we'll quit on the first but all 6 should be processed before quitting. 136 for (int i = 1; i <= 6; i++) { 137 smQuitTest.sendMessage(smQuitTest.obtainMessage(i)); 138 } 139 140 try { 141 // wait for the messages to be handled 142 smQuitTest.wait(); 143 } catch (InterruptedException e) { 144 Log.e(TAG, "testStateMachineQuit: exception while waiting " + e.getMessage()); 145 } 146 } 147 148 assertEquals(8, smQuitTest.getLogRecCount()); 149 150 LogRec lr; 151 152 for (int i = 0; i < 6; i++) { 153 lr = smQuitTest.getLogRec(i); 154 assertEquals(i+1, lr.getWhat()); 155 assertEquals(smQuitTest.mS1, lr.getState()); 156 assertEquals(smQuitTest.mS1, lr.getOriginalState()); 157 } 158 lr = smQuitTest.getLogRec(6); 159 assertEquals(EXIT, lr.getInfo()); 160 assertEquals(smQuitTest.mS1, lr.getState()); 161 162 lr = smQuitTest.getLogRec(7); 163 assertEquals(ON_QUITTING, lr.getInfo()); 164 165 synchronized (smQuitTest.mWaitUntilTestDone) { 166 smQuitTest.mWaitUntilTestDone.notifyAll(); 167 } 168 if (smQuitTest.isDbg()) Log.d(TAG, "testStateMachineQuit X"); 169 } 170 171 /** 172 * Tests {@link StateMachine#quitNow()} 173 */ 174 class StateMachineQuitNowTest extends StateMachine { 175 Object mWaitUntilTestDone = new Object(); 176 177 StateMachineQuitNowTest(String name) { 178 super(name); 179 mThisSm = this; 180 setDbg(DBG); 181 182 // Setup state machine with 1 state 183 addState(mS1); 184 185 // Set the initial state 186 setInitialState(mS1); 187 } 188 189 @Override 190 public void onQuitting() { 191 Log.d(TAG, "onQuitting"); 192 addLogRec(ON_QUITTING); 193 synchronized (mThisSm) { 194 mThisSm.notifyAll(); 195 } 196 197 // Don't leave onQuitting before the test is done as everything is cleared 198 // including the log records. 199 synchronized (mWaitUntilTestDone) { 200 try { 201 mWaitUntilTestDone.wait(); 202 } catch(InterruptedException e) { 203 } 204 } 205 } 206 207 class S1 extends State { 208 public void exit() { 209 Log.d(TAG, "S1.exit"); 210 addLogRec(EXIT, mS1); 211 } 212 @Override 213 public boolean processMessage(Message message) { 214 switch(message.what) { 215 // Sleep and assume the other messages will be queued up. 216 case TEST_CMD_1: { 217 Log.d(TAG, "TEST_CMD_1"); 218 sleep(500); 219 quitNow(); 220 break; 221 } 222 default: { 223 Log.d(TAG, "default what=" + message.what); 224 break; 225 } 226 } 227 return HANDLED; 228 } 229 } 230 231 private StateMachineQuitNowTest mThisSm; 232 private S1 mS1 = new S1(); 233 } 234 235 @SmallTest 236 public void testStateMachineQuitNow() throws Exception { 237 if (WAIT_FOR_DEBUGGER) Debug.waitForDebugger(); 238 239 StateMachineQuitNowTest smQuitNowTest = new StateMachineQuitNowTest("smQuitNowTest"); 240 smQuitNowTest.start(); 241 if (smQuitNowTest.isDbg()) Log.d(TAG, "testStateMachineQuitNow E"); 242 243 synchronized (smQuitNowTest) { 244 245 // Send 6 messages but we'll QuitNow on the first so even though 246 // we send 6 only one will be processed. 247 for (int i = 1; i <= 6; i++) { 248 smQuitNowTest.sendMessage(smQuitNowTest.obtainMessage(i)); 249 } 250 251 try { 252 // wait for the messages to be handled 253 smQuitNowTest.wait(); 254 } catch (InterruptedException e) { 255 Log.e(TAG, "testStateMachineQuitNow: exception while waiting " + e.getMessage()); 256 } 257 } 258 259 // Only three records because we executed quitNow. 260 assertEquals(3, smQuitNowTest.getLogRecCount()); 261 262 LogRec lr; 263 264 lr = smQuitNowTest.getLogRec(0); 265 assertEquals(1, lr.getWhat()); 266 assertEquals(smQuitNowTest.mS1, lr.getState()); 267 assertEquals(smQuitNowTest.mS1, lr.getOriginalState()); 268 269 lr = smQuitNowTest.getLogRec(1); 270 assertEquals(EXIT, lr.getInfo()); 271 assertEquals(smQuitNowTest.mS1, lr.getState()); 272 273 lr = smQuitNowTest.getLogRec(2); 274 assertEquals(ON_QUITTING, lr.getInfo()); 275 276 synchronized (smQuitNowTest.mWaitUntilTestDone) { 277 smQuitNowTest.mWaitUntilTestDone.notifyAll(); 278 } 279 if (smQuitNowTest.isDbg()) Log.d(TAG, "testStateMachineQuitNow X"); 280 } 281 282 /** 283 * Test enter/exit can use transitionTo 284 */ 285 class StateMachineEnterExitTransitionToTest extends StateMachine { 286 287 StateMachineEnterExitTransitionToTest(String name) { 288 super(name); 289 mThisSm = this; 290 setDbg(DBG); 291 292 // Setup state machine with 1 state 293 addState(mS1); 294 addState(mS2); 295 addState(mS3); 296 addState(mS4); 297 298 // Set the initial state 299 setInitialState(mS1); 300 } 301 302 class S1 extends State { 303 @Override 304 public void enter() { 305 // Test transitions in enter on the initial state work 306 addLogRec(ENTER, mS1); 307 transitionTo(mS2); 308 Log.d(TAG, "S1.enter"); 309 } 310 @Override 311 public void exit() { 312 // Test that message is HSM_INIT_CMD 313 addLogRec(EXIT, mS1); 314 Log.d(TAG, "S1.exit"); 315 } 316 } 317 318 class S2 extends State { 319 @Override 320 public void enter() { 321 addLogRec(ENTER, mS2); 322 Log.d(TAG, "S2.enter"); 323 } 324 @Override 325 public void exit() { 326 addLogRec(EXIT, mS2); 327 assertEquals(TEST_CMD_1, getCurrentMessage().what); 328 329 // Test transition in exit work 330 transitionTo(mS4); 331 Log.d(TAG, "S2.exit"); 332 } 333 @Override 334 public boolean processMessage(Message message) { 335 // Start a transition to S3 but it will be 336 // changed to a transition to S4 in exit 337 transitionTo(mS3); 338 Log.d(TAG, "S2.processMessage"); 339 return HANDLED; 340 } 341 } 342 343 class S3 extends State { 344 @Override 345 public void enter() { 346 addLogRec(ENTER, mS3); 347 Log.d(TAG, "S3.enter"); 348 } 349 @Override 350 public void exit() { 351 addLogRec(EXIT, mS3); 352 Log.d(TAG, "S3.exit"); 353 } 354 } 355 356 class S4 extends State { 357 @Override 358 public void enter() { 359 addLogRec(ENTER, mS4); 360 // Test that we can do halting in an enter/exit 361 transitionToHaltingState(); 362 Log.d(TAG, "S4.enter"); 363 } 364 @Override 365 public void exit() { 366 addLogRec(EXIT, mS4); 367 Log.d(TAG, "S4.exit"); 368 } 369 } 370 371 @Override 372 protected void onHalting() { 373 synchronized (mThisSm) { 374 mThisSm.notifyAll(); 375 } 376 } 377 378 private StateMachineEnterExitTransitionToTest mThisSm; 379 private S1 mS1 = new S1(); 380 private S2 mS2 = new S2(); 381 private S3 mS3 = new S3(); 382 private S4 mS4 = new S4(); 383 } 384 385 @SmallTest 386 public void testStateMachineEnterExitTransitionToTest() throws Exception { 387 //if (WAIT_FOR_DEBUGGER) Debug.waitForDebugger(); 388 389 StateMachineEnterExitTransitionToTest smEnterExitTranstionToTest = 390 new StateMachineEnterExitTransitionToTest("smEnterExitTranstionToTest"); 391 smEnterExitTranstionToTest.start(); 392 if (smEnterExitTranstionToTest.isDbg()) { 393 Log.d(TAG, "testStateMachineEnterExitTransitionToTest E"); 394 } 395 396 synchronized (smEnterExitTranstionToTest) { 397 smEnterExitTranstionToTest.sendMessage(TEST_CMD_1); 398 399 try { 400 // wait for the messages to be handled 401 smEnterExitTranstionToTest.wait(); 402 } catch (InterruptedException e) { 403 Log.e(TAG, "testStateMachineEnterExitTransitionToTest: exception while waiting " 404 + e.getMessage()); 405 } 406 } 407 408 assertEquals(smEnterExitTranstionToTest.getLogRecCount(), 9); 409 410 LogRec lr; 411 412 lr = smEnterExitTranstionToTest.getLogRec(0); 413 assertEquals(ENTER, lr.getInfo()); 414 assertEquals(smEnterExitTranstionToTest.mS1, lr.getState()); 415 416 lr = smEnterExitTranstionToTest.getLogRec(1); 417 assertEquals(EXIT, lr.getInfo()); 418 assertEquals(smEnterExitTranstionToTest.mS1, lr.getState()); 419 420 lr = smEnterExitTranstionToTest.getLogRec(2); 421 assertEquals(ENTER, lr.getInfo()); 422 assertEquals(smEnterExitTranstionToTest.mS2, lr.getState()); 423 424 lr = smEnterExitTranstionToTest.getLogRec(3); 425 assertEquals(TEST_CMD_1, lr.getWhat()); 426 assertEquals(smEnterExitTranstionToTest.mS2, lr.getState()); 427 assertEquals(smEnterExitTranstionToTest.mS2, lr.getOriginalState()); 428 429 lr = smEnterExitTranstionToTest.getLogRec(4); 430 assertEquals(EXIT, lr.getInfo()); 431 assertEquals(smEnterExitTranstionToTest.mS2, lr.getState()); 432 433 lr = smEnterExitTranstionToTest.getLogRec(5); 434 assertEquals(ENTER, lr.getInfo()); 435 assertEquals(smEnterExitTranstionToTest.mS3, lr.getState()); 436 437 lr = smEnterExitTranstionToTest.getLogRec(6); 438 assertEquals(EXIT, lr.getInfo()); 439 assertEquals(smEnterExitTranstionToTest.mS3, lr.getState()); 440 441 lr = smEnterExitTranstionToTest.getLogRec(7); 442 assertEquals(ENTER, lr.getInfo()); 443 assertEquals(smEnterExitTranstionToTest.mS4, lr.getState()); 444 445 lr = smEnterExitTranstionToTest.getLogRec(8); 446 assertEquals(EXIT, lr.getInfo()); 447 assertEquals(smEnterExitTranstionToTest.mS4, lr.getState()); 448 449 if (smEnterExitTranstionToTest.isDbg()) { 450 Log.d(TAG, "testStateMachineEnterExitTransitionToTest X"); 451 } 452 } 453 454 /** 455 * Tests that ProcessedMessage works as a circular buffer. 456 */ 457 class StateMachine0 extends StateMachine { 458 StateMachine0(String name) { 459 super(name); 460 mThisSm = this; 461 setDbg(DBG); 462 setLogRecSize(3); 463 464 // Setup state machine with 1 state 465 addState(mS1); 466 467 // Set the initial state 468 setInitialState(mS1); 469 } 470 471 class S1 extends State { 472 @Override 473 public boolean processMessage(Message message) { 474 if (message.what == TEST_CMD_6) { 475 transitionToHaltingState(); 476 } 477 return HANDLED; 478 } 479 } 480 481 @Override 482 protected void onHalting() { 483 synchronized (mThisSm) { 484 mThisSm.notifyAll(); 485 } 486 } 487 488 private StateMachine0 mThisSm; 489 private S1 mS1 = new S1(); 490 } 491 492 @SmallTest 493 public void testStateMachine0() throws Exception { 494 //if (WAIT_FOR_DEBUGGER) Debug.waitForDebugger(); 495 496 StateMachine0 sm0 = new StateMachine0("sm0"); 497 sm0.start(); 498 if (sm0.isDbg()) Log.d(TAG, "testStateMachine0 E"); 499 500 synchronized (sm0) { 501 // Send 6 messages 502 for (int i = 1; i <= 6; i++) { 503 sm0.sendMessage(sm0.obtainMessage(i)); 504 } 505 506 try { 507 // wait for the messages to be handled 508 sm0.wait(); 509 } catch (InterruptedException e) { 510 Log.e(TAG, "testStateMachine0: exception while waiting " + e.getMessage()); 511 } 512 } 513 514 assertEquals(6, sm0.getLogRecCount()); 515 assertEquals(3, sm0.getLogRecSize()); 516 517 LogRec lr; 518 lr = sm0.getLogRec(0); 519 assertEquals(TEST_CMD_4, lr.getWhat()); 520 assertEquals(sm0.mS1, lr.getState()); 521 assertEquals(sm0.mS1, lr.getOriginalState()); 522 523 lr = sm0.getLogRec(1); 524 assertEquals(TEST_CMD_5, lr.getWhat()); 525 assertEquals(sm0.mS1, lr.getState()); 526 assertEquals(sm0.mS1, lr.getOriginalState()); 527 528 lr = sm0.getLogRec(2); 529 assertEquals(TEST_CMD_6, lr.getWhat()); 530 assertEquals(sm0.mS1, lr.getState()); 531 assertEquals(sm0.mS1, lr.getOriginalState()); 532 533 if (sm0.isDbg()) Log.d(TAG, "testStateMachine0 X"); 534 } 535 536 /** 537 * This tests enter/exit and transitions to the same state. 538 * The state machine has one state, it receives two messages 539 * in state mS1. With the first message it transitions to 540 * itself which causes it to be exited and reentered. 541 */ 542 class StateMachine1 extends StateMachine { 543 StateMachine1(String name) { 544 super(name); 545 mThisSm = this; 546 setDbg(DBG); 547 548 // Setup state machine with 1 state 549 addState(mS1); 550 551 // Set the initial state 552 setInitialState(mS1); 553 if (DBG) Log.d(TAG, "StateMachine1: ctor X"); 554 } 555 556 class S1 extends State { 557 @Override 558 public void enter() { 559 mEnterCount++; 560 } 561 @Override 562 public void exit() { 563 mExitCount++; 564 } 565 @Override 566 public boolean processMessage(Message message) { 567 if (message.what == TEST_CMD_1) { 568 assertEquals(1, mEnterCount); 569 assertEquals(0, mExitCount); 570 transitionTo(mS1); 571 } else if (message.what == TEST_CMD_2) { 572 assertEquals(2, mEnterCount); 573 assertEquals(1, mExitCount); 574 transitionToHaltingState(); 575 } 576 return HANDLED; 577 } 578 } 579 580 @Override 581 protected void onHalting() { 582 synchronized (mThisSm) { 583 mThisSm.notifyAll(); 584 } 585 } 586 587 private StateMachine1 mThisSm; 588 private S1 mS1 = new S1(); 589 590 private int mEnterCount; 591 private int mExitCount; 592 } 593 594 @MediumTest 595 public void testStateMachine1() throws Exception { 596 StateMachine1 sm1 = new StateMachine1("sm1"); 597 sm1.start(); 598 if (sm1.isDbg()) Log.d(TAG, "testStateMachine1 E"); 599 600 synchronized (sm1) { 601 // Send two messages 602 sm1.sendMessage(TEST_CMD_1); 603 sm1.sendMessage(TEST_CMD_2); 604 605 try { 606 // wait for the messages to be handled 607 sm1.wait(); 608 } catch (InterruptedException e) { 609 Log.e(TAG, "testStateMachine1: exception while waiting " + e.getMessage()); 610 } 611 } 612 613 assertEquals(2, sm1.mEnterCount); 614 assertEquals(2, sm1.mExitCount); 615 616 assertEquals(2, sm1.getLogRecSize()); 617 618 LogRec lr; 619 lr = sm1.getLogRec(0); 620 assertEquals(TEST_CMD_1, lr.getWhat()); 621 assertEquals(sm1.mS1, lr.getState()); 622 assertEquals(sm1.mS1, lr.getOriginalState()); 623 624 lr = sm1.getLogRec(1); 625 assertEquals(TEST_CMD_2, lr.getWhat()); 626 assertEquals(sm1.mS1, lr.getState()); 627 assertEquals(sm1.mS1, lr.getOriginalState()); 628 629 assertEquals(2, sm1.mEnterCount); 630 assertEquals(2, sm1.mExitCount); 631 632 if (sm1.isDbg()) Log.d(TAG, "testStateMachine1 X"); 633 } 634 635 /** 636 * Test deferring messages and states with no parents. The state machine 637 * has two states, it receives two messages in state mS1 deferring them 638 * until what == TEST_CMD_2 and then transitions to state mS2. State 639 * mS2 then receives both of the deferred messages first TEST_CMD_1 and 640 * then TEST_CMD_2. 641 */ 642 class StateMachine2 extends StateMachine { 643 StateMachine2(String name) { 644 super(name); 645 mThisSm = this; 646 setDbg(DBG); 647 648 // Setup the hierarchy 649 addState(mS1); 650 addState(mS2); 651 652 // Set the initial state 653 setInitialState(mS1); 654 if (DBG) Log.d(TAG, "StateMachine2: ctor X"); 655 } 656 657 class S1 extends State { 658 @Override 659 public void enter() { 660 mDidEnter = true; 661 } 662 @Override 663 public void exit() { 664 mDidExit = true; 665 } 666 @Override 667 public boolean processMessage(Message message) { 668 deferMessage(message); 669 if (message.what == TEST_CMD_2) { 670 transitionTo(mS2); 671 } 672 return HANDLED; 673 } 674 } 675 676 class S2 extends State { 677 @Override 678 public boolean processMessage(Message message) { 679 if (message.what == TEST_CMD_2) { 680 transitionToHaltingState(); 681 } 682 return HANDLED; 683 } 684 } 685 686 @Override 687 protected void onHalting() { 688 synchronized (mThisSm) { 689 mThisSm.notifyAll(); 690 } 691 } 692 693 private StateMachine2 mThisSm; 694 private S1 mS1 = new S1(); 695 private S2 mS2 = new S2(); 696 697 private boolean mDidEnter = false; 698 private boolean mDidExit = false; 699 } 700 701 @MediumTest 702 public void testStateMachine2() throws Exception { 703 StateMachine2 sm2 = new StateMachine2("sm2"); 704 sm2.start(); 705 if (sm2.isDbg()) Log.d(TAG, "testStateMachine2 E"); 706 707 synchronized (sm2) { 708 // Send two messages 709 sm2.sendMessage(TEST_CMD_1); 710 sm2.sendMessage(TEST_CMD_2); 711 712 try { 713 // wait for the messages to be handled 714 sm2.wait(); 715 } catch (InterruptedException e) { 716 Log.e(TAG, "testStateMachine2: exception while waiting " + e.getMessage()); 717 } 718 } 719 720 assertEquals(4, sm2.getLogRecSize()); 721 722 LogRec lr; 723 lr = sm2.getLogRec(0); 724 assertEquals(TEST_CMD_1, lr.getWhat()); 725 assertEquals(sm2.mS1, lr.getState()); 726 727 lr = sm2.getLogRec(1); 728 assertEquals(TEST_CMD_2, lr.getWhat()); 729 assertEquals(sm2.mS1, lr.getState()); 730 731 lr = sm2.getLogRec(2); 732 assertEquals(TEST_CMD_1, lr.getWhat()); 733 assertEquals(sm2.mS2, lr.getState()); 734 735 lr = sm2.getLogRec(3); 736 assertEquals(TEST_CMD_2, lr.getWhat()); 737 assertEquals(sm2.mS2, lr.getState()); 738 739 assertTrue(sm2.mDidEnter); 740 assertTrue(sm2.mDidExit); 741 742 if (sm2.isDbg()) Log.d(TAG, "testStateMachine2 X"); 743 } 744 745 /** 746 * Test that unhandled messages in a child are handled by the parent. 747 * When TEST_CMD_2 is received. 748 */ 749 class StateMachine3 extends StateMachine { 750 StateMachine3(String name) { 751 super(name); 752 mThisSm = this; 753 setDbg(DBG); 754 755 // Setup the simplest hierarchy of two states 756 // mParentState and mChildState. 757 // (Use indentation to help visualize hierarchy) 758 addState(mParentState); 759 addState(mChildState, mParentState); 760 761 // Set the initial state will be the child 762 setInitialState(mChildState); 763 if (DBG) Log.d(TAG, "StateMachine3: ctor X"); 764 } 765 766 class ParentState extends State { 767 @Override 768 public boolean processMessage(Message message) { 769 if (message.what == TEST_CMD_2) { 770 transitionToHaltingState(); 771 } 772 return HANDLED; 773 } 774 } 775 776 class ChildState extends State { 777 @Override 778 public boolean processMessage(Message message) { 779 return NOT_HANDLED; 780 } 781 } 782 783 @Override 784 protected void onHalting() { 785 synchronized (mThisSm) { 786 mThisSm.notifyAll(); 787 } 788 } 789 790 private StateMachine3 mThisSm; 791 private ParentState mParentState = new ParentState(); 792 private ChildState mChildState = new ChildState(); 793 } 794 795 @MediumTest 796 public void testStateMachine3() throws Exception { 797 StateMachine3 sm3 = new StateMachine3("sm3"); 798 sm3.start(); 799 if (sm3.isDbg()) Log.d(TAG, "testStateMachine3 E"); 800 801 synchronized (sm3) { 802 // Send two messages 803 sm3.sendMessage(TEST_CMD_1); 804 sm3.sendMessage(TEST_CMD_2); 805 806 try { 807 // wait for the messages to be handled 808 sm3.wait(); 809 } catch (InterruptedException e) { 810 Log.e(TAG, "testStateMachine3: exception while waiting " + e.getMessage()); 811 } 812 } 813 814 assertEquals(2, sm3.getLogRecSize()); 815 816 LogRec lr; 817 lr = sm3.getLogRec(0); 818 assertEquals(TEST_CMD_1, lr.getWhat()); 819 assertEquals(sm3.mParentState, lr.getState()); 820 assertEquals(sm3.mChildState, lr.getOriginalState()); 821 822 lr = sm3.getLogRec(1); 823 assertEquals(TEST_CMD_2, lr.getWhat()); 824 assertEquals(sm3.mParentState, lr.getState()); 825 assertEquals(sm3.mChildState, lr.getOriginalState()); 826 827 if (sm3.isDbg()) Log.d(TAG, "testStateMachine3 X"); 828 } 829 830 /** 831 * Test a hierarchy of 3 states a parent and two children 832 * with transition from child 1 to child 2 and child 2 833 * lets the parent handle the messages. 834 */ 835 class StateMachine4 extends StateMachine { 836 StateMachine4(String name) { 837 super(name); 838 mThisSm = this; 839 setDbg(DBG); 840 841 // Setup a hierarchy of three states 842 // mParentState, mChildState1 & mChildState2 843 // (Use indentation to help visualize hierarchy) 844 addState(mParentState); 845 addState(mChildState1, mParentState); 846 addState(mChildState2, mParentState); 847 848 // Set the initial state will be child 1 849 setInitialState(mChildState1); 850 if (DBG) Log.d(TAG, "StateMachine4: ctor X"); 851 } 852 853 class ParentState extends State { 854 @Override 855 public boolean processMessage(Message message) { 856 if (message.what == TEST_CMD_2) { 857 transitionToHaltingState(); 858 } 859 return HANDLED; 860 } 861 } 862 863 class ChildState1 extends State { 864 @Override 865 public boolean processMessage(Message message) { 866 transitionTo(mChildState2); 867 return HANDLED; 868 } 869 } 870 871 class ChildState2 extends State { 872 @Override 873 public boolean processMessage(Message message) { 874 return NOT_HANDLED; 875 } 876 } 877 878 @Override 879 protected void onHalting() { 880 synchronized (mThisSm) { 881 mThisSm.notifyAll(); 882 } 883 } 884 885 private StateMachine4 mThisSm; 886 private ParentState mParentState = new ParentState(); 887 private ChildState1 mChildState1 = new ChildState1(); 888 private ChildState2 mChildState2 = new ChildState2(); 889 } 890 891 @MediumTest 892 public void testStateMachine4() throws Exception { 893 StateMachine4 sm4 = new StateMachine4("sm4"); 894 sm4.start(); 895 if (sm4.isDbg()) Log.d(TAG, "testStateMachine4 E"); 896 897 synchronized (sm4) { 898 // Send two messages 899 sm4.sendMessage(TEST_CMD_1); 900 sm4.sendMessage(TEST_CMD_2); 901 902 try { 903 // wait for the messages to be handled 904 sm4.wait(); 905 } catch (InterruptedException e) { 906 Log.e(TAG, "testStateMachine4: exception while waiting " + e.getMessage()); 907 } 908 } 909 910 911 assertEquals(2, sm4.getLogRecSize()); 912 913 LogRec lr; 914 lr = sm4.getLogRec(0); 915 assertEquals(TEST_CMD_1, lr.getWhat()); 916 assertEquals(sm4.mChildState1, lr.getState()); 917 assertEquals(sm4.mChildState1, lr.getOriginalState()); 918 919 lr = sm4.getLogRec(1); 920 assertEquals(TEST_CMD_2, lr.getWhat()); 921 assertEquals(sm4.mParentState, lr.getState()); 922 assertEquals(sm4.mChildState2, lr.getOriginalState()); 923 924 if (sm4.isDbg()) Log.d(TAG, "testStateMachine4 X"); 925 } 926 927 /** 928 * Test transition from one child to another of a "complex" 929 * hierarchy with two parents and multiple children. 930 */ 931 class StateMachine5 extends StateMachine { 932 StateMachine5(String name) { 933 super(name); 934 mThisSm = this; 935 setDbg(DBG); 936 937 // Setup a hierarchy with two parents and some children. 938 // (Use indentation to help visualize hierarchy) 939 addState(mParentState1); 940 addState(mChildState1, mParentState1); 941 addState(mChildState2, mParentState1); 942 943 addState(mParentState2); 944 addState(mChildState3, mParentState2); 945 addState(mChildState4, mParentState2); 946 addState(mChildState5, mChildState4); 947 948 // Set the initial state will be the child 949 setInitialState(mChildState1); 950 if (DBG) Log.d(TAG, "StateMachine5: ctor X"); 951 } 952 953 class ParentState1 extends State { 954 @Override 955 public void enter() { 956 mParentState1EnterCount += 1; 957 } 958 @Override 959 public void exit() { 960 mParentState1ExitCount += 1; 961 } 962 @Override 963 public boolean processMessage(Message message) { 964 return HANDLED; 965 } 966 } 967 968 class ChildState1 extends State { 969 @Override 970 public void enter() { 971 mChildState1EnterCount += 1; 972 } 973 @Override 974 public void exit() { 975 mChildState1ExitCount += 1; 976 } 977 @Override 978 public boolean processMessage(Message message) { 979 assertEquals(1, mParentState1EnterCount); 980 assertEquals(0, mParentState1ExitCount); 981 assertEquals(1, mChildState1EnterCount); 982 assertEquals(0, mChildState1ExitCount); 983 assertEquals(0, mChildState2EnterCount); 984 assertEquals(0, mChildState2ExitCount); 985 assertEquals(0, mParentState2EnterCount); 986 assertEquals(0, mParentState2ExitCount); 987 assertEquals(0, mChildState3EnterCount); 988 assertEquals(0, mChildState3ExitCount); 989 assertEquals(0, mChildState4EnterCount); 990 assertEquals(0, mChildState4ExitCount); 991 assertEquals(0, mChildState5EnterCount); 992 assertEquals(0, mChildState5ExitCount); 993 994 transitionTo(mChildState2); 995 return HANDLED; 996 } 997 } 998 999 class ChildState2 extends State { 1000 @Override 1001 public void enter() { 1002 mChildState2EnterCount += 1; 1003 } 1004 @Override 1005 public void exit() { 1006 mChildState2ExitCount += 1; 1007 } 1008 @Override 1009 public boolean processMessage(Message message) { 1010 assertEquals(1, mParentState1EnterCount); 1011 assertEquals(0, mParentState1ExitCount); 1012 assertEquals(1, mChildState1EnterCount); 1013 assertEquals(1, mChildState1ExitCount); 1014 assertEquals(1, mChildState2EnterCount); 1015 assertEquals(0, mChildState2ExitCount); 1016 assertEquals(0, mParentState2EnterCount); 1017 assertEquals(0, mParentState2ExitCount); 1018 assertEquals(0, mChildState3EnterCount); 1019 assertEquals(0, mChildState3ExitCount); 1020 assertEquals(0, mChildState4EnterCount); 1021 assertEquals(0, mChildState4ExitCount); 1022 assertEquals(0, mChildState5EnterCount); 1023 assertEquals(0, mChildState5ExitCount); 1024 1025 transitionTo(mChildState5); 1026 return HANDLED; 1027 } 1028 } 1029 1030 class ParentState2 extends State { 1031 @Override 1032 public void enter() { 1033 mParentState2EnterCount += 1; 1034 } 1035 @Override 1036 public void exit() { 1037 mParentState2ExitCount += 1; 1038 } 1039 @Override 1040 public boolean processMessage(Message message) { 1041 assertEquals(1, mParentState1EnterCount); 1042 assertEquals(1, mParentState1ExitCount); 1043 assertEquals(1, mChildState1EnterCount); 1044 assertEquals(1, mChildState1ExitCount); 1045 assertEquals(1, mChildState2EnterCount); 1046 assertEquals(1, mChildState2ExitCount); 1047 assertEquals(2, mParentState2EnterCount); 1048 assertEquals(1, mParentState2ExitCount); 1049 assertEquals(1, mChildState3EnterCount); 1050 assertEquals(1, mChildState3ExitCount); 1051 assertEquals(2, mChildState4EnterCount); 1052 assertEquals(2, mChildState4ExitCount); 1053 assertEquals(1, mChildState5EnterCount); 1054 assertEquals(1, mChildState5ExitCount); 1055 1056 transitionToHaltingState(); 1057 return HANDLED; 1058 } 1059 } 1060 1061 class ChildState3 extends State { 1062 @Override 1063 public void enter() { 1064 mChildState3EnterCount += 1; 1065 } 1066 @Override 1067 public void exit() { 1068 mChildState3ExitCount += 1; 1069 } 1070 @Override 1071 public boolean processMessage(Message message) { 1072 assertEquals(1, mParentState1EnterCount); 1073 assertEquals(1, mParentState1ExitCount); 1074 assertEquals(1, mChildState1EnterCount); 1075 assertEquals(1, mChildState1ExitCount); 1076 assertEquals(1, mChildState2EnterCount); 1077 assertEquals(1, mChildState2ExitCount); 1078 assertEquals(1, mParentState2EnterCount); 1079 assertEquals(0, mParentState2ExitCount); 1080 assertEquals(1, mChildState3EnterCount); 1081 assertEquals(0, mChildState3ExitCount); 1082 assertEquals(1, mChildState4EnterCount); 1083 assertEquals(1, mChildState4ExitCount); 1084 assertEquals(1, mChildState5EnterCount); 1085 assertEquals(1, mChildState5ExitCount); 1086 1087 transitionTo(mChildState4); 1088 return HANDLED; 1089 } 1090 } 1091 1092 class ChildState4 extends State { 1093 @Override 1094 public void enter() { 1095 mChildState4EnterCount += 1; 1096 } 1097 @Override 1098 public void exit() { 1099 mChildState4ExitCount += 1; 1100 } 1101 @Override 1102 public boolean processMessage(Message message) { 1103 assertEquals(1, mParentState1EnterCount); 1104 assertEquals(1, mParentState1ExitCount); 1105 assertEquals(1, mChildState1EnterCount); 1106 assertEquals(1, mChildState1ExitCount); 1107 assertEquals(1, mChildState2EnterCount); 1108 assertEquals(1, mChildState2ExitCount); 1109 assertEquals(1, mParentState2EnterCount); 1110 assertEquals(0, mParentState2ExitCount); 1111 assertEquals(1, mChildState3EnterCount); 1112 assertEquals(1, mChildState3ExitCount); 1113 assertEquals(2, mChildState4EnterCount); 1114 assertEquals(1, mChildState4ExitCount); 1115 assertEquals(1, mChildState5EnterCount); 1116 assertEquals(1, mChildState5ExitCount); 1117 1118 transitionTo(mParentState2); 1119 return HANDLED; 1120 } 1121 } 1122 1123 class ChildState5 extends State { 1124 @Override 1125 public void enter() { 1126 mChildState5EnterCount += 1; 1127 } 1128 @Override 1129 public void exit() { 1130 mChildState5ExitCount += 1; 1131 } 1132 @Override 1133 public boolean processMessage(Message message) { 1134 assertEquals(1, mParentState1EnterCount); 1135 assertEquals(1, mParentState1ExitCount); 1136 assertEquals(1, mChildState1EnterCount); 1137 assertEquals(1, mChildState1ExitCount); 1138 assertEquals(1, mChildState2EnterCount); 1139 assertEquals(1, mChildState2ExitCount); 1140 assertEquals(1, mParentState2EnterCount); 1141 assertEquals(0, mParentState2ExitCount); 1142 assertEquals(0, mChildState3EnterCount); 1143 assertEquals(0, mChildState3ExitCount); 1144 assertEquals(1, mChildState4EnterCount); 1145 assertEquals(0, mChildState4ExitCount); 1146 assertEquals(1, mChildState5EnterCount); 1147 assertEquals(0, mChildState5ExitCount); 1148 1149 transitionTo(mChildState3); 1150 return HANDLED; 1151 } 1152 } 1153 1154 @Override 1155 protected void onHalting() { 1156 synchronized (mThisSm) { 1157 mThisSm.notifyAll(); 1158 } 1159 } 1160 1161 private StateMachine5 mThisSm; 1162 private ParentState1 mParentState1 = new ParentState1(); 1163 private ChildState1 mChildState1 = new ChildState1(); 1164 private ChildState2 mChildState2 = new ChildState2(); 1165 private ParentState2 mParentState2 = new ParentState2(); 1166 private ChildState3 mChildState3 = new ChildState3(); 1167 private ChildState4 mChildState4 = new ChildState4(); 1168 private ChildState5 mChildState5 = new ChildState5(); 1169 1170 private int mParentState1EnterCount = 0; 1171 private int mParentState1ExitCount = 0; 1172 private int mChildState1EnterCount = 0; 1173 private int mChildState1ExitCount = 0; 1174 private int mChildState2EnterCount = 0; 1175 private int mChildState2ExitCount = 0; 1176 private int mParentState2EnterCount = 0; 1177 private int mParentState2ExitCount = 0; 1178 private int mChildState3EnterCount = 0; 1179 private int mChildState3ExitCount = 0; 1180 private int mChildState4EnterCount = 0; 1181 private int mChildState4ExitCount = 0; 1182 private int mChildState5EnterCount = 0; 1183 private int mChildState5ExitCount = 0; 1184 } 1185 1186 @MediumTest 1187 public void testStateMachine5() throws Exception { 1188 StateMachine5 sm5 = new StateMachine5("sm5"); 1189 sm5.start(); 1190 if (sm5.isDbg()) Log.d(TAG, "testStateMachine5 E"); 1191 1192 synchronized (sm5) { 1193 // Send 6 messages 1194 sm5.sendMessage(TEST_CMD_1); 1195 sm5.sendMessage(TEST_CMD_2); 1196 sm5.sendMessage(TEST_CMD_3); 1197 sm5.sendMessage(TEST_CMD_4); 1198 sm5.sendMessage(TEST_CMD_5); 1199 sm5.sendMessage(TEST_CMD_6); 1200 1201 try { 1202 // wait for the messages to be handled 1203 sm5.wait(); 1204 } catch (InterruptedException e) { 1205 Log.e(TAG, "testStateMachine5: exception while waiting " + e.getMessage()); 1206 } 1207 } 1208 1209 1210 assertEquals(6, sm5.getLogRecSize()); 1211 1212 assertEquals(1, sm5.mParentState1EnterCount); 1213 assertEquals(1, sm5.mParentState1ExitCount); 1214 assertEquals(1, sm5.mChildState1EnterCount); 1215 assertEquals(1, sm5.mChildState1ExitCount); 1216 assertEquals(1, sm5.mChildState2EnterCount); 1217 assertEquals(1, sm5.mChildState2ExitCount); 1218 assertEquals(2, sm5.mParentState2EnterCount); 1219 assertEquals(2, sm5.mParentState2ExitCount); 1220 assertEquals(1, sm5.mChildState3EnterCount); 1221 assertEquals(1, sm5.mChildState3ExitCount); 1222 assertEquals(2, sm5.mChildState4EnterCount); 1223 assertEquals(2, sm5.mChildState4ExitCount); 1224 assertEquals(1, sm5.mChildState5EnterCount); 1225 assertEquals(1, sm5.mChildState5ExitCount); 1226 1227 LogRec lr; 1228 lr = sm5.getLogRec(0); 1229 assertEquals(TEST_CMD_1, lr.getWhat()); 1230 assertEquals(sm5.mChildState1, lr.getState()); 1231 assertEquals(sm5.mChildState1, lr.getOriginalState()); 1232 1233 lr = sm5.getLogRec(1); 1234 assertEquals(TEST_CMD_2, lr.getWhat()); 1235 assertEquals(sm5.mChildState2, lr.getState()); 1236 assertEquals(sm5.mChildState2, lr.getOriginalState()); 1237 1238 lr = sm5.getLogRec(2); 1239 assertEquals(TEST_CMD_3, lr.getWhat()); 1240 assertEquals(sm5.mChildState5, lr.getState()); 1241 assertEquals(sm5.mChildState5, lr.getOriginalState()); 1242 1243 lr = sm5.getLogRec(3); 1244 assertEquals(TEST_CMD_4, lr.getWhat()); 1245 assertEquals(sm5.mChildState3, lr.getState()); 1246 assertEquals(sm5.mChildState3, lr.getOriginalState()); 1247 1248 lr = sm5.getLogRec(4); 1249 assertEquals(TEST_CMD_5, lr.getWhat()); 1250 assertEquals(sm5.mChildState4, lr.getState()); 1251 assertEquals(sm5.mChildState4, lr.getOriginalState()); 1252 1253 lr = sm5.getLogRec(5); 1254 assertEquals(TEST_CMD_6, lr.getWhat()); 1255 assertEquals(sm5.mParentState2, lr.getState()); 1256 assertEquals(sm5.mParentState2, lr.getOriginalState()); 1257 1258 if (sm5.isDbg()) Log.d(TAG, "testStateMachine5 X"); 1259 } 1260 1261 /** 1262 * Test that the initial state enter is invoked immediately 1263 * after construction and before any other messages arrive and that 1264 * sendMessageDelayed works. 1265 */ 1266 class StateMachine6 extends StateMachine { 1267 StateMachine6(String name) { 1268 super(name); 1269 mThisSm = this; 1270 setDbg(DBG); 1271 1272 // Setup state machine with 1 state 1273 addState(mS1); 1274 1275 // Set the initial state 1276 setInitialState(mS1); 1277 if (DBG) Log.d(TAG, "StateMachine6: ctor X"); 1278 } 1279 1280 class S1 extends State { 1281 @Override 1282 public void enter() { 1283 sendMessage(TEST_CMD_1); 1284 } 1285 @Override 1286 public boolean processMessage(Message message) { 1287 if (message.what == TEST_CMD_1) { 1288 mArrivalTimeMsg1 = SystemClock.elapsedRealtime(); 1289 } else if (message.what == TEST_CMD_2) { 1290 mArrivalTimeMsg2 = SystemClock.elapsedRealtime(); 1291 transitionToHaltingState(); 1292 } 1293 return HANDLED; 1294 } 1295 } 1296 1297 @Override 1298 protected void onHalting() { 1299 synchronized (mThisSm) { 1300 mThisSm.notifyAll(); 1301 } 1302 } 1303 1304 private StateMachine6 mThisSm; 1305 private S1 mS1 = new S1(); 1306 1307 private long mArrivalTimeMsg1; 1308 private long mArrivalTimeMsg2; 1309 } 1310 1311 @MediumTest 1312 public void testStateMachine6() throws Exception { 1313 final int DELAY_TIME = 250; 1314 final int DELAY_FUDGE = 20; 1315 1316 StateMachine6 sm6 = new StateMachine6("sm6"); 1317 sm6.start(); 1318 if (sm6.isDbg()) Log.d(TAG, "testStateMachine6 E"); 1319 1320 synchronized (sm6) { 1321 // Send a message 1322 sm6.sendMessageDelayed(TEST_CMD_2, DELAY_TIME); 1323 1324 try { 1325 // wait for the messages to be handled 1326 sm6.wait(); 1327 } catch (InterruptedException e) { 1328 Log.e(TAG, "testStateMachine6: exception while waiting " + e.getMessage()); 1329 } 1330 } 1331 1332 /** 1333 * TEST_CMD_1 was sent in enter and must always have been processed 1334 * immediately after construction and hence the arrival time difference 1335 * should always >= to the DELAY_TIME 1336 */ 1337 long arrivalTimeDiff = sm6.mArrivalTimeMsg2 - sm6.mArrivalTimeMsg1; 1338 long expectedDelay = DELAY_TIME - DELAY_FUDGE; 1339 if (sm6.isDbg()) Log.d(TAG, "testStateMachine6: expect " + arrivalTimeDiff 1340 + " >= " + expectedDelay); 1341 assertTrue(arrivalTimeDiff >= expectedDelay); 1342 1343 if (sm6.isDbg()) Log.d(TAG, "testStateMachine6 X"); 1344 } 1345 1346 /** 1347 * Test that enter is invoked immediately after exit. This validates 1348 * that enter can be used to send a watch dog message for its state. 1349 */ 1350 class StateMachine7 extends StateMachine { 1351 private final int SM7_DELAY_TIME = 250; 1352 1353 StateMachine7(String name) { 1354 super(name); 1355 mThisSm = this; 1356 setDbg(DBG); 1357 1358 // Setup state machine with 1 state 1359 addState(mS1); 1360 addState(mS2); 1361 1362 // Set the initial state 1363 setInitialState(mS1); 1364 if (DBG) Log.d(TAG, "StateMachine7: ctor X"); 1365 } 1366 1367 class S1 extends State { 1368 @Override 1369 public void exit() { 1370 sendMessage(TEST_CMD_2); 1371 } 1372 @Override 1373 public boolean processMessage(Message message) { 1374 transitionTo(mS2); 1375 return HANDLED; 1376 } 1377 } 1378 1379 class S2 extends State { 1380 @Override 1381 public void enter() { 1382 // Send a delayed message as a watch dog 1383 sendMessageDelayed(TEST_CMD_3, SM7_DELAY_TIME); 1384 } 1385 @Override 1386 public boolean processMessage(Message message) { 1387 if (message.what == TEST_CMD_2) { 1388 mMsgCount += 1; 1389 mArrivalTimeMsg2 = SystemClock.elapsedRealtime(); 1390 } else if (message.what == TEST_CMD_3) { 1391 mMsgCount += 1; 1392 mArrivalTimeMsg3 = SystemClock.elapsedRealtime(); 1393 } 1394 1395 if (mMsgCount == 2) { 1396 transitionToHaltingState(); 1397 } 1398 return HANDLED; 1399 } 1400 } 1401 1402 @Override 1403 protected void onHalting() { 1404 synchronized (mThisSm) { 1405 mThisSm.notifyAll(); 1406 } 1407 } 1408 1409 private StateMachine7 mThisSm; 1410 private S1 mS1 = new S1(); 1411 private S2 mS2 = new S2(); 1412 1413 private int mMsgCount = 0; 1414 private long mArrivalTimeMsg2; 1415 private long mArrivalTimeMsg3; 1416 } 1417 1418 @MediumTest 1419 public void testStateMachine7() throws Exception { 1420 final int SM7_DELAY_FUDGE = 20; 1421 1422 StateMachine7 sm7 = new StateMachine7("sm7"); 1423 sm7.start(); 1424 if (sm7.isDbg()) Log.d(TAG, "testStateMachine7 E"); 1425 1426 synchronized (sm7) { 1427 // Send a message 1428 sm7.sendMessage(TEST_CMD_1); 1429 1430 try { 1431 // wait for the messages to be handled 1432 sm7.wait(); 1433 } catch (InterruptedException e) { 1434 Log.e(TAG, "testStateMachine7: exception while waiting " + e.getMessage()); 1435 } 1436 } 1437 1438 /** 1439 * TEST_CMD_3 was sent in S2.enter with a delay and must always have been 1440 * processed immediately after S1.exit. Since S1.exit sent TEST_CMD_2 1441 * without a delay the arrival time difference should always >= to SM7_DELAY_TIME. 1442 */ 1443 long arrivalTimeDiff = sm7.mArrivalTimeMsg3 - sm7.mArrivalTimeMsg2; 1444 long expectedDelay = sm7.SM7_DELAY_TIME - SM7_DELAY_FUDGE; 1445 if (sm7.isDbg()) Log.d(TAG, "testStateMachine7: expect " + arrivalTimeDiff 1446 + " >= " + expectedDelay); 1447 assertTrue(arrivalTimeDiff >= expectedDelay); 1448 1449 if (sm7.isDbg()) Log.d(TAG, "testStateMachine7 X"); 1450 } 1451 1452 /** 1453 * Test unhandledMessage. 1454 */ 1455 class StateMachineUnhandledMessage extends StateMachine { 1456 StateMachineUnhandledMessage(String name) { 1457 super(name); 1458 mThisSm = this; 1459 setDbg(DBG); 1460 1461 // Setup state machine with 1 state 1462 addState(mS1); 1463 1464 // Set the initial state 1465 setInitialState(mS1); 1466 } 1467 @Override 1468 public void unhandledMessage(Message message) { 1469 mUnhandledMessageCount += 1; 1470 } 1471 1472 class S1 extends State { 1473 @Override 1474 public boolean processMessage(Message message) { 1475 if (message.what == TEST_CMD_2) { 1476 transitionToHaltingState(); 1477 } 1478 return NOT_HANDLED; 1479 } 1480 } 1481 1482 @Override 1483 protected void onHalting() { 1484 synchronized (mThisSm) { 1485 mThisSm.notifyAll(); 1486 } 1487 } 1488 1489 private StateMachineUnhandledMessage mThisSm; 1490 private int mUnhandledMessageCount; 1491 private S1 mS1 = new S1(); 1492 } 1493 1494 @SmallTest 1495 public void testStateMachineUnhandledMessage() throws Exception { 1496 1497 StateMachineUnhandledMessage sm = new StateMachineUnhandledMessage("sm"); 1498 sm.start(); 1499 if (sm.isDbg()) Log.d(TAG, "testStateMachineUnhandledMessage E"); 1500 1501 synchronized (sm) { 1502 // Send 2 messages 1503 for (int i = 1; i <= 2; i++) { 1504 sm.sendMessage(i); 1505 } 1506 1507 try { 1508 // wait for the messages to be handled 1509 sm.wait(); 1510 } catch (InterruptedException e) { 1511 Log.e(TAG, "testStateMachineUnhandledMessage: exception while waiting " 1512 + e.getMessage()); 1513 } 1514 } 1515 1516 assertEquals(sm.getLogRecCount(), 2); 1517 assertEquals(2, sm.mUnhandledMessageCount); 1518 1519 if (sm.isDbg()) Log.d(TAG, "testStateMachineUnhandledMessage X"); 1520 } 1521 1522 /** 1523 * Test state machines sharing the same thread/looper. Multiple instances 1524 * of the same state machine will be created. They will all share the 1525 * same thread and thus each can update <code>sharedCounter</code> which 1526 * will be used to notify testStateMachineSharedThread that the test is 1527 * complete. 1528 */ 1529 class StateMachineSharedThread extends StateMachine { 1530 StateMachineSharedThread(String name, Looper looper, int maxCount) { 1531 super(name, looper); 1532 mMaxCount = maxCount; 1533 setDbg(DBG); 1534 1535 // Setup state machine with 1 state 1536 addState(mS1); 1537 1538 // Set the initial state 1539 setInitialState(mS1); 1540 } 1541 1542 class S1 extends State { 1543 @Override 1544 public boolean processMessage(Message message) { 1545 if (message.what == TEST_CMD_4) { 1546 transitionToHaltingState(); 1547 } 1548 return HANDLED; 1549 } 1550 } 1551 1552 @Override 1553 protected void onHalting() { 1554 // Update the shared counter, which is OK since all state 1555 // machines are using the same thread. 1556 sharedCounter += 1; 1557 if (sharedCounter == mMaxCount) { 1558 synchronized (waitObject) { 1559 waitObject.notifyAll(); 1560 } 1561 } 1562 } 1563 1564 private int mMaxCount; 1565 private S1 mS1 = new S1(); 1566 } 1567 private static int sharedCounter = 0; 1568 private static Object waitObject = new Object(); 1569 1570 @MediumTest 1571 public void testStateMachineSharedThread() throws Exception { 1572 if (DBG) Log.d(TAG, "testStateMachineSharedThread E"); 1573 1574 // Create and start the handler thread 1575 HandlerThread smThread = new HandlerThread("testStateMachineSharedThread"); 1576 smThread.start(); 1577 1578 // Create the state machines 1579 StateMachineSharedThread sms[] = new StateMachineSharedThread[10]; 1580 for (int i = 0; i < sms.length; i++) { 1581 sms[i] = new StateMachineSharedThread("sm", smThread.getLooper(), sms.length); 1582 sms[i].start(); 1583 } 1584 1585 synchronized (waitObject) { 1586 // Send messages to each of the state machines 1587 for (StateMachineSharedThread sm : sms) { 1588 for (int i = 1; i <= 4; i++) { 1589 sm.sendMessage(i); 1590 } 1591 } 1592 1593 // Wait for the last state machine to notify its done 1594 try { 1595 waitObject.wait(); 1596 } catch (InterruptedException e) { 1597 Log.e(TAG, "testStateMachineSharedThread: exception while waiting " 1598 + e.getMessage()); 1599 } 1600 } 1601 1602 for (StateMachineSharedThread sm : sms) { 1603 assertEquals(sm.getLogRecCount(), 4); 1604 for (int i = 0; i < sm.getLogRecCount(); i++) { 1605 LogRec lr = sm.getLogRec(i); 1606 assertEquals(i+1, lr.getWhat()); 1607 assertEquals(sm.mS1, lr.getState()); 1608 assertEquals(sm.mS1, lr.getOriginalState()); 1609 } 1610 } 1611 1612 if (DBG) Log.d(TAG, "testStateMachineSharedThread X"); 1613 } 1614 1615 @MediumTest 1616 public void testHsm1() throws Exception { 1617 if (DBG) Log.d(TAG, "testHsm1 E"); 1618 1619 Hsm1 sm = Hsm1.makeHsm1(); 1620 1621 // Send messages 1622 sm.sendMessage(Hsm1.CMD_1); 1623 sm.sendMessage(Hsm1.CMD_2); 1624 1625 synchronized (sm) { 1626 // Wait for the last state machine to notify its done 1627 try { 1628 sm.wait(); 1629 } catch (InterruptedException e) { 1630 Log.e(TAG, "testHsm1: exception while waiting " + e.getMessage()); 1631 } 1632 } 1633 1634 assertEquals(7, sm.getLogRecCount()); 1635 LogRec lr = sm.getLogRec(0); 1636 assertEquals(Hsm1.CMD_1, lr.getWhat()); 1637 assertEquals(sm.mS1, lr.getState()); 1638 assertEquals(sm.mS1, lr.getOriginalState()); 1639 1640 lr = sm.getLogRec(1); 1641 assertEquals(Hsm1.CMD_2, lr.getWhat()); 1642 assertEquals(sm.mP1, lr.getState()); 1643 assertEquals(sm.mS1, lr.getOriginalState()); 1644 1645 lr = sm.getLogRec(2); 1646 assertEquals(Hsm1.CMD_2, lr.getWhat()); 1647 assertEquals(sm.mS2, lr.getState()); 1648 assertEquals(sm.mS2, lr.getOriginalState()); 1649 1650 lr = sm.getLogRec(3); 1651 assertEquals(Hsm1.CMD_3, lr.getWhat()); 1652 assertEquals(sm.mS2, lr.getState()); 1653 assertEquals(sm.mS2, lr.getOriginalState()); 1654 1655 lr = sm.getLogRec(4); 1656 assertEquals(Hsm1.CMD_3, lr.getWhat()); 1657 assertEquals(sm.mP2, lr.getState()); 1658 assertEquals(sm.mP2, lr.getOriginalState()); 1659 1660 lr = sm.getLogRec(5); 1661 assertEquals(Hsm1.CMD_4, lr.getWhat()); 1662 assertEquals(sm.mP2, lr.getState()); 1663 assertEquals(sm.mP2, lr.getOriginalState()); 1664 1665 lr = sm.getLogRec(6); 1666 assertEquals(Hsm1.CMD_5, lr.getWhat()); 1667 assertEquals(sm.mP2, lr.getState()); 1668 assertEquals(sm.mP2, lr.getOriginalState()); 1669 1670 if (DBG) Log.d(TAG, "testStateMachineSharedThread X"); 1671 } 1672} 1673 1674class Hsm1 extends StateMachine { 1675 private static final String TAG = "hsm1"; 1676 1677 public static final int CMD_1 = 1; 1678 public static final int CMD_2 = 2; 1679 public static final int CMD_3 = 3; 1680 public static final int CMD_4 = 4; 1681 public static final int CMD_5 = 5; 1682 1683 public static Hsm1 makeHsm1() { 1684 Log.d(TAG, "makeHsm1 E"); 1685 Hsm1 sm = new Hsm1("hsm1"); 1686 sm.start(); 1687 Log.d(TAG, "makeHsm1 X"); 1688 return sm; 1689 } 1690 1691 Hsm1(String name) { 1692 super(name); 1693 Log.d(TAG, "ctor E"); 1694 1695 // Add states, use indentation to show hierarchy 1696 addState(mP1); 1697 addState(mS1, mP1); 1698 addState(mS2, mP1); 1699 addState(mP2); 1700 1701 // Set the initial state 1702 setInitialState(mS1); 1703 Log.d(TAG, "ctor X"); 1704 } 1705 1706 class P1 extends State { 1707 @Override 1708 public void enter() { 1709 Log.d(TAG, "P1.enter"); 1710 } 1711 @Override 1712 public void exit() { 1713 Log.d(TAG, "P1.exit"); 1714 } 1715 @Override 1716 public boolean processMessage(Message message) { 1717 boolean retVal; 1718 Log.d(TAG, "P1.processMessage what=" + message.what); 1719 switch(message.what) { 1720 case CMD_2: 1721 // CMD_2 will arrive in mS2 before CMD_3 1722 sendMessage(CMD_3); 1723 deferMessage(message); 1724 transitionTo(mS2); 1725 retVal = true; 1726 break; 1727 default: 1728 // Any message we don't understand in this state invokes unhandledMessage 1729 retVal = false; 1730 break; 1731 } 1732 return retVal; 1733 } 1734 } 1735 1736 class S1 extends State { 1737 @Override 1738 public void enter() { 1739 Log.d(TAG, "S1.enter"); 1740 } 1741 @Override 1742 public void exit() { 1743 Log.d(TAG, "S1.exit"); 1744 } 1745 @Override 1746 public boolean processMessage(Message message) { 1747 Log.d(TAG, "S1.processMessage what=" + message.what); 1748 if (message.what == CMD_1) { 1749 // Transition to ourself to show that enter/exit is called 1750 transitionTo(mS1); 1751 return HANDLED; 1752 } else { 1753 // Let parent process all other messages 1754 return NOT_HANDLED; 1755 } 1756 } 1757 } 1758 1759 class S2 extends State { 1760 @Override 1761 public void enter() { 1762 Log.d(TAG, "S2.enter"); 1763 } 1764 @Override 1765 public void exit() { 1766 Log.d(TAG, "S2.exit"); 1767 } 1768 @Override 1769 public boolean processMessage(Message message) { 1770 boolean retVal; 1771 Log.d(TAG, "S2.processMessage what=" + message.what); 1772 switch(message.what) { 1773 case(CMD_2): 1774 sendMessage(CMD_4); 1775 retVal = true; 1776 break; 1777 case(CMD_3): 1778 deferMessage(message); 1779 transitionTo(mP2); 1780 retVal = true; 1781 break; 1782 default: 1783 retVal = false; 1784 break; 1785 } 1786 return retVal; 1787 } 1788 } 1789 1790 class P2 extends State { 1791 @Override 1792 public void enter() { 1793 Log.d(TAG, "P2.enter"); 1794 sendMessage(CMD_5); 1795 } 1796 @Override 1797 public void exit() { 1798 Log.d(TAG, "P2.exit"); 1799 } 1800 @Override 1801 public boolean processMessage(Message message) { 1802 Log.d(TAG, "P2.processMessage what=" + message.what); 1803 switch(message.what) { 1804 case(CMD_3): 1805 break; 1806 case(CMD_4): 1807 break; 1808 case(CMD_5): 1809 transitionToHaltingState(); 1810 break; 1811 } 1812 return HANDLED; 1813 } 1814 } 1815 1816 @Override 1817 protected void onHalting() { 1818 Log.d(TAG, "halting"); 1819 synchronized (this) { 1820 this.notifyAll(); 1821 } 1822 } 1823 1824 P1 mP1 = new P1(); 1825 S1 mS1 = new S1(); 1826 S2 mS2 = new S2(); 1827 P2 mP2 = new P2(); 1828} 1829