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