Merge "MediaSessionService: Fix crash with implicit media button receiver"
This commit is contained in:
committed by
Android (Google) Code Review
commit
87bdd6503a
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user