Merge "MediaSessionService: Fix crash with implicit media button receiver"

This commit is contained in:
TreeHugger Robot
2020-02-10 07:10:36 +00:00
committed by Android (Google) Code Review
4 changed files with 406 additions and 166 deletions

View File

@@ -266,8 +266,12 @@ public final class MediaSession {
* playback after the session has been stopped. If your app is started in
* this way an {@link Intent#ACTION_MEDIA_BUTTON} intent will be sent via
* the pending intent.
* <p>
* The pending intent is recommended to be explicit to follow the security recommendation of
* {@link PendingIntent#getActivity}.
*
* @param mbr The {@link PendingIntent} to send the media button event to.
* @see PendingIntent#getActivity
*/
public void setMediaButtonReceiver(@Nullable PendingIntent mbr) {
try {

View File

@@ -0,0 +1,360 @@
/*
* Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.media;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ComponentInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.os.Handler;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
import android.view.KeyEvent;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
/**
* Holds the media button receiver, and also provides helper methods around it.
*/
final class MediaButtonReceiverHolder {
public static final int COMPONENT_TYPE_INVALID = 0;
public static final int COMPONENT_TYPE_BROADCAST = 1;
public static final int COMPONENT_TYPE_ACTIVITY = 2;
public static final int COMPONENT_TYPE_SERVICE = 3;
@IntDef(value = {
COMPONENT_TYPE_INVALID,
COMPONENT_TYPE_BROADCAST,
COMPONENT_TYPE_ACTIVITY,
COMPONENT_TYPE_SERVICE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ComponentType {}
private static final String TAG = "PendingIntentHolder";
private static final boolean DEBUG_KEY_EVENT = MediaSessionService.DEBUG_KEY_EVENT;
private static final String COMPONENT_NAME_USER_ID_DELIM = ",";
private final int mUserId;
private final PendingIntent mPendingIntent;
private final ComponentName mComponentName;
private final String mPackageName;
@ComponentType
private final int mComponentType;
/**
* Unflatten from string which is previously flattened string via flattenToString().
* <p>
* It's used to store and restore media button receiver across the boot, by keeping the intent's
* component name to the persistent storage.
*
* @param mediaButtonReceiverInfo previously flattened string via flattenToString()
* @return new instance if the string was valid. {@code null} otherwise.
*/
public static MediaButtonReceiverHolder unflattenFromString(
Context context, String mediaButtonReceiverInfo) {
if (TextUtils.isEmpty(mediaButtonReceiverInfo)) {
return null;
}
String[] tokens = mediaButtonReceiverInfo.split(COMPONENT_NAME_USER_ID_DELIM);
if (tokens == null || (tokens.length != 2 && tokens.length != 3)) {
return null;
}
ComponentName componentName = ComponentName.unflattenFromString(tokens[0]);
int userId = Integer.parseInt(tokens[1]);
// Guess component type if the OS version is updated from the older version.
int componentType = (tokens.length == 3)
? Integer.parseInt(tokens[2])
: getComponentType(context, componentName);
return new MediaButtonReceiverHolder(userId, null, componentName, componentType);
}
/**
* Creates a new instance.
*
* @param context context
* @param userId userId
* @param pendingIntent pending intent
* @return Can be {@code null} if pending intent was null.
*/
public static MediaButtonReceiverHolder create(Context context, int userId,
PendingIntent pendingIntent) {
if (pendingIntent == null) {
return null;
}
ComponentName componentName = (pendingIntent != null && pendingIntent.getIntent() != null)
? pendingIntent.getIntent().getComponent() : null;
if (componentName != null) {
// Explicit intent, where component name is in the PendingIntent.
return new MediaButtonReceiverHolder(userId, pendingIntent, componentName,
getComponentType(context, componentName));
}
// Implicit intent, where component name isn't in the PendingIntent. Try resolve.
PackageManager pm = context.getPackageManager();
Intent intent = pendingIntent.getIntent();
if ((componentName = resolveImplicitServiceIntent(pm, intent)) != null) {
return new MediaButtonReceiverHolder(
userId, pendingIntent, componentName, COMPONENT_TYPE_SERVICE);
} else if ((componentName = resolveManifestDeclaredBroadcastReceiverIntent(pm, intent))
!= null) {
return new MediaButtonReceiverHolder(
userId, pendingIntent, componentName, COMPONENT_TYPE_BROADCAST);
} else if ((componentName = resolveImplicitActivityIntent(pm, intent)) != null) {
return new MediaButtonReceiverHolder(
userId, pendingIntent, componentName, COMPONENT_TYPE_ACTIVITY);
}
// Failed to resolve target component for the pending intent. It's unlikely to be usable.
// However, the pending intent would be still used, just to follow the legacy behavior.
Log.w(TAG, "Unresolvable implicit intent is set, pi=" + pendingIntent);
String packageName = (pendingIntent != null && pendingIntent.getIntent() != null)
? pendingIntent.getIntent().getPackage() : null;
return new MediaButtonReceiverHolder(userId, pendingIntent,
packageName != null ? packageName : "");
}
private MediaButtonReceiverHolder(int userId, PendingIntent pendingIntent,
ComponentName componentName, @ComponentType int componentType) {
mUserId = userId;
mPendingIntent = pendingIntent;
mComponentName = componentName;
mPackageName = componentName.getPackageName();
mComponentType = componentType;
}
private MediaButtonReceiverHolder(int userId, PendingIntent pendingIntent, String packageName) {
mUserId = userId;
mPendingIntent = pendingIntent;
mComponentName = null;
mPackageName = packageName;
mComponentType = COMPONENT_TYPE_INVALID;
}
/**
* @return the user id
*/
public int getUserId() {
return mUserId;
}
/**
* @return package name that the media button receiver would be sent to.
*/
@NonNull
public String getPackageName() {
return mPackageName;
}
/**
* Sends the media key event to the media button receiver.
* <p>
* This prioritizes using use pending intent for sending media key event.
*
* @param context context to be used to call PendingIntent#send
* @param keyEvent keyEvent to send
* @param resultCode result code to be used to call PendingIntent#send
* Ignored if there's no valid pending intent.
* @param onFinishedListener callback to be used to get result of PendingIntent#send.
* Ignored if there's no valid pending intent.
* @param handler handler to be used to call onFinishedListener
* Ignored if there's no valid pending intent.
* @see PendingIntent#send(Context, int, Intent, PendingIntent.OnFinished, Handler)
*/
public boolean send(Context context, KeyEvent keyEvent, String callingPackageName,
int resultCode, PendingIntent.OnFinished onFinishedListener, Handler handler) {
Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
mediaButtonIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
// TODO: Find a way to also send PID/UID in secure way.
mediaButtonIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, callingPackageName);
if (mPendingIntent != null) {
if (DEBUG_KEY_EVENT) {
Log.d(TAG, "Sending " + keyEvent + " to the last known PendingIntent "
+ mPendingIntent);
}
try {
mPendingIntent.send(
context, resultCode, mediaButtonIntent, onFinishedListener, handler);
} catch (PendingIntent.CanceledException e) {
Log.w(TAG, "Error sending key event to media button receiver " + mPendingIntent, e);
return false;
}
} else if (mComponentName != null) {
if (DEBUG_KEY_EVENT) {
Log.d(TAG, "Sending " + keyEvent + " to the restored intent "
+ mComponentName + ", type=" + mComponentType);
}
mediaButtonIntent.setComponent(mComponentName);
UserHandle userHandle = UserHandle.of(mUserId);
try {
switch (mComponentType) {
case COMPONENT_TYPE_ACTIVITY:
context.startActivityAsUser(mediaButtonIntent, userHandle);
break;
case COMPONENT_TYPE_SERVICE:
context.startForegroundServiceAsUser(mediaButtonIntent,
userHandle);
break;
default:
// Legacy behavior for other cases.
context.sendBroadcastAsUser(mediaButtonIntent, userHandle);
}
} catch (Exception e) {
Log.w(TAG, "Error sending media button to the restored intent "
+ mComponentName + ", type=" + mComponentType, e);
return false;
}
} else {
// Leave log, just in case.
Log.e(TAG, "Shouldn't be happen -- pending intent or component name must be set");
return false;
}
return true;
}
@Override
public String toString() {
if (mPendingIntent != null) {
return "MBR {pi=" + mPendingIntent + ", type=" + mComponentType + "}";
}
return "Restored MBR {component=" + mComponentName + ", type=" + mComponentType + "}";
}
/**
* @return flattened string. Can be empty string if the MBR is created with implicit intent.
*/
public String flattenToString() {
if (mComponentName == null) {
// We don't know which component would receive the key event.
return "";
}
return String.join(COMPONENT_NAME_USER_ID_DELIM,
mComponentName.toString(),
String.valueOf(mUserId),
String.valueOf(mComponentType));
}
/**
* Gets the type of the component
*
* @param context context
* @param componentName component name
* @return A component type
*/
@ComponentType
private static int getComponentType(Context context, ComponentName componentName) {
if (componentName == null) {
return COMPONENT_TYPE_INVALID;
}
PackageManager pm = context.getPackageManager();
try {
ActivityInfo activityInfo = pm.getActivityInfo(componentName,
PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE
| PackageManager.GET_ACTIVITIES);
if (activityInfo != null) {
return COMPONENT_TYPE_ACTIVITY;
}
} catch (PackageManager.NameNotFoundException e) {
}
try {
ServiceInfo serviceInfo = pm.getServiceInfo(componentName,
PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE
| PackageManager.GET_SERVICES);
if (serviceInfo != null) {
return COMPONENT_TYPE_SERVICE;
}
} catch (PackageManager.NameNotFoundException e) {
}
// Pick legacy behavior for BroadcastReceiver or unknown.
return COMPONENT_TYPE_BROADCAST;
}
private static ComponentName resolveImplicitServiceIntent(PackageManager pm, Intent intent) {
// Flag explanations.
// - MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE:
// filter apps regardless of the phone's locked/unlocked state.
// - GET_SERVICES: Return service
return createComponentName(pm.resolveService(intent,
PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE
| PackageManager.GET_SERVICES));
}
private static ComponentName resolveManifestDeclaredBroadcastReceiverIntent(
PackageManager pm, Intent intent) {
// Flag explanations.
// - MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE:
// filter apps regardless of the phone's locked/unlocked state.
List<ResolveInfo> resolveInfos = pm.queryBroadcastReceivers(intent,
PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
return (resolveInfos != null && !resolveInfos.isEmpty())
? createComponentName(resolveInfos.get(0)) : null;
}
private static ComponentName resolveImplicitActivityIntent(PackageManager pm, Intent intent) {
// Flag explanations.
// - MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE:
// Filter apps regardless of the phone's locked/unlocked state.
// - MATCH_DEFAULT_ONLY:
// Implicit intent receiver should be set as default. Only needed for activity.
// - GET_ACTIVITIES: Return activity
return createComponentName(pm.resolveActivity(intent,
PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE
| PackageManager.MATCH_DEFAULT_ONLY
| PackageManager.GET_ACTIVITIES));
}
private static ComponentName createComponentName(ResolveInfo resolveInfo) {
if (resolveInfo == null) {
return null;
}
ComponentInfo componentInfo;
// Code borrowed from ResolveInfo#getComponentInfo().
if (resolveInfo.activityInfo != null) {
componentInfo = resolveInfo.activityInfo;
} else if (resolveInfo.serviceInfo != null) {
componentInfo = resolveInfo.serviceInfo;
} else {
// We're not interested in content provider.
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;
}
}
}

View File

@@ -127,7 +127,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
new ArrayList<>();
private long mFlags;
private PendingIntent mMediaButtonReceiver;
private MediaButtonReceiverHolder mMediaButtonReceiverHolder;
private PendingIntent mLaunchIntent;
// TransportPerformer fields
@@ -220,8 +220,8 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
*
* @return The pending intent set by the app or null.
*/
public PendingIntent getMediaButtonReceiver() {
return mMediaButtonReceiver;
public MediaButtonReceiverHolder getMediaButtonReceiver() {
return mMediaButtonReceiverHolder;
}
/**
@@ -471,7 +471,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
+ ", userId=" + mUserId);
pw.println(indent + "package=" + mPackageName);
pw.println(indent + "launchIntent=" + mLaunchIntent);
pw.println(indent + "mediaButtonReceiver=" + mMediaButtonReceiver);
pw.println(indent + "mediaButtonReceiver=" + mMediaButtonReceiverHolder);
pw.println(indent + "active=" + mIsActive);
pw.println(indent + "flags=" + mFlags);
pw.println(indent + "rating type=" + mRatingType);
@@ -833,12 +833,14 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
@Override
public void setMediaButtonReceiver(PendingIntent pi) throws RemoteException {
if ((mPolicies & SessionPolicyProvider.SESSION_POLICY_IGNORE_BUTTON_RECEIVER) == 1) {
return;
}
mMediaButtonReceiver = pi;
final long token = Binder.clearCallingIdentity();
try {
if ((mPolicies & SessionPolicyProvider.SESSION_POLICY_IGNORE_BUTTON_RECEIVER)
!= 0) {
return;
}
mMediaButtonReceiverHolder =
MediaButtonReceiverHolder.create(mContext, mUserId, pi);
mService.onMediaButtonReceiverChanged(MediaSessionRecord.this);
} finally {
Binder.restoreCallingIdentity(token);
@@ -1529,5 +1531,4 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
msg.sendToTarget();
}
}
}

View File

@@ -18,22 +18,17 @@ package com.android.server.media;
import static android.os.UserHandle.USER_ALL;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.INotificationManager;
import android.app.KeyguardManager;
import android.app.PendingIntent;
import android.app.PendingIntent.CanceledException;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ParceledListSlice;
import android.content.pm.ServiceInfo;
import android.content.pm.UserInfo;
import android.database.ContentObserver;
import android.media.AudioManager;
@@ -99,7 +94,7 @@ public class MediaSessionService extends SystemService implements Monitor {
private static final String TAG = "MediaSessionService";
static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
// Leave log for key event always.
private static final boolean DEBUG_KEY_EVENT = true;
static final boolean DEBUG_KEY_EVENT = true;
private static final int WAKELOCK_TIMEOUT = 5000;
private static final int MEDIA_KEY_LISTENER_TIMEOUT = 1000;
@@ -760,12 +755,6 @@ public class MediaSessionService extends SystemService implements Monitor {
* <p>The contents of this object is guarded by {@link #mLock}.
*/
final class FullUserRecord implements MediaSessionStack.OnMediaButtonSessionChangedListener {
public static final int COMPONENT_TYPE_INVALID = 0;
public static final int COMPONENT_TYPE_BROADCAST = 1;
public static final int COMPONENT_TYPE_ACTIVITY = 2;
public static final int COMPONENT_TYPE_SERVICE = 3;
private static final String COMPONENT_NAME_USER_ID_DELIM = ",";
private final int mFullUserId;
private final MediaSessionStack mPriorityStack;
private final HashMap<IBinder, OnMediaKeyEventDispatchedListenerRecord>
@@ -774,10 +763,7 @@ public class MediaSessionService extends SystemService implements Monitor {
mOnMediaKeyEventSessionChangedListeners = new HashMap<>();
private final SparseIntArray mUidToSessionCount = new SparseIntArray();
private PendingIntent mLastMediaButtonReceiver;
private ComponentName mRestoredMediaButtonReceiver;
private int mRestoredMediaButtonReceiverComponentType;
private int mRestoredMediaButtonReceiverUserId;
private MediaButtonReceiverHolder mLastMediaButtonReceiverHolder;
private IOnVolumeKeyLongPressListener mOnVolumeKeyLongPressListener;
private int mOnVolumeKeyLongPressListenerUid;
@@ -794,21 +780,9 @@ public class MediaSessionService extends SystemService implements Monitor {
// Restore the remembered media button receiver before the boot.
String mediaButtonReceiverInfo = Settings.Secure.getStringForUser(mContentResolver,
Settings.System.MEDIA_BUTTON_RECEIVER, mFullUserId);
if (mediaButtonReceiverInfo == null) {
return;
}
String[] tokens = mediaButtonReceiverInfo.split(COMPONENT_NAME_USER_ID_DELIM);
if (tokens == null || (tokens.length != 2 && tokens.length != 3)) {
return;
}
mRestoredMediaButtonReceiver = ComponentName.unflattenFromString(tokens[0]);
mRestoredMediaButtonReceiverUserId = Integer.parseInt(tokens[1]);
if (tokens.length == 3) {
mRestoredMediaButtonReceiverComponentType = Integer.parseInt(tokens[2]);
} else {
mRestoredMediaButtonReceiverComponentType =
getComponentType(mRestoredMediaButtonReceiver);
}
mLastMediaButtonReceiverHolder =
MediaButtonReceiverHolder.unflattenFromString(
mContext, mediaButtonReceiverInfo);
}
public void destroySessionsForUserLocked(int userId) {
@@ -892,10 +866,7 @@ public class MediaSessionService extends SystemService implements Monitor {
: mOnMediaKeyEventSessionChangedListeners.values()) {
pw.println(indent + " from " + getCallingPackageName(cr.uid));
}
pw.println(indent + "Last MediaButtonReceiver: " + mLastMediaButtonReceiver);
pw.println(indent + "Restored MediaButtonReceiver: " + mRestoredMediaButtonReceiver);
pw.println(indent + "Restored MediaButtonReceiverComponentType: "
+ mRestoredMediaButtonReceiverComponentType);
pw.println(indent + "Last MediaButtonReceiver: " + mLastMediaButtonReceiverHolder);
mPriorityStack.dump(pw, indent);
}
@@ -924,25 +895,12 @@ public class MediaSessionService extends SystemService implements Monitor {
return;
}
MediaSessionRecord sessionRecord = (MediaSessionRecord) record;
PendingIntent receiver = sessionRecord.getMediaButtonReceiver();
mLastMediaButtonReceiver = receiver;
mRestoredMediaButtonReceiver = null;
mRestoredMediaButtonReceiverComponentType = COMPONENT_TYPE_INVALID;
String mediaButtonReceiverInfo = "";
if (receiver != null) {
ComponentName component = receiver.getIntent().getComponent();
if (component != null
&& record.getPackageName().equals(component.getPackageName())) {
String componentName = component.flattenToString();
int componentType = getComponentType(component);
mediaButtonReceiverInfo = String.join(COMPONENT_NAME_USER_ID_DELIM,
componentName, String.valueOf(record.getUserId()),
String.valueOf(componentType));
}
}
mLastMediaButtonReceiverHolder = sessionRecord.getMediaButtonReceiver();
String mediaButtonReceiverInfo = (mLastMediaButtonReceiverHolder == null)
? "" : mLastMediaButtonReceiverHolder.flattenToString();
Settings.Secure.putStringForUser(mContentResolver,
Settings.System.MEDIA_BUTTON_RECEIVER, mediaButtonReceiverInfo,
Settings.System.MEDIA_BUTTON_RECEIVER,
mediaButtonReceiverInfo,
mFullUserId);
}
@@ -958,15 +916,9 @@ public class MediaSessionService extends SystemService implements Monitor {
} else {
// TODO(jaewan): Implement
}
} else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null) {
callback.onMediaKeyEventSessionChanged(
mCurrentFullUserRecord.mLastMediaButtonReceiver
.getIntent().getComponent().getPackageName(),
null);
} else if (mCurrentFullUserRecord.mRestoredMediaButtonReceiver != null) {
callback.onMediaKeyEventSessionChanged(
mCurrentFullUserRecord.mRestoredMediaButtonReceiver.getPackageName(),
null);
} else if (mCurrentFullUserRecord.mLastMediaButtonReceiverHolder != null) {
String packageName = mLastMediaButtonReceiverHolder.getPackageName();
callback.onMediaKeyEventSessionChanged(packageName, null);
}
} catch (RemoteException e) {
Log.w(TAG, "Failed to pushAddressedPlayerChangedLocked", e);
@@ -985,35 +937,6 @@ public class MediaSessionService extends SystemService implements Monitor {
? mGlobalPrioritySession : mPriorityStack.getMediaButtonSession();
}
private int getComponentType(@Nullable ComponentName componentName) {
if (componentName == null) {
return COMPONENT_TYPE_INVALID;
}
PackageManager pm = mContext.getPackageManager();
try {
ActivityInfo activityInfo = pm.getActivityInfo(componentName,
PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE
| PackageManager.GET_ACTIVITIES);
if (activityInfo != null) {
return COMPONENT_TYPE_ACTIVITY;
}
} catch (NameNotFoundException e) {
}
try {
ServiceInfo serviceInfo = pm.getServiceInfo(componentName,
PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE
| PackageManager.GET_SERVICES);
if (serviceInfo != null) {
return COMPONENT_TYPE_SERVICE;
}
} catch (NameNotFoundException e) {
}
// Pick legacy behavior for BroadcastReceiver or unknown.
return COMPONENT_TYPE_BROADCAST;
}
final class OnMediaKeyEventDispatchedListenerRecord implements IBinder.DeathRecipient {
public final IOnMediaKeyEventDispatchedListener callback;
public final int uid;
@@ -2166,79 +2089,31 @@ public class MediaSessionService extends SystemService implements Monitor {
} catch (RemoteException e) {
Log.w(TAG, "Failed to send callback", e);
}
} else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null
|| mCurrentFullUserRecord.mRestoredMediaButtonReceiver != null) {
} else if (mCurrentFullUserRecord.mLastMediaButtonReceiverHolder != null) {
if (needWakeLock) {
mKeyEventReceiver.acquireWakeLockLocked();
}
Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
mediaButtonIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
// TODO: Find a way to also send PID/UID in secure way.
String callerPackageName =
String callingPackageName =
(asSystemService) ? mContext.getPackageName() : packageName;
mediaButtonIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, callerPackageName);
try {
if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null) {
PendingIntent receiver = mCurrentFullUserRecord.mLastMediaButtonReceiver;
if (DEBUG_KEY_EVENT) {
Log.d(TAG, "Sending " + keyEvent
+ " to the last known PendingIntent " + receiver);
}
receiver.send(mContext,
needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
mediaButtonIntent, mKeyEventReceiver, mHandler);
ComponentName componentName = mCurrentFullUserRecord
.mLastMediaButtonReceiver.getIntent().getComponent();
if (componentName != null) {
for (FullUserRecord.OnMediaKeyEventDispatchedListenerRecord cr
: mCurrentFullUserRecord
.mOnMediaKeyEventDispatchedListeners.values()) {
cr.callback.onMediaKeyEventDispatched(keyEvent,
componentName.getPackageName(), null);
}
}
} else {
ComponentName receiver =
mCurrentFullUserRecord.mRestoredMediaButtonReceiver;
int componentType = mCurrentFullUserRecord
.mRestoredMediaButtonReceiverComponentType;
UserHandle userHandle = UserHandle.of(mCurrentFullUserRecord
.mRestoredMediaButtonReceiverUserId);
if (DEBUG_KEY_EVENT) {
Log.d(TAG, "Sending " + keyEvent + " to the restored intent "
+ receiver + ", type=" + componentType);
}
mediaButtonIntent.setComponent(receiver);
MediaButtonReceiverHolder mediaButtonReceiverHolder =
mCurrentFullUserRecord.mLastMediaButtonReceiverHolder;
boolean sent = mediaButtonReceiverHolder.send(
mContext, keyEvent, callingPackageName,
needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1, mKeyEventReceiver,
mHandler);
if (sent) {
String pkgName = mediaButtonReceiverHolder.getPackageName();
for (FullUserRecord.OnMediaKeyEventDispatchedListenerRecord cr
: mCurrentFullUserRecord
.mOnMediaKeyEventDispatchedListeners.values()) {
try {
switch (componentType) {
case FullUserRecord.COMPONENT_TYPE_ACTIVITY:
mContext.startActivityAsUser(mediaButtonIntent, userHandle);
break;
case FullUserRecord.COMPONENT_TYPE_SERVICE:
mContext.startForegroundServiceAsUser(mediaButtonIntent,
userHandle);
break;
default:
// Legacy behavior for other cases.
mContext.sendBroadcastAsUser(mediaButtonIntent, userHandle);
}
} catch (Exception e) {
Log.w(TAG, "Error sending media button to the restored intent "
+ receiver + ", type=" + componentType, e);
}
for (FullUserRecord.OnMediaKeyEventDispatchedListenerRecord cr
: mCurrentFullUserRecord
.mOnMediaKeyEventDispatchedListeners.values()) {
cr.callback.onMediaKeyEventDispatched(keyEvent,
receiver.getPackageName(), null);
cr.callback.onMediaKeyEventDispatched(keyEvent, pkgName, null);
} catch (RemoteException e) {
Log.w(TAG, "Failed notify key event dispatch, uid=" + cr.uid, e);
}
}
} catch (CanceledException e) {
Log.i(TAG, "Error sending key event to media button receiver "
+ mCurrentFullUserRecord.mLastMediaButtonReceiver, e);
} catch (RemoteException e) {
Log.w(TAG, "Failed to send callback", e);
}
}
}