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