• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 Class<T> mListElementsClass;
49     private final Consumer<List<T>> mConsumer;
50 
51     private final Object mLock = new Object();
52 
53     @GuardedBy("mLock")
54     private final List<T> mList = new ArrayList<>();
55 
56     @GuardedBy("mLock")
57     private int mCount;
58 
59     @GuardedBy("mLock")
60     private boolean mConsumed;
61 
62     /**
63      * Creates an instance.
64      *
65      * @param listElementsClass the class of the list elements.
66      * @param consumer a consumer that consumes the list received
67      */
ParcelableListBinder(Class<T> listElementsClass, @NonNull Consumer<List<T>> consumer)68     public ParcelableListBinder(Class<T> listElementsClass, @NonNull Consumer<List<T>> consumer) {
69         mListElementsClass = listElementsClass;
70         mConsumer = consumer;
71     }
72 
73     @Override
onTransact(int code, Parcel data, Parcel reply, int flags)74     protected boolean onTransact(int code, Parcel data, Parcel reply, int flags)
75             throws RemoteException {
76         if (code != FIRST_CALL_TRANSACTION) {
77             return super.onTransact(code, data, reply, flags);
78         }
79         List<T> listToBeConsumed;
80         synchronized (mLock) {
81             if (mConsumed) {
82                 return false;
83             }
84             int i = mList.size();
85             if (i == 0) {
86                 mCount = data.readInt();
87             }
88             while (i < mCount && data.readInt() != END_OF_PARCEL) {
89                 Object object = data.readParcelable(null);
90                 if (mListElementsClass.isAssignableFrom(object.getClass())) {
91                     // Checking list items are of compaitible types to validate against malicious
92                     // apps calling it directly via reflection with non compilable items.
93                     // See b/317048338 for more details
94                     mList.add((T) object);
95                 }
96                 i++;
97             }
98             if (i >= mCount) {
99                 listToBeConsumed = mList;
100                 mConsumed = true;
101             } else {
102                 listToBeConsumed = null;
103             }
104         }
105         if (listToBeConsumed != null) {
106             mConsumer.accept(listToBeConsumed);
107         }
108         return true;
109     }
110 
111     /**
112      * Sends a list of {@link Parcelable} to a binder.
113      *
114      * @param binder a binder interface backed by {@link ParcelableListBinder}
115      * @param list a list to send
116      */
send(@onNull IBinder binder, @NonNull List<T> list)117     public static <T extends Parcelable> void send(@NonNull IBinder binder, @NonNull List<T> list)
118             throws RemoteException {
119         int count = list.size();
120         int i = 0;
121         do {
122             Parcel data = Parcel.obtain();
123             Parcel reply = Parcel.obtain();
124             if (i == 0) {
125                 data.writeInt(count);
126             }
127             while (i < count && data.dataSize() < SUGGESTED_MAX_IPC_SIZE) {
128                 data.writeInt(ITEM_CONTINUED);
129                 data.writeParcelable(list.get(i), 0);
130                 i++;
131             }
132             if (i < count) {
133                 data.writeInt(END_OF_PARCEL);
134             }
135             binder.transact(FIRST_CALL_TRANSACTION, data, reply, 0);
136             reply.recycle();
137             data.recycle();
138         } while (i < count);
139     }
140 }
141