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

Commit e8030569 authored by Iván Budnik's avatar Iván Budnik Committed by Cherrypicker Worker
Browse files

Validate ComponentName for MediaButtonBroadcastReceiver

This is a security fix for b/270049379.

Bug: 270049379
Test: atest CtsMediaBetterTogetherTestCases
(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:c573c83a2aa36ca022302f675d705518dd723a3c)
Merged-In: I4851eb506683c2666d57ec45bf9de787833d9636
Change-Id: I4851eb506683c2666d57ec45bf9de787833d9636
parent 4680610c
Loading
Loading
Loading
Loading
+8 −4
Original line number Original line Diff line number Diff line
@@ -293,12 +293,16 @@ public final class MediaSession {
     * Set the component name of the manifest-declared {@link android.content.BroadcastReceiver}
     * Set the component name of the manifest-declared {@link android.content.BroadcastReceiver}
     * class that should receive media buttons. This allows restarting playback after the session
     * class that should receive media buttons. This allows restarting playback after the session
     * has been stopped. If your app is started in this way an {@link Intent#ACTION_MEDIA_BUTTON}
     * has been stopped. If your app is started in this way an {@link Intent#ACTION_MEDIA_BUTTON}
     * intent will be sent to the broadcast receiver.
     * intent will be sent to the broadcast receiver. On apps targeting Android U and above, this
     * <p>
     * will throw an {@link IllegalArgumentException} if the provided {@link ComponentName} does not
     * Note: The given {@link android.content.BroadcastReceiver} should belong to the same package
     * resolve to an existing {@link android.content.BroadcastReceiver broadcast receiver}.
     * as the context that was given when creating {@link MediaSession}.
     *
     * <p>Note: The given {@link android.content.BroadcastReceiver} should belong to the same
     * package as the context that was given when creating {@link MediaSession}.
     *
     *
     * @param broadcastReceiver the component name of the BroadcastReceiver class
     * @param broadcastReceiver the component name of the BroadcastReceiver class
     * @throws IllegalArgumentException if {@code broadcastReceiver} does not exist on apps
     *     targeting Android U and above
     */
     */
    public void setMediaButtonBroadcastReceiver(@Nullable ComponentName broadcastReceiver) {
    public void setMediaButtonBroadcastReceiver(@Nullable ComponentName broadcastReceiver) {
        try {
        try {
+53 −0
Original line number Original line Diff line number Diff line
@@ -16,14 +16,22 @@


package com.android.server.media;
package com.android.server.media;


import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.app.ActivityManager;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.ActivityManagerInternal;
import android.app.PendingIntent;
import android.app.PendingIntent;
import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
import android.content.ComponentName;
import android.content.ComponentName;
import android.content.Context;
import android.content.Context;
import android.content.Intent;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
import android.media.AudioAttributes;
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.AudioManager;
import android.media.AudioSystem;
import android.media.AudioSystem;
@@ -42,6 +50,7 @@ import android.media.session.ParcelableListBinder;
import android.media.session.PlaybackState;
import android.media.session.PlaybackState;
import android.net.Uri;
import android.net.Uri;
import android.os.Binder;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.Bundle;
import android.os.DeadObjectException;
import android.os.DeadObjectException;
import android.os.Handler;
import android.os.Handler;
@@ -52,6 +61,7 @@ import android.os.Process;
import android.os.RemoteException;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ResultReceiver;
import android.os.SystemClock;
import android.os.SystemClock;
import android.os.UserHandle;
import android.text.TextUtils;
import android.text.TextUtils;
import android.util.EventLog;
import android.util.EventLog;
import android.util.Log;
import android.util.Log;
@@ -73,6 +83,17 @@ import java.util.concurrent.CopyOnWriteArrayList;
 */
 */
// TODO(jaewan): Do not call service method directly -- introduce listener instead.
// TODO(jaewan): Do not call service method directly -- introduce listener instead.
public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionRecordImpl {
public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionRecordImpl {

    /**
     * {@link MediaSession#setMediaButtonBroadcastReceiver(ComponentName)} throws an {@link
     * IllegalArgumentException} if the provided {@link ComponentName} does not resolve to a valid
     * {@link android.content.BroadcastReceiver broadcast receiver} for apps targeting Android U and
     * above. For apps targeting Android T and below, the request will be ignored.
     */
    @ChangeId
    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
    static final long THROW_FOR_INVALID_BROADCAST_RECEIVER = 270049379L;

    private static final String TAG = "MediaSessionRecord";
    private static final String TAG = "MediaSessionRecord";
    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);


@@ -871,6 +892,22 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
        }
        }
    };
    };


    @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
    private static boolean componentNameExists(
            @NonNull ComponentName componentName, @NonNull Context context, int userId) {
        Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
        mediaButtonIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
        mediaButtonIntent.setComponent(componentName);

        UserHandle userHandle = UserHandle.of(userId);
        PackageManager pm = context.getPackageManager();

        List<ResolveInfo> resolveInfos =
                pm.queryBroadcastReceiversAsUser(
                        mediaButtonIntent, PackageManager.ResolveInfoFlags.of(0), userHandle);
        return !resolveInfos.isEmpty();
    }

    private final class SessionStub extends ISession.Stub {
    private final class SessionStub extends ISession.Stub {
        @Override
        @Override
        public void destroySession() throws RemoteException {
        public void destroySession() throws RemoteException {
@@ -955,7 +992,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
        }
        }


        @Override
        @Override
        @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
        public void setMediaButtonBroadcastReceiver(ComponentName receiver) throws RemoteException {
        public void setMediaButtonBroadcastReceiver(ComponentName receiver) throws RemoteException {
            final int uid = Binder.getCallingUid();
            final long token = Binder.clearCallingIdentity();
            final long token = Binder.clearCallingIdentity();
            try {
            try {
                //mPackageName has been verified in MediaSessionService.enforcePackageName().
                //mPackageName has been verified in MediaSessionService.enforcePackageName().
@@ -970,6 +1009,20 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
                        != 0) {
                        != 0) {
                    return;
                    return;
                }
                }

                if (!componentNameExists(receiver, mContext, mUserId)) {
                    if (CompatChanges.isChangeEnabled(THROW_FOR_INVALID_BROADCAST_RECEIVER, uid)) {
                        throw new IllegalArgumentException("Invalid component name: " + receiver);
                    } else {
                        Log.w(
                                TAG,
                                "setMediaButtonBroadcastReceiver(): "
                                        + "Ignoring invalid component name="
                                        + receiver);
                    }
                    return;
                }

                mMediaButtonReceiverHolder = MediaButtonReceiverHolder.create(mUserId, receiver);
                mMediaButtonReceiverHolder = MediaButtonReceiverHolder.create(mUserId, receiver);
                mService.onMediaButtonReceiverChanged(MediaSessionRecord.this);
                mService.onMediaButtonReceiverChanged(MediaSessionRecord.this);
            } finally {
            } finally {