Loading media/java/android/media/session/ISessionManager.aidl +3 −0 Original line number Diff line number Diff line Loading @@ -80,4 +80,7 @@ interface ISessionManager { boolean hasCustomMediaSessionPolicyProvider(String componentName); int getSessionPolicies(in MediaSession.Token token); void setSessionPolicies(in MediaSession.Token token, int policies); // For testing of temporarily engaged sessions. void expireTempEngagedSessions(); } services/core/java/com/android/server/media/MediaSession2Record.java +5 −0 Original line number Diff line number Diff line Loading @@ -156,6 +156,11 @@ public class MediaSession2Record extends MediaSessionRecordImpl { } } @Override public void expireTempEngaged() { // 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) { Loading services/core/java/com/android/server/media/MediaSessionRecord.java +97 −2 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import static android.media.session.MediaController.PlaybackInfo.PLAYBACK_TYPE_L import static android.media.session.MediaController.PlaybackInfo.PLAYBACK_TYPE_REMOTE; import android.Manifest; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; Loading Loading @@ -85,6 +86,8 @@ import com.android.server.LocalServices; import com.android.server.uri.UriGrantsManagerInternal; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; Loading Loading @@ -225,6 +228,49 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde private int mPolicies; private @UserEngagementState int mUserEngagementState = USER_DISENGAGED; @IntDef({USER_PERMANENTLY_ENGAGED, USER_TEMPORARY_ENGAGED, USER_DISENGAGED}) @Retention(RetentionPolicy.SOURCE) private @interface UserEngagementState {} /** * Indicates that the session is active and in one of the user engaged states. * * @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) () */ 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) () */ private static final int USER_DISENGAGED = 2; /** * Indicates the duration of the temporary engaged states. * * <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 * duration of this timeout, and only if coming from an engaged state. * * <p>For example, if a session is transitioning from a user-engaged state {@link * PlaybackState#STATE_PLAYING} to a temporary user-engaged state {@link * PlaybackState#STATE_PAUSED}, then the session will be considered in a user-engaged state for * the duration of this timeout, starting at the transition instant. However, a temporary * 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; public MediaSessionRecord( int ownerPid, int ownerUid, Loading Loading @@ -548,6 +594,7 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde mSessionCb.mCb.asBinder().unlinkToDeath(this, 0); mDestroyed = true; mPlaybackState = null; updateUserEngagedStateIfNeededLocked(/* isTimeoutExpired= */ true); mHandler.post(MessageHandler.MSG_DESTROYED); } } Loading @@ -559,6 +606,12 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde } } @Override public void expireTempEngaged() { mHandler.removeCallbacks(mHandleTempEngagedSessionTimeout); updateUserEngagedStateIfNeededLocked(/* isTimeoutExpired= */ true); } /** * Sends media button. * Loading Loading @@ -1129,6 +1182,11 @@ 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) { Loading @@ -1145,6 +1203,40 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde return !resolveInfos.isEmpty(); } private void updateUserEngagedStateIfNeededLocked(boolean isTimeoutExpired) { int oldUserEngagedState = mUserEngagementState; int newUserEngagedState; if (!isActive() || mPlaybackState == null) { newUserEngagedState = USER_DISENGAGED; } else if (isActive() && mPlaybackState.isActive()) { newUserEngagedState = USER_PERMANENTLY_ENGAGED; } else if (mPlaybackState.getState() == PlaybackState.STATE_PAUSED) { newUserEngagedState = oldUserEngagedState == USER_PERMANENTLY_ENGAGED || !isTimeoutExpired ? USER_TEMPORARY_ENGAGED : USER_DISENGAGED; } else { newUserEngagedState = USER_DISENGAGED; } if (oldUserEngagedState == newUserEngagedState) { return; } if (newUserEngagedState == USER_TEMPORARY_ENGAGED) { mHandler.postDelayed(mHandleTempEngagedSessionTimeout, TEMP_USER_ENGAGED_TIMEOUT); } else if (oldUserEngagedState == USER_TEMPORARY_ENGAGED) { mHandler.removeCallbacks(mHandleTempEngagedSessionTimeout); } boolean wasUserEngaged = oldUserEngagedState != USER_DISENGAGED; boolean isNowUserEngaged = newUserEngagedState != USER_DISENGAGED; mUserEngagementState = newUserEngagedState; if (wasUserEngaged != isNowUserEngaged) { mService.onSessionUserEngagementStateChange( /* mediaSessionRecord= */ this, /* isUserEngaged= */ isNowUserEngaged); } } private final class SessionStub extends ISession.Stub { @Override public void destroySession() throws RemoteException { Loading Loading @@ -1182,8 +1274,10 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde .logFgsApiEnd(ActivityManager.FOREGROUND_SERVICE_API_TYPE_MEDIA_PLAYBACK, callingUid, callingPid); } synchronized (mLock) { mIsActive = active; updateUserEngagedStateIfNeededLocked(/* isTimeoutExpired= */ false); } long token = Binder.clearCallingIdentity(); try { mService.onSessionActiveStateChanged(MediaSessionRecord.this, mPlaybackState); Loading Loading @@ -1341,6 +1435,7 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde && TRANSITION_PRIORITY_STATES.contains(newState)); synchronized (mLock) { mPlaybackState = state; updateUserEngagedStateIfNeededLocked(/* isTimeoutExpired= */ false); } final long token = Binder.clearCallingIdentity(); try { Loading services/core/java/com/android/server/media/MediaSessionRecordImpl.java +6 −0 Original line number Diff line number Diff line Loading @@ -196,6 +196,12 @@ public abstract class MediaSessionRecordImpl { */ public abstract boolean isClosed(); /** * Note: This method is only used for testing purposes If the session is temporary engaged, the * timeout will expire and it will become disengaged. */ public abstract void expireTempEngaged(); @Override public final boolean equals(Object o) { if (this == o) return true; Loading services/core/java/com/android/server/media/MediaSessionService.java +108 −66 Original line number Diff line number Diff line Loading @@ -367,11 +367,13 @@ public class MediaSessionService extends SystemService implements Monitor { } boolean isUserEngaged = isUserEngaged(record, playbackState); Log.d(TAG, "onSessionActiveStateChanged: " + "record=" + record + "playbackState=" + playbackState + "allowRunningInForeground=" + isUserEngaged); setForegroundServiceAllowance(record, /* allowRunningInForeground= */ isUserEngaged); Log.d( TAG, "onSessionActiveStateChanged:" + " record=" + record + " playbackState=" + playbackState); reportMediaInteractionEvent(record, isUserEngaged); mHandler.postSessionsChanged(record); } Loading Loading @@ -479,11 +481,13 @@ public class MediaSessionService extends SystemService implements Monitor { } user.mPriorityStack.onPlaybackStateChanged(record, shouldUpdatePriority); boolean isUserEngaged = isUserEngaged(record, playbackState); Log.d(TAG, "onSessionPlaybackStateChanged: " + "record=" + record + "playbackState=" + playbackState + "allowRunningInForeground=" + isUserEngaged); setForegroundServiceAllowance(record, /* allowRunningInForeground= */ isUserEngaged); Log.d( TAG, "onSessionPlaybackStateChanged:" + " record=" + record + " playbackState=" + playbackState); reportMediaInteractionEvent(record, isUserEngaged); } } Loading Loading @@ -650,69 +654,113 @@ public class MediaSessionService extends SystemService implements Monitor { session.close(); Log.d(TAG, "destroySessionLocked: record=" + session); setForegroundServiceAllowance(session, /* allowRunningInForeground= */ false); reportMediaInteractionEvent(session, /* userEngaged= */ false); mHandler.postSessionsChanged(session); } private void setForegroundServiceAllowance( MediaSessionRecordImpl record, boolean allowRunningInForeground) { if (!Flags.enableNotifyingActivityManagerWithMediaSessionStatusChange()) { return; } ForegroundServiceDelegationOptions foregroundServiceDelegationOptions = record.getForegroundServiceDelegationOptions(); if (foregroundServiceDelegationOptions == null) { return; } if (allowRunningInForeground) { onUserSessionEngaged(record); void onSessionUserEngagementStateChange( MediaSessionRecordImpl mediaSessionRecord, boolean isUserEngaged) { if (isUserEngaged) { addUserEngagedSession(mediaSessionRecord); startFgsIfSessionIsLinkedToNotification(mediaSessionRecord); } else { onUserDisengaged(record); removeUserEngagedSession(mediaSessionRecord); stopFgsIfNoSessionIsLinkedToNotification(mediaSessionRecord); } } private void onUserSessionEngaged(MediaSessionRecordImpl mediaSessionRecord) { private void addUserEngagedSession(MediaSessionRecordImpl mediaSessionRecord) { synchronized (mLock) { int uid = mediaSessionRecord.getUid(); mUserEngagedSessionsForFgs.putIfAbsent(uid, new HashSet<>()); mUserEngagedSessionsForFgs.get(uid).add(mediaSessionRecord); } } private void removeUserEngagedSession(MediaSessionRecordImpl mediaSessionRecord) { synchronized (mLock) { int uid = mediaSessionRecord.getUid(); Set<MediaSessionRecordImpl> mUidUserEngagedSessionsForFgs = mUserEngagedSessionsForFgs.get(uid); if (mUidUserEngagedSessionsForFgs == null) { return; } mUidUserEngagedSessionsForFgs.remove(mediaSessionRecord); if (mUidUserEngagedSessionsForFgs.isEmpty()) { mUserEngagedSessionsForFgs.remove(uid); } } } private void startFgsIfSessionIsLinkedToNotification( MediaSessionRecordImpl mediaSessionRecord) { Log.d(TAG, "startFgsIfSessionIsLinkedToNotification: record=" + mediaSessionRecord); if (!Flags.enableNotifyingActivityManagerWithMediaSessionStatusChange()) { return; } synchronized (mLock) { int uid = mediaSessionRecord.getUid(); for (Notification mediaNotification : mMediaNotifications.getOrDefault(uid, Set.of())) { if (mediaSessionRecord.isLinkedToNotification(mediaNotification)) { mActivityManagerInternal.startForegroundServiceDelegate( mediaSessionRecord.getForegroundServiceDelegationOptions(), /* connection= */ null); startFgsDelegate(mediaSessionRecord.getForegroundServiceDelegationOptions()); return; } } } } private void onUserDisengaged(MediaSessionRecordImpl mediaSessionRecord) { private void startFgsDelegate( ForegroundServiceDelegationOptions foregroundServiceDelegationOptions) { final long token = Binder.clearCallingIdentity(); try { mActivityManagerInternal.startForegroundServiceDelegate( foregroundServiceDelegationOptions, /* connection= */ null); } finally { Binder.restoreCallingIdentity(token); } } private void stopFgsIfNoSessionIsLinkedToNotification( MediaSessionRecordImpl mediaSessionRecord) { Log.d(TAG, "stopFgsIfNoSessionIsLinkedToNotification: record=" + mediaSessionRecord); if (!Flags.enableNotifyingActivityManagerWithMediaSessionStatusChange()) { return; } synchronized (mLock) { int uid = mediaSessionRecord.getUid(); if (mUserEngagedSessionsForFgs.containsKey(uid)) { mUserEngagedSessionsForFgs.get(uid).remove(mediaSessionRecord); if (mUserEngagedSessionsForFgs.get(uid).isEmpty()) { mUserEngagedSessionsForFgs.remove(uid); } ForegroundServiceDelegationOptions foregroundServiceDelegationOptions = mediaSessionRecord.getForegroundServiceDelegationOptions(); if (foregroundServiceDelegationOptions == null) { return; } boolean shouldStopFgs = true; for (MediaSessionRecordImpl sessionRecord : for (MediaSessionRecordImpl record : mUserEngagedSessionsForFgs.getOrDefault(uid, Set.of())) { for (Notification mediaNotification : mMediaNotifications.getOrDefault(uid, Set.of())) { if (sessionRecord.isLinkedToNotification(mediaNotification)) { shouldStopFgs = false; for (Notification mediaNotification : mMediaNotifications.getOrDefault(uid, Set.of())) { if (record.isLinkedToNotification(mediaNotification)) { // A user engaged session linked with a media notification is found. // We shouldn't call stop FGS in this case. return; } } } if (shouldStopFgs) { mActivityManagerInternal.stopForegroundServiceDelegate( mediaSessionRecord.getForegroundServiceDelegationOptions()); stopFgsDelegate(foregroundServiceDelegationOptions); } } private void stopFgsDelegate( ForegroundServiceDelegationOptions foregroundServiceDelegationOptions) { final long token = Binder.clearCallingIdentity(); try { mActivityManagerInternal.stopForegroundServiceDelegate( foregroundServiceDelegationOptions); } finally { Binder.restoreCallingIdentity(token); } } private void reportMediaInteractionEvent(MediaSessionRecordImpl record, boolean userEngaged) { Loading Loading @@ -2502,7 +2550,6 @@ public class MediaSessionService extends SystemService implements Monitor { } MediaSessionRecord session = null; MediaButtonReceiverHolder mediaButtonReceiverHolder = null; if (mCustomMediaKeyDispatcher != null) { MediaSession.Token token = mCustomMediaKeyDispatcher.getMediaSession( keyEvent, uid, asSystemService); Loading Loading @@ -2630,6 +2677,18 @@ public class MediaSessionService extends SystemService implements Monitor { && streamType <= AudioManager.STREAM_NOTIFICATION; } @Override public void expireTempEngagedSessions() { synchronized (mLock) { for (Set<MediaSessionRecordImpl> uidSessions : mUserEngagedSessionsForFgs.values()) { for (MediaSessionRecordImpl sessionRecord : uidSessions) { sessionRecord.expireTempEngaged(); } } } } private class MediaKeyListenerResultReceiver extends ResultReceiver implements Runnable { private final String mPackageName; private final int mPid; Loading Loading @@ -3127,7 +3186,6 @@ public class MediaSessionService extends SystemService implements Monitor { super.onNotificationPosted(sbn); Notification postedNotification = sbn.getNotification(); int uid = sbn.getUid(); if (!postedNotification.isMediaNotification()) { return; } Loading @@ -3138,11 +3196,9 @@ public class MediaSessionService extends SystemService implements Monitor { mUserEngagedSessionsForFgs.getOrDefault(uid, Set.of())) { ForegroundServiceDelegationOptions foregroundServiceDelegationOptions = mediaSessionRecord.getForegroundServiceDelegationOptions(); if (mediaSessionRecord.isLinkedToNotification(postedNotification) && foregroundServiceDelegationOptions != null) { mActivityManagerInternal.startForegroundServiceDelegate( foregroundServiceDelegationOptions, /* connection= */ null); if (foregroundServiceDelegationOptions != null && mediaSessionRecord.isLinkedToNotification(postedNotification)) { startFgsDelegate(foregroundServiceDelegationOptions); return; } } Loading Loading @@ -3173,21 +3229,7 @@ public class MediaSessionService extends SystemService implements Monitor { return; } boolean shouldStopFgs = true; for (MediaSessionRecordImpl mediaSessionRecord : mUserEngagedSessionsForFgs.getOrDefault(uid, Set.of())) { for (Notification mediaNotification : mMediaNotifications.getOrDefault(uid, Set.of())) { if (mediaSessionRecord.isLinkedToNotification(mediaNotification)) { shouldStopFgs = false; } } } if (shouldStopFgs && notificationRecord.getForegroundServiceDelegationOptions() != null) { mActivityManagerInternal.stopForegroundServiceDelegate( notificationRecord.getForegroundServiceDelegationOptions()); } stopFgsIfNoSessionIsLinkedToNotification(notificationRecord); } } Loading Loading
media/java/android/media/session/ISessionManager.aidl +3 −0 Original line number Diff line number Diff line Loading @@ -80,4 +80,7 @@ interface ISessionManager { boolean hasCustomMediaSessionPolicyProvider(String componentName); int getSessionPolicies(in MediaSession.Token token); void setSessionPolicies(in MediaSession.Token token, int policies); // For testing of temporarily engaged sessions. void expireTempEngagedSessions(); }
services/core/java/com/android/server/media/MediaSession2Record.java +5 −0 Original line number Diff line number Diff line Loading @@ -156,6 +156,11 @@ public class MediaSession2Record extends MediaSessionRecordImpl { } } @Override public void expireTempEngaged() { // 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) { Loading
services/core/java/com/android/server/media/MediaSessionRecord.java +97 −2 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import static android.media.session.MediaController.PlaybackInfo.PLAYBACK_TYPE_L import static android.media.session.MediaController.PlaybackInfo.PLAYBACK_TYPE_REMOTE; import android.Manifest; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; Loading Loading @@ -85,6 +86,8 @@ import com.android.server.LocalServices; import com.android.server.uri.UriGrantsManagerInternal; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; Loading Loading @@ -225,6 +228,49 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde private int mPolicies; private @UserEngagementState int mUserEngagementState = USER_DISENGAGED; @IntDef({USER_PERMANENTLY_ENGAGED, USER_TEMPORARY_ENGAGED, USER_DISENGAGED}) @Retention(RetentionPolicy.SOURCE) private @interface UserEngagementState {} /** * Indicates that the session is active and in one of the user engaged states. * * @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) () */ 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) () */ private static final int USER_DISENGAGED = 2; /** * Indicates the duration of the temporary engaged states. * * <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 * duration of this timeout, and only if coming from an engaged state. * * <p>For example, if a session is transitioning from a user-engaged state {@link * PlaybackState#STATE_PLAYING} to a temporary user-engaged state {@link * PlaybackState#STATE_PAUSED}, then the session will be considered in a user-engaged state for * the duration of this timeout, starting at the transition instant. However, a temporary * 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; public MediaSessionRecord( int ownerPid, int ownerUid, Loading Loading @@ -548,6 +594,7 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde mSessionCb.mCb.asBinder().unlinkToDeath(this, 0); mDestroyed = true; mPlaybackState = null; updateUserEngagedStateIfNeededLocked(/* isTimeoutExpired= */ true); mHandler.post(MessageHandler.MSG_DESTROYED); } } Loading @@ -559,6 +606,12 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde } } @Override public void expireTempEngaged() { mHandler.removeCallbacks(mHandleTempEngagedSessionTimeout); updateUserEngagedStateIfNeededLocked(/* isTimeoutExpired= */ true); } /** * Sends media button. * Loading Loading @@ -1129,6 +1182,11 @@ 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) { Loading @@ -1145,6 +1203,40 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde return !resolveInfos.isEmpty(); } private void updateUserEngagedStateIfNeededLocked(boolean isTimeoutExpired) { int oldUserEngagedState = mUserEngagementState; int newUserEngagedState; if (!isActive() || mPlaybackState == null) { newUserEngagedState = USER_DISENGAGED; } else if (isActive() && mPlaybackState.isActive()) { newUserEngagedState = USER_PERMANENTLY_ENGAGED; } else if (mPlaybackState.getState() == PlaybackState.STATE_PAUSED) { newUserEngagedState = oldUserEngagedState == USER_PERMANENTLY_ENGAGED || !isTimeoutExpired ? USER_TEMPORARY_ENGAGED : USER_DISENGAGED; } else { newUserEngagedState = USER_DISENGAGED; } if (oldUserEngagedState == newUserEngagedState) { return; } if (newUserEngagedState == USER_TEMPORARY_ENGAGED) { mHandler.postDelayed(mHandleTempEngagedSessionTimeout, TEMP_USER_ENGAGED_TIMEOUT); } else if (oldUserEngagedState == USER_TEMPORARY_ENGAGED) { mHandler.removeCallbacks(mHandleTempEngagedSessionTimeout); } boolean wasUserEngaged = oldUserEngagedState != USER_DISENGAGED; boolean isNowUserEngaged = newUserEngagedState != USER_DISENGAGED; mUserEngagementState = newUserEngagedState; if (wasUserEngaged != isNowUserEngaged) { mService.onSessionUserEngagementStateChange( /* mediaSessionRecord= */ this, /* isUserEngaged= */ isNowUserEngaged); } } private final class SessionStub extends ISession.Stub { @Override public void destroySession() throws RemoteException { Loading Loading @@ -1182,8 +1274,10 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde .logFgsApiEnd(ActivityManager.FOREGROUND_SERVICE_API_TYPE_MEDIA_PLAYBACK, callingUid, callingPid); } synchronized (mLock) { mIsActive = active; updateUserEngagedStateIfNeededLocked(/* isTimeoutExpired= */ false); } long token = Binder.clearCallingIdentity(); try { mService.onSessionActiveStateChanged(MediaSessionRecord.this, mPlaybackState); Loading Loading @@ -1341,6 +1435,7 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde && TRANSITION_PRIORITY_STATES.contains(newState)); synchronized (mLock) { mPlaybackState = state; updateUserEngagedStateIfNeededLocked(/* isTimeoutExpired= */ false); } final long token = Binder.clearCallingIdentity(); try { Loading
services/core/java/com/android/server/media/MediaSessionRecordImpl.java +6 −0 Original line number Diff line number Diff line Loading @@ -196,6 +196,12 @@ public abstract class MediaSessionRecordImpl { */ public abstract boolean isClosed(); /** * Note: This method is only used for testing purposes If the session is temporary engaged, the * timeout will expire and it will become disengaged. */ public abstract void expireTempEngaged(); @Override public final boolean equals(Object o) { if (this == o) return true; Loading
services/core/java/com/android/server/media/MediaSessionService.java +108 −66 Original line number Diff line number Diff line Loading @@ -367,11 +367,13 @@ public class MediaSessionService extends SystemService implements Monitor { } boolean isUserEngaged = isUserEngaged(record, playbackState); Log.d(TAG, "onSessionActiveStateChanged: " + "record=" + record + "playbackState=" + playbackState + "allowRunningInForeground=" + isUserEngaged); setForegroundServiceAllowance(record, /* allowRunningInForeground= */ isUserEngaged); Log.d( TAG, "onSessionActiveStateChanged:" + " record=" + record + " playbackState=" + playbackState); reportMediaInteractionEvent(record, isUserEngaged); mHandler.postSessionsChanged(record); } Loading Loading @@ -479,11 +481,13 @@ public class MediaSessionService extends SystemService implements Monitor { } user.mPriorityStack.onPlaybackStateChanged(record, shouldUpdatePriority); boolean isUserEngaged = isUserEngaged(record, playbackState); Log.d(TAG, "onSessionPlaybackStateChanged: " + "record=" + record + "playbackState=" + playbackState + "allowRunningInForeground=" + isUserEngaged); setForegroundServiceAllowance(record, /* allowRunningInForeground= */ isUserEngaged); Log.d( TAG, "onSessionPlaybackStateChanged:" + " record=" + record + " playbackState=" + playbackState); reportMediaInteractionEvent(record, isUserEngaged); } } Loading Loading @@ -650,69 +654,113 @@ public class MediaSessionService extends SystemService implements Monitor { session.close(); Log.d(TAG, "destroySessionLocked: record=" + session); setForegroundServiceAllowance(session, /* allowRunningInForeground= */ false); reportMediaInteractionEvent(session, /* userEngaged= */ false); mHandler.postSessionsChanged(session); } private void setForegroundServiceAllowance( MediaSessionRecordImpl record, boolean allowRunningInForeground) { if (!Flags.enableNotifyingActivityManagerWithMediaSessionStatusChange()) { return; } ForegroundServiceDelegationOptions foregroundServiceDelegationOptions = record.getForegroundServiceDelegationOptions(); if (foregroundServiceDelegationOptions == null) { return; } if (allowRunningInForeground) { onUserSessionEngaged(record); void onSessionUserEngagementStateChange( MediaSessionRecordImpl mediaSessionRecord, boolean isUserEngaged) { if (isUserEngaged) { addUserEngagedSession(mediaSessionRecord); startFgsIfSessionIsLinkedToNotification(mediaSessionRecord); } else { onUserDisengaged(record); removeUserEngagedSession(mediaSessionRecord); stopFgsIfNoSessionIsLinkedToNotification(mediaSessionRecord); } } private void onUserSessionEngaged(MediaSessionRecordImpl mediaSessionRecord) { private void addUserEngagedSession(MediaSessionRecordImpl mediaSessionRecord) { synchronized (mLock) { int uid = mediaSessionRecord.getUid(); mUserEngagedSessionsForFgs.putIfAbsent(uid, new HashSet<>()); mUserEngagedSessionsForFgs.get(uid).add(mediaSessionRecord); } } private void removeUserEngagedSession(MediaSessionRecordImpl mediaSessionRecord) { synchronized (mLock) { int uid = mediaSessionRecord.getUid(); Set<MediaSessionRecordImpl> mUidUserEngagedSessionsForFgs = mUserEngagedSessionsForFgs.get(uid); if (mUidUserEngagedSessionsForFgs == null) { return; } mUidUserEngagedSessionsForFgs.remove(mediaSessionRecord); if (mUidUserEngagedSessionsForFgs.isEmpty()) { mUserEngagedSessionsForFgs.remove(uid); } } } private void startFgsIfSessionIsLinkedToNotification( MediaSessionRecordImpl mediaSessionRecord) { Log.d(TAG, "startFgsIfSessionIsLinkedToNotification: record=" + mediaSessionRecord); if (!Flags.enableNotifyingActivityManagerWithMediaSessionStatusChange()) { return; } synchronized (mLock) { int uid = mediaSessionRecord.getUid(); for (Notification mediaNotification : mMediaNotifications.getOrDefault(uid, Set.of())) { if (mediaSessionRecord.isLinkedToNotification(mediaNotification)) { mActivityManagerInternal.startForegroundServiceDelegate( mediaSessionRecord.getForegroundServiceDelegationOptions(), /* connection= */ null); startFgsDelegate(mediaSessionRecord.getForegroundServiceDelegationOptions()); return; } } } } private void onUserDisengaged(MediaSessionRecordImpl mediaSessionRecord) { private void startFgsDelegate( ForegroundServiceDelegationOptions foregroundServiceDelegationOptions) { final long token = Binder.clearCallingIdentity(); try { mActivityManagerInternal.startForegroundServiceDelegate( foregroundServiceDelegationOptions, /* connection= */ null); } finally { Binder.restoreCallingIdentity(token); } } private void stopFgsIfNoSessionIsLinkedToNotification( MediaSessionRecordImpl mediaSessionRecord) { Log.d(TAG, "stopFgsIfNoSessionIsLinkedToNotification: record=" + mediaSessionRecord); if (!Flags.enableNotifyingActivityManagerWithMediaSessionStatusChange()) { return; } synchronized (mLock) { int uid = mediaSessionRecord.getUid(); if (mUserEngagedSessionsForFgs.containsKey(uid)) { mUserEngagedSessionsForFgs.get(uid).remove(mediaSessionRecord); if (mUserEngagedSessionsForFgs.get(uid).isEmpty()) { mUserEngagedSessionsForFgs.remove(uid); } ForegroundServiceDelegationOptions foregroundServiceDelegationOptions = mediaSessionRecord.getForegroundServiceDelegationOptions(); if (foregroundServiceDelegationOptions == null) { return; } boolean shouldStopFgs = true; for (MediaSessionRecordImpl sessionRecord : for (MediaSessionRecordImpl record : mUserEngagedSessionsForFgs.getOrDefault(uid, Set.of())) { for (Notification mediaNotification : mMediaNotifications.getOrDefault(uid, Set.of())) { if (sessionRecord.isLinkedToNotification(mediaNotification)) { shouldStopFgs = false; for (Notification mediaNotification : mMediaNotifications.getOrDefault(uid, Set.of())) { if (record.isLinkedToNotification(mediaNotification)) { // A user engaged session linked with a media notification is found. // We shouldn't call stop FGS in this case. return; } } } if (shouldStopFgs) { mActivityManagerInternal.stopForegroundServiceDelegate( mediaSessionRecord.getForegroundServiceDelegationOptions()); stopFgsDelegate(foregroundServiceDelegationOptions); } } private void stopFgsDelegate( ForegroundServiceDelegationOptions foregroundServiceDelegationOptions) { final long token = Binder.clearCallingIdentity(); try { mActivityManagerInternal.stopForegroundServiceDelegate( foregroundServiceDelegationOptions); } finally { Binder.restoreCallingIdentity(token); } } private void reportMediaInteractionEvent(MediaSessionRecordImpl record, boolean userEngaged) { Loading Loading @@ -2502,7 +2550,6 @@ public class MediaSessionService extends SystemService implements Monitor { } MediaSessionRecord session = null; MediaButtonReceiverHolder mediaButtonReceiverHolder = null; if (mCustomMediaKeyDispatcher != null) { MediaSession.Token token = mCustomMediaKeyDispatcher.getMediaSession( keyEvent, uid, asSystemService); Loading Loading @@ -2630,6 +2677,18 @@ public class MediaSessionService extends SystemService implements Monitor { && streamType <= AudioManager.STREAM_NOTIFICATION; } @Override public void expireTempEngagedSessions() { synchronized (mLock) { for (Set<MediaSessionRecordImpl> uidSessions : mUserEngagedSessionsForFgs.values()) { for (MediaSessionRecordImpl sessionRecord : uidSessions) { sessionRecord.expireTempEngaged(); } } } } private class MediaKeyListenerResultReceiver extends ResultReceiver implements Runnable { private final String mPackageName; private final int mPid; Loading Loading @@ -3127,7 +3186,6 @@ public class MediaSessionService extends SystemService implements Monitor { super.onNotificationPosted(sbn); Notification postedNotification = sbn.getNotification(); int uid = sbn.getUid(); if (!postedNotification.isMediaNotification()) { return; } Loading @@ -3138,11 +3196,9 @@ public class MediaSessionService extends SystemService implements Monitor { mUserEngagedSessionsForFgs.getOrDefault(uid, Set.of())) { ForegroundServiceDelegationOptions foregroundServiceDelegationOptions = mediaSessionRecord.getForegroundServiceDelegationOptions(); if (mediaSessionRecord.isLinkedToNotification(postedNotification) && foregroundServiceDelegationOptions != null) { mActivityManagerInternal.startForegroundServiceDelegate( foregroundServiceDelegationOptions, /* connection= */ null); if (foregroundServiceDelegationOptions != null && mediaSessionRecord.isLinkedToNotification(postedNotification)) { startFgsDelegate(foregroundServiceDelegationOptions); return; } } Loading Loading @@ -3173,21 +3229,7 @@ public class MediaSessionService extends SystemService implements Monitor { return; } boolean shouldStopFgs = true; for (MediaSessionRecordImpl mediaSessionRecord : mUserEngagedSessionsForFgs.getOrDefault(uid, Set.of())) { for (Notification mediaNotification : mMediaNotifications.getOrDefault(uid, Set.of())) { if (mediaSessionRecord.isLinkedToNotification(mediaNotification)) { shouldStopFgs = false; } } } if (shouldStopFgs && notificationRecord.getForegroundServiceDelegationOptions() != null) { mActivityManagerInternal.stopForegroundServiceDelegate( notificationRecord.getForegroundServiceDelegationOptions()); } stopFgsIfNoSessionIsLinkedToNotification(notificationRecord); } } Loading