Home | History | Annotate | Download | only in os
      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