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

Commit 5fd93737 authored by Bishoy Gendy's avatar Bishoy Gendy Committed by Santiago Seifert
Browse files

Fix deadlock between MediaSessionService and MediaSessionRecord

Bug: 335454915
Test: atest CtsMediaBetterTogetherTestCases
Test: atest cts/tests/app/src/android/app/cts/ActivityManagerFgsDelegateTest.java
Flag: com.android.media.flags.enable_notifying_activity_manager_with_media_session_status_change
Change-Id: I4160e62184fafe2508b904e35a1adf10e96d4d23
parent 07d8de08
Loading
Loading
Loading
Loading
+31 −19
Original line number Diff line number Diff line
@@ -81,6 +81,8 @@ import android.util.Log;
import android.util.Slog;
import android.view.KeyEvent;

import com.android.internal.annotations.GuardedBy;
import com.android.media.flags.Flags;
import com.android.server.LocalServices;
import com.android.server.uri.UriGrantsManagerInternal;

@@ -229,6 +231,14 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde

    private int mPolicies;

    private final Runnable mUserEngagementTimeoutExpirationRunnable =
            () -> {
                synchronized (mLock) {
                    updateUserEngagedStateIfNeededLocked(/* isTimeoutExpired= */ true);
                }
            };

    @GuardedBy("mLock")
    private @UserEngagementState int mUserEngagementState = USER_DISENGAGED;

    @IntDef({USER_PERMANENTLY_ENGAGED, USER_TEMPORARY_ENGAGED, USER_DISENGAGED})
@@ -238,26 +248,26 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
    /**
     * Indicates that the session is active and in one of the user engaged states.
     *
     * @see #updateUserEngagedStateIfNeededLocked(boolean) ()
     * @see #updateUserEngagedStateIfNeededLocked(boolean)
     */
    private static final int USER_PERMANENTLY_ENGAGED = 0;

    /**
     * Indicates that the session is active and in {@link PlaybackState#STATE_PAUSED} state.
     *
     * @see #updateUserEngagedStateIfNeededLocked(boolean) ()
     * @see #updateUserEngagedStateIfNeededLocked(boolean)
     */
    private static final int USER_TEMPORARY_ENGAGED = 1;

    /**
     * Indicates that the session is either not active or in one of the user disengaged states
     *
     * @see #updateUserEngagedStateIfNeededLocked(boolean) ()
     * @see #updateUserEngagedStateIfNeededLocked(boolean)
     */
    private static final int USER_DISENGAGED = 2;

    /**
     * Indicates the duration of the temporary engaged states.
     * Indicates the duration of the temporary engaged states, in milliseconds.
     *
     * <p>Some {@link MediaSession} states like {@link PlaybackState#STATE_PAUSED} are temporarily
     * engaged, meaning the corresponding session is only considered in an engaged state for the
@@ -270,7 +280,7 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
     * user-engaged state is not considered user-engaged when transitioning from a non-user engaged
     * state {@link PlaybackState#STATE_STOPPED}.
     */
    private static final int TEMP_USER_ENGAGED_TIMEOUT = 600000;
    private static final int TEMP_USER_ENGAGED_TIMEOUT_MS = 600000;

    public MediaSessionRecord(
            int ownerPid,
@@ -609,8 +619,7 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde

    @Override
    public void expireTempEngaged() {
        mHandler.removeCallbacks(mHandleTempEngagedSessionTimeout);
        updateUserEngagedStateIfNeededLocked(/* isTimeoutExpired= */ true);
        mHandler.post(mUserEngagementTimeoutExpirationRunnable);
    }

    /**
@@ -1086,11 +1095,6 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
                }
            };

    private final Runnable mHandleTempEngagedSessionTimeout =
            () -> {
                updateUserEngagedStateIfNeededLocked(/* isTimeoutExpired= */ true);
            };

    @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
    private static boolean componentNameExists(
            @NonNull ComponentName componentName, @NonNull Context context, int userId) {
@@ -1107,10 +1111,14 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
        return !resolveInfos.isEmpty();
    }

    @GuardedBy("mLock")
    private void updateUserEngagedStateIfNeededLocked(boolean isTimeoutExpired) {
        if (!Flags.enableNotifyingActivityManagerWithMediaSessionStatusChange()) {
            return;
        }
        int oldUserEngagedState = mUserEngagementState;
        int newUserEngagedState;
        if (!isActive() || mPlaybackState == null) {
        if (!isActive() || mPlaybackState == null || mDestroyed) {
            newUserEngagedState = USER_DISENGAGED;
        } else if (isActive() && mPlaybackState.isActive()) {
            newUserEngagedState = USER_PERMANENTLY_ENGAGED;
@@ -1126,18 +1134,22 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
            return;
        }

        mUserEngagementState = newUserEngagedState;
        if (newUserEngagedState == USER_TEMPORARY_ENGAGED) {
            mHandler.postDelayed(mHandleTempEngagedSessionTimeout, TEMP_USER_ENGAGED_TIMEOUT);
        } else if (oldUserEngagedState == USER_TEMPORARY_ENGAGED) {
            mHandler.removeCallbacks(mHandleTempEngagedSessionTimeout);
            mHandler.postDelayed(
                    mUserEngagementTimeoutExpirationRunnable, TEMP_USER_ENGAGED_TIMEOUT_MS);
        } else {
            mHandler.removeCallbacks(mUserEngagementTimeoutExpirationRunnable);
        }

        boolean wasUserEngaged = oldUserEngagedState != USER_DISENGAGED;
        boolean isNowUserEngaged = newUserEngagedState != USER_DISENGAGED;
        mUserEngagementState = newUserEngagedState;
        if (wasUserEngaged != isNowUserEngaged) {
            mHandler.post(
                    () ->
                            mService.onSessionUserEngagementStateChange(
                    /* mediaSessionRecord= */ this, /* isUserEngaged= */ isNowUserEngaged);
                                    /* mediaSessionRecord= */ this,
                                    /* isUserEngaged= */ isNowUserEngaged));
        }
    }

+15 −0
Original line number Diff line number Diff line
@@ -715,6 +715,12 @@ public class MediaSessionService extends SystemService implements Monitor {
            ForegroundServiceDelegationOptions foregroundServiceDelegationOptions) {
        final long token = Binder.clearCallingIdentity();
        try {
            Log.i(
                    TAG,
                    TextUtils.formatSimple(
                            "startFgsDelegate: pkg=%s uid=%d",
                            foregroundServiceDelegationOptions.mClientPackageName,
                            foregroundServiceDelegationOptions.mClientUid));
            mActivityManagerInternal.startForegroundServiceDelegate(
                    foregroundServiceDelegationOptions, /* connection= */ null);
        } finally {
@@ -756,6 +762,12 @@ public class MediaSessionService extends SystemService implements Monitor {
            ForegroundServiceDelegationOptions foregroundServiceDelegationOptions) {
        final long token = Binder.clearCallingIdentity();
        try {
            Log.i(
                    TAG,
                    TextUtils.formatSimple(
                            "stopFgsDelegate: pkg=%s uid=%d",
                            foregroundServiceDelegationOptions.mClientPackageName,
                            foregroundServiceDelegationOptions.mClientUid));
            mActivityManagerInternal.stopForegroundServiceDelegate(
                    foregroundServiceDelegationOptions);
        } finally {
@@ -2679,6 +2691,9 @@ public class MediaSessionService extends SystemService implements Monitor {

        @Override
        public void expireTempEngagedSessions() {
            if (!Flags.enableNotifyingActivityManagerWithMediaSessionStatusChange()) {
                return;
            }
            synchronized (mLock) {
                for (Set<MediaSessionRecordImpl> uidSessions :
                        mUserEngagedSessionsForFgs.values()) {