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
This commit is contained in:
Evan Severson
2022-09-15 12:46:00 -07:00
parent 8885371b88
commit c3ea28b213
2 changed files with 388 additions and 25 deletions

View File

@@ -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<UidStateChangedCallback, Handler> 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);

View File

@@ -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<Message> 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<Message> 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;