diff options
| author | 2019-06-19 15:47:54 +0900 | |
|---|---|---|
| committer | 2019-06-21 15:21:07 +0900 | |
| commit | 6cddbe14e1ff67dc4691a013fe38a2eb0893fe03 (patch) | |
| tree | 2121007848a253e2411b8528ec642adf23f56ed3 | |
| parent | 762aa842ae7fee5d483b0400c1f04bc3d80d3af1 (diff) | |
Do not allow adding custom Parcelable in Bundles
This CL prevents the API users from passing any Bundles with
custom Parcelable to following APIs:
- MediaSession2.Builder#setExtras()
- MediaController2.Builder#setConnectionHints()
- MediaSession constructor
Bug: 135572812
Test: Passed followings tests
atest CtsMediaTestCases:android.media.cts.MediaSessionTest;
atest CtsMediaTestCases:android.media.cts.MediaControllerTest;
atest CtsMediaTestCases:android.media.cts.MediaBrowserTest;
atest CtsMediaTestCases:android.media.cts.MediaSessionManagerTest;
atest CtsMediaTestCases:android.media.cts.MediaSession2Test;
atest CtsMediaTestCases:android.media.cts.MediaController2Test;
atest CtsMediaTestCases:android.media.cts.MediaSession2ServiceTest;
Change-Id: I703c9fc0b180fb7bb3bf9bbec677f01a2a128c7a
| -rw-r--r-- | media/apex/java/android/media/Media2Utils.java | 1 | ||||
| -rw-r--r-- | media/apex/java/android/media/MediaController2.java | 25 | ||||
| -rw-r--r-- | media/apex/java/android/media/MediaSession2.java | 63 | ||||
| -rw-r--r-- | media/apex/java/android/media/MediaSession2Service.java | 12 | ||||
| -rw-r--r-- | media/apex/java/android/media/Session2Token.java | 17 | ||||
| -rw-r--r-- | media/java/android/media/session/MediaController.java | 6 | ||||
| -rw-r--r-- | media/java/android/media/session/MediaSession.java | 37 |
7 files changed, 144 insertions, 17 deletions
diff --git a/media/apex/java/android/media/Media2Utils.java b/media/apex/java/android/media/Media2Utils.java index 5fd61915b4aa..a87e9676d017 100644 --- a/media/apex/java/android/media/Media2Utils.java +++ b/media/apex/java/android/media/Media2Utils.java @@ -75,5 +75,4 @@ public class Media2Utils { Log.v(TAG, "storeCookies: cookieHandler: " + cookieHandler + " Cookies: " + cookies); } - } diff --git a/media/apex/java/android/media/MediaController2.java b/media/apex/java/android/media/MediaController2.java index 9848f1a10a54..63a4510e7fe7 100644 --- a/media/apex/java/android/media/MediaController2.java +++ b/media/apex/java/android/media/MediaController2.java @@ -100,7 +100,7 @@ public class MediaController2 implements AutoCloseable { * @param callback controller callback to receive changes in. */ MediaController2(@NonNull Context context, @NonNull Session2Token token, - @Nullable Bundle connectionHints, @NonNull Executor executor, + @NonNull Bundle connectionHints, @NonNull Executor executor, @NonNull ControllerCallback callback) { if (context == null) { throw new IllegalArgumentException("context shouldn't be null"); @@ -259,7 +259,16 @@ public class MediaController2 implements AutoCloseable { Session2CommandGroup allowedCommands = connectionResult.getParcelable(KEY_ALLOWED_COMMANDS); boolean playbackActive = connectionResult.getBoolean(KEY_PLAYBACK_ACTIVE); + Bundle tokenExtras = connectionResult.getBundle(KEY_TOKEN_EXTRAS); + if (tokenExtras == null) { + Log.w(TAG, "extras shouldn't be null."); + tokenExtras = Bundle.EMPTY; + } else if (MediaSession2.hasCustomParcelable(tokenExtras)) { + Log.w(TAG, "extras contain custom parcelable. Ignoring."); + tokenExtras = Bundle.EMPTY; + } + if (DEBUG) { Log.d(TAG, "notifyConnected sessionBinder=" + sessionBinder + ", allowedCommands=" + allowedCommands); @@ -343,7 +352,7 @@ public class MediaController2 implements AutoCloseable { } } - private Bundle createConnectionRequest(@Nullable Bundle connectionHints) { + private Bundle createConnectionRequest(@NonNull Bundle connectionHints) { Bundle connectionRequest = new Bundle(); connectionRequest.putString(KEY_PACKAGE_NAME, mContext.getPackageName()); connectionRequest.putInt(KEY_PID, Process.myPid()); @@ -351,7 +360,7 @@ public class MediaController2 implements AutoCloseable { return connectionRequest; } - private boolean requestConnectToSession(@Nullable Bundle connectionHints) { + private boolean requestConnectToSession(@NonNull Bundle connectionHints) { Session2Link sessionBinder = mSessionToken.getSessionLink(); Bundle connectionRequest = createConnectionRequest(connectionHints); try { @@ -430,6 +439,9 @@ public class MediaController2 implements AutoCloseable { * <p> * {@code connectionHints} is a session-specific argument to send to the session when * connecting. The contents of this bundle may affect the connection result. + * <p> + * An {@link IllegalArgumentException} will be thrown if the bundle contains any + * non-framework Parcelable objects. * * @param connectionHints a bundle which contains the connection hints * @return The Builder to allow chaining @@ -439,6 +451,10 @@ public class MediaController2 implements AutoCloseable { if (connectionHints == null) { throw new IllegalArgumentException("connectionHints shouldn't be null"); } + if (MediaSession2.hasCustomParcelable(connectionHints)) { + throw new IllegalArgumentException("connectionHints shouldn't contain any custom " + + "parcelables"); + } mConnectionHints = new Bundle(connectionHints); return this; } @@ -477,6 +493,9 @@ public class MediaController2 implements AutoCloseable { if (mCallback == null) { mCallback = new ControllerCallback() {}; } + if (mConnectionHints == null) { + mConnectionHints = Bundle.EMPTY; + } return new MediaController2( mContext, mToken, mConnectionHints, mCallbackExecutor, mCallback); } diff --git a/media/apex/java/android/media/MediaSession2.java b/media/apex/java/android/media/MediaSession2.java index 081911896c04..b3edf3f50866 100644 --- a/media/apex/java/android/media/MediaSession2.java +++ b/media/apex/java/android/media/MediaSession2.java @@ -34,8 +34,10 @@ import android.content.Context; import android.content.Intent; import android.media.session.MediaSessionManager; import android.media.session.MediaSessionManager.RemoteUserInfo; +import android.os.BadParcelableException; import android.os.Bundle; import android.os.Handler; +import android.os.Parcel; import android.os.Process; import android.os.ResultReceiver; import android.util.ArrayMap; @@ -97,7 +99,7 @@ public class MediaSession2 implements AutoCloseable { MediaSession2(@NonNull Context context, @NonNull String id, PendingIntent sessionActivity, @NonNull Executor callbackExecutor, @NonNull SessionCallback callback, - Bundle tokenExtras) { + @NonNull Bundle tokenExtras) { synchronized (MediaSession2.class) { if (SESSION_ID_LIST.contains(id)) { throw new IllegalStateException("Session ID must be unique. ID=" + id); @@ -276,6 +278,35 @@ public class MediaSession2 implements AutoCloseable { return controllers; } + /** + * Returns whether the given bundle includes non-framework Parcelables. + */ + static boolean hasCustomParcelable(@Nullable Bundle bundle) { + if (bundle == null) { + return false; + } + + // Try writing the bundle to parcel, and read it with framework classloader. + Parcel parcel = null; + try { + parcel = Parcel.obtain(); + parcel.writeBundle(bundle); + parcel.setDataPosition(0); + Bundle out = parcel.readBundle(null); + + // Calling Bundle#size() will trigger Bundle#unparcel(). + out.size(); + } catch (BadParcelableException e) { + Log.d(TAG, "Custom parcelable in bundle.", e); + return true; + } finally { + if (parcel != null) { + parcel.recycle(); + } + } + return false; + } + boolean isClosed() { synchronized (mLock) { return mClosed; @@ -309,11 +340,21 @@ public class MediaSession2 implements AutoCloseable { String callingPkg = connectionRequest.getString(KEY_PACKAGE_NAME); RemoteUserInfo remoteUserInfo = new RemoteUserInfo(callingPkg, callingPid, callingUid); + + Bundle connectionHints = connectionRequest.getBundle(KEY_CONNECTION_HINTS); + if (connectionHints == null) { + Log.w(TAG, "connectionHints shouldn't be null."); + connectionHints = Bundle.EMPTY; + } else if (hasCustomParcelable(connectionHints)) { + Log.w(TAG, "connectionHints contain custom parcelable. Ignoring."); + connectionHints = Bundle.EMPTY; + } + final ControllerInfo controllerInfo = new ControllerInfo( remoteUserInfo, mSessionManager.isTrustedForMediaControl(remoteUserInfo), controller, - connectionRequest.getBundle(KEY_CONNECTION_HINTS)); + connectionHints); mCallbackExecutor.execute(() -> { boolean connected = false; try { @@ -516,7 +557,8 @@ public class MediaSession2 implements AutoCloseable { /** * Set extras for the session token. If null or not set, {@link Session2Token#getExtras()} - * will return {@link Bundle#EMPTY}. + * will return an empty {@link Bundle}. An {@link IllegalArgumentException} will be thrown + * if the bundle contains any non-framework Parcelable objects. * * @return The Builder to allow chaining * @see Session2Token#getExtras() @@ -526,7 +568,11 @@ public class MediaSession2 implements AutoCloseable { if (extras == null) { throw new NullPointerException("extras shouldn't be null"); } - mExtras = extras; + if (hasCustomParcelable(extras)) { + throw new IllegalArgumentException( + "extras shouldn't contain any custom parcelables"); + } + mExtras = new Bundle(extras); return this; } @@ -548,6 +594,9 @@ public class MediaSession2 implements AutoCloseable { if (mId == null) { mId = ""; } + if (mExtras == null) { + mExtras = Bundle.EMPTY; + } MediaSession2 session2 = new MediaSession2(mContext, mId, mSessionActivity, mCallbackExecutor, mCallback, mExtras); @@ -596,7 +645,7 @@ public class MediaSession2 implements AutoCloseable { * connection result. */ ControllerInfo(@NonNull RemoteUserInfo remoteUserInfo, boolean trusted, - @Nullable Controller2Link controllerBinder, @Nullable Bundle connectionHints) { + @Nullable Controller2Link controllerBinder, @NonNull Bundle connectionHints) { mRemoteUserInfo = remoteUserInfo; mIsTrusted = trusted; mControllerBinder = controllerBinder; @@ -629,11 +678,11 @@ public class MediaSession2 implements AutoCloseable { } /** - * @return connection hints sent from controller, or {@link Bundle#EMPTY} if none. + * @return connection hints sent from controller. */ @NonNull public Bundle getConnectionHints() { - return mConnectionHints == null ? Bundle.EMPTY : new Bundle(mConnectionHints); + return new Bundle(mConnectionHints); } /** diff --git a/media/apex/java/android/media/MediaSession2Service.java b/media/apex/java/android/media/MediaSession2Service.java index b8bf3842aae8..ee584e5eac30 100644 --- a/media/apex/java/android/media/MediaSession2Service.java +++ b/media/apex/java/android/media/MediaSession2Service.java @@ -378,12 +378,22 @@ public abstract class MediaSession2Service extends Service { callingPkg, pid == 0 ? connectionRequest.getInt(KEY_PID) : pid, uid); + + Bundle connectionHints = connectionRequest.getBundle(KEY_CONNECTION_HINTS); + if (connectionHints == null) { + Log.w(TAG, "connectionHints shouldn't be null."); + connectionHints = Bundle.EMPTY; + } else if (MediaSession2.hasCustomParcelable(connectionHints)) { + Log.w(TAG, "connectionHints contain custom parcelable. Ignoring."); + connectionHints = Bundle.EMPTY; + } + final ControllerInfo controllerInfo = new ControllerInfo( remoteUserInfo, service.getMediaSessionManager() .isTrustedForMediaControl(remoteUserInfo), caller, - connectionRequest.getBundle(KEY_CONNECTION_HINTS)); + connectionHints); if (DEBUG) { Log.d(TAG, "Handling incoming connection request from the" diff --git a/media/apex/java/android/media/Session2Token.java b/media/apex/java/android/media/Session2Token.java index d7cb9787cf08..6d499fa88815 100644 --- a/media/apex/java/android/media/Session2Token.java +++ b/media/apex/java/android/media/Session2Token.java @@ -118,11 +118,11 @@ public final class Session2Token implements Parcelable { mUid = uid; mType = TYPE_SESSION_SERVICE; mSessionLink = null; - mExtras = null; + mExtras = Bundle.EMPTY; } Session2Token(int uid, int type, String packageName, Session2Link sessionLink, - Bundle tokenExtras) { + @NonNull Bundle tokenExtras) { mUid = uid; mType = type; mPackageName = packageName; @@ -139,7 +139,16 @@ public final class Session2Token implements Parcelable { mServiceName = in.readString(); mSessionLink = in.readParcelable(null); mComponentName = ComponentName.unflattenFromString(in.readString()); - mExtras = in.readBundle(); + + Bundle extras = in.readBundle(); + if (extras == null) { + Log.w(TAG, "extras shouldn't be null."); + extras = Bundle.EMPTY; + } else if (MediaSession2.hasCustomParcelable(extras)) { + Log.w(TAG, "extras contain custom parcelable. Ignoring."); + extras = Bundle.EMPTY; + } + mExtras = extras; } @Override @@ -220,7 +229,7 @@ public final class Session2Token implements Parcelable { */ @NonNull public Bundle getExtras() { - return mExtras == null ? Bundle.EMPTY : mExtras; + return new Bundle(mExtras); } Session2Link getSessionLink() { diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java index c1c7fcac0a8d..1fc4f7d59ca5 100644 --- a/media/java/android/media/session/MediaController.java +++ b/media/java/android/media/session/MediaController.java @@ -414,7 +414,7 @@ public final class MediaController { /** * Gets the additional session information which was set when the session was created. * - * @return The additional session information, or {@link Bundle#EMPTY} if not set. + * @return The additional session information, or an empty {@link Bundle} if not set. */ @NonNull public Bundle getSessionInfo() { @@ -430,6 +430,10 @@ public final class MediaController { } if (mSessionInfo == null) { + Log.w(TAG, "sessionInfo shouldn't be null."); + mSessionInfo = Bundle.EMPTY; + } else if (MediaSession.hasCustomParcelable(mSessionInfo)) { + Log.w(TAG, "sessionInfo contains custom parcelable. Ignoring."); mSessionInfo = Bundle.EMPTY; } return new Bundle(mSessionInfo); diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java index c4085f876136..e11715ff7299 100644 --- a/media/java/android/media/session/MediaSession.java +++ b/media/java/android/media/session/MediaSession.java @@ -32,6 +32,7 @@ import android.media.Rating; import android.media.VolumeProvider; import android.media.session.MediaSessionManager.RemoteUserInfo; import android.net.Uri; +import android.os.BadParcelableException; import android.os.Bundle; import android.os.Handler; import android.os.Looper; @@ -168,6 +169,8 @@ public final class MediaSession { * @param sessionInfo A bundle for additional information about this session. * Controllers can get this information by calling * {@link MediaController#getSessionInfo()}. + * An {@link IllegalArgumentException} will be thrown if this contains + * any non-framework Parcelable objects. */ public MediaSession(@NonNull Context context, @NonNull String tag, @Nullable Bundle sessionInfo) { @@ -177,6 +180,11 @@ public final class MediaSession { if (TextUtils.isEmpty(tag)) { throw new IllegalArgumentException("tag cannot be null or empty"); } + if (hasCustomParcelable(sessionInfo)) { + throw new IllegalArgumentException("sessionInfo shouldn't contain any custom " + + "parcelables"); + } + mMaxBitmapSize = context.getResources().getDimensionPixelSize( com.android.internal.R.dimen.config_mediaMetadataBitmapMaxSize); mCbStub = new CallbackStub(this); @@ -600,6 +608,35 @@ public final class MediaSession { return false; } + /** + * Returns whether the given bundle includes non-framework Parcelables. + */ + static boolean hasCustomParcelable(@Nullable Bundle bundle) { + if (bundle == null) { + return false; + } + + // Try writing the bundle to parcel, and read it with framework classloader. + Parcel parcel = null; + try { + parcel = Parcel.obtain(); + parcel.writeBundle(bundle); + parcel.setDataPosition(0); + Bundle out = parcel.readBundle(null); + + // Calling Bundle#size() will trigger Bundle#unparcel(). + out.size(); + } catch (BadParcelableException e) { + Log.d(TAG, "Custom parcelable in bundle.", e); + return true; + } finally { + if (parcel != null) { + parcel.recycle(); + } + } + return false; + } + void dispatchPrepare(RemoteUserInfo caller) { postToCallback(caller, CallbackMessageHandler.MSG_PREPARE, null, null); } |