1 /* 2 * Copyright 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.media.session; 18 19 import android.annotation.NonNull; 20 import android.os.Binder; 21 import android.os.IBinder; 22 import android.os.Parcel; 23 import android.os.Parcelable; 24 import android.os.RemoteException; 25 26 import com.android.internal.annotations.GuardedBy; 27 28 import java.util.ArrayList; 29 import java.util.List; 30 import java.util.function.Consumer; 31 32 /** 33 * Binder to receive a list that has a large number of {@link Parcelable} items. 34 * 35 * It's similar to {@link android.content.pm.ParceledListSlice}, but transactions are performed in 36 * the opposite direction. 37 * 38 * @param <T> the type of {@link Parcelable} 39 * @hide 40 */ 41 public class ParcelableListBinder<T extends Parcelable> extends Binder { 42 43 private static final int SUGGESTED_MAX_IPC_SIZE = IBinder.getSuggestedMaxIpcSizeBytes(); 44 45 private static final int END_OF_PARCEL = 0; 46 private static final int ITEM_CONTINUED = 1; 47 48 private final Consumer<List<T>> mConsumer; 49 50 private final Object mLock = new Object(); 51 52 @GuardedBy("mLock") 53 private final List<T> mList = new ArrayList<>(); 54 55 @GuardedBy("mLock") 56 private int mCount; 57 58 @GuardedBy("mLock") 59 private boolean mConsumed; 60 61 /** 62 * Creates an instance. 63 * 64 * @param consumer a consumer that consumes the list received 65 */ ParcelableListBinder(@onNull Consumer<List<T>> consumer)66 public ParcelableListBinder(@NonNull Consumer<List<T>> consumer) { 67 mConsumer = consumer; 68 } 69 70 @Override onTransact(int code, Parcel data, Parcel reply, int flags)71 protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) 72 throws RemoteException { 73 if (code != FIRST_CALL_TRANSACTION) { 74 return super.onTransact(code, data, reply, flags); 75 } 76 List<T> listToBeConsumed; 77 synchronized (mLock) { 78 if (mConsumed) { 79 return false; 80 } 81 int i = mList.size(); 82 if (i == 0) { 83 mCount = data.readInt(); 84 } 85 while (i < mCount && data.readInt() != END_OF_PARCEL) { 86 mList.add(data.readParcelable(null)); 87 i++; 88 } 89 if (i >= mCount) { 90 listToBeConsumed = mList; 91 mConsumed = true; 92 } else { 93 listToBeConsumed = null; 94 } 95 } 96 if (listToBeConsumed != null) { 97 mConsumer.accept(listToBeConsumed); 98 } 99 return true; 100 } 101 102 /** 103 * Sends a list of {@link Parcelable} to a binder. 104 * 105 * @param binder a binder interface backed by {@link ParcelableListBinder} 106 * @param list a list to send 107 */ send(@onNull IBinder binder, @NonNull List<T> list)108 public static <T extends Parcelable> void send(@NonNull IBinder binder, @NonNull List<T> list) 109 throws RemoteException { 110 int count = list.size(); 111 int i = 0; 112 do { 113 Parcel data = Parcel.obtain(); 114 Parcel reply = Parcel.obtain(); 115 if (i == 0) { 116 data.writeInt(count); 117 } 118 while (i < count && data.dataSize() < SUGGESTED_MAX_IPC_SIZE) { 119 data.writeInt(ITEM_CONTINUED); 120 data.writeParcelable(list.get(i), 0); 121 i++; 122 } 123 if (i < count) { 124 data.writeInt(END_OF_PARCEL); 125 } 126 binder.transact(FIRST_CALL_TRANSACTION, data, reply, 0); 127 reply.recycle(); 128 data.recycle(); 129 } while (i < count); 130 } 131 } 132