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