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