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

Commit f4dca525 authored by Lee Shombert's avatar Lee Shombert Committed by Android (Google) Code Review
Browse files

Merge "DisplayManagerService: use per-pid freezer listener" into main

parents 745ebaa4 21d6448c
Loading
Loading
Loading
Loading
+290 −8
Original line number Diff line number Diff line
@@ -48,6 +48,7 @@ import static android.os.Process.ROOT_UID;
import static android.provider.Settings.Secure.RESOLUTION_MODE_FULL;
import static android.provider.Settings.Secure.RESOLUTION_MODE_HIGH;
import static android.provider.Settings.Secure.RESOLUTION_MODE_UNKNOWN;
import static android.text.TextUtils.formatSimple;
import static android.view.Display.HdrCapabilities.HDR_TYPE_INVALID;

import static com.android.server.display.layout.Layout.Display.POSITION_REAR;
@@ -279,6 +280,8 @@ public final class DisplayManagerService extends SystemService {
    private InputManagerInternal mInputManagerInternal;
    private ActivityManagerInternal mActivityManagerInternal;
    private final UidImportanceListener mUidImportanceListener = new UidImportanceListener();
    private final DisplayFrozenProcessListener mDisplayFrozenProcessListener;

    @Nullable
    private IMediaProjectionManager mProjectionService;
    private DeviceStateManagerInternal mDeviceStateManager;
@@ -321,6 +324,12 @@ public final class DisplayManagerService extends SystemService {
    @GuardedBy("mSyncRoot")
    private final SparseArray<CallbackRecord> mCallbacks = new SparseArray<>();

    // All callback records indexed by [uid][pid], for fast lookup by uid.
    // This is only used if {@link deferDisplayEventsWhenFrozen()} is true.
    @GuardedBy("mSyncRoot")
    private final SparseArray<SparseArray<CallbackRecord>> mCallbackRecordByPidByUid =
            new SparseArray<>();

    /**
     *  All {@link IVirtualDevice} and {@link DisplayWindowPolicyController}s indexed by
     *  {@link DisplayInfo#displayId}.
@@ -472,6 +481,7 @@ public final class DisplayManagerService extends SystemService {

    // Pending callback records indexed by calling process uid and pid.
    // Must be used outside of the lock mSyncRoot and should be self-locked.
    // This is only used when {@link deferDisplayEventsWhenFrozen()} is false.
    @GuardedBy("mPendingCallbackSelfLocked")
    private final SparseArray<SparseArray<PendingCallback>> mPendingCallbackSelfLocked =
            new SparseArray<>();
@@ -611,6 +621,7 @@ public final class DisplayManagerService extends SystemService {
        mFlags = injector.getFlags();
        mHandler = new DisplayManagerHandler(displayThreadLooper);
        mHandlerExecutor = new HandlerExecutor(mHandler);
        mDisplayFrozenProcessListener = new DisplayFrozenProcessListener();
        mUiHandler = UiThread.getHandler();
        mDisplayDeviceRepo = new DisplayDeviceRepository(mSyncRoot, mPersistentDataStore);
        mLogicalDisplayMapper = new LogicalDisplayMapper(mContext,
@@ -1034,10 +1045,14 @@ public final class DisplayManagerService extends SystemService {
    private class UidImportanceListener implements ActivityManager.OnUidImportanceListener {
        @Override
        public void onUidImportance(int uid, int importance) {
          onUidImportanceInternal(uid, importance);
            if (deferDisplayEventsWhenFrozen()) {
                onUidImportanceFlagged(uid, importance);
            } else {
                onUidImportanceUnflagged(uid, importance);
            }
        }

        private void onUidImportanceInternal(int uid, int importance) {
        private void onUidImportanceUnflagged(int uid, int importance) {
            synchronized (mPendingCallbackSelfLocked) {
                if (importance >= IMPORTANCE_GONE) {
                    // Clean up as the app is already gone
@@ -1068,6 +1083,83 @@ public final class DisplayManagerService extends SystemService {
                mPendingCallbackSelfLocked.delete(uid);
            }
        }

        private void onUidImportanceFlagged(int uid, int importance) {
            final boolean cached = (importance >= IMPORTANCE_CACHED);
            List<CallbackRecord> readyCallbackRecords = null;
            synchronized (mSyncRoot) {
                final SparseArray<CallbackRecord> procs = mCallbackRecordByPidByUid.get(uid);
                if (procs == null) {
                    return;
                }
                if (cached) {
                    setCachedLocked(procs);
                } else {
                    readyCallbackRecords = setUncachedLocked(procs);
                }
            }
            if (readyCallbackRecords != null) {
                // Attempt to dispatch pending events if the UID is coming out of cached state.
                for (int i = 0; i < readyCallbackRecords.size(); i++) {
                    readyCallbackRecords.get(i).dispatchPending();
                }
            }
        }

        // Set all processes in the list to cached.
        @GuardedBy("mSyncRoot")
        private void setCachedLocked(SparseArray<CallbackRecord> procs) {
            for (int i = 0; i < procs.size(); i++) {
                final CallbackRecord cb = procs.valueAt(i);
                if (cb != null) {
                    cb.setCached(true);
                }
            }
        }

        // Set all processes to uncached and return the list of processes that were modified.
        @GuardedBy("mSyncRoot")
        private List<CallbackRecord> setUncachedLocked(SparseArray<CallbackRecord> procs) {
            ArrayList<CallbackRecord> ready = null;
            for (int i = 0; i < procs.size(); i++) {
                final CallbackRecord cb = procs.valueAt(i);
                if (cb != null) {
                    if (cb.setCached(false)) {
                        if (ready == null) ready = new ArrayList<>();
                        ready.add(cb);
                    }
                }
            }
            return ready;
        }
    }

    private class DisplayFrozenProcessListener
            implements ActivityManagerInternal.FrozenProcessListener {
        public void onProcessFrozen(int pid) {
            synchronized (mSyncRoot) {
                CallbackRecord callback = mCallbacks.get(pid);
                if (callback == null) {
                    return;
                }
                callback.setFrozen(true);
            }
        }

        public void onProcessUnfrozen(int pid) {
            // First, see if there is a callback associated with this pid.  If there's no
            // callback, then there is nothing to do.
            CallbackRecord callback;
            synchronized (mSyncRoot) {
                callback = mCallbacks.get(pid);
                if (callback == null) {
                    return;
                }
                callback.setFrozen(false);
            }
            // Attempt to dispatch pending events if the process is coming out of frozen.
            callback.dispatchPending();
        }
    }

    private class SettingsObserver extends ContentObserver {
@@ -1314,12 +1406,29 @@ public final class DisplayManagerService extends SystemService {
            }

            mCallbacks.put(callingPid, record);
            if (deferDisplayEventsWhenFrozen()) {
                SparseArray<CallbackRecord> uidPeers = mCallbackRecordByPidByUid.get(record.mUid);
                if (uidPeers == null) {
                    uidPeers = new SparseArray<CallbackRecord>();
                    mCallbackRecordByPidByUid.put(record.mUid, uidPeers);
                }
                uidPeers.put(record.mPid, record);
            }
        }
    }

    private void onCallbackDied(CallbackRecord record) {
        synchronized (mSyncRoot) {
            mCallbacks.remove(record.mPid);
            if (deferDisplayEventsWhenFrozen()) {
                SparseArray<CallbackRecord> uidPeers = mCallbackRecordByPidByUid.get(record.mUid);
                if (uidPeers != null) {
                    uidPeers.remove(record.mPid);
                    if (uidPeers.size() == 0) {
                        mCallbackRecordByPidByUid.remove(record.mUid);
                    }
                }
            }
            stopWifiDisplayScanLocked(record);
        }
    }
@@ -3296,12 +3405,16 @@ public final class DisplayManagerService extends SystemService {
        // After releasing the lock, send the notifications out.
        for (int i = 0; i < mTempCallbacks.size(); i++) {
            CallbackRecord callbackRecord = mTempCallbacks.get(i);
            deliverEventInternal(callbackRecord, displayId, event);
            if (deferDisplayEventsWhenFrozen()) {
                deliverEventFlagged(callbackRecord, displayId, event);
            } else {
                deliverEventUnflagged(callbackRecord, displayId, event);
            }
        }
        mTempCallbacks.clear();
    }

    private void deliverEventInternal(CallbackRecord callbackRecord, int displayId, int event) {
    private void deliverEventUnflagged(CallbackRecord callbackRecord, int displayId, int event) {
        final int uid = callbackRecord.mUid;
        final int pid = callbackRecord.mPid;
        if (isUidCached(uid)) {
@@ -3330,6 +3443,10 @@ public final class DisplayManagerService extends SystemService {
        }
    }

    private void deliverEventFlagged(CallbackRecord callbackRecord, int displayId, int event) {
        callbackRecord.notifyDisplayEventAsync(displayId, event);
    }

    private boolean extraLogging(String packageName) {
        return mExtraDisplayEventLogging && mExtraDisplayLoggingPackageName.equals(packageName);
    }
@@ -3454,9 +3571,7 @@ public final class DisplayManagerService extends SystemService {
            pw.println("Callbacks: size=" + callbackCount);
            pw.println("-----------------");
            for (int i = 0; i < callbackCount; i++) {
                CallbackRecord callback = mCallbacks.valueAt(i);
                pw.println("  " + i + ": mPid=" + callback.mPid
                        + ", mWifiDisplayScanRequested=" + callback.mWifiDisplayScanRequested);
                pw.println("  " + i + ": " + mCallbacks.valueAt(i).dump());
            }

            pw.println();
@@ -3855,12 +3970,43 @@ public final class DisplayManagerService extends SystemService {

        public boolean mWifiDisplayScanRequested;

        // A single pending event.
        private record Event(int displayId, @DisplayEvent int event) { };

        // The list of pending events.  This is null until there is a pending event to be saved.
        // This is only used if {@link deferDisplayEventsWhenFrozen()} is true.
        @GuardedBy("mCallback")
        private ArrayList<Event> mPendingEvents;

        // Process states: a process is ready to receive events if it is neither cached nor
        // frozen.
        @GuardedBy("mCallback")
        private boolean mCached;
        @GuardedBy("mCallback")
        private boolean mFrozen;

        CallbackRecord(int pid, int uid, @NonNull IDisplayManagerCallback callback,
                @EventsMask long eventsMask) {
            mPid = pid;
            mUid = uid;
            mCallback = callback;
            mEventsMask = new AtomicLong(eventsMask);
            mCached = false;
            mFrozen = false;

            if (deferDisplayEventsWhenFrozen()) {
                // Some CallbackRecords are registered very early in system boot, before
                // mActivityManagerInternal is initialized. If mActivityManagerInternal is null,
                // do not register the frozen process listener.  However, do verify that all such
                // registrations are for the self pid (which can never be frozen, so the frozen
                // process listener does not matter).
                if (mActivityManagerInternal != null) {
                    mActivityManagerInternal.addFrozenProcessListener(pid, mHandlerExecutor,
                            mDisplayFrozenProcessListener);
                } else if (Process.myPid() != pid) {
                    Slog.e(TAG, "DisplayListener registered too early");
                }
            }

            String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid);
            mPackageName = packageNames == null ? null : packageNames[0];
@@ -3870,6 +4016,46 @@ public final class DisplayManagerService extends SystemService {
            mEventsMask.set(eventsMask);
        }

        /**
         * Return true if the process can accept events.
         */
        @GuardedBy("mCallback")
        private boolean isReadyLocked() {
            return !mCached && !mFrozen;
        }

        /**
         * Return true if the process is now ready and has pending events to be delivered.
         */
        @GuardedBy("mCallback")
        private boolean hasPendingAndIsReadyLocked() {
            return isReadyLocked() && mPendingEvents != null && !mPendingEvents.isEmpty();
        }

        /**
         * Set the frozen flag for this process.  Return true if the process is now ready to
         * receive events and there are pending events to be delivered.
         * This is only used if {@link deferDisplayEventsWhenFrozen()} is true.
         */
        public boolean setFrozen(boolean frozen) {
            synchronized (mCallback) {
                mFrozen = frozen;
                return hasPendingAndIsReadyLocked();
            }
        }

        /**
         * Set the cached flag for this process.  Return true if the process is now ready to
         * receive events and there are pending events to be delivered.
         * This is only used if {@link deferDisplayEventsWhenFrozen()} is true.
         */
        public boolean setCached(boolean cached) {
            synchronized (mCallback) {
                mCached = cached;
                return hasPendingAndIsReadyLocked();
            }
        }

        @Override
        public void binderDied() {
            if (DEBUG || extraLogging(mPackageName)) {
@@ -3885,7 +4071,7 @@ public final class DisplayManagerService extends SystemService {
        /**
         * @return {@code false} if RemoteException happens; otherwise {@code true} for
         * success.  This returns true even if the event was deferred because the remote client is
         * cached.
         * cached or frozen.
         */
        public boolean notifyDisplayEventAsync(int displayId, @DisplayEvent int event) {
            if (!shouldSendEvent(event)) {
@@ -3902,6 +4088,22 @@ public final class DisplayManagerService extends SystemService {
                return true;
            }

            if (deferDisplayEventsWhenFrozen()) {
                synchronized (mCallback) {
                    // Add the new event to the pending list if the client frozen or cached (not
                    // ready) or if there are existing pending events.  The latter condition
                    // occurs as the client is transitioning to ready but pending events have not
                    // been dispatched.  The new event must be added to the pending list to
                    // preserve event ordering.
                    if (!isReadyLocked() || (mPendingEvents != null && !mPendingEvents.isEmpty())) {
                        // The client is interested in the event but is not ready to receive it.
                        // Put the event on the pending list.
                        addDisplayEvent(displayId, event);
                        return true;
                    }
                }
            }

            return transmitDisplayEvent(displayId, event);
        }

@@ -3948,8 +4150,81 @@ public final class DisplayManagerService extends SystemService {
                    return true;
            }
        }

        // Add a single event to the pending list, possibly combining or collapsing events in the
        // list.
        // This is only used if {@link deferDisplayEventsWhenFrozen()} is true.
        @GuardedBy("mCallback")
        private void addDisplayEvent(int displayId, int event) {
            if (mPendingEvents == null) {
                mPendingEvents = new ArrayList<>();
            }
            if (!mPendingEvents.isEmpty()) {
                // Ignore redundant events. Further optimization is possible by merging adjacent
                // events.
                Event last = mPendingEvents.get(mPendingEvents.size() - 1);
                if (last.displayId == displayId && last.event == event) {
                    if (DEBUG) {
                        Slog.d(TAG, "Ignore redundant display event " + displayId + "/" + event
                                + " to " + mUid + "/" + mPid);
                    }
                    return;
                }
            }
            mPendingEvents.add(new Event(displayId, event));
        }

        // Send all pending events.  This can safely be called if the process is not ready, but it
        // would be unusual to do so.  The method returns true on success.
        // This is only used if {@link deferDisplayEventsWhenFrozen()} is true.
        public boolean dispatchPending() {
            synchronized (mCallback) {
                if (mPendingEvents == null || mPendingEvents.isEmpty()) {
                    return true;
                }
                if (!isReadyLocked()) {
                    return false;
                }
                for (int i = 0; i < mPendingEvents.size(); i++) {
                    Event displayEvent = mPendingEvents.get(i);
                    if (DEBUG) {
                        Slog.d(TAG, "Send pending display event #" + i + " "
                                + displayEvent.displayId + "/"
                                + displayEvent.event + " to " + mUid + "/" + mPid);
                    }
                    if (!transmitDisplayEvent(displayEvent.displayId, displayEvent.event)) {
                        Slog.d(TAG, "Drop pending events for dead process " + mPid);
                        break;
                    }
                }
                mPendingEvents.clear();
                return true;
            }
        }

        // Return a string suitable for dumpsys.
        private String dump() {
            if (deferDisplayEventsWhenFrozen()) {
                final String fmt =
                        "mPid=%d mUid=%d mWifiDisplayScanRequested=%s"
                        + " cached=%s frozen=%s pending=%d";
                synchronized (mCallback) {
                    return formatSimple(fmt,
                            mPid, mUid, mWifiDisplayScanRequested, mCached, mFrozen,
                            (mPendingEvents == null) ? 0 : mPendingEvents.size());
                }
            } else {
                final String fmt =
                        "mPid=%d mUid=%d mWifiDisplayScanRequested=%s";
                return formatSimple(fmt,
                        mPid, mUid, mWifiDisplayScanRequested);
            }
        }
    }

    /**
     * This is only used if {@link deferDisplayEventsWhenFrozen()} is false.
     */
    private static final class PendingCallback {
        private final CallbackRecord mCallbackRecord;
        private final ArrayList<Pair<Integer, Integer>> mDisplayEvents;
@@ -5504,4 +5779,11 @@ public final class DisplayManagerService extends SystemService {
            return mExternalDisplayStatsService;
        }
    }

    /**
     * Return the value of the pause
     */
    private static boolean deferDisplayEventsWhenFrozen() {
        return com.android.server.am.Flags.deferDisplayEventsWhenFrozen();
    }
}
+158 −30

File changed.

Preview size limit exceeded, changes collapsed.