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