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

Commit b0e32b08 authored by Jaewan Kim's avatar Jaewan Kim
Browse files

MediaSessionManager: Add isTrustedForMediaControl()

The API checks whether an app is granted MEDIA_CONTENT_CONTROL
permisison or has enabled notification listener. Such apps can be
considered as the system component (e.g. Bluetooth) or equivalent (e.g.
Auto/Wearable companion app), so sessions shouldn't refuse connection
request from it.

It needs to be public because API for checking whether an app has an
enabled notification listener can only be called by the system service.
(see: NotificationManager.getEnabledNotificationListeners(int))
With the MediaSessionManager#isTrustedForMediaControl(),
MediaSessionService will query the information indirectly for a session
to tell an app is trusted.

Note that this isn't workaround for bypassing permission check of
NotificationManager API calls. It's indirectly available through
the android.provider.Settings.Secure with the *deprecated* key
ENABLD_NOTIFICATION_LISTENERS and it doesn't need any permission.
MediaSessionManager#isTrustedForMediaControl() is needed to avoid using
deprecated key for querying notification listeners.

Bug: 75500592
Test: Build
Change-Id: I8d1183aa946aa0d2ec35dbd7f31933d1c2717f99
parent 1c73370d
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -25786,6 +25786,7 @@ package android.media.session {
    method public void addOnActiveSessionsChangedListener(android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, android.content.ComponentName);
    method public void addOnActiveSessionsChangedListener(android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, android.content.ComponentName, android.os.Handler);
    method public java.util.List<android.media.session.MediaController> getActiveSessions(android.content.ComponentName);
    method public boolean isTrustedForMediaControl(android.media.session.MediaSessionManager.RemoteUserInfo);
    method public void removeOnActiveSessionsChangedListener(android.media.session.MediaSessionManager.OnActiveSessionsChangedListener);
  }
@@ -25793,6 +25794,13 @@ package android.media.session {
    method public abstract void onActiveSessionsChanged(java.util.List<android.media.session.MediaController>);
  }
  public static final class MediaSessionManager.RemoteUserInfo {
    ctor public MediaSessionManager.RemoteUserInfo(java.lang.String, int, int);
    method public java.lang.String getPackageName();
    method public int getPid();
    method public int getUid();
  }
  public final class PlaybackState implements android.os.Parcelable {
    method public int describeContents();
    method public long getActions();
+61 −8
Original line number Diff line number Diff line
@@ -37,7 +37,9 @@ import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.service.media.MediaBrowserService;
import android.service.notification.NotificationListenerService;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
import android.view.KeyEvent;
@@ -340,19 +342,20 @@ public final class MediaSessionManager {
    }

    /**
     * Returns whether the api
     * Returns whether the app is trusted.
     * <p>
     * An app is trusted if the app holds the android.Manifest.permission.MEDIA_CONTENT_CONTROL
     * permission or has an enabled notification listener.
     *
     * @param packageName packageName
     * @param pid pid of the app
     * @param uid uid of the app
     * @hide
     * @param userInfo The remote user info
     */
    public boolean isTrusted(@NonNull String packageName, int pid, int uid) {
        if (packageName == null) {
    public boolean isTrustedForMediaControl(RemoteUserInfo userInfo) {
        if (userInfo.getPackageName() == null) {
            return false;
        }
        try {
            return mService.isTrusted(packageName, pid, uid);
            return mService.isTrusted(
                    userInfo.getPackageName(), userInfo.getPid(), userInfo.getUid());
        } catch (RemoteException e) {
            Log.wtf(TAG, "Cannot communicate with the service.", e);
        }
@@ -763,6 +766,56 @@ public final class MediaSessionManager {
        public abstract void onAddressedPlayerChanged(ComponentName mediaButtonReceiver);
    }

    /**
     * Information of a remote user of {@link MediaSession} or {@link MediaBrowserService}.
     * This can be used to decide whether the remote user is trusted app.
     *
     * @see #isTrustedForMediaControl(RemoteUserInfo)
     */
    public static final class RemoteUserInfo {
        private String mPackageName;
        private int mPid;
        private int mUid;

        public RemoteUserInfo(String packageName, int pid, int uid) {
            mPackageName = packageName;
            mPid = pid;
            mUid = uid;
        }

        /**
         * @return package name of the controller
         */
        public String getPackageName() {
            return mPackageName;
        }

        /**
         * @return pid of the controller
         */
        public int getPid() {
            return mPid;
        }

        /**
         * @return uid of the controller
         */
        public int getUid() {
            return mUid;
        }

        @Override
        public boolean equals(Object obj) {
            if (!(obj instanceof RemoteUserInfo)) {
                return false;
            }
            RemoteUserInfo otherUserInfo = (RemoteUserInfo) obj;
            return TextUtils.equals(mPackageName, otherUserInfo.mPackageName)
                    && mPid == otherUserInfo.mPid
                    && mUid == otherUserInfo.mUid;
        }
    }

    private static final class SessionsChangedWrapper {
        private Context mContext;
        private OnActiveSessionsChangedListener mListener;