Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c3ea28b2 authored by Evan Severson's avatar Evan Severson
Browse files

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
parent 8885371b
Loading
Loading
Loading
Loading
+17 −16
Original line number Diff line number Diff line
@@ -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));
            }
        }

        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);
+371 −9
Original line number Diff line number Diff line
@@ -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;