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

Commit 5adc3f5a authored by tonihei's avatar tonihei
Browse files

Interrupt temporarily engaged timer while priority session is active

Session are expected to enter a temporarily engaged state (e.g.
PAUSED) while the global priority session is active, typically
due a transient audio focus loss. While the global priority session
is active, we can keep the temporary engaged state without a timeout
because the session is expected to go back to an engaged state after
the priority session became inactive.

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
Bug: 295518668

Change-Id: Ia815d9d84baeea9e92f6daa940571e2f69386b40
parent 0d7fcc1d
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -160,6 +160,11 @@ public class MediaSession2Record extends MediaSessionRecordImpl {
        // NA as MediaSession2 doesn't support UserEngagementStates for FGS.
    }

    @Override
    public void onGlobalPrioritySessionActiveChanged(boolean isGlobalPrioritySessionActive) {
        // NA as MediaSession2 doesn't support UserEngagementStates for FGS.
    }

    @Override
    public boolean sendMediaButton(String packageName, int pid, int uid, boolean asSystemService,
            KeyEvent ke, int sequenceId, ResultReceiver cb) {
+32 −6
Original line number Diff line number Diff line
@@ -234,7 +234,9 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
    private final Runnable mUserEngagementTimeoutExpirationRunnable =
            () -> {
                synchronized (mLock) {
                    updateUserEngagedStateIfNeededLocked(/* isTimeoutExpired= */ true);
                    updateUserEngagedStateIfNeededLocked(
                            /* isTimeoutExpired= */ true,
                            /* isGlobalPrioritySessionActive= */ false);
                }
            };

@@ -600,7 +602,8 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
            mSessionCb.mCb.asBinder().unlinkToDeath(this, 0);
            mDestroyed = true;
            mPlaybackState = null;
            updateUserEngagedStateIfNeededLocked(/* isTimeoutExpired= */ true);
            updateUserEngagedStateIfNeededLocked(
                    /* isTimeoutExpired= */ true, /* isGlobalPrioritySessionActive= */ false);
            mHandler.post(MessageHandler.MSG_DESTROYED);
        }
    }
@@ -617,6 +620,24 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
        mHandler.post(mUserEngagementTimeoutExpirationRunnable);
    }

    @Override
    public void onGlobalPrioritySessionActiveChanged(boolean isGlobalPrioritySessionActive) {
        mHandler.post(
                () -> {
                    synchronized (mLock) {
                        if (isGlobalPrioritySessionActive) {
                            mHandler.removeCallbacks(mUserEngagementTimeoutExpirationRunnable);
                        } else {
                            if (mUserEngagementState == USER_TEMPORARILY_ENGAGED) {
                                mHandler.postDelayed(
                                        mUserEngagementTimeoutExpirationRunnable,
                                        TEMP_USER_ENGAGED_TIMEOUT_MS);
                            }
                        }
                    }
                });
    }

    /**
     * Sends media button.
     *
@@ -1107,7 +1128,8 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
    }

    @GuardedBy("mLock")
    private void updateUserEngagedStateIfNeededLocked(boolean isTimeoutExpired) {
    private void updateUserEngagedStateIfNeededLocked(
            boolean isTimeoutExpired, boolean isGlobalPrioritySessionActive) {
        if (!Flags.enableNotifyingActivityManagerWithMediaSessionStatusChange()) {
            return;
        }
@@ -1128,7 +1150,7 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
        }

        mUserEngagementState = newUserEngagedState;
        if (newUserEngagedState == USER_TEMPORARILY_ENGAGED) {
        if (newUserEngagedState == USER_TEMPORARILY_ENGAGED && !isGlobalPrioritySessionActive) {
            mHandler.postDelayed(
                    mUserEngagementTimeoutExpirationRunnable, TEMP_USER_ENGAGED_TIMEOUT_MS);
        } else {
@@ -1183,9 +1205,11 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
                        .logFgsApiEnd(ActivityManager.FOREGROUND_SERVICE_API_TYPE_MEDIA_PLAYBACK,
                                callingUid, callingPid);
            }
            boolean isGlobalPrioritySessionActive = mService.isGlobalPrioritySessionActive();
            synchronized (mLock) {
                mIsActive = active;
                updateUserEngagedStateIfNeededLocked(/* isTimeoutExpired= */ false);
                updateUserEngagedStateIfNeededLocked(
                        /* isTimeoutExpired= */ false, isGlobalPrioritySessionActive);
            }
            long token = Binder.clearCallingIdentity();
            try {
@@ -1342,9 +1366,11 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
            boolean shouldUpdatePriority = ALWAYS_PRIORITY_STATES.contains(newState)
                    || (!TRANSITION_PRIORITY_STATES.contains(oldState)
                    && TRANSITION_PRIORITY_STATES.contains(newState));
            boolean isGlobalPrioritySessionActive = mService.isGlobalPrioritySessionActive();
            synchronized (mLock) {
                mPlaybackState = state;
                updateUserEngagedStateIfNeededLocked(/* isTimeoutExpired= */ false);
                updateUserEngagedStateIfNeededLocked(
                        /* isTimeoutExpired= */ false, isGlobalPrioritySessionActive);
            }
            final long token = Binder.clearCallingIdentity();
            try {
+4 −0
Original line number Diff line number Diff line
@@ -206,6 +206,10 @@ public abstract class MediaSessionRecordImpl {
     */
    public abstract void expireTempEngaged();

    /** Notifies record that the global priority session active state changed. */
    public abstract void onGlobalPrioritySessionActiveChanged(
            boolean isGlobalPrioritySessionActive);

    @Override
    public final boolean equals(Object o) {
        if (this == o) return true;
+35 −2
Original line number Diff line number Diff line
@@ -362,6 +362,7 @@ public class MediaSessionService extends SystemService implements Monitor {
                                    + record.isActive());
                }
                user.pushAddressedPlayerChangedLocked();
                mHandler.post(this::notifyGlobalPrioritySessionActiveChanged);
            } else {
                if (!user.mPriorityStack.contains(record)) {
                    Log.w(TAG, "Unknown session updated. Ignoring.");
@@ -394,11 +395,16 @@ public class MediaSessionService extends SystemService implements Monitor {

    // Currently only media1 can become global priority session.
    void setGlobalPrioritySession(MediaSessionRecord record) {
        boolean globalPrioritySessionActiveChanged = false;
        synchronized (mLock) {
            FullUserRecord user = getFullUserRecordLocked(record.getUserId());
            if (mGlobalPrioritySession != record) {
                Log.d(TAG, "Global priority session is changed from " + mGlobalPrioritySession
                        + " to " + record);
                globalPrioritySessionActiveChanged =
                        (mGlobalPrioritySession == null && record.isActive())
                                || (mGlobalPrioritySession != null
                                        && mGlobalPrioritySession.isActive() != record.isActive());
                mGlobalPrioritySession = record;
                if (user != null && user.mPriorityStack.contains(record)) {
                    // Handle the global priority session separately.
@@ -409,6 +415,30 @@ public class MediaSessionService extends SystemService implements Monitor {
                }
            }
        }
        if (globalPrioritySessionActiveChanged) {
            mHandler.post(this::notifyGlobalPrioritySessionActiveChanged);
        }
    }

    /** Returns whether the global priority session is active. */
    boolean isGlobalPrioritySessionActive() {
        synchronized (mLock) {
            return isGlobalPriorityActiveLocked();
        }
    }

    private void notifyGlobalPrioritySessionActiveChanged() {
        if (!Flags.enableNotifyingActivityManagerWithMediaSessionStatusChange()) {
            return;
        }
        synchronized (mLock) {
            boolean isGlobalPriorityActive = isGlobalPriorityActiveLocked();
            for (Set<MediaSessionRecordImpl> records : mUserEngagedSessionsForFgs.values()) {
                for (MediaSessionRecordImpl record : records) {
                    record.onGlobalPrioritySessionActiveChanged(isGlobalPriorityActive);
                }
            }
        }
    }

    private List<MediaSessionRecord> getActiveSessionsLocked(int userId) {
@@ -646,9 +676,12 @@ public class MediaSessionService extends SystemService implements Monitor {

        if (mGlobalPrioritySession == session) {
            mGlobalPrioritySession = null;
            if (session.isActive() && user != null) {
            if (session.isActive()) {
                if (user != null) {
                    user.pushAddressedPlayerChangedLocked();
                }
                mHandler.post(this::notifyGlobalPrioritySessionActiveChanged);
            }
        } else {
            if (user != null) {
                user.mPriorityStack.removeSession(session);