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

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

Invoke uid state change callback for all nonexistent apps

Uids transitioning from UID_STATE_CACHED to UID_STATE_NONEXISTENT don't
currently invoke the callback since NONEXISTENT is equivalent to CACHED
externally to AppOpsService. This lead to an issue where an app getting
killed from the CACHED state doesn't invoke the cleanup routine for
running ops.

In this change we treat NONEXISTENT as a separate state inside of
AppOpsUidStateTracker so that we can track killed uids the same way as
other transistions and then external to the tracker translate back to
CACHED and invoke that callback if transistioning from something other
than CACHED. Then finally the NONEXISTENT callback will always be
invoked so that AppOpsService can run its cleanup routine.

Mocking tests are updated to be more rigorous to expect exact amounts
of invocations.

Test: atest CtsMediaAudioPermissionTestCases CtsAppOpsTestCases \
          AppOpsUidStateTrackerTest
Flag: android.permission.flags.finish_running_ops_for_killed_packages
Bug: 399667889
Change-Id: I515173921cd4df88c35e2a4847122b5ab56cb937
parent 34f9f07e
Loading
Loading
Loading
Loading
+37 −33
Original line number Diff line number Diff line
@@ -55,7 +55,6 @@ import static android.app.AppOpsManager.SAMPLING_STRATEGY_RARELY_USED;
import static android.app.AppOpsManager.SAMPLING_STRATEGY_UNIFORM;
import static android.app.AppOpsManager.SAMPLING_STRATEGY_UNIFORM_OPS;
import static android.app.AppOpsManager.SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE;
import static android.app.AppOpsManager.UID_STATE_NONEXISTENT;
import static android.app.AppOpsManager.WATCH_FOREGROUND_CHANGES;
import static android.app.AppOpsManager._NUM_OP;
import static android.app.AppOpsManager.extractFlagsFromKey;
@@ -464,7 +463,19 @@ public class AppOpsService extends IAppOpsService.Stub {
                    Clock.SYSTEM_CLOCK, mConstants);

            mUidStateTracker.addUidStateChangedCallback(new HandlerExecutor(mHandler),
                    this::onUidStateChanged);
                    new AppOpsUidStateTracker.UidStateChangedCallback() {
                        @Override
                        public void onUidStateChanged(int uid, int uidState,
                                boolean foregroundModeMayChange) {
                            AppOpsService.this
                                    .onUidStateChanged(uid, uidState, foregroundModeMayChange);
                        }

                        @Override
                        public void onUidProcessDeath(int uid) {
                            AppOpsService.this.onUidProcessDeath(uid);
                        }
                    });
        }
        return mUidStateTracker;
    }
@@ -1500,9 +1511,6 @@ public class AppOpsService extends IAppOpsService.Stub {
    // The callback method from AppOpsUidStateTracker
    private void onUidStateChanged(int uid, int state, boolean foregroundModeMayChange) {
        synchronized (this) {
            if (state == UID_STATE_NONEXISTENT) {
                onUidProcessDeathLocked(uid);
            }
            UidState uidState = getUidStateLocked(uid, false);

            boolean hasForegroundWatchers = false;
@@ -1590,11 +1598,6 @@ public class AppOpsService extends IAppOpsService.Stub {
                }
            }

            if (state == UID_STATE_NONEXISTENT) {
                // For UID_STATE_NONEXISTENT, we don't call onUidStateChanged for AttributedOps
                return;
            }

            if (uidState != null) {
                int numPkgs = uidState.pkgOps.size();
                for (int pkgNum = 0; pkgNum < numPkgs; pkgNum++) {
@@ -1619,8 +1622,8 @@ public class AppOpsService extends IAppOpsService.Stub {
        }
    }

    @GuardedBy("this")
    private void onUidProcessDeathLocked(int uid) {
    private void onUidProcessDeath(int uid) {
        synchronized (this) {
            if (!mUidStates.contains(uid) || !Flags.finishRunningOpsForKilledPackages()) {
                return;
            }
@@ -1645,6 +1648,7 @@ public class AppOpsService extends IAppOpsService.Stub {
            });
            finishChainsLocked(chainsToFinish);
        }
    }

    @GuardedBy("this")
    private void finishChainsLocked(SparseLongArray chainsToFinish) {
+13 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.server.appop;
import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
import static android.app.ActivityManager.PROCESS_STATE_BOUND_TOP;
import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
import static android.app.ActivityManager.PROCESS_STATE_PERSISTENT_UI;
import static android.app.ActivityManager.PROCESS_STATE_RECEIVER;
import static android.app.ActivityManager.PROCESS_STATE_TOP;
@@ -27,8 +28,10 @@ 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_NONEXISTENT;
import static android.app.AppOpsManager.UID_STATE_PERSISTENT;
import static android.app.AppOpsManager.UID_STATE_TOP;
import static android.permission.flags.Flags.finishRunningOpsForKilledPackages;

import android.annotation.CallbackExecutor;
import android.util.SparseArray;
@@ -68,6 +71,14 @@ interface AppOpsUidStateTracker {
            return UID_STATE_BACKGROUND;
        }

        if (finishRunningOpsForKilledPackages()) {
            if (procState < PROCESS_STATE_NONEXISTENT) {
                return UID_STATE_CACHED;
            }

            return UID_STATE_NONEXISTENT;
        }

        // UID_STATE_NONEXISTENT is deliberately excluded here
        return UID_STATE_CACHED;
    }
@@ -119,6 +130,8 @@ interface AppOpsUidStateTracker {
         *                               evaluated result may have changed.
         */
        void onUidStateChanged(int uid, int uidState, boolean foregroundModeMayChange);

        void onUidProcessDeath(int uid);
    }

    void dumpUidState(PrintWriter pw, int uid, long nowElapsed);
+20 −17
Original line number Diff line number Diff line
@@ -21,7 +21,6 @@ import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_CAMERA;
import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION;
import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_MICROPHONE;
import static android.app.ActivityManager.PROCESS_CAPABILITY_NONE;
import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
import static android.app.ActivityManager.ProcessCapability;
import static android.app.AppOpsManager.MIN_PRIORITY_UID_STATE;
import static android.app.AppOpsManager.MODE_ALLOWED;
@@ -32,6 +31,7 @@ import static android.app.AppOpsManager.OP_CONTROL_AUDIO;
import static android.app.AppOpsManager.OP_NONE;
import static android.app.AppOpsManager.OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO;
import static android.app.AppOpsManager.OP_RECORD_AUDIO;
import static android.app.AppOpsManager.UID_STATE_CACHED;
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_NONEXISTENT;
@@ -75,7 +75,6 @@ class AppOpsUidStateTrackerImpl implements AppOpsUidStateTracker {
    private SparseBooleanArray mAppWidgetVisible = new SparseBooleanArray();
    private SparseBooleanArray mPendingAppWidgetVisible = new SparseBooleanArray();
    private SparseLongArray mPendingCommitTime = new SparseLongArray();
    private SparseBooleanArray mPendingGone = new SparseBooleanArray();

    private ArrayMap<UidStateChangedCallback, Executor>
            mUidStateChangedCallbacks = new ArrayMap<>();
@@ -221,11 +220,12 @@ class AppOpsUidStateTrackerImpl implements AppOpsUidStateTracker {
    public void updateUidProcState(int uid, int procState, int capability) {
        int uidState = processStateToUidState(procState);

        int prevUidState = mUidStates.get(uid, AppOpsManager.MIN_PRIORITY_UID_STATE);
        int prevUidState = mUidStates.get(uid, AppOpsManager.UID_STATE_NONEXISTENT);
        int prevCapability = mCapability.get(uid, PROCESS_CAPABILITY_NONE);
        int pendingUidState = mPendingUidStates.get(uid, MIN_PRIORITY_UID_STATE);
        int pendingUidState = mPendingUidStates.get(uid, UID_STATE_NONEXISTENT);
        int pendingCapability = mPendingCapability.get(uid, PROCESS_CAPABILITY_NONE);
        long pendingStateCommitTime = mPendingCommitTime.get(uid, 0);

        if ((pendingStateCommitTime == 0
                && (uidState != prevUidState || capability != prevCapability))
                || (pendingStateCommitTime != 0
@@ -239,8 +239,7 @@ class AppOpsUidStateTrackerImpl implements AppOpsUidStateTracker {

            boolean hasLostCapability = (prevCapability & ~capability) != 0;

            if (procState == PROCESS_STATE_NONEXISTENT) {
                mPendingGone.put(uid, true);
            if (uidState == UID_STATE_NONEXISTENT) {
                commitUidPendingState(uid);
            } else if (uidState < prevUidState) {
                // We are moving to a more important state, or the new state may be in the
@@ -342,7 +341,7 @@ class AppOpsUidStateTrackerImpl implements AppOpsUidStateTracker {

    private void commitUidPendingState(int uid) {

        int uidState = mUidStates.get(uid, MIN_PRIORITY_UID_STATE);
        int uidState = mUidStates.get(uid, UID_STATE_NONEXISTENT);
        int capability = mCapability.get(uid, PROCESS_CAPABILITY_NONE);
        boolean appWidgetVisible = mAppWidgetVisible.get(uid, false);

@@ -350,18 +349,23 @@ class AppOpsUidStateTrackerImpl implements AppOpsUidStateTracker {
        int pendingCapability = mPendingCapability.get(uid, capability);
        boolean pendingAppWidgetVisible = mPendingAppWidgetVisible.get(uid, appWidgetVisible);

        boolean foregroundChange = uidState <= UID_STATE_MAX_LAST_NON_RESTRICTED
                != pendingUidState <= UID_STATE_MAX_LAST_NON_RESTRICTED
        // UID_STATE_NONEXISTENT is a state that isn't used outside of this class, nonexistent
        // processes have always been represented as CACHED
        int externalUidState = Math.min(uidState, UID_STATE_CACHED);
        int externalPendingUidState = Math.min(pendingUidState, UID_STATE_CACHED);

        boolean foregroundChange = externalUidState <= UID_STATE_MAX_LAST_NON_RESTRICTED
                != externalPendingUidState <= UID_STATE_MAX_LAST_NON_RESTRICTED
                || capability != pendingCapability
                || appWidgetVisible != pendingAppWidgetVisible;

        if (uidState != pendingUidState
        if (externalUidState != externalPendingUidState
                || capability != pendingCapability
                || appWidgetVisible != pendingAppWidgetVisible) {

            if (foregroundChange) {
                // To save on memory usage, log only interesting changes.
                mEventLog.logCommitUidState(uid, pendingUidState, pendingCapability,
                mEventLog.logCommitUidState(uid, externalPendingUidState, pendingCapability,
                        pendingAppWidgetVisible, appWidgetVisible != pendingAppWidgetVisible);
            }

@@ -370,24 +374,23 @@ class AppOpsUidStateTrackerImpl implements AppOpsUidStateTracker {
                Executor executor = mUidStateChangedCallbacks.valueAt(i);

                executor.execute(PooledLambda.obtainRunnable(
                        UidStateChangedCallback::onUidStateChanged, cb, uid, pendingUidState,
                        foregroundChange));
                        UidStateChangedCallback::onUidStateChanged, cb, uid,
                        externalPendingUidState, foregroundChange));
            }
        }

        if (mPendingGone.get(uid, false)) {
        if (pendingUidState == UID_STATE_NONEXISTENT && uidState != pendingUidState) {
            mUidStates.delete(uid);
            mCapability.delete(uid);
            mAppWidgetVisible.delete(uid);
            mPendingGone.delete(uid);
            if (finishRunningOpsForKilledPackages()) {
                for (int i = 0; i < mUidStateChangedCallbacks.size(); i++) {
                    UidStateChangedCallback cb = mUidStateChangedCallbacks.keyAt(i);
                    Executor executor = mUidStateChangedCallbacks.valueAt(i);

                    // If foregroundness changed it should be handled in earlier callback invocation
                    executor.execute(PooledLambda.obtainRunnable(
                            UidStateChangedCallback::onUidStateChanged, cb, uid,
                            UID_STATE_NONEXISTENT, foregroundChange));
                            UidStateChangedCallback::onUidProcessDeath, cb, uid));
                }
            }
        } else {
+0 −247
Original line number Diff line number Diff line
@@ -26,15 +26,12 @@ import static android.app.AppOpsManager.OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUD
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 android.permission.flags.Flags.delayUidStateChangesFromCapabilityUpdates;

import static com.android.server.appop.AppOpsUidStateTracker.processStateToUidState;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -42,7 +39,6 @@ import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
@@ -64,7 +60,6 @@ import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.quality.Strictness;

import java.util.PriorityQueue;

@@ -94,7 +89,6 @@ public class AppOpsUidStateTrackerTest {
    public void setUp() {
        mSession = ExtendedMockito.mockitoSession()
                .initMocks(this)
                .strictness(Strictness.LENIENT)
                .startMocking();
        mConstants.TOP_STATE_SETTLE_TIME = 10 * 1000L;
        mConstants.FG_SERVICE_STATE_SETTLE_TIME = 5 * 1000L;
@@ -590,221 +584,6 @@ public class AppOpsUidStateTrackerTest {
        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(cb, never()).onUidStateChanged(anyInt(), anyInt(), anyBoolean());
    }

    @Test
    public void testUidStateChangedCallbackBackgroundToNonexistent() {
        UidStateChangedCallback cb = addUidStateChangeCallback();

        procStateBuilder(UID)
                .backgroundState()
                .update();

        procStateBuilder(UID)
                .nonExistentState()
                .update();

        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();

        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();

        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();

        verify(cb, atLeastOnce()).onUidStateChanged(eq(UID), eq(UID_STATE_CACHED), eq(true));
    }

    @Test
    public void testUidStateChangedBackgroundThenForegroundImmediately() {
        procStateBuilder(UID)
@@ -882,32 +661,6 @@ public class AppOpsUidStateTrackerTest {
        assertEquals(UID_STATE_TOP, mIntf.getUidState(UID));
    }

    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) {
            mClock.advanceTime(mConstants.TOP_STATE_SETTLE_TIME + 1);
        }

        verify(cb, atLeastOnce())
                .onUidStateChanged(eq(UID), eq(finalUidState), eq(foregroundChange));
    }

    private UidStateChangedCallback addUidStateChangeCallback() {
        UidStateChangedCallback cb =
+260 −0

File added.

Preview size limit exceeded, changes collapsed.