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