Enforce MediaButtonReceiver extracted component name matches session package name

This change makes sure that the extracted component name in a
MediaButtonReceiverHolder matches the Media Session owner's package
name. This avoids incorrectly routing media button events and potential
security issues.

Bug: 244312001
Bug: 238177121
Test: atest CtsMediaBetterTogetherTestCases
Change-Id: Ifac9cf53889222e31d18c14c1e096ee68c0a346c
(cherry picked from commit 185c3e2523)
Merged-In: Ifac9cf53889222e31d18c14c1e096ee68c0a346c
This commit is contained in:
Iván Budnik
2022-10-04 15:52:30 +00:00
parent 31fc4ee296
commit 48c3882778
5 changed files with 43 additions and 33 deletions

View File

@@ -35,7 +35,7 @@ interface ISession {
ISessionController getController(); ISessionController getController();
void setFlags(int flags); void setFlags(int flags);
void setActive(boolean active); void setActive(boolean active);
void setMediaButtonReceiver(in PendingIntent mbr, String sessionPackageName); void setMediaButtonReceiver(in PendingIntent mbr);
void setMediaButtonBroadcastReceiver(in ComponentName broadcastReceiver); void setMediaButtonBroadcastReceiver(in ComponentName broadcastReceiver);
void setLaunchPendingIntent(in PendingIntent pi); void setLaunchPendingIntent(in PendingIntent pi);
void destroySession(); void destroySession();

View File

@@ -286,7 +286,7 @@ public final class MediaSession {
@Deprecated @Deprecated
public void setMediaButtonReceiver(@Nullable PendingIntent mbr) { public void setMediaButtonReceiver(@Nullable PendingIntent mbr) {
try { try {
mBinder.setMediaButtonReceiver(mbr, mContext.getPackageName()); mBinder.setMediaButtonReceiver(mbr);
} catch (RemoteException e) { } catch (RemoteException e) {
Log.wtf(TAG, "Failure in setMediaButtonReceiver.", e); Log.wtf(TAG, "Failure in setMediaButtonReceiver.", e);
} }

View File

@@ -18,6 +18,7 @@ package com.android.server.media;
import android.annotation.IntDef; import android.annotation.IntDef;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.BroadcastOptions; import android.app.BroadcastOptions;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.ComponentName; import android.content.ComponentName;
@@ -37,6 +38,7 @@ import android.view.KeyEvent;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.util.Collections;
import java.util.List; import java.util.List;
/** /**
@@ -102,15 +104,19 @@ final class MediaButtonReceiverHolder {
} }
/** /**
* Creates a new instance. * Creates a new instance from a {@link PendingIntent}.
*
* <p>This method assumes the session package name has been validated and effectively belongs to
* the media session's owner.
* *
* @param context context
* @param userId userId * @param userId userId
* @param pendingIntent pending intent * @param pendingIntent pending intent that will receive media button events
* @return Can be {@code null} if pending intent was null. * @param sessionPackageName package name of media session owner
* @return {@link MediaButtonReceiverHolder} instance or {@code null} if pending intent was
* null.
*/ */
public static MediaButtonReceiverHolder create(Context context, int userId, public static MediaButtonReceiverHolder create(
PendingIntent pendingIntent, String sessionPackageName) { int userId, @Nullable PendingIntent pendingIntent, String sessionPackageName) {
if (pendingIntent == null) { if (pendingIntent == null) {
return null; return null;
} }
@@ -312,7 +318,7 @@ final class MediaButtonReceiverHolder {
} }
private static ComponentName getComponentName(PendingIntent pendingIntent, int componentType) { private static ComponentName getComponentName(PendingIntent pendingIntent, int componentType) {
List<ResolveInfo> resolveInfos = null; List<ResolveInfo> resolveInfos = Collections.emptyList();
switch (componentType) { switch (componentType) {
case COMPONENT_TYPE_ACTIVITY: case COMPONENT_TYPE_ACTIVITY:
resolveInfos = pendingIntent.queryIntentComponents( resolveInfos = pendingIntent.queryIntentComponents(
@@ -330,32 +336,37 @@ final class MediaButtonReceiverHolder {
PACKAGE_MANAGER_COMMON_FLAGS | PackageManager.GET_RECEIVERS); PACKAGE_MANAGER_COMMON_FLAGS | PackageManager.GET_RECEIVERS);
break; break;
} }
if (resolveInfos != null && !resolveInfos.isEmpty()) {
return createComponentName(resolveInfos.get(0)); for (ResolveInfo resolveInfo : resolveInfos) {
ComponentInfo componentInfo = getComponentInfo(resolveInfo);
if (componentInfo != null && TextUtils.equals(componentInfo.packageName,
pendingIntent.getCreatorPackage())
&& componentInfo.packageName != null && componentInfo.name != null) {
return new ComponentName(componentInfo.packageName, componentInfo.name);
}
} }
return null; return null;
} }
private static ComponentName createComponentName(ResolveInfo resolveInfo) { /**
if (resolveInfo == null) { * Retrieves the {@link ComponentInfo} from a {@link ResolveInfo} instance. Similar to {@link
return null; * ResolveInfo#getComponentInfo()}, but returns {@code null} if this {@link ResolveInfo} points
} * to a content provider.
ComponentInfo componentInfo; *
* @param resolveInfo Where to extract the {@link ComponentInfo} from.
* @return Either a non-null {@link ResolveInfo#activityInfo} or {@link
* ResolveInfo#serviceInfo}. Otherwise {@code null} if {@link ResolveInfo#providerInfo} is
* not {@code null}.
*/
private static ComponentInfo getComponentInfo(@NonNull ResolveInfo resolveInfo) {
// Code borrowed from ResolveInfo#getComponentInfo(). // Code borrowed from ResolveInfo#getComponentInfo().
if (resolveInfo.activityInfo != null) { if (resolveInfo.activityInfo != null) {
componentInfo = resolveInfo.activityInfo; return resolveInfo.activityInfo;
} else if (resolveInfo.serviceInfo != null) { } else if (resolveInfo.serviceInfo != null) {
componentInfo = resolveInfo.serviceInfo; return resolveInfo.serviceInfo;
} else { } else {
// We're not interested in content provider. // We're not interested in content providers.
return null;
}
// Code borrowed from ComponentInfo#getComponentName().
try {
return new ComponentName(componentInfo.packageName, componentInfo.name);
} catch (IllegalArgumentException | NullPointerException e) {
// This may be happen if resolveActivity() end up with matching multiple activities.
// see PackageManager#resolveActivity().
return null; return null;
} }
} }

View File

@@ -933,8 +933,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
} }
@Override @Override
public void setMediaButtonReceiver(PendingIntent pi, String sessionPackageName) public void setMediaButtonReceiver(PendingIntent pi) throws RemoteException {
throws RemoteException {
final long token = Binder.clearCallingIdentity(); final long token = Binder.clearCallingIdentity();
try { try {
if ((mPolicies & MediaSessionPolicyProvider.SESSION_POLICY_IGNORE_BUTTON_RECEIVER) if ((mPolicies & MediaSessionPolicyProvider.SESSION_POLICY_IGNORE_BUTTON_RECEIVER)
@@ -942,7 +941,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
return; return;
} }
mMediaButtonReceiverHolder = mMediaButtonReceiverHolder =
MediaButtonReceiverHolder.create(mContext, mUserId, pi, sessionPackageName); MediaButtonReceiverHolder.create(mUserId, pi, mPackageName);
mService.onMediaButtonReceiverChanged(MediaSessionRecord.this); mService.onMediaButtonReceiverChanged(MediaSessionRecord.this);
} finally { } finally {
Binder.restoreCallingIdentity(token); Binder.restoreCallingIdentity(token);

View File

@@ -2229,9 +2229,9 @@ public class MediaSessionService extends SystemService implements Monitor {
PendingIntent pi = mCustomMediaKeyDispatcher.getMediaButtonReceiver(keyEvent, PendingIntent pi = mCustomMediaKeyDispatcher.getMediaButtonReceiver(keyEvent,
uid, asSystemService); uid, asSystemService);
if (pi != null) { if (pi != null) {
mediaButtonReceiverHolder = MediaButtonReceiverHolder.create(mContext, mediaButtonReceiverHolder =
mCurrentFullUserRecord.mFullUserId, pi, MediaButtonReceiverHolder.create(
/* sessionPackageName= */ ""); mCurrentFullUserRecord.mFullUserId, pi, "");
} }
} }
} }