/* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.am; import static android.app.ActivityManager.PROCESS_CAPABILITY_ALL; import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION; import static android.app.ActivityManager.PROCESS_CAPABILITY_NONE; import static android.app.ActivityManager.PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK; import static android.app.ActivityManager.PROCESS_STATE_CACHED_RECENT; import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE; import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND; import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND; import static android.app.ActivityManager.PROCESS_STATE_RECEIVER; import static android.app.ActivityManager.PROCESS_STATE_SERVICE; import static android.app.ActivityManager.PROCESS_STATE_TOP; import static com.android.server.am.ProcessList.UNKNOWN_ADJ; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import android.app.ActivityManager; import android.app.IUidObserver; import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; import android.util.DebugUtils; import android.util.Pair; import android.util.SparseArray; import androidx.test.filters.SmallTest; import com.android.server.am.UidObserverController.ChangeRecord; import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import java.util.ArrayList; @Presubmit @SmallTest public class UidObserverControllerTest { private static final int TEST_UID1 = 1111; private static final int TEST_UID2 = 2222; private static final int TEST_UID3 = 3333; private static final String TEST_PKG1 = "com.example1"; private static final String TEST_PKG2 = "com.example2"; private static final String TEST_PKG3 = "com.example3"; private UidObserverController mUidObserverController; @Before public void setUp() { MockitoAnnotations.initMocks(this); mUidObserverController = new UidObserverController(mock(Handler.class)); } @Test public void testEnqueueUidChange() { int change = mUidObserverController.enqueueUidChange(null, TEST_UID1, UidRecord.CHANGE_ACTIVE, PROCESS_STATE_FOREGROUND_SERVICE, UNKNOWN_ADJ, PROCESS_CAPABILITY_ALL, 0, false); assertEquals("expected=ACTIVE,actual=" + changeToStr(change), UidRecord.CHANGE_ACTIVE, change); assertPendingChange(TEST_UID1, UidRecord.CHANGE_ACTIVE, PROCESS_STATE_FOREGROUND_SERVICE, PROCESS_CAPABILITY_ALL, 0, false, null); final ChangeRecord record1 = getLatestPendingChange(TEST_UID1); assertNull(getLatestPendingChange(TEST_UID2)); final ChangeRecord record2 = new ChangeRecord(); change = mUidObserverController.enqueueUidChange(record2, TEST_UID2, UidRecord.CHANGE_CACHED, PROCESS_STATE_CACHED_RECENT, UNKNOWN_ADJ, PROCESS_CAPABILITY_NONE, 99, true); assertEquals("expected=ACTIVE,actual=" + changeToStr(change), UidRecord.CHANGE_CACHED, change); assertPendingChange(TEST_UID1, UidRecord.CHANGE_ACTIVE, PROCESS_STATE_FOREGROUND_SERVICE, PROCESS_CAPABILITY_ALL, 0, false, null); assertPendingChange(TEST_UID2, UidRecord.CHANGE_CACHED, PROCESS_STATE_CACHED_RECENT, PROCESS_CAPABILITY_NONE, 99, true, record2); change = mUidObserverController.enqueueUidChange(record1, TEST_UID1, UidRecord.CHANGE_UNCACHED, PROCESS_STATE_TOP, UNKNOWN_ADJ, PROCESS_CAPABILITY_ALL, 0, false); assertEquals("expected=ACTIVE|UNCACHED,actual=" + changeToStr(change), UidRecord.CHANGE_ACTIVE | UidRecord.CHANGE_UNCACHED, change); assertPendingChange(TEST_UID1, UidRecord.CHANGE_ACTIVE | UidRecord.CHANGE_UNCACHED, PROCESS_STATE_TOP, PROCESS_CAPABILITY_ALL, 0, false, record1); assertPendingChange(TEST_UID2, UidRecord.CHANGE_CACHED, PROCESS_STATE_CACHED_RECENT, PROCESS_CAPABILITY_NONE, 99, true, record2); } @Test public void testMergeWithPendingChange() { // Map of expectedChange -> {(currentChange, pendingChange)} final SparseArray> changesToVerify = new SparseArray<>(); changesToVerify.put(UidRecord.CHANGE_ACTIVE, Pair.create(UidRecord.CHANGE_ACTIVE, UidRecord.CHANGE_IDLE)); changesToVerify.put(UidRecord.CHANGE_IDLE, Pair.create(UidRecord.CHANGE_IDLE, UidRecord.CHANGE_ACTIVE)); changesToVerify.put(UidRecord.CHANGE_CACHED, Pair.create(UidRecord.CHANGE_CACHED, UidRecord.CHANGE_UNCACHED)); changesToVerify.put(UidRecord.CHANGE_UNCACHED, Pair.create(UidRecord.CHANGE_UNCACHED, UidRecord.CHANGE_CACHED)); changesToVerify.put(UidRecord.CHANGE_ACTIVE | UidRecord.CHANGE_UNCACHED, Pair.create(UidRecord.CHANGE_ACTIVE, UidRecord.CHANGE_UNCACHED)); changesToVerify.put(UidRecord.CHANGE_IDLE | UidRecord.CHANGE_CACHED, Pair.create(UidRecord.CHANGE_IDLE, UidRecord.CHANGE_CACHED)); changesToVerify.put(UidRecord.CHANGE_GONE, Pair.create(UidRecord.CHANGE_GONE, UidRecord.CHANGE_ACTIVE)); changesToVerify.put(UidRecord.CHANGE_GONE, Pair.create(UidRecord.CHANGE_GONE, UidRecord.CHANGE_CACHED)); changesToVerify.put(UidRecord.CHANGE_PROCSTATE | UidRecord.CHANGE_CAPABILITY, Pair.create(UidRecord.CHANGE_PROCSTATE, UidRecord.CHANGE_CAPABILITY)); for (int i = 0; i < changesToVerify.size(); ++i) { final int expectedChange = changesToVerify.keyAt(i); final int currentChange = changesToVerify.valueAt(i).first; final int pendingChange = changesToVerify.valueAt(i).second; assertEquals("current=" + changeToStr(currentChange) + ", pending=" + changeToStr(pendingChange) + "exp=" + changeToStr(expectedChange), expectedChange, UidObserverController.mergeWithPendingChange( currentChange, pendingChange)); } } @Test public void testDispatchUidsChanged() throws RemoteException { addPendingChange(TEST_UID1, UidRecord.CHANGE_ACTIVE | UidRecord.CHANGE_PROCSTATE, PROCESS_STATE_TOP, 0, PROCESS_CAPABILITY_ALL, false); final IUidObserver observer1 = mock(IUidObserver.Stub.class); registerObserver(observer1, ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_ACTIVE, PROCESS_STATE_IMPORTANT_FOREGROUND, TEST_PKG2, TEST_UID2); final IUidObserver observer2 = mock(IUidObserver.Stub.class); registerObserver(observer2, ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_CAPABILITY, PROCESS_STATE_SERVICE, TEST_PKG3, TEST_UID3); mUidObserverController.dispatchUidsChanged(); verify(observer1).onUidStateChanged(TEST_UID1, PROCESS_STATE_TOP, 0, PROCESS_CAPABILITY_ALL); verify(observer1).onUidActive(TEST_UID1); verifyNoMoreInteractions(observer1); verify(observer2).onUidStateChanged(TEST_UID1, PROCESS_STATE_TOP, 0, PROCESS_CAPABILITY_ALL); verifyNoMoreInteractions(observer2); addPendingChange(TEST_UID1, UidRecord.CHANGE_PROCSTATE, PROCESS_STATE_IMPORTANT_BACKGROUND, 99, PROCESS_CAPABILITY_FOREGROUND_LOCATION, false); mUidObserverController.dispatchUidsChanged(); verify(observer1).onUidStateChanged(TEST_UID1, PROCESS_STATE_IMPORTANT_BACKGROUND, 99, PROCESS_CAPABILITY_FOREGROUND_LOCATION); verifyNoMoreInteractions(observer1); verifyNoMoreInteractions(observer2); addPendingChange(TEST_UID1, UidRecord.CHANGE_PROCSTATE, PROCESS_STATE_RECEIVER, 111, PROCESS_CAPABILITY_NONE, false); mUidObserverController.dispatchUidsChanged(); verify(observer2).onUidStateChanged(TEST_UID1, PROCESS_STATE_RECEIVER, 111, PROCESS_CAPABILITY_NONE); verifyNoMoreInteractions(observer1); verifyNoMoreInteractions(observer2); addPendingChange(TEST_UID1, UidRecord.CHANGE_PROCSTATE | UidRecord.CHANGE_CAPABILITY, PROCESS_STATE_RECEIVER, 111, PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK, false); mUidObserverController.dispatchUidsChanged(); verify(observer2).onUidStateChanged(TEST_UID1, PROCESS_STATE_RECEIVER, 111, PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK); verifyNoMoreInteractions(observer1); verifyNoMoreInteractions(observer2); unregisterObserver(observer1); addPendingChange(TEST_UID1, UidRecord.CHANGE_PROCSTATE, PROCESS_STATE_TOP, 112, PROCESS_CAPABILITY_ALL, false); mUidObserverController.dispatchUidsChanged(); verify(observer2).onUidStateChanged(TEST_UID1, PROCESS_STATE_TOP, 112, PROCESS_CAPABILITY_ALL); verifyNoMoreInteractions(observer1); verifyNoMoreInteractions(observer2); unregisterObserver(observer2); addPendingChange(TEST_UID1, UidRecord.CHANGE_PROCSTATE, PROCESS_STATE_CACHED_RECENT, 112, PROCESS_CAPABILITY_NONE, false); mUidObserverController.dispatchUidsChanged(); verifyNoMoreInteractions(observer1); verifyNoMoreInteractions(observer2); } private void registerObserver(IUidObserver observer, int which, int cutpoint, String callingPackage, int callingUid) { when(observer.asBinder()).thenReturn((IBinder) observer); mUidObserverController.register(observer, which, cutpoint, callingPackage, callingUid, /*uids*/null); Mockito.reset(observer); } private void unregisterObserver(IUidObserver observer) { when(observer.asBinder()).thenReturn((IBinder) observer); mUidObserverController.unregister(observer); Mockito.reset(observer); } private void addPendingChange(int uid, int change, int procState, long procStateSeq, int capability, boolean ephemeral) { final ChangeRecord record = new ChangeRecord(); record.uid = uid; record.change = change; record.procState = procState; record.procStateSeq = procStateSeq; record.capability = capability; record.ephemeral = ephemeral; mUidObserverController.getPendingUidChangesForTest().add(record); } private void assertPendingChange(int uid, int change, int procState, long procStateSeq, int capability, boolean ephemeral, ChangeRecord expectedRecord) { final ChangeRecord record = getLatestPendingChange(uid); assertNotNull(record); if (expectedRecord != null) { assertEquals(expectedRecord, record); } assertEquals(change, record.change); assertEquals(procState, record.procState); assertEquals(procStateSeq, record.procStateSeq); assertEquals(capability, record.capability); assertEquals(ephemeral, record.ephemeral); } private ChangeRecord getLatestPendingChange(int uid) { final ArrayList changeRecords = mUidObserverController .getPendingUidChangesForTest(); for (int i = changeRecords.size() - 1; i >= 0; --i) { final ChangeRecord record = changeRecords.get(i); if (record.uid == uid) { return record; } } return null; } private static String changeToStr(int change) { return DebugUtils.flagsToString(UidRecord.class, "CHANGE_", change); } }