OSDN Git Service

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