From c3ea28b213f8e33b2731ae01be9052cdcf2bd460 Mon Sep 17 00:00:00 2001 From: Evan Severson Date: Thu, 15 Sep 2022 12:46:00 -0700 Subject: [PATCH] Fix a couple issues with AppOpsUidStateTrackerImpl 1. When a process goes to NONEXSISTENT we should dispatch the uid state change if necessary 2. Dispatch the new UID state on changes, not the last one (how did this not break anything?!) 3. Fix issue in logic for detecting change between fg/bg uid states. This probably didn't break anything since all appops that currently care about foregroundness today also have capability that also changes. Test: CtsAppOpsTestCases, CtsAppOps2TestCases, AppOpUidStateTrackerTest Bug: 241447638 Change-Id: Ib1b93148669ee04d7c16195540906b388550ee68 --- .../appop/AppOpsUidStateTrackerImpl.java | 33 +- .../appop/AppOpsUidStateTrackerTest.java | 380 +++++++++++++++++- 2 files changed, 388 insertions(+), 25 deletions(-) diff --git a/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java b/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java index a308c813ea454..ca5bfb36723cd 100644 --- a/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java +++ b/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java @@ -66,6 +66,7 @@ class AppOpsUidStateTrackerImpl implements AppOpsUidStateTracker { private SparseBooleanArray mVisibleAppWidget = new SparseBooleanArray(); private SparseBooleanArray mPendingVisibleAppWidget = new SparseBooleanArray(); private SparseLongArray mPendingCommitTime = new SparseLongArray(); + private SparseBooleanArray mPendingGone = new SparseBooleanArray(); private ArrayMap mUidStateChangedCallbacks = new ArrayMap<>(); @@ -185,16 +186,6 @@ class AppOpsUidStateTrackerImpl implements AppOpsUidStateTracker { @Override public void updateUidProcState(int uid, int procState, int capability) { mEventLog.logUpdateUidProcState(uid, procState, capability); - if (procState == PROCESS_STATE_NONEXISTENT) { - mUidStates.delete(uid); - mPendingUidStates.delete(uid); - mCapability.delete(uid); - mPendingCapability.delete(uid); - mVisibleAppWidget.delete(uid); - mPendingVisibleAppWidget.delete(uid); - mPendingCommitTime.delete(uid); - return; - } int uidState = processStateToUidState(procState); @@ -205,7 +196,10 @@ class AppOpsUidStateTrackerImpl implements AppOpsUidStateTracker { mPendingUidStates.put(uid, uidState); mPendingCapability.put(uid, capability); - if (uidState < prevUidState + if (procState == PROCESS_STATE_NONEXISTENT) { + mPendingGone.put(uid, true); + commitUidPendingState(uid); + } else if (uidState < prevUidState || (uidState <= UID_STATE_MAX_LAST_NON_RESTRICTED && prevUidState > UID_STATE_MAX_LAST_NON_RESTRICTED)) { // We are moving to a more important state, or the new state may be in the @@ -312,7 +306,7 @@ class AppOpsUidStateTrackerImpl implements AppOpsUidStateTracker { || capability != pendingCapability || visibleAppWidget != pendingVisibleAppWidget) { boolean foregroundChange = uidState <= UID_STATE_MAX_LAST_NON_RESTRICTED - != pendingUidState > UID_STATE_MAX_LAST_NON_RESTRICTED + != pendingUidState <= UID_STATE_MAX_LAST_NON_RESTRICTED || capability != pendingCapability || visibleAppWidget != pendingVisibleAppWidget; @@ -327,13 +321,20 @@ class AppOpsUidStateTrackerImpl implements AppOpsUidStateTracker { Handler h = mUidStateChangedCallbacks.valueAt(i); h.sendMessage(PooledLambda.obtainMessage(UidStateChangedCallback::onUidStateChanged, - cb, uid, uidState, foregroundChange)); + cb, uid, pendingUidState, foregroundChange)); } } - mUidStates.put(uid, pendingUidState); - mCapability.put(uid, pendingCapability); - mVisibleAppWidget.put(uid, pendingVisibleAppWidget); + if (mPendingGone.get(uid, false)) { + mUidStates.delete(uid); + mCapability.delete(uid); + mVisibleAppWidget.delete(uid); + mPendingGone.delete(uid); + } else { + mUidStates.put(uid, pendingUidState); + mCapability.put(uid, pendingCapability); + mVisibleAppWidget.put(uid, pendingVisibleAppWidget); + } mPendingUidStates.delete(uid); mPendingCapability.delete(uid); diff --git a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUidStateTrackerTest.java b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUidStateTrackerTest.java index 3bf2db2062174..86e1264756543 100644 --- a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUidStateTrackerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUidStateTrackerTest.java @@ -24,11 +24,26 @@ import static android.app.AppOpsManager.OP_COARSE_LOCATION; import static android.app.AppOpsManager.OP_FINE_LOCATION; import static android.app.AppOpsManager.OP_RECORD_AUDIO; import static android.app.AppOpsManager.OP_WIFI_SCAN; +import static android.app.AppOpsManager.UID_STATE_BACKGROUND; +import static android.app.AppOpsManager.UID_STATE_CACHED; +import static android.app.AppOpsManager.UID_STATE_FOREGROUND; +import static android.app.AppOpsManager.UID_STATE_FOREGROUND_SERVICE; +import static android.app.AppOpsManager.UID_STATE_MAX_LAST_NON_RESTRICTED; +import static android.app.AppOpsManager.UID_STATE_TOP; + +import static com.android.server.appop.AppOpsUidStateTracker.processStateToUidState; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.atLeast; +import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import android.app.ActivityManager; @@ -41,12 +56,14 @@ import android.util.SparseArray; import com.android.dx.mockito.inline.extended.ExtendedMockito; import com.android.dx.mockito.inline.extended.StaticMockitoSession; import com.android.internal.os.Clock; +import com.android.server.appop.AppOpsUidStateTracker.UidStateChangedCallback; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.quality.Strictness; import java.util.concurrent.atomic.AtomicLong; @@ -101,7 +118,7 @@ public class AppOpsUidStateTrackerTest { @Test public void testConstantFirstUnrestrictedUidState() { for (int i = 0; i < AppOpsManager.getNumOps(); i++) { - assertEquals(AppOpsManager.UID_STATE_MAX_LAST_NON_RESTRICTED, + assertEquals(UID_STATE_MAX_LAST_NON_RESTRICTED, AppOpsManager.resolveFirstUnrestrictedUidState(i)); } } @@ -109,7 +126,7 @@ public class AppOpsUidStateTrackerTest { @Test public void testNoCapability() { procStateBuilder(UID) - .foregroundState() + .topState() .update(); assertEquals(MODE_IGNORED, mIntf.evalMode(UID, OP_RECORD_AUDIO, MODE_FOREGROUND)); @@ -121,7 +138,7 @@ public class AppOpsUidStateTrackerTest { @Test public void testForegroundWithMicrophoneCapability() { procStateBuilder(UID) - .foregroundState() + .topState() .microphoneCapability() .update(); @@ -149,7 +166,7 @@ public class AppOpsUidStateTrackerTest { @Test public void testForegroundWithCameraCapability() { procStateBuilder(UID) - .foregroundState() + .topState() .cameraCapability() .update(); @@ -177,7 +194,7 @@ public class AppOpsUidStateTrackerTest { @Test public void testForegroundWithLocationCapability() { procStateBuilder(UID) - .foregroundState() + .topState() .locationCapability() .update(); @@ -205,7 +222,7 @@ public class AppOpsUidStateTrackerTest { @Test public void testForegroundNotCapabilitiesTracked() { procStateBuilder(UID) - .foregroundState() + .topState() .update(); assertEquals(MODE_ALLOWED, mIntf.evalMode(UID, OP_NO_CAPABILITIES, MODE_FOREGROUND)); @@ -228,7 +245,7 @@ public class AppOpsUidStateTrackerTest { assertBackground(UID); procStateBuilder(UID) - .foregroundState() + .topState() .update(); assertForeground(UID); } @@ -236,7 +253,7 @@ public class AppOpsUidStateTrackerTest { @Test public void testForegroundToBackgroundTransition() { procStateBuilder(UID) - .foregroundState() + .topState() .update(); assertForeground(UID); @@ -446,6 +463,328 @@ public class AppOpsUidStateTrackerTest { assertEquals(MODE_ALLOWED, mIntf.evalMode(UID, OP_FINE_LOCATION, MODE_FOREGROUND)); } + @Test + public void testUidStateChangedCallbackNewProcessTop() { + UidStateChangedCallback cb = addUidStateChangeCallback(); + + procStateBuilder(UID) + .topState() + .update(); + + getLatestPostMessageArgument().getCallback().run(); + + verify(cb).onUidStateChanged(eq(UID), eq(UID_STATE_TOP), eq(true)); + } + + @Test + public void testUidStateChangedCallbackNewProcessForegroundService() { + UidStateChangedCallback cb = addUidStateChangeCallback(); + + procStateBuilder(UID) + .foregroundServiceState() + .update(); + + getLatestPostMessageArgument().getCallback().run(); + + verify(cb).onUidStateChanged(eq(UID), eq(UID_STATE_FOREGROUND_SERVICE), eq(true)); + } + + @Test + public void testUidStateChangedCallbackNewProcessForeground() { + UidStateChangedCallback cb = addUidStateChangeCallback(); + + procStateBuilder(UID) + .foregroundState() + .update(); + + getLatestPostMessageArgument().getCallback().run(); + + verify(cb).onUidStateChanged(eq(UID), eq(UID_STATE_FOREGROUND), eq(true)); + } + + @Test + public void testUidStateChangedCallbackNewProcessBackground() { + UidStateChangedCallback cb = addUidStateChangeCallback(); + + procStateBuilder(UID) + .backgroundState() + .update(); + + getLatestPostMessageArgument().getCallback().run(); + + verify(cb).onUidStateChanged(eq(UID), eq(UID_STATE_BACKGROUND), eq(false)); + } + + @Test + public void testUidStateChangedCallbackNewProcessCached() { + UidStateChangedCallback cb = addUidStateChangeCallback(); + + procStateBuilder(UID) + .cachedState() + .update(); + + // Cached is the default, no change in uid state. + verify(cb, times(0)).onUidStateChanged(anyInt(), anyInt(), anyBoolean()); + } + + @Test + public void testUidStateChangedCallbackCachedToBackground() { + testUidStateChangedCallback( + ActivityManager.PROCESS_STATE_CACHED_ACTIVITY, + ActivityManager.PROCESS_STATE_RECEIVER); + } + + @Test + public void testUidStateChangedCallbackCachedToForeground() { + testUidStateChangedCallback( + ActivityManager.PROCESS_STATE_CACHED_ACTIVITY, + ActivityManager.PROCESS_STATE_BOUND_TOP); + } + + @Test + public void testUidStateChangedCallbackCachedToForegroundService() { + testUidStateChangedCallback( + ActivityManager.PROCESS_STATE_CACHED_ACTIVITY, + ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE); + } + + @Test + public void testUidStateChangedCallbackCachedToTop() { + testUidStateChangedCallback( + ActivityManager.PROCESS_STATE_CACHED_ACTIVITY, + ActivityManager.PROCESS_STATE_TOP); + } + + @Test + public void testUidStateChangedCallbackBackgroundToCached() { + testUidStateChangedCallback( + ActivityManager.PROCESS_STATE_RECEIVER, + ActivityManager.PROCESS_STATE_CACHED_ACTIVITY); + } + + @Test + public void testUidStateChangedCallbackBackgroundToForeground() { + testUidStateChangedCallback( + ActivityManager.PROCESS_STATE_RECEIVER, + ActivityManager.PROCESS_STATE_BOUND_TOP); + } + + @Test + public void testUidStateChangedCallbackBackgroundToForegroundService() { + testUidStateChangedCallback( + ActivityManager.PROCESS_STATE_RECEIVER, + ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE); + } + + @Test + public void testUidStateChangedCallbackBackgroundToTop() { + testUidStateChangedCallback( + ActivityManager.PROCESS_STATE_RECEIVER, + ActivityManager.PROCESS_STATE_TOP); + } + + @Test + public void testUidStateChangedCallbackForegroundToCached() { + testUidStateChangedCallback( + ActivityManager.PROCESS_STATE_BOUND_TOP, + ActivityManager.PROCESS_STATE_CACHED_ACTIVITY); + } + + @Test + public void testUidStateChangedCallbackForegroundToBackground() { + testUidStateChangedCallback( + ActivityManager.PROCESS_STATE_BOUND_TOP, + ActivityManager.PROCESS_STATE_RECEIVER); + } + + @Test + public void testUidStateChangedCallbackForegroundToForegroundService() { + testUidStateChangedCallback( + ActivityManager.PROCESS_STATE_BOUND_TOP, + ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE); + } + + @Test + public void testUidStateChangedCallbackForegroundToTop() { + testUidStateChangedCallback( + ActivityManager.PROCESS_STATE_BOUND_TOP, + ActivityManager.PROCESS_STATE_TOP); + } + + @Test + public void testUidStateChangedCallbackForegroundServiceToCached() { + testUidStateChangedCallback( + ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, + ActivityManager.PROCESS_STATE_CACHED_ACTIVITY); + } + + @Test + public void testUidStateChangedCallbackForegroundServiceToBackground() { + testUidStateChangedCallback( + ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, + ActivityManager.PROCESS_STATE_RECEIVER); + } + + @Test + public void testUidStateChangedCallbackForegroundServiceToForeground() { + testUidStateChangedCallback( + ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, + ActivityManager.PROCESS_STATE_BOUND_TOP); + } + + @Test + public void testUidStateChangedCallbackForegroundServiceToTop() { + testUidStateChangedCallback( + ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, + ActivityManager.PROCESS_STATE_TOP); + } + + @Test + public void testUidStateChangedCallbackTopToCached() { + testUidStateChangedCallback( + ActivityManager.PROCESS_STATE_TOP, + ActivityManager.PROCESS_STATE_CACHED_ACTIVITY); + } + + @Test + public void testUidStateChangedCallbackTopToBackground() { + testUidStateChangedCallback( + ActivityManager.PROCESS_STATE_TOP, + ActivityManager.PROCESS_STATE_RECEIVER); + } + + @Test + public void testUidStateChangedCallbackTopToForeground() { + testUidStateChangedCallback( + ActivityManager.PROCESS_STATE_TOP, + ActivityManager.PROCESS_STATE_BOUND_TOP); + } + + @Test + public void testUidStateChangedCallbackTopToForegroundService() { + testUidStateChangedCallback( + ActivityManager.PROCESS_STATE_TOP, + ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE); + } + + @Test + public void testUidStateChangedCallbackCachedToNonexistent() { + UidStateChangedCallback cb = addUidStateChangeCallback(); + + procStateBuilder(UID) + .cachedState() + .update(); + + procStateBuilder(UID) + .nonExistentState() + .update(); + + verify(mHandler, never()).post(any()); + verify(cb, never()).onUidStateChanged(anyInt(), anyInt(), anyBoolean()); + } + + @Test + public void testUidStateChangedCallbackBackgroundToNonexistent() { + UidStateChangedCallback cb = addUidStateChangeCallback(); + + procStateBuilder(UID) + .backgroundState() + .update(); + + procStateBuilder(UID) + .nonExistentState() + .update(); + + getLatestPostMessageArgument().getCallback().run(); + verify(cb, atLeastOnce()).onUidStateChanged(eq(UID), eq(UID_STATE_CACHED), eq(false)); + } + + @Test + public void testUidStateChangedCallbackForegroundToNonexistent() { + UidStateChangedCallback cb = addUidStateChangeCallback(); + + procStateBuilder(UID) + .foregroundState() + .update(); + + procStateBuilder(UID) + .nonExistentState() + .update(); + + getLatestPostMessageArgument().getCallback().run(); + verify(cb, atLeastOnce()).onUidStateChanged(eq(UID), eq(UID_STATE_CACHED), eq(true)); + } + + @Test + public void testUidStateChangedCallbackForegroundServiceToNonexistent() { + UidStateChangedCallback cb = addUidStateChangeCallback(); + + procStateBuilder(UID) + .foregroundServiceState() + .update(); + + procStateBuilder(UID) + .nonExistentState() + .update(); + + getLatestPostMessageArgument().getCallback().run(); + verify(cb, atLeastOnce()).onUidStateChanged(eq(UID), eq(UID_STATE_CACHED), eq(true)); + } + + @Test + public void testUidStateChangedCallbackTopToNonexistent() { + UidStateChangedCallback cb = addUidStateChangeCallback(); + + procStateBuilder(UID) + .topState() + .update(); + + procStateBuilder(UID) + .nonExistentState() + .update(); + + getLatestPostMessageArgument().getCallback().run(); + verify(cb, atLeastOnce()).onUidStateChanged(eq(UID), eq(UID_STATE_CACHED), eq(true)); + } + + public void testUidStateChangedCallback(int initialState, int finalState) { + int initialUidState = processStateToUidState(initialState); + int finalUidState = processStateToUidState(finalState); + boolean foregroundChange = initialUidState <= UID_STATE_MAX_LAST_NON_RESTRICTED + != finalUidState <= UID_STATE_MAX_LAST_NON_RESTRICTED; + boolean finalUidStateIsBackgroundAndLessImportant = + finalUidState > UID_STATE_MAX_LAST_NON_RESTRICTED + && finalUidState > initialUidState; + + UidStateChangedCallback cb = addUidStateChangeCallback(); + + procStateBuilder(UID) + .setState(initialState) + .update(); + + procStateBuilder(UID) + .setState(finalState) + .update(); + + if (finalUidStateIsBackgroundAndLessImportant) { + AtomicReference delayedMessage = new AtomicReference<>(); + getPostDelayedMessageArguments(delayedMessage, new AtomicLong()); + mClock.advanceTime(mConstants.TOP_STATE_SETTLE_TIME + 1); + delayedMessage.get().getCallback().run(); + } + + getLatestPostMessageArgument().getCallback().run(); + verify(cb, atLeastOnce()) + .onUidStateChanged(eq(UID), eq(finalUidState), eq(foregroundChange)); + } + + private UidStateChangedCallback addUidStateChangeCallback() { + UidStateChangedCallback cb = + Mockito.mock(UidStateChangedCallback.class); + mIntf.addUidStateChangedCallback(mHandler, cb); + return cb; + } + /* If testForegroundNotCapabilitiesTracked fails, this assertion is probably incorrect */ private void assertForeground(int uid) { assertEquals(MODE_ALLOWED, mIntf.evalMode(uid, OP_NO_CAPABILITIES, MODE_FOREGROUND)); @@ -472,6 +811,14 @@ public class AppOpsUidStateTrackerTest { } } + private Message getLatestPostMessageArgument() { + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + + verify(mHandler, atLeast(1)).sendMessage(messageCaptor.capture()); + + return messageCaptor.getValue(); + } + private UidProcStateUpdateBuilder procStateBuilder(int uid) { return new UidProcStateUpdateBuilder(mIntf, uid); } @@ -496,7 +843,12 @@ public class AppOpsUidStateTrackerTest { return this; } - public UidProcStateUpdateBuilder foregroundState() { + public UidProcStateUpdateBuilder setState(int procState) { + mProcState = procState; + return this; + } + + public UidProcStateUpdateBuilder topState() { mProcState = ActivityManager.PROCESS_STATE_TOP; return this; } @@ -506,6 +858,11 @@ public class AppOpsUidStateTrackerTest { return this; } + public UidProcStateUpdateBuilder foregroundState() { + mProcState = ActivityManager.PROCESS_STATE_BOUND_TOP; + return this; + } + public UidProcStateUpdateBuilder backgroundState() { mProcState = ActivityManager.PROCESS_STATE_SERVICE; return this; @@ -516,6 +873,11 @@ public class AppOpsUidStateTrackerTest { return this; } + public UidProcStateUpdateBuilder nonExistentState() { + mProcState = ActivityManager.PROCESS_STATE_NONEXISTENT; + return this; + } + public UidProcStateUpdateBuilder locationCapability() { mCapability |= ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION; return this;