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

Commit 7879f67a authored by Pradeep Sawlani's avatar Pradeep Sawlani Committed by Android (Google) Code Review
Browse files

Merge "Use active/inactive fgs calls instead of delegate." into main

parents b83c0f52 32af37d8
Loading
Loading
Loading
Loading
+2 −6
Original line number Diff line number Diff line
@@ -16,7 +16,6 @@

package com.android.server.media;

import android.app.ForegroundServiceDelegationOptions;
import android.app.Notification;
import android.media.MediaController2;
import android.media.Session2CommandGroup;
@@ -98,11 +97,8 @@ public class MediaSession2Record extends MediaSessionRecordImpl {
    }

    @Override
    public ForegroundServiceDelegationOptions getForegroundServiceDelegationOptions() {
        // For an app to be eligible for FGS delegation, it needs a media session liked to a media
        // notification. Currently, notifications cannot be linked to MediaSession2 so it is not
        // supported.
        return null;
    public boolean hasLinkedNotificationSupport() {
        return false;
    }

    @Override
+5 −30
Original line number Diff line number Diff line
@@ -29,7 +29,6 @@ import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.ForegroundServiceDelegationOptions;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.compat.CompatChanges;
@@ -184,8 +183,6 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
    private final UriGrantsManagerInternal mUgmInternal;
    private final Context mContext;

    private final ForegroundServiceDelegationOptions mForegroundServiceDelegationOptions;

    private final Object mLock = new Object();
    // This field is partially guarded by mLock. Writes and non-atomic iterations (for example:
    // index-based-iterations) must be guarded by mLock. But it is safe to acquire an iterator
@@ -306,32 +303,10 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
        mPolicies = policies;
        mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class);

        mForegroundServiceDelegationOptions = createForegroundServiceDelegationOptions();

        // May throw RemoteException if the session app is killed.
        mSessionCb.mCb.asBinder().linkToDeath(this, 0);
    }

    private ForegroundServiceDelegationOptions createForegroundServiceDelegationOptions() {
        return new ForegroundServiceDelegationOptions.Builder()
                .setClientPid(mOwnerPid)
                .setClientUid(getUid())
                .setClientPackageName(getPackageName())
                .setClientAppThread(null)
                .setSticky(false)
                .setClientInstanceName(
                        "MediaSessionFgsDelegate_"
                                + getUid()
                                + "_"
                                + mOwnerPid
                                + "_"
                                + getPackageName())
                .setForegroundServiceTypes(0)
                .setDelegationService(
                        ForegroundServiceDelegationOptions.DELEGATION_SERVICE_MEDIA_PLAYBACK)
                .build();
    }

    /**
     * Get the session binder for the {@link MediaSession}.
     *
@@ -389,6 +364,11 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
        return mUserId;
    }

    @Override
    public boolean hasLinkedNotificationSupport() {
        return true;
    }

    /**
     * Check if this session has system priorty and should receive media buttons
     * before any other sessions.
@@ -752,11 +732,6 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
        return mPackageName + "/" + mTag + "/" + getUniqueId() + " (userId=" + mUserId + ")";
    }

    @Override
    public ForegroundServiceDelegationOptions getForegroundServiceDelegationOptions() {
        return mForegroundServiceDelegationOptions;
    }

    private void postAdjustLocalVolume(final int stream, final int direction, final int flags,
            final String callingOpPackageName, final int callingPid, final int callingUid,
            final boolean asSystemService, final boolean useSuggested,
+6 −7
Original line number Diff line number Diff line
@@ -16,10 +16,8 @@

package com.android.server.media;

import android.app.ForegroundServiceDelegationOptions;
import android.app.Notification;
import android.media.AudioManager;
import android.media.session.PlaybackState;
import android.os.ResultReceiver;
import android.view.KeyEvent;

@@ -63,13 +61,14 @@ public abstract class MediaSessionRecordImpl {
    public abstract int getUserId();

    /**
     * Get the {@link ForegroundServiceDelegationOptions} needed for notifying activity manager
     * service with changes in the {@link PlaybackState} for this session.
     * Returns whether this session supports linked notifications.
     *
     * @return the {@link ForegroundServiceDelegationOptions} needed for notifying the activity
     *     manager service with changes in the {@link PlaybackState} for this session.
     * <p>A notification is linked to a media session if it contains
     * {@link android.app.Notification#EXTRA_MEDIA_SESSION}.
     *
     * @return {@code true} if this session supports linked notifications, {@code false} otherwise.
     */
    public abstract ForegroundServiceDelegationOptions getForegroundServiceDelegationOptions();
    public abstract boolean hasLinkedNotificationSupport();

    /**
     * Check if this session has system priority and should receive media buttons before any other
+68 −56
Original line number Diff line number Diff line
@@ -30,7 +30,6 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.ForegroundServiceDelegationOptions;
import android.app.KeyguardManager;
import android.app.Notification;
import android.app.NotificationManager;
@@ -185,9 +184,9 @@ public class MediaSessionService extends SystemService implements Monitor {

    /**
     * Maps uid with all user engaged session records associated to it. It's used for calling
     * ActivityManagerInternal startFGS and stopFGS. This collection doesn't contain
     * MediaSession2Record(s). When the media session is paused, There exists a timeout before
     * calling stopFGS unlike usage logging which considers it disengaged immediately.
     * ActivityManagerInternal internal api to set fgs active/inactive. This collection doesn't
     * contain MediaSession2Record(s). When the media session is paused, There exists a timeout
     * before setting FGS inactive unlike usage logging which considers it disengaged immediately.
     */
    @GuardedBy("mLock")
    private final Map<Integer, Set<MediaSessionRecordImpl>> mUserEngagedSessionsForFgs =
@@ -195,7 +194,7 @@ public class MediaSessionService extends SystemService implements Monitor {

    /* Maps uid with all media notifications associated to it */
    @GuardedBy("mLock")
    private final Map<Integer, Set<Notification>> mMediaNotifications = new HashMap<>();
    private final Map<Integer, Set<StatusBarNotification>> mMediaNotifications = new HashMap<>();

    /**
     * Holds all {@link MediaSessionRecordImpl} which we've reported as being {@link
@@ -700,10 +699,10 @@ public class MediaSessionService extends SystemService implements Monitor {
            MediaSessionRecordImpl mediaSessionRecord, boolean isUserEngaged) {
        if (isUserEngaged) {
            addUserEngagedSession(mediaSessionRecord);
            startFgsIfSessionIsLinkedToNotification(mediaSessionRecord);
            setFgsActiveIfSessionIsLinkedToNotification(mediaSessionRecord);
        } else {
            removeUserEngagedSession(mediaSessionRecord);
            stopFgsIfNoSessionIsLinkedToNotification(mediaSessionRecord);
            setFgsInactiveIfNoSessionIsLinkedToNotification(mediaSessionRecord);
        }
    }

@@ -737,17 +736,20 @@ public class MediaSessionService extends SystemService implements Monitor {
        }
    }

    private void startFgsIfSessionIsLinkedToNotification(
    private void setFgsActiveIfSessionIsLinkedToNotification(
            MediaSessionRecordImpl mediaSessionRecord) {
        Log.d(TAG, "startFgsIfSessionIsLinkedToNotification: record=" + mediaSessionRecord);
        Log.d(TAG, "setFgsIfSessionIsLinkedToNotification: record=" + mediaSessionRecord);
        if (!Flags.enableNotifyingActivityManagerWithMediaSessionStatusChange()) {
            return;
        }
        if (!mediaSessionRecord.hasLinkedNotificationSupport()) {
            return;
        }
        synchronized (mLock) {
            int uid = mediaSessionRecord.getUid();
            for (Notification mediaNotification : mMediaNotifications.getOrDefault(uid, Set.of())) {
                if (mediaSessionRecord.isLinkedToNotification(mediaNotification)) {
                    startFgsDelegateLocked(mediaSessionRecord);
            for (StatusBarNotification sbn : mMediaNotifications.getOrDefault(uid, Set.of())) {
                if (mediaSessionRecord.isLinkedToNotification(sbn.getNotification())) {
                    setFgsActiveLocked(mediaSessionRecord, sbn);
                    return;
                }
            }
@@ -755,81 +757,92 @@ public class MediaSessionService extends SystemService implements Monitor {
    }

    @GuardedBy("mLock")
    private void startFgsDelegateLocked(MediaSessionRecordImpl mediaSessionRecord) {
        ForegroundServiceDelegationOptions foregroundServiceDelegationOptions =
                mediaSessionRecord.getForegroundServiceDelegationOptions();
        if (foregroundServiceDelegationOptions == null) {
            return; // This record doesn't support FGS. Typically a MediaSession2 record.
        }
    private void setFgsActiveLocked(MediaSessionRecordImpl mediaSessionRecord,
            StatusBarNotification sbn) {
        if (!mFgsAllowedMediaSessionRecords.add(mediaSessionRecord)) {
            return; // This record is already FGS-started.
            return; // This record already is FGS-activated.
        }
        final long token = Binder.clearCallingIdentity();
        try {
            final String packageName = sbn.getPackageName();
            final int uid = sbn.getUid();
            final int notificationId = sbn.getId();
            Log.i(
                    TAG,
                    TextUtils.formatSimple(
                            "startFgsDelegate: pkg=%s uid=%d",
                            foregroundServiceDelegationOptions.mClientPackageName,
                            foregroundServiceDelegationOptions.mClientUid));
            mActivityManagerInternal.startForegroundServiceDelegate(
                    foregroundServiceDelegationOptions, /* connection= */ null);
                            "setFgsActiveLocked: pkg=%s uid=%d notification=%d",
                            packageName, uid, notificationId));
            mActivityManagerInternal.notifyActiveMediaForegroundService(packageName,
                    sbn.getUser().getIdentifier(), notificationId);
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }

    private void stopFgsIfNoSessionIsLinkedToNotification(
    @Nullable
    private StatusBarNotification getLinkedNotification(
            int uid, MediaSessionRecordImpl record) {
        synchronized (mLock) {
            for (StatusBarNotification sbn :
                    mMediaNotifications.getOrDefault(uid, Set.of())) {
                if (record.isLinkedToNotification(sbn.getNotification())) {
                    return sbn;
                }
            }
        }
        return null;
    }

    private void setFgsInactiveIfNoSessionIsLinkedToNotification(
            MediaSessionRecordImpl mediaSessionRecord) {
        Log.d(TAG, "stopFgsIfNoSessionIsLinkedToNotification: record=" + mediaSessionRecord);
        Log.d(TAG, "setFgsIfNoSessionIsLinkedToNotification: record=" + mediaSessionRecord);
        if (!Flags.enableNotifyingActivityManagerWithMediaSessionStatusChange()) {
            return;
        }
        synchronized (mLock) {
            int uid = mediaSessionRecord.getUid();
            ForegroundServiceDelegationOptions foregroundServiceDelegationOptions =
                    mediaSessionRecord.getForegroundServiceDelegationOptions();
            if (foregroundServiceDelegationOptions == null) {
        if (!mediaSessionRecord.hasLinkedNotificationSupport()) {
            return;
        }

        synchronized (mLock) {
            final int uid = mediaSessionRecord.getUid();
            for (MediaSessionRecordImpl record :
                    mUserEngagedSessionsForFgs.getOrDefault(uid, Set.of())) {
                for (Notification mediaNotification :
                for (StatusBarNotification sbn :
                        mMediaNotifications.getOrDefault(uid, Set.of())) {
                    if (record.isLinkedToNotification(mediaNotification)) {
                    if (record.isLinkedToNotification(sbn.getNotification())) {
                        // A user engaged session linked with a media notification is found.
                        // We shouldn't call stop FGS in this case.
                        return;
                    }
                }
            }

            stopFgsDelegateLocked(mediaSessionRecord);
            final StatusBarNotification linkedNotification =
                    getLinkedNotification(uid, mediaSessionRecord);
            if (linkedNotification != null) {
                setFgsInactiveLocked(mediaSessionRecord, linkedNotification);
            }
        }
    }

    @GuardedBy("mLock")
    private void stopFgsDelegateLocked(MediaSessionRecordImpl mediaSessionRecord) {
        ForegroundServiceDelegationOptions foregroundServiceDelegationOptions =
                mediaSessionRecord.getForegroundServiceDelegationOptions();
        if (foregroundServiceDelegationOptions == null) {
            return; // This record doesn't support FGS. Typically a MediaSession2 record.
        }
    private void setFgsInactiveLocked(MediaSessionRecordImpl mediaSessionRecord,
            StatusBarNotification sbn) {
        if (!mFgsAllowedMediaSessionRecords.remove(mediaSessionRecord)) {
            return; // This record is not FGS-started. No need to stop it.
            return; // This record is not FGS-active. No need to set inactive.
        }

        final long token = Binder.clearCallingIdentity();
        try {
            final String packageName = sbn.getPackageName();
            final int userId = sbn.getUser().getIdentifier();
            final int uid = sbn.getUid();
            final int notificationId = sbn.getId();
            Log.i(
                    TAG,
                    TextUtils.formatSimple(
                            "stopFgsDelegate: pkg=%s uid=%d",
                            foregroundServiceDelegationOptions.mClientPackageName,
                            foregroundServiceDelegationOptions.mClientUid));
            mActivityManagerInternal.stopForegroundServiceDelegate(
                    foregroundServiceDelegationOptions);
                            "setFgsInactiveLocked: pkg=%s uid=%d notification=%d",
                            packageName, uid, notificationId));
            mActivityManagerInternal.notifyInactiveMediaForegroundService(packageName,
                    userId, notificationId);
        } finally {
            Binder.restoreCallingIdentity(token);
        }
@@ -3259,18 +3272,18 @@ public class MediaSessionService extends SystemService implements Monitor {
        @Override
        public void onNotificationPosted(StatusBarNotification sbn) {
            super.onNotificationPosted(sbn);
            Notification postedNotification = sbn.getNotification();
            int uid = sbn.getUid();
            final Notification postedNotification = sbn.getNotification();
            if (!postedNotification.isMediaNotification()) {
                return;
            }
            synchronized (mLock) {
                mMediaNotifications.putIfAbsent(uid, new HashSet<>());
                mMediaNotifications.get(uid).add(postedNotification);
                mMediaNotifications.get(uid).add(sbn);
                for (MediaSessionRecordImpl mediaSessionRecord :
                        mUserEngagedSessionsForFgs.getOrDefault(uid, Set.of())) {
                    if (mediaSessionRecord.isLinkedToNotification(postedNotification)) {
                        startFgsDelegateLocked(mediaSessionRecord);
                        setFgsActiveLocked(mediaSessionRecord, sbn);
                        return;
                    }
                }
@@ -3286,9 +3299,9 @@ public class MediaSessionService extends SystemService implements Monitor {
                return;
            }
            synchronized (mLock) {
                Set<Notification> uidMediaNotifications = mMediaNotifications.get(uid);
                Set<StatusBarNotification> uidMediaNotifications = mMediaNotifications.get(uid);
                if (uidMediaNotifications != null) {
                    uidMediaNotifications.remove(removedNotification);
                    uidMediaNotifications.remove(sbn);
                    if (uidMediaNotifications.isEmpty()) {
                        mMediaNotifications.remove(uid);
                    }
@@ -3300,8 +3313,7 @@ public class MediaSessionService extends SystemService implements Monitor {
                if (notificationRecord == null) {
                    return;
                }

                stopFgsIfNoSessionIsLinkedToNotification(notificationRecord);
                setFgsInactiveIfNoSessionIsLinkedToNotification(notificationRecord);
            }
        }