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