diff options
14 files changed, 264 insertions, 45 deletions
diff --git a/config/hiddenapi-greylist.txt b/config/hiddenapi-greylist.txt index 13f8dd924d4a..700df641659c 100644 --- a/config/hiddenapi-greylist.txt +++ b/config/hiddenapi-greylist.txt @@ -1419,11 +1419,6 @@ Landroid/service/euicc/IGetEuiccProfileInfoListCallback;->onComplete(Landroid/se Landroid/service/euicc/IRetainSubscriptionsForFactoryResetCallback;->onComplete(I)V Landroid/service/euicc/ISwitchToSubscriptionCallback;->onComplete(I)V Landroid/service/euicc/IUpdateSubscriptionNicknameCallback;->onComplete(I)V -Landroid/service/media/IMediaBrowserServiceCallbacks$Stub;->asInterface(Landroid/os/IBinder;)Landroid/service/media/IMediaBrowserServiceCallbacks; -Landroid/service/media/IMediaBrowserServiceCallbacks;->onConnect(Ljava/lang/String;Landroid/media/session/MediaSession$Token;Landroid/os/Bundle;)V -Landroid/service/media/IMediaBrowserServiceCallbacks;->onConnectFailed()V -Landroid/service/media/IMediaBrowserServiceCallbacks;->onLoadChildren(Ljava/lang/String;Landroid/content/pm/ParceledListSlice;)V -Landroid/service/media/IMediaBrowserServiceCallbacks;->onLoadChildrenWithOptions(Ljava/lang/String;Landroid/content/pm/ParceledListSlice;Landroid/os/Bundle;)V Landroid/service/notification/INotificationListener$Stub;-><init>()V Landroid/service/persistentdata/IPersistentDataBlockService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/service/persistentdata/IPersistentDataBlockService; Landroid/service/vr/IVrManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/service/vr/IVrManager; diff --git a/media/java/android/media/MediaDescription.java b/media/java/android/media/MediaDescription.java index e6aea99ef50b..31079e5c54d4 100644 --- a/media/java/android/media/MediaDescription.java +++ b/media/java/android/media/MediaDescription.java @@ -7,6 +7,7 @@ import android.net.Uri; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; +import android.text.TextUtils; /** * A simple set of metadata for a media item suitable for display. This can be @@ -122,9 +123,9 @@ public class MediaDescription implements Parcelable { private MediaDescription(Parcel in) { mMediaId = in.readString(); - mTitle = in.readCharSequence(); - mSubtitle = in.readCharSequence(); - mDescription = in.readCharSequence(); + mTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); + mSubtitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); + mDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); mIcon = in.readParcelable(null); mIconUri = in.readParcelable(null); mExtras = in.readBundle(); @@ -210,9 +211,9 @@ public class MediaDescription implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(mMediaId); - dest.writeCharSequence(mTitle); - dest.writeCharSequence(mSubtitle); - dest.writeCharSequence(mDescription); + TextUtils.writeToParcel(mTitle, dest, 0); + TextUtils.writeToParcel(mSubtitle, dest, 0); + TextUtils.writeToParcel(mDescription, dest, 0); dest.writeParcelable(mIcon, flags); dest.writeParcelable(mIconUri, flags); dest.writeBundle(mExtras); diff --git a/media/java/android/media/MediaMetadata.java b/media/java/android/media/MediaMetadata.java index 2721ad12b16a..a3d75a30c2b7 100644 --- a/media/java/android/media/MediaMetadata.java +++ b/media/java/android/media/MediaMetadata.java @@ -34,8 +34,8 @@ import android.util.SparseArray; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.Set; import java.util.Objects; +import java.util.Set; /** * Contains metadata about an item, such as the title, artist, etc. @@ -422,7 +422,7 @@ public final class MediaMetadata implements Parcelable { } private MediaMetadata(Parcel in) { - mBundle = Bundle.setDefusable(in.readBundle(), true); + mBundle = in.readBundle(); } /** diff --git a/media/java/android/media/MediaParceledListSlice.aidl b/media/java/android/media/MediaParceledListSlice.aidl new file mode 100644 index 000000000000..5c0e5bc84720 --- /dev/null +++ b/media/java/android/media/MediaParceledListSlice.aidl @@ -0,0 +1,20 @@ +/* + * Copyright 2019 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 android.media; + +/** @hide */ +parcelable MediaParceledListSlice; diff --git a/media/java/android/media/MediaParceledListSlice.java b/media/java/android/media/MediaParceledListSlice.java new file mode 100644 index 000000000000..16a37d99fb86 --- /dev/null +++ b/media/java/android/media/MediaParceledListSlice.java @@ -0,0 +1,200 @@ +/* + * Copyright 2019 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 android.media; + +import android.os.Binder; +import android.os.IBinder; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.RemoteException; +import android.util.Log; + +import java.util.ArrayList; +import java.util.List; + +/** + * Transfer a large list of objects across an IPC. Splits into multiple transactions if needed. + * Note: Only use classes declared final in order to avoid subclasses overriding reading/writing + * parcel logic. + * + * TODO: Add test for sending large data + * @param <T> A Parcelable class which will be sent over the binder calls. + * @hide + */ +public class MediaParceledListSlice<T extends Parcelable> implements Parcelable { + private static final String TAG = "MediaParceledListSlice"; + private static final boolean DEBUG = false; + + private static final int MAX_IPC_SIZE = 64 * 1024; // IBinder.MAX_IPC_SIZE + + final List<T> mList; + + public MediaParceledListSlice(List<T> list) { + if (list == null) { + throw new IllegalArgumentException("list shouldn't be null"); + } + mList = list; + } + + MediaParceledListSlice(Parcel p) { + final int itemCount = p.readInt(); + mList = new ArrayList<>(itemCount); + if (DEBUG) { + Log.d(TAG, "Retrieving " + itemCount + " items"); + } + if (itemCount <= 0) { + return; + } + + int i = 0; + while (i < itemCount) { + if (p.readInt() == 0) { + break; + } + + final T parcelable = p.readParcelable(null); + mList.add(parcelable); + + if (DEBUG) { + Log.d(TAG, "Read inline #" + i + ": " + mList.get(mList.size() - 1)); + } + i++; + } + if (i >= itemCount) { + return; + } + final IBinder retriever = p.readStrongBinder(); + while (i < itemCount) { + if (DEBUG) { + Log.d(TAG, "Reading more @" + i + " of " + itemCount + ": retriever=" + retriever); + } + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInt(i); + try { + retriever.transact(IBinder.FIRST_CALL_TRANSACTION, data, reply, 0); + } catch (RemoteException e) { + Log.w(TAG, "Failure retrieving array; only received " + i + " of " + itemCount, e); + return; + } + while (i < itemCount && reply.readInt() != 0) { + final T parcelable = reply.readParcelable(null); + mList.add(parcelable); + + if (DEBUG) { + Log.d(TAG, "Read extra #" + i + ": " + mList.get(mList.size() - 1)); + } + i++; + } + reply.recycle(); + data.recycle(); + } + } + + public List<T> getList() { + return mList; + } + + /** + * Write this to another Parcel. Note that this discards the internal Parcel + * and should not be used anymore. This is so we can pass this to a Binder + * where we won't have a chance to call recycle on this. + */ + @Override + public void writeToParcel(Parcel dest, int flags) { + final int itemCount = mList.size(); + dest.writeInt(itemCount); + if (DEBUG) { + Log.d(TAG, "Writing " + itemCount + " items"); + } + if (itemCount > 0) { + int i = 0; + while (i < itemCount && dest.dataSize() < MAX_IPC_SIZE) { + dest.writeInt(1); + + final T parcelable = mList.get(i); + dest.writeParcelable(parcelable, flags); + + if (DEBUG) { + Log.d(TAG, "Wrote inline #" + i + ": " + mList.get(i)); + } + i++; + } + if (i < itemCount) { + dest.writeInt(0); + Binder retriever = new Binder() { + @Override + protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) + throws RemoteException { + if (code != FIRST_CALL_TRANSACTION) { + return super.onTransact(code, data, reply, flags); + } + int i = data.readInt(); + if (DEBUG) { + Log.d(TAG, "Writing more @" + i + " of " + itemCount); + } + while (i < itemCount && reply.dataSize() < MAX_IPC_SIZE) { + reply.writeInt(1); + + final T parcelable = mList.get(i); + reply.writeParcelable(parcelable, flags); + + if (DEBUG) { + Log.d(TAG, "Wrote extra #" + i + ": " + mList.get(i)); + } + i++; + } + if (i < itemCount) { + if (DEBUG) { + Log.d(TAG, "Breaking @" + i + " of " + itemCount); + } + reply.writeInt(0); + } + return true; + } + }; + if (DEBUG) { + Log.d(TAG, "Breaking @" + i + " of " + itemCount + ": retriever=" + retriever); + } + dest.writeStrongBinder(retriever); + } + } + } + + @Override + public int describeContents() { + int contents = 0; + final List<T> list = getList(); + for (int i = 0; i < list.size(); i++) { + contents |= list.get(i).describeContents(); + } + return contents; + } + + public static final Parcelable.Creator<MediaParceledListSlice> CREATOR = + new Parcelable.Creator<MediaParceledListSlice>() { + @Override + public MediaParceledListSlice createFromParcel(Parcel in) { + return new MediaParceledListSlice(in); + } + + @Override + public MediaParceledListSlice[] newArray(int size) { + return new MediaParceledListSlice[size]; + } + }; +} diff --git a/media/java/android/media/browse/MediaBrowser.java b/media/java/android/media/browse/MediaBrowser.java index 2bccd884bea4..b1b14c6e4ba5 100644 --- a/media/java/android/media/browse/MediaBrowser.java +++ b/media/java/android/media/browse/MediaBrowser.java @@ -23,8 +23,8 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; -import android.content.pm.ParceledListSlice; import android.media.MediaDescription; +import android.media.MediaParceledListSlice; import android.media.session.MediaController; import android.media.session.MediaSession; import android.os.Binder; @@ -653,7 +653,7 @@ public final class MediaBrowser { } private final void onLoadChildren(final IMediaBrowserServiceCallbacks callback, - final String parentId, final ParceledListSlice list, final Bundle options) { + final String parentId, final MediaParceledListSlice list, final Bundle options) { mHandler.post(new Runnable() { @Override public void run() { @@ -1107,12 +1107,12 @@ public final class MediaBrowser { } @Override - public void onLoadChildren(String parentId, ParceledListSlice list) { + public void onLoadChildren(String parentId, MediaParceledListSlice list) { onLoadChildrenWithOptions(parentId, list, null); } @Override - public void onLoadChildrenWithOptions(String parentId, ParceledListSlice list, + public void onLoadChildrenWithOptions(String parentId, MediaParceledListSlice list, final Bundle options) { MediaBrowser mediaBrowser = mMediaBrowser.get(); if (mediaBrowser != null) { diff --git a/media/java/android/media/session/ControllerLink.java b/media/java/android/media/session/ControllerLink.java index df1d6497b9e6..937df20949f0 100644 --- a/media/java/android/media/session/ControllerLink.java +++ b/media/java/android/media/session/ControllerLink.java @@ -21,6 +21,7 @@ import android.annotation.Nullable; import android.annotation.SystemApi; import android.app.PendingIntent; import android.media.MediaMetadata; +import android.media.MediaParceledListSlice; import android.media.Rating; import android.media.session.MediaController.PlaybackInfo; import android.net.Uri; @@ -544,7 +545,8 @@ public final class ControllerLink implements Parcelable { @Nullable public List<MediaSession.QueueItem> getQueue() { try { - return mISessionController.getQueue(); + MediaParceledListSlice queue = mISessionController.getQueue(); + return queue == null ? null : queue.getList(); } catch (RemoteException e) { throw new RuntimeException(e); } @@ -961,8 +963,9 @@ public final class ControllerLink implements Parcelable { } @Override - public List<MediaSession.QueueItem> getQueue() { - return mControllerStub.getQueue(); + public MediaParceledListSlice getQueue() { + List<MediaSession.QueueItem> queue = mControllerStub.getQueue(); + return queue == null ? null : new MediaParceledListSlice(queue); } @Override diff --git a/media/java/android/media/session/ISession.aidl b/media/java/android/media/session/ISession.aidl index 9b4e2bca2ea4..9b1ad7bcf77c 100644 --- a/media/java/android/media/session/ISession.aidl +++ b/media/java/android/media/session/ISession.aidl @@ -18,6 +18,7 @@ package android.media.session; import android.app.PendingIntent; import android.media.AudioAttributes; import android.media.MediaMetadata; +import android.media.MediaParceledListSlice; import android.media.session.ControllerLink; import android.media.session.PlaybackState; import android.media.session.MediaSession; @@ -40,8 +41,7 @@ interface ISession { // These commands are for the TransportPerformer void setMetadata(in MediaMetadata metadata, long duration, String metadataDescription); void setPlaybackState(in PlaybackState state); - // TODO(b/122432476): Replace List with MediaParceledListSlice - void setQueue(in List<MediaSession.QueueItem> queue); + void setQueue(in MediaParceledListSlice queue); void setQueueTitle(CharSequence title); void setExtras(in Bundle extras); void setRatingType(int type); diff --git a/media/java/android/media/session/ISessionController.aidl b/media/java/android/media/session/ISessionController.aidl index 787cb7770514..a3439a1a8deb 100644 --- a/media/java/android/media/session/ISessionController.aidl +++ b/media/java/android/media/session/ISessionController.aidl @@ -18,6 +18,7 @@ package android.media.session; import android.app.PendingIntent; import android.content.Intent; import android.media.MediaMetadata; +import android.media.MediaParceledListSlice; import android.media.Rating; import android.media.session.ControllerCallbackLink; import android.media.session.MediaController; @@ -80,7 +81,7 @@ interface ISessionController { String action, in Bundle args); MediaMetadata getMetadata(); PlaybackState getPlaybackState(); - List<MediaSession.QueueItem> getQueue(); + MediaParceledListSlice getQueue(); CharSequence getQueueTitle(); Bundle getExtras(); int getRatingType(); diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java index 881e6ee6a907..f02d9ba1b7df 100644 --- a/media/java/android/media/session/MediaSession.java +++ b/media/java/android/media/session/MediaSession.java @@ -154,7 +154,7 @@ public final class MediaSession { throw new IllegalArgumentException("tag cannot be null or empty"); } mMaxBitmapSize = context.getResources().getDimensionPixelSize( - com.android.internal.R.dimen.config_mediaMetadataBitmapMaxSize); + android.R.dimen.config_mediaMetadataBitmapMaxSize); mCbStub = new SessionCallbackLink(context, new CallbackStub(this)); MediaSessionManager manager = (MediaSessionManager) context .getSystemService(Context.MEDIA_SESSION_SERVICE); diff --git a/media/java/android/media/session/PlaybackState.java b/media/java/android/media/session/PlaybackState.java index 17d16b896679..2c57d1f676ed 100644 --- a/media/java/android/media/session/PlaybackState.java +++ b/media/java/android/media/session/PlaybackState.java @@ -25,11 +25,11 @@ import android.os.Parcel; import android.os.Parcelable; import android.os.SystemClock; import android.text.TextUtils; -import java.util.ArrayList; -import java.util.List; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.List; /** * Playback state for a {@link MediaSession}. This includes a state like @@ -318,7 +318,7 @@ public final class PlaybackState implements Parcelable { mActions = in.readLong(); mCustomActions = in.createTypedArrayList(CustomAction.CREATOR); mActiveItemId = in.readLong(); - mErrorMessage = in.readCharSequence(); + mErrorMessage = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); mExtras = in.readBundle(); } @@ -353,7 +353,7 @@ public final class PlaybackState implements Parcelable { dest.writeLong(mActions); dest.writeTypedList(mCustomActions); dest.writeLong(mActiveItemId); - dest.writeCharSequence(mErrorMessage); + TextUtils.writeToParcel(mErrorMessage, dest, 0); dest.writeBundle(mExtras); } diff --git a/media/java/android/media/session/SessionLink.java b/media/java/android/media/session/SessionLink.java index 466077ebc1b7..0da0a5ae2fe1 100644 --- a/media/java/android/media/session/SessionLink.java +++ b/media/java/android/media/session/SessionLink.java @@ -22,6 +22,7 @@ import android.annotation.SystemApi; import android.app.PendingIntent; import android.media.AudioAttributes; import android.media.MediaMetadata; +import android.media.MediaParceledListSlice; import android.media.Rating; import android.media.VolumeProvider; import android.media.session.MediaSession.QueueItem; @@ -196,7 +197,7 @@ public final class SessionLink implements Parcelable { */ void setQueue(@Nullable List<QueueItem> queue) { try { - mISession.setQueue(queue); + mISession.setQueue(queue == null ? null : new MediaParceledListSlice(queue)); } catch (RemoteException e) { throw new RuntimeException(e); } @@ -416,8 +417,8 @@ public final class SessionLink implements Parcelable { } @Override - public void setQueue(List<QueueItem> queue) { - mSessionStub.setQueue(queue); + public void setQueue(MediaParceledListSlice queue) { + mSessionStub.setQueue(queue == null ? null : queue.getList()); } @Override diff --git a/media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl b/media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl index deeab1a8a28b..8dc480d6bff7 100644 --- a/media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl +++ b/media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl @@ -2,8 +2,8 @@ package android.service.media; -import android.content.pm.ParceledListSlice; import android.graphics.Bitmap; +import android.media.MediaParceledListSlice; import android.media.session.MediaSession; import android.os.Bundle; @@ -22,6 +22,7 @@ oneway interface IMediaBrowserServiceCallbacks { */ void onConnect(String root, in MediaSession.Token session, in Bundle extras); void onConnectFailed(); - void onLoadChildren(String mediaId, in ParceledListSlice list); - void onLoadChildrenWithOptions(String mediaId, in ParceledListSlice list, in Bundle options); + void onLoadChildren(String mediaId, in MediaParceledListSlice list); + void onLoadChildrenWithOptions(String mediaId, in MediaParceledListSlice list, + in Bundle options); } diff --git a/media/java/android/service/media/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java index 5a60ac10fd82..d19d1177cae7 100644 --- a/media/java/android/service/media/MediaBrowserService.java +++ b/media/java/android/service/media/MediaBrowserService.java @@ -25,21 +25,18 @@ import android.annotation.UnsupportedAppUsage; import android.app.Service; import android.content.Intent; import android.content.pm.PackageManager; -import android.content.pm.ParceledListSlice; +import android.media.MediaParceledListSlice; import android.media.browse.MediaBrowser; import android.media.browse.MediaBrowserUtils; import android.media.session.MediaSession; +import android.media.session.MediaSessionManager; +import android.media.session.MediaSessionManager.RemoteUserInfo; import android.os.Binder; import android.os.Bundle; import android.os.Handler; -import android.media.session.MediaSessionManager; -import android.media.session.MediaSessionManager.RemoteUserInfo; import android.os.IBinder; import android.os.RemoteException; import android.os.ResultReceiver; -import android.service.media.IMediaBrowserService; -import android.service.media.IMediaBrowserServiceCallbacks; -import android.text.TextUtils; import android.util.ArrayMap; import android.util.Log; import android.util.Pair; @@ -672,8 +669,8 @@ public abstract class MediaBrowserService extends Service { */ private void performLoadChildren(final String parentId, final ConnectionRecord connection, final Bundle options) { - final Result<List<MediaBrowser.MediaItem>> result - = new Result<List<MediaBrowser.MediaItem>>(parentId) { + final Result<List<MediaBrowser.MediaItem>> result = + new Result<List<MediaBrowser.MediaItem>>(parentId) { @Override void onResultSent(List<MediaBrowser.MediaItem> list, @ResultFlags int flag) { if (mConnections.get(connection.callbacks.asBinder()) != connection) { @@ -686,9 +683,9 @@ public abstract class MediaBrowserService extends Service { List<MediaBrowser.MediaItem> filteredList = (flag & RESULT_FLAG_OPTION_NOT_HANDLED) != 0 - ? applyOptions(list, options) : list; - final ParceledListSlice<MediaBrowser.MediaItem> pls = - filteredList == null ? null : new ParceledListSlice<>(filteredList); + ? applyOptions(list, options) : list; + final MediaParceledListSlice<MediaBrowser.MediaItem> pls = + filteredList == null ? null : new MediaParceledListSlice<>(filteredList); try { connection.callbacks.onLoadChildrenWithOptions(parentId, pls, options); } catch (RemoteException ex) { |