ActivityManagerServiceTest.java revision 51ab3acf270c655ed90706895b43915433d022c7
1/*
2 * Copyright (C) 2017 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
17package com.android.server.am;
18
19import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
20import static android.app.ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
21import static android.app.ActivityManager.PROCESS_STATE_CACHED_EMPTY;
22import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
23import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND;
24import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
25import static android.app.ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
26import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
27import static android.app.ActivityManager.PROCESS_STATE_RECEIVER;
28import static android.app.ActivityManager.PROCESS_STATE_SERVICE;
29import static android.app.ActivityManager.PROCESS_STATE_TOP;
30import static android.util.DebugUtils.valueToString;
31import static com.android.server.am.ActivityManagerService.DISPATCH_UIDS_CHANGED_UI_MSG;
32import static com.android.server.am.ActivityManagerService.Injector;
33
34import static org.junit.Assert.assertEquals;
35import static org.junit.Assert.assertFalse;
36import static org.junit.Assert.assertNotEquals;
37import static org.junit.Assert.assertNotNull;
38import static org.junit.Assert.assertNull;
39import static org.junit.Assert.assertTrue;
40import static org.junit.Assert.fail;
41import static org.mockito.Mockito.verify;
42import static org.mockito.Mockito.verifyNoMoreInteractions;
43import static org.mockito.Mockito.when;
44
45import android.app.ActivityManager;
46import android.app.AppOpsManager;
47import android.app.IUidObserver;
48import android.os.Handler;
49import android.os.HandlerThread;
50import android.os.IBinder;
51import android.os.Looper;
52import android.os.Message;
53import android.os.Process;
54import android.os.RemoteException;
55import android.os.SystemClock;
56import android.support.test.filters.MediumTest;
57import android.support.test.filters.SmallTest;
58import android.support.test.runner.AndroidJUnit4;
59
60import com.android.server.AppOpsService;
61
62import org.junit.After;
63import org.junit.Before;
64import org.junit.Test;
65import org.junit.runner.RunWith;
66import org.mockito.Mock;
67import org.mockito.Mockito;
68import org.mockito.MockitoAnnotations;
69
70import java.util.ArrayList;
71import java.util.HashMap;
72import java.util.HashSet;
73import java.util.Map;
74import java.util.Set;
75import java.util.function.Function;
76
77/**
78 * Test class for {@link ActivityManagerService}.
79 *
80 * To run the tests, use
81 *
82 * runtest -c com.android.server.am.ActivityManagerServiceTest frameworks-services
83 *
84 * or the following steps:
85 *
86 * Build: m FrameworksServicesTests
87 * Install: adb install -r \
88 *     ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk
89 * Run: adb shell am instrument -e class com.android.server.am.ActivityManagerServiceTest -w \
90 *     com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
91 */
92@SmallTest
93@RunWith(AndroidJUnit4.class)
94public class ActivityManagerServiceTest {
95    private static final String TAG = ActivityManagerServiceTest.class.getSimpleName();
96
97    private static final int TEST_UID = 111;
98
99    private static final long TEST_PROC_STATE_SEQ1 = 555;
100    private static final long TEST_PROC_STATE_SEQ2 = 556;
101
102    private static final int[] UID_RECORD_CHANGES = {
103        UidRecord.CHANGE_PROCSTATE,
104        UidRecord.CHANGE_GONE,
105        UidRecord.CHANGE_GONE_IDLE,
106        UidRecord.CHANGE_IDLE,
107        UidRecord.CHANGE_ACTIVE
108    };
109
110    @Mock private AppOpsService mAppOpsService;
111
112    private ActivityManagerService mAms;
113    private HandlerThread mHandlerThread;
114    private TestHandler mHandler;
115
116    @Before
117    public void setUp() {
118        MockitoAnnotations.initMocks(this);
119
120        mHandlerThread = new HandlerThread(TAG);
121        mHandlerThread.start();
122        mHandler = new TestHandler(mHandlerThread.getLooper());
123        mAms = new ActivityManagerService(new TestInjector());
124    }
125
126    @After
127    public void tearDown() {
128        mHandlerThread.quit();
129    }
130
131    @Test
132    public void testIncrementProcStateSeqIfNeeded() {
133        final UidRecord uidRec = new UidRecord(TEST_UID);
134
135        assertEquals("Initially global seq counter should be 0", 0, mAms.mProcStateSeqCounter);
136        assertEquals("Initially seq counter in uidRecord should be 0", 0, uidRec.curProcStateSeq);
137
138        // Uid state is not moving from background to foreground or vice versa.
139        uidRec.setProcState = PROCESS_STATE_TOP;
140        uidRec.curProcState = PROCESS_STATE_TOP;
141        mAms.incrementProcStateSeqIfNeeded(uidRec);
142        assertEquals(0, mAms.mProcStateSeqCounter);
143        assertEquals(0, uidRec.curProcStateSeq);
144
145        // Uid state is moving from foreground to background.
146        uidRec.curProcState = PROCESS_STATE_FOREGROUND_SERVICE;
147        uidRec.setProcState = PROCESS_STATE_SERVICE;
148        mAms.incrementProcStateSeqIfNeeded(uidRec);
149        assertEquals(1, mAms.mProcStateSeqCounter);
150        assertEquals(1, uidRec.curProcStateSeq);
151
152        // Explicitly setting the seq counter for more verification.
153        mAms.mProcStateSeqCounter = 42;
154
155        // Uid state is not moving from background to foreground or vice versa.
156        uidRec.setProcState = PROCESS_STATE_IMPORTANT_BACKGROUND;
157        uidRec.curProcState = PROCESS_STATE_IMPORTANT_FOREGROUND;
158        mAms.incrementProcStateSeqIfNeeded(uidRec);
159        assertEquals(42, mAms.mProcStateSeqCounter);
160        assertEquals(1, uidRec.curProcStateSeq);
161
162        // Uid state is moving from background to foreground.
163        uidRec.setProcState = PROCESS_STATE_LAST_ACTIVITY;
164        uidRec.curProcState = PROCESS_STATE_TOP;
165        mAms.incrementProcStateSeqIfNeeded(uidRec);
166        assertEquals(43, mAms.mProcStateSeqCounter);
167        assertEquals(43, uidRec.curProcStateSeq);
168    }
169
170    @Test
171    public void testShouldIncrementProcStateSeq() {
172        final UidRecord uidRec = new UidRecord(TEST_UID);
173
174        final String error1 = "Seq should be incremented: prevState: %s, curState: %s";
175        final String error2 = "Seq should not be incremented: prevState: %s, curState: %s";
176        Function<String, String> errorMsg = errorTemplate -> {
177            return String.format(errorTemplate,
178                    valueToString(ActivityManager.class, "PROCESS_STATE_", uidRec.setProcState),
179                    valueToString(ActivityManager.class, "PROCESS_STATE_", uidRec.curProcState));
180        };
181
182        // No change in uid state
183        uidRec.setProcState = PROCESS_STATE_RECEIVER;
184        uidRec.curProcState = PROCESS_STATE_RECEIVER;
185        assertFalse(errorMsg.apply(error2), mAms.shouldIncrementProcStateSeq(uidRec));
186
187        // Foreground to foreground
188        uidRec.setProcState = PROCESS_STATE_FOREGROUND_SERVICE;
189        uidRec.curProcState = PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
190        assertFalse(errorMsg.apply(error2), mAms.shouldIncrementProcStateSeq(uidRec));
191
192        // Background to background
193        uidRec.setProcState = PROCESS_STATE_CACHED_ACTIVITY;
194        uidRec.curProcState = PROCESS_STATE_CACHED_EMPTY;
195        assertFalse(errorMsg.apply(error2), mAms.shouldIncrementProcStateSeq(uidRec));
196
197        // Background to background
198        uidRec.setProcState = PROCESS_STATE_NONEXISTENT;
199        uidRec.curProcState = PROCESS_STATE_CACHED_ACTIVITY;
200        assertFalse(errorMsg.apply(error2), mAms.shouldIncrementProcStateSeq(uidRec));
201
202        // Background to foreground
203        uidRec.setProcState = PROCESS_STATE_SERVICE;
204        uidRec.curProcState = PROCESS_STATE_FOREGROUND_SERVICE;
205        assertTrue(errorMsg.apply(error1), mAms.shouldIncrementProcStateSeq(uidRec));
206
207        // Foreground to background
208        uidRec.setProcState = PROCESS_STATE_TOP;
209        uidRec.curProcState = PROCESS_STATE_LAST_ACTIVITY;
210        assertTrue(errorMsg.apply(error1), mAms.shouldIncrementProcStateSeq(uidRec));
211    }
212
213    /**
214     * This test verifies that process state changes are dispatched to observers based on the
215     * changes they wanted to listen (this is specified when registering the observer).
216     */
217    @Test
218    public void testDispatchUids_dispatchNeededChanges() throws RemoteException {
219        when(mAppOpsService.checkOperation(AppOpsManager.OP_GET_USAGE_STATS, Process.myUid(), null))
220                .thenReturn(AppOpsManager.MODE_ALLOWED);
221
222        final int[] changesToObserve = {
223            ActivityManager.UID_OBSERVER_PROCSTATE,
224            ActivityManager.UID_OBSERVER_GONE,
225            ActivityManager.UID_OBSERVER_IDLE,
226            ActivityManager.UID_OBSERVER_ACTIVE,
227            ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE
228                    | ActivityManager.UID_OBSERVER_ACTIVE | ActivityManager.UID_OBSERVER_IDLE
229        };
230        final IUidObserver[] observers = new IUidObserver.Stub[changesToObserve.length];
231        for (int i = 0; i < observers.length; ++i) {
232            observers[i] = Mockito.mock(IUidObserver.Stub.class);
233            when(observers[i].asBinder()).thenReturn((IBinder) observers[i]);
234            mAms.registerUidObserver(observers[i], changesToObserve[i] /* which */,
235                    ActivityManager.PROCESS_STATE_UNKNOWN /* cutpoint */, null /* caller */);
236
237            // When we invoke AMS.registerUidObserver, there are some interactions with observers[i]
238            // mock in RemoteCallbackList class. We don't want to test those interactions and
239            // at the same time, we don't want those to interfere with verifyNoMoreInteractions.
240            // So, resetting the mock here.
241            Mockito.reset(observers[i]);
242        }
243
244        // Add pending uid records each corresponding to a different change type UidRecord.CHANGE_*
245        final int[] changesForPendingUidRecords = UID_RECORD_CHANGES;
246
247        final int[] procStatesForPendingUidRecords = {
248            ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE,
249            ActivityManager.PROCESS_STATE_NONEXISTENT,
250            ActivityManager.PROCESS_STATE_CACHED_EMPTY,
251            ActivityManager.PROCESS_STATE_CACHED_ACTIVITY,
252            ActivityManager.PROCESS_STATE_TOP
253        };
254        final Map<Integer, UidRecord.ChangeItem> changeItems = new HashMap<>();
255        for (int i = 0; i < changesForPendingUidRecords.length; ++i) {
256            final UidRecord.ChangeItem pendingChange = new UidRecord.ChangeItem();
257            pendingChange.change = changesForPendingUidRecords[i];
258            pendingChange.uid = i;
259            pendingChange.processState = procStatesForPendingUidRecords[i];
260            pendingChange.procStateSeq = i;
261            changeItems.put(changesForPendingUidRecords[i], pendingChange);
262            mAms.mPendingUidChanges.add(pendingChange);
263        }
264
265        mAms.dispatchUidsChanged();
266        // Verify the required changes have been dispatched to observers.
267        for (int i = 0; i < observers.length; ++i) {
268            final int changeToObserve = changesToObserve[i];
269            final IUidObserver observerToTest = observers[i];
270            if ((changeToObserve & ActivityManager.UID_OBSERVER_IDLE) != 0) {
271                // Observer listens to uid idle changes, so change items corresponding to
272                // UidRecord.CHANGE_IDLE or UidRecord.CHANGE_IDLE_GONE needs to be
273                // delivered to this observer.
274                final int[] changesToVerify = {
275                    UidRecord.CHANGE_IDLE,
276                    UidRecord.CHANGE_GONE_IDLE
277                };
278                verifyObserverReceivedChanges(observerToTest, changesToVerify, changeItems,
279                        (observer, changeItem) -> {
280                            verify(observer).onUidIdle(changeItem.uid, changeItem.ephemeral);
281                        });
282            }
283            if ((changeToObserve & ActivityManager.UID_OBSERVER_ACTIVE) != 0) {
284                // Observer listens to uid active changes, so change items corresponding to
285                // UidRecord.CHANGE_ACTIVE needs to be delivered to this observer.
286                final int[] changesToVerify = { UidRecord.CHANGE_ACTIVE };
287                verifyObserverReceivedChanges(observerToTest, changesToVerify, changeItems,
288                        (observer, changeItem) -> {
289                            verify(observer).onUidActive(changeItem.uid);
290                        });
291            }
292            if ((changeToObserve & ActivityManager.UID_OBSERVER_GONE) != 0) {
293                // Observer listens to uid gone changes, so change items corresponding to
294                // UidRecord.CHANGE_GONE or UidRecord.CHANGE_IDLE_GONE needs to be
295                // delivered to this observer.
296                final int[] changesToVerify = {
297                        UidRecord.CHANGE_GONE,
298                        UidRecord.CHANGE_GONE_IDLE
299                };
300                verifyObserverReceivedChanges(observerToTest, changesToVerify, changeItems,
301                        (observer, changeItem) -> {
302                            verify(observer).onUidGone(changeItem.uid, changeItem.ephemeral);
303                        });
304            }
305            if ((changeToObserve & ActivityManager.UID_OBSERVER_PROCSTATE) != 0) {
306                // Observer listens to uid procState changes, so change items corresponding to
307                // UidRecord.CHANGE_PROCSTATE or UidRecord.CHANGE_IDLE or UidRecord.CHANGE_ACTIVE
308                // needs to be delivered to this observer.
309                final int[] changesToVerify = {
310                        UidRecord.CHANGE_PROCSTATE,
311                        UidRecord.CHANGE_ACTIVE,
312                        UidRecord.CHANGE_IDLE
313                };
314                verifyObserverReceivedChanges(observerToTest, changesToVerify, changeItems,
315                        (observer, changeItem) -> {
316                            verify(observer).onUidStateChanged(changeItem.uid,
317                                    changeItem.processState, changeItem.procStateSeq);
318                        });
319            }
320            // Verify there are no other callbacks for this observer.
321            verifyNoMoreInteractions(observerToTest);
322        }
323    }
324
325    private interface ObserverChangesVerifier {
326        void verify(IUidObserver observer, UidRecord.ChangeItem changeItem) throws RemoteException;
327    }
328
329    private void verifyObserverReceivedChanges(IUidObserver observer, int[] changesToVerify,
330            Map<Integer, UidRecord.ChangeItem> changeItems, ObserverChangesVerifier verifier)
331            throws RemoteException {
332        for (int change : changesToVerify) {
333            final UidRecord.ChangeItem changeItem = changeItems.get(change);
334            verifier.verify(observer, changeItem);
335        }
336    }
337
338    /**
339     * This test verifies that process state changes are dispatched to observers only when they
340     * change across the cutpoint (this is specified when registering the observer).
341     */
342    @Test
343    public void testDispatchUidChanges_procStateCutpoint() throws RemoteException {
344        final IUidObserver observer = Mockito.mock(IUidObserver.Stub.class);
345
346        when(observer.asBinder()).thenReturn((IBinder) observer);
347        mAms.registerUidObserver(observer, ActivityManager.UID_OBSERVER_PROCSTATE /* which */,
348                ActivityManager.PROCESS_STATE_SERVICE /* cutpoint */, null /* callingPackage */);
349        // When we invoke AMS.registerUidObserver, there are some interactions with observer
350        // mock in RemoteCallbackList class. We don't want to test those interactions and
351        // at the same time, we don't want those to interfere with verifyNoMoreInteractions.
352        // So, resetting the mock here.
353        Mockito.reset(observer);
354
355        final UidRecord.ChangeItem changeItem = new UidRecord.ChangeItem();
356        changeItem.uid = TEST_UID;
357        changeItem.change = UidRecord.CHANGE_PROCSTATE;
358        changeItem.processState = ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
359        changeItem.procStateSeq = 111;
360        mAms.mPendingUidChanges.add(changeItem);
361        mAms.dispatchUidsChanged();
362        // First process state message is always delivered regardless of whether the process state
363        // change is above or below the cutpoint (PROCESS_STATE_SERVICE).
364        verify(observer).onUidStateChanged(TEST_UID,
365                changeItem.processState, changeItem.procStateSeq);
366        verifyNoMoreInteractions(observer);
367
368        changeItem.processState = ActivityManager.PROCESS_STATE_RECEIVER;
369        mAms.mPendingUidChanges.add(changeItem);
370        mAms.dispatchUidsChanged();
371        // Previous process state change is below cutpoint (PROCESS_STATE_SERVICE) and
372        // the current process state change is also below cutpoint, so no callback will be invoked.
373        verifyNoMoreInteractions(observer);
374
375        changeItem.processState = ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
376        mAms.mPendingUidChanges.add(changeItem);
377        mAms.dispatchUidsChanged();
378        // Previous process state change is below cutpoint (PROCESS_STATE_SERVICE) and
379        // the current process state change is above cutpoint, so callback will be invoked with the
380        // current process state change.
381        verify(observer).onUidStateChanged(TEST_UID,
382                changeItem.processState, changeItem.procStateSeq);
383        verifyNoMoreInteractions(observer);
384
385        changeItem.processState = ActivityManager.PROCESS_STATE_TOP;
386        mAms.mPendingUidChanges.add(changeItem);
387        mAms.dispatchUidsChanged();
388        // Previous process state change is above cutpoint (PROCESS_STATE_SERVICE) and
389        // the current process state change is also above cutpoint, so no callback will be invoked.
390        verifyNoMoreInteractions(observer);
391
392        changeItem.processState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
393        mAms.mPendingUidChanges.add(changeItem);
394        mAms.dispatchUidsChanged();
395        // Previous process state change is above cutpoint (PROCESS_STATE_SERVICE) and
396        // the current process state change is below cutpoint, so callback will be invoked with the
397        // current process state change.
398        verify(observer).onUidStateChanged(TEST_UID,
399                changeItem.processState, changeItem.procStateSeq);
400        verifyNoMoreInteractions(observer);
401    }
402
403    /**
404     * This test verifies that {@link ActivityManagerService#mValidateUids} which is a
405     * part of dumpsys is correctly updated.
406     */
407    @Test
408    public void testDispatchUidChanges_validateUidsUpdated() {
409        final int[] changesForPendingItems = UID_RECORD_CHANGES;
410
411        final int[] procStatesForPendingItems = {
412            ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE,
413            ActivityManager.PROCESS_STATE_CACHED_EMPTY,
414            ActivityManager.PROCESS_STATE_CACHED_ACTIVITY,
415            ActivityManager.PROCESS_STATE_SERVICE,
416            ActivityManager.PROCESS_STATE_RECEIVER
417        };
418        final ArrayList<UidRecord.ChangeItem> pendingItemsForUids
419                = new ArrayList<>(changesForPendingItems.length);
420        for (int i = 0; i < changesForPendingItems.length; ++i) {
421            final UidRecord.ChangeItem item = new UidRecord.ChangeItem();
422            item.uid = i;
423            item.change = changesForPendingItems[i];
424            item.processState = procStatesForPendingItems[i];
425            pendingItemsForUids.add(i, item);
426        }
427
428        // Verify that when there no observers listening to uid state changes, then there will
429        // be no changes to validateUids.
430        mAms.mPendingUidChanges.addAll(pendingItemsForUids);
431        mAms.dispatchUidsChanged();
432        assertEquals("No observers registered, so validateUids should be empty",
433                0, mAms.mValidateUids.size());
434
435        final IUidObserver observer = Mockito.mock(IUidObserver.Stub.class);
436        when(observer.asBinder()).thenReturn((IBinder) observer);
437        mAms.registerUidObserver(observer, 0, 0, null);
438        // Verify that when observers are registered, then validateUids is correctly updated.
439        mAms.mPendingUidChanges.addAll(pendingItemsForUids);
440        mAms.dispatchUidsChanged();
441        for (int i = 0; i < pendingItemsForUids.size(); ++i) {
442            final UidRecord.ChangeItem item = pendingItemsForUids.get(i);
443            final UidRecord validateUidRecord = mAms.mValidateUids.get(item.uid);
444            if (item.change == UidRecord.CHANGE_GONE || item.change == UidRecord.CHANGE_GONE_IDLE) {
445                assertNull("validateUidRecord should be null since the change is either "
446                        + "CHANGE_GONE or CHANGE_GONE_IDLE", validateUidRecord);
447            } else {
448                assertNotNull("validateUidRecord should not be null since the change is neither "
449                        + "CHANGE_GONE nor CHANGE_GONE_IDLE", validateUidRecord);
450                assertEquals("processState: " + item.processState + " curProcState: "
451                        + validateUidRecord.curProcState + " should have been equal",
452                        item.processState, validateUidRecord.curProcState);
453                assertEquals("processState: " + item.processState + " setProcState: "
454                        + validateUidRecord.curProcState + " should have been equal",
455                        item.processState, validateUidRecord.setProcState);
456                if (item.change == UidRecord.CHANGE_IDLE) {
457                    assertTrue("UidRecord.idle should be updated to true for CHANGE_IDLE",
458                            validateUidRecord.idle);
459                } else if (item.change == UidRecord.CHANGE_ACTIVE) {
460                    assertFalse("UidRecord.idle should be updated to false for CHANGE_ACTIVE",
461                            validateUidRecord.idle);
462                }
463            }
464        }
465
466        // Verify that when uid state changes to CHANGE_GONE or CHANGE_GONE_IDLE, then it
467        // will be removed from validateUids.
468        assertNotEquals("validateUids should not be empty", 0, mAms.mValidateUids.size());
469        for (int i = 0; i < pendingItemsForUids.size(); ++i) {
470            final UidRecord.ChangeItem item = pendingItemsForUids.get(i);
471            // Assign CHANGE_GONE_IDLE to some items and CHANGE_GONE to the others, using even/odd
472            // distribution for this assignment.
473            item.change = (i % 2) == 0 ? UidRecord.CHANGE_GONE_IDLE : UidRecord.CHANGE_GONE;
474        }
475        mAms.mPendingUidChanges.addAll(pendingItemsForUids);
476        mAms.dispatchUidsChanged();
477        assertEquals("validateUids should be empty, validateUids: " + mAms.mValidateUids,
478                0, mAms.mValidateUids.size());
479    }
480
481    @Test
482    public void testEnqueueUidChangeLocked_procStateSeqUpdated() {
483        final UidRecord uidRecord = new UidRecord(TEST_UID);
484        uidRecord.curProcStateSeq = TEST_PROC_STATE_SEQ1;
485
486        // Verify with no pending changes for TEST_UID.
487        verifyLastProcStateSeqUpdated(uidRecord, -1, TEST_PROC_STATE_SEQ1);
488
489        // Add a pending change for TEST_UID and verify enqueueUidChangeLocked still works as
490        // expected.
491        final UidRecord.ChangeItem changeItem = new UidRecord.ChangeItem();
492        uidRecord.pendingChange = changeItem;
493        uidRecord.curProcStateSeq = TEST_PROC_STATE_SEQ2;
494        verifyLastProcStateSeqUpdated(uidRecord, -1, TEST_PROC_STATE_SEQ2);
495
496        // Use "null" uidRecord to make sure there is no crash.
497        // TODO: currently it crashes, uncomment after fixing it.
498        // mAms.enqueueUidChangeLocked(null, TEST_UID, UidRecord.CHANGE_ACTIVE);
499    }
500
501    private void verifyLastProcStateSeqUpdated(UidRecord uidRecord, int uid, long curProcstateSeq) {
502        // Test enqueueUidChangeLocked with every UidRecord.CHANGE_*
503        for (int i = 0; i < UID_RECORD_CHANGES.length; ++i) {
504            final int changeToDispatch = UID_RECORD_CHANGES[i];
505            // Reset lastProcStateSeqDispatchToObservers after every test.
506            uidRecord.lastDispatchedProcStateSeq = 0;
507            mAms.enqueueUidChangeLocked(uidRecord, uid, changeToDispatch);
508            // Verify there is no effect on curProcStateSeq.
509            assertEquals(curProcstateSeq, uidRecord.curProcStateSeq);
510            if (changeToDispatch == UidRecord.CHANGE_GONE
511                    || changeToDispatch == UidRecord.CHANGE_GONE_IDLE) {
512                // Since the change is CHANGE_GONE or CHANGE_GONE_IDLE, verify that
513                // lastProcStateSeqDispatchedToObservers is not updated.
514                assertNotEquals(uidRecord.curProcStateSeq,
515                        uidRecord.lastDispatchedProcStateSeq);
516            } else {
517                // Since the change is neither CHANGE_GONE nor CHANGE_GONE_IDLE, verify that
518                // lastProcStateSeqDispatchedToObservers has been updated to curProcStateSeq.
519                assertEquals(uidRecord.curProcStateSeq,
520                        uidRecord.lastDispatchedProcStateSeq);
521            }
522        }
523    }
524
525    @MediumTest
526    @Test
527    public void testEnqueueUidChangeLocked_dispatchUidsChanged() {
528        final UidRecord uidRecord = new UidRecord(TEST_UID);
529        final int expectedProcState = PROCESS_STATE_SERVICE;
530        uidRecord.setProcState = expectedProcState;
531        uidRecord.curProcStateSeq = TEST_PROC_STATE_SEQ1;
532
533        // Test with no pending uid records.
534        for (int i = 0; i < UID_RECORD_CHANGES.length; ++i) {
535            final int changeToDispatch = UID_RECORD_CHANGES[i];
536
537            // Reset the current state
538            mHandler.reset();
539            uidRecord.pendingChange = null;
540            mAms.mPendingUidChanges.clear();
541
542            mAms.enqueueUidChangeLocked(uidRecord, -1, changeToDispatch);
543
544            // Verify that UidRecord.pendingChange is updated correctly.
545            assertNotNull(uidRecord.pendingChange);
546            assertEquals(TEST_UID, uidRecord.pendingChange.uid);
547            assertEquals(expectedProcState, uidRecord.pendingChange.processState);
548            assertEquals(TEST_PROC_STATE_SEQ1, uidRecord.pendingChange.procStateSeq);
549
550            // Verify that DISPATCH_UIDS_CHANGED_UI_MSG is posted to handler.
551            mHandler.waitForMessage(DISPATCH_UIDS_CHANGED_UI_MSG);
552        }
553    }
554
555    private class TestHandler extends Handler {
556        private static final long WAIT_FOR_MSG_TIMEOUT_MS = 4000; // 4 sec
557        private static final long WAIT_FOR_MSG_INTERVAL_MS = 400; // 0.4 sec
558
559        private Set<Integer> mMsgsHandled = new HashSet<>();
560
561        TestHandler(Looper looper) {
562            super(looper);
563        }
564
565        @Override
566        public void handleMessage(Message msg) {
567            mMsgsHandled.add(msg.what);
568        }
569
570        public void waitForMessage(int msg) {
571            final long endTime = System.currentTimeMillis() + WAIT_FOR_MSG_TIMEOUT_MS;
572            while (!mMsgsHandled.contains(msg) && System.currentTimeMillis() < endTime) {
573                SystemClock.sleep(WAIT_FOR_MSG_INTERVAL_MS);
574            }
575            if (!mMsgsHandled.contains(msg)) {
576                fail("Timed out waiting for the message to be handled, msg: " + msg);
577            }
578        }
579
580        public void reset() {
581            mMsgsHandled.clear();
582        }
583    }
584
585    private class TestInjector implements Injector {
586        @Override
587        public AppOpsService getAppOpsService() {
588            return mAppOpsService;
589        }
590
591        @Override
592        public Handler getHandler() {
593            return mHandler;
594        }
595    }
596}