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

Commit 3e99f654 authored by Dianne Hackborn's avatar Dianne Hackborn
Browse files

Fix issue #34471029: Don't allow audio use from background apps.

This is becoming a common pattern (keeping track of which processes
are cached and not allowing them to do stuff in that state), so I
am turning this in to a general mechanism for monitoring this state
through the activity manager's IUidObserver.  Now we can just have
AudoService implement its own IUidObserver to get this state and
update which uids it is blocking.

This required making some changes to uid change reports so that
the integer is now a bit mask instead of an enumerations, but that
is what it was already turning in to anyway.  (This gets rid of
the crazy GONE_IDLE constant that we'd needed to add before because
it wasn't a bit mask).

Eventually the power manager should be changed to be told about
these changes to cached state instead of listening to every proc
state change, but we'll do that later, it is more disruption than
I want to take for now.  However, while working on this, I noticed
that the power manager had regressed in the cached uids it would
actually block, because the activity manager was no longer telling
it about all uids that are idle.  (I think this happened when I had
to change the default idle state of UidRecord to true.)  So I am
adding a bit of new code to keep track of what idle state we last
reported to observers, to make sure we tell it about newly created
uids that are idle but have never actually become active.

Test: runtest -c com.android.server.am.ActivityManagerServiceTest frameworks-services

Change-Id: I7bfd46bacadd4cab2a69f40e6e52afb4e67b456a
parent f490ae3e
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -172,6 +172,9 @@ public class ActivityManager {
        @Override
        public void onUidIdle(int uid, boolean disabled) {
        }

        @Override public void onUidCachedChanged(int uid, boolean cached) {
        }
    }

    final ArrayMap<OnUidImportanceListener, UidObserver> mImportanceListeners = new ArrayMap<>();
@@ -563,6 +566,9 @@ public class ActivityManager {
    /** @hide Flag for registerUidObserver: report uid has become active. */
    public static final int UID_OBSERVER_ACTIVE = 1<<3;

    /** @hide Flag for registerUidObserver: report uid cached state has changed. */
    public static final int UID_OBSERVER_CACHED = 1<<4;

    /** @hide Mode for {@link IActivityManager#isAppStartModeDisabled}: normal free-to-run operation. */
    public static final int APP_START_MODE_NORMAL = 0;

+10 −0
Original line number Diff line number Diff line
@@ -43,4 +43,14 @@ oneway interface IUidObserver {
     * a sufficient period of time, or all of its processes have gone away.
     */
    void onUidIdle(int uid, boolean disabled);

    /**
     * Report when the cached state of a uid has changed.
     * If true, a uid has become cached -- that is, it has some active processes that are
     * all in the cached state.  It should be doing as little as possible at this point.
     * If false, that a uid is no longer cached.  This will only be called after
     * onUidCached() has been reported true.  It will happen when either one of its actively
     * running processes is no longer cached, or it no longer has any actively running processes.
     */
    void onUidCachedChanged(int uid, boolean cached);
}
+0 −11
Original line number Diff line number Diff line
@@ -59,15 +59,4 @@ public abstract class AudioManagerInternal {

        int getRingerModeAffectedStreams(int streams);
    }

    /**
     * Disable or restore the ability to play audio for a given UID.
     * When a UID isn't meant to be tracked anymore (e.g. client died), re-enable audio for this UID
     * to prevent disabling audio for future UIDs that would reuse the same value.
     * This operation is asynchronous.
     * @param disable when true, prevents playback of audio streams from the given uid. If false,
     *         restores the ability to play, or no-op if playback hadn't been disabled before.
     * @param uid the client UID whose ability to play will be affected.
     */
    public abstract void disableAudioForUid(boolean disable, int uid);
}
+7 −5
Original line number Diff line number Diff line
@@ -2893,11 +2893,10 @@ class AlarmManagerService extends SystemService {
    }

    final class UidObserver extends IUidObserver.Stub {
        @Override public void onUidStateChanged(int uid, int procState,
                long procStateSeq) throws RemoteException {
        @Override public void onUidStateChanged(int uid, int procState, long procStateSeq) {
        }

        @Override public void onUidGone(int uid, boolean disabled) throws RemoteException {
        @Override public void onUidGone(int uid, boolean disabled) {
            if (disabled) {
                synchronized (mLock) {
                    removeForStoppedLocked(uid);
@@ -2905,16 +2904,19 @@ class AlarmManagerService extends SystemService {
            }
        }

        @Override public void onUidActive(int uid) throws RemoteException {
        @Override public void onUidActive(int uid) {
        }

        @Override public void onUidIdle(int uid, boolean disabled) throws RemoteException {
        @Override public void onUidIdle(int uid, boolean disabled) {
            if (disabled) {
                synchronized (mLock) {
                    removeForStoppedLocked(uid);
                }
            }
        }

        @Override public void onUidCachedChanged(int uid, boolean cached) {
        }
    };

    private final BroadcastStats getStatsLocked(PendingIntent pi) {
+73 −28
Original line number Diff line number Diff line
@@ -4344,8 +4344,7 @@ public class ActivityManagerService extends IActivityManager.Stub
        if (VALIDATE_UID_STATES && mUidObservers.getRegisteredCallbackCount() > 0) {
            for (int j = 0; j < N; ++j) {
                final UidRecord.ChangeItem item = mActiveUidChanges[j];
                if (item.change == UidRecord.CHANGE_GONE
                        || item.change == UidRecord.CHANGE_GONE_IDLE) {
                if ((item.change & UidRecord.CHANGE_GONE) != 0) {
                    mValidateUids.remove(item.uid);
                } else {
                    UidRecord validateUid = mValidateUids.get(item.uid);
@@ -4353,9 +4352,9 @@ public class ActivityManagerService extends IActivityManager.Stub
                        validateUid = new UidRecord(item.uid);
                        mValidateUids.put(item.uid, validateUid);
                    }
                    if (item.change == UidRecord.CHANGE_IDLE) {
                    if ((item.change & UidRecord.CHANGE_IDLE) != 0) {
                        validateUid.idle = true;
                    } else if (item.change == UidRecord.CHANGE_ACTIVE) {
                    } else if ((item.change & UidRecord.CHANGE_ACTIVE) != 0) {
                        validateUid.idle = false;
                    }
                    validateUid.curProcState = validateUid.setProcState = item.processState;
@@ -4380,22 +4379,37 @@ public class ActivityManagerService extends IActivityManager.Stub
            for (int j = 0; j < changesSize; j++) {
                UidRecord.ChangeItem item = mActiveUidChanges[j];
                final int change = item.change;
                if (change == UidRecord.CHANGE_IDLE
                        || change == UidRecord.CHANGE_GONE_IDLE) {
                if (change == UidRecord.CHANGE_PROCSTATE &&
                        (reg.which & ActivityManager.UID_OBSERVER_PROCSTATE) == 0) {
                    // No-op common case: no significant change, the observer is not
                    // interested in all proc state changes.
                    continue;
                }
                if ((change & UidRecord.CHANGE_IDLE) != 0) {
                    if ((reg.which & ActivityManager.UID_OBSERVER_IDLE) != 0) {
                        if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
                                "UID idle uid=" + item.uid);
                        observer.onUidIdle(item.uid, item.ephemeral);
                    }
                } else if (change == UidRecord.CHANGE_ACTIVE) {
                } else if ((change & UidRecord.CHANGE_ACTIVE) != 0) {
                    if ((reg.which & ActivityManager.UID_OBSERVER_ACTIVE) != 0) {
                        if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
                                "UID active uid=" + item.uid);
                        observer.onUidActive(item.uid);
                    }
                }
                if (change == UidRecord.CHANGE_GONE
                        || change == UidRecord.CHANGE_GONE_IDLE) {
                if ((reg.which & ActivityManager.UID_OBSERVER_CACHED) != 0) {
                    if ((change & UidRecord.CHANGE_CACHED) != 0) {
                        if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
                                "UID cached uid=" + item.uid);
                        observer.onUidCachedChanged(item.uid, true);
                    } else if ((change & UidRecord.CHANGE_UNCACHED) != 0) {
                        if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
                                "UID active uid=" + item.uid);
                        observer.onUidCachedChanged(item.uid, false);
                    }
                }
                if ((change & UidRecord.CHANGE_GONE) != 0) {
                    if ((reg.which & ActivityManager.UID_OBSERVER_GONE) != 0) {
                        if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
                                "UID gone uid=" + item.uid);
@@ -22170,10 +22184,10 @@ public class ActivityManagerService extends IActivityManager.Stub
            }
            if (uidRec != null) {
                uidRec.pendingChange = pendingChange;
                if (change == UidRecord.CHANGE_GONE && !uidRec.idle) {
                if ((change & UidRecord.CHANGE_GONE) != 0 && !uidRec.idle) {
                    // If this uid is going away, and we haven't yet reported it is gone,
                    // then do so now.
                    change = UidRecord.CHANGE_GONE_IDLE;
                    change |= UidRecord.CHANGE_IDLE;
                }
            } else if (uid < 0) {
                throw new IllegalArgumentException("No UidRecord or uid");
@@ -22183,8 +22197,26 @@ public class ActivityManagerService extends IActivityManager.Stub
            mPendingUidChanges.add(pendingChange);
        } else {
            pendingChange = uidRec.pendingChange;
            if (change == UidRecord.CHANGE_GONE && pendingChange.change == UidRecord.CHANGE_IDLE) {
                change = UidRecord.CHANGE_GONE_IDLE;
            // If there is no change in idle or active state, then keep whatever was pending.
            if ((change & (UidRecord.CHANGE_IDLE | UidRecord.CHANGE_ACTIVE)) == 0) {
                change |= (pendingChange.change & (UidRecord.CHANGE_IDLE
                        | UidRecord.CHANGE_ACTIVE));
            }
            // If there is no change in cached or uncached state, then keep whatever was pending.
            if ((change & (UidRecord.CHANGE_CACHED | UidRecord.CHANGE_UNCACHED)) == 0) {
                change |= (pendingChange.change & (UidRecord.CHANGE_CACHED
                        | UidRecord.CHANGE_UNCACHED));
            }
            // If this is a report of the UID being gone, then we shouldn't keep any previous
            // report of it being active or cached.  (That is, a gone uid is never active,
            // and never cached.)
            if ((change & UidRecord.CHANGE_GONE) != 0) {
                change &= ~(UidRecord.CHANGE_ACTIVE | UidRecord.CHANGE_CACHED);
                if (!uidRec.idle) {
                    // If this uid is going away, and we haven't yet reported it is gone,
                    // then do so now.
                    change |= UidRecord.CHANGE_IDLE;
                }
            }
        }
        pendingChange.change = change;
@@ -22193,27 +22225,26 @@ public class ActivityManagerService extends IActivityManager.Stub
        pendingChange.ephemeral = uidRec != null ? uidRec.ephemeral : isEphemeralLocked(uid);
        pendingChange.procStateSeq = uidRec != null ? uidRec.curProcStateSeq : 0;
        if (uidRec != null) {
            uidRec.lastReportedChange = change;
            uidRec.updateLastDispatchedProcStateSeq(change);
        }
        // Directly update the power manager, since we sit on top of it and it is critical
        // it be kept in sync (so wake locks will be held as soon as appropriate).
        if (mLocalPowerManager != null) {
            switch (change) {
                case UidRecord.CHANGE_GONE:
                case UidRecord.CHANGE_GONE_IDLE:
                    mLocalPowerManager.uidGone(pendingChange.uid);
                    break;
                case UidRecord.CHANGE_IDLE:
                    mLocalPowerManager.uidIdle(pendingChange.uid);
                    break;
                case UidRecord.CHANGE_ACTIVE:
            // TO DO: dispatch cached/uncached changes here, so we don't need to report
            // all proc state changes.
            if ((change & UidRecord.CHANGE_ACTIVE) != 0) {
                mLocalPowerManager.uidActive(pendingChange.uid);
                    break;
                default:
            }
            if ((change & UidRecord.CHANGE_IDLE) != 0) {
                mLocalPowerManager.uidIdle(pendingChange.uid);
            }
            if ((change & UidRecord.CHANGE_GONE) != 0) {
                mLocalPowerManager.uidGone(pendingChange.uid);
            } else {
                mLocalPowerManager.updateUidProcState(pendingChange.uid,
                        pendingChange.processState);
                    break;
            }
        }
    }
@@ -22797,6 +22828,9 @@ public class ActivityManagerService extends IActivityManager.Stub
                                    mConstants.BACKGROUND_SETTLE_TIME);
                        }
                    }
                    if (!uidRec.setIdle) {
                        uidChange = UidRecord.CHANGE_IDLE;
                    }
                } else {
                    if (uidRec.idle) {
                        uidChange = UidRecord.CHANGE_ACTIVE;
@@ -22805,8 +22839,17 @@ public class ActivityManagerService extends IActivityManager.Stub
                    }
                    uidRec.lastBackgroundTime = 0;
                }
                final boolean wasCached = uidRec.setProcState
                        > ActivityManager.PROCESS_STATE_RECEIVER && uidRec.setProcState
                        != ActivityManager.PROCESS_STATE_NONEXISTENT;
                final boolean isCached = uidRec.curProcState
                        > ActivityManager.PROCESS_STATE_RECEIVER;
                if (wasCached != isCached) {
                    uidChange |= isCached ? UidRecord.CHANGE_CACHED : UidRecord.CHANGE_UNCACHED;
                }
                uidRec.setProcState = uidRec.curProcState;
                uidRec.setWhitelist = uidRec.curWhitelist;
                uidRec.setIdle = uidRec.idle;
                enqueueUidChangeLocked(uidRec, -1, uidChange);
                noteUidProcessState(uidRec.uid, uidRec.curProcState);
                if (uidRec.foregroundServices) {
@@ -22881,6 +22924,7 @@ public class ActivityManagerService extends IActivityManager.Stub
                                    userId == UserHandle.getUserId(uidRec.uid)) {
                                EventLogTags.writeAmUidIdle(uidRec.uid);
                                uidRec.idle = true;
                                uidRec.setIdle = true;
                                Slog.w(TAG, "Idling uid " + UserHandle.formatUid(uidRec.uid)
                                        + " from package " + packageName + " user " + userId);
                                doStopUidLocked(uidRec.uid, uidRec);
@@ -22916,6 +22960,7 @@ public class ActivityManagerService extends IActivityManager.Stub
                    if (bgTime <= maxBgTime) {
                        EventLogTags.writeAmUidIdle(uidRec.uid);
                        uidRec.idle = true;
                        uidRec.setIdle = true;
                        doStopUidLocked(uidRec.uid, uidRec);
                    } else {
                        if (nextTime == 0 || nextTime > bgTime) {
Loading