• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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 package com.android.car.internal.util;
17 
18 import static java.util.Collections.emptySet;
19 
20 import android.annotation.Nullable;
21 import android.car.builtin.os.ParcelHelper;
22 import android.os.Parcel;
23 import android.util.ArrayMap;
24 import android.util.ArraySet;
25 
26 import java.util.ArrayList;
27 import java.util.Collections;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Set;
31 import java.util.UUID;
32 import java.util.regex.Pattern;
33 
34 /**
35  * Describes a 2-way parcelling contract of type {@code T} into/out of a {@link Parcel}
36  *
37  * Implementations should be stateless.
38  *
39  * @param <T> the type being [un]parcelled
40  *
41  * @hide
42  */
43 public interface Parcelling<T> {
44 
45     /**
46      * Write an item into parcel.
47      */
parcel(T item, Parcel dest, int parcelFlags)48     void parcel(T item, Parcel dest, int parcelFlags);
49 
50     /**
51      * Read an item from parcel.
52      */
unparcel(Parcel source)53     T unparcel(Parcel source);
54 
55 
56     /**
57      * A registry of {@link Parcelling} singletons.
58      */
59     class Cache {
Cache()60         private Cache() {}
61 
62         private static ArrayMap<Class, Parcelling> sCache = new ArrayMap<>();
63 
64         /**
65          * Retrieves an instance of a given {@link Parcelling} class if present.
66          */
get(Class<P> clazz)67         public static @Nullable <P extends Parcelling<?>> P get(Class<P> clazz) {
68             return (P) sCache.get(clazz);
69         }
70 
71         /**
72          * Stores an instance of a given {@link Parcelling}.
73          *
74          * @return the provided parcelling for convenience.
75          */
put(P parcelling)76         public static <P extends Parcelling<?>> P put(P parcelling) {
77             sCache.put(parcelling.getClass(), parcelling);
78             return parcelling;
79         }
80 
81         /**
82          * Produces an instance of a given {@link Parcelling} class, by either retrieving a cached
83          * instance or reflectively creating one.
84          */
getOrCreate(Class<P> clazz)85         public static <P extends Parcelling<?>> P getOrCreate(Class<P> clazz) {
86             // No synchronization - creating an extra instance in a race case is ok
87             P cached = get(clazz);
88             if (cached != null) {
89                 return cached;
90             } else {
91                 try {
92                     return put(clazz.newInstance());
93                 } catch (Exception e) {
94                     throw new RuntimeException(e);
95                 }
96             }
97         }
98     }
99 
100     /**
101      * Common {@link Parcelling} implementations.
102      */
103     interface BuiltIn {
104 
105         class ForInternedString implements Parcelling<String> {
106             @Override
parcel(@ullable String item, Parcel dest, int parcelFlags)107             public void parcel(@Nullable String item, Parcel dest, int parcelFlags) {
108                 dest.writeString(item);
109             }
110 
111             @Nullable
112             @Override
unparcel(Parcel source)113             public String unparcel(Parcel source) {
114                 return TextUtils.safeIntern(source.readString());
115             }
116         }
117 
118         class ForInternedStringArray implements Parcelling<String[]> {
119             @Override
parcel(String[] item, Parcel dest, int parcelFlags)120             public void parcel(String[] item, Parcel dest, int parcelFlags) {
121                 dest.writeStringArray(item);
122             }
123 
124             @Nullable
125             @Override
unparcel(Parcel source)126             public String[] unparcel(Parcel source) {
127                 String[] array = ParcelHelper.readStringArray(source);
128                 if (array != null) {
129                     int size = array.length;
130                     for (int index = 0; index < size; index++) {
131                         array[index] = TextUtils.safeIntern(array[index]);
132                     }
133                 }
134                 return array;
135             }
136         }
137 
138         class ForInternedStringList implements Parcelling<List<String>> {
139             @Override
parcel(List<String> item, Parcel dest, int parcelFlags)140             public void parcel(List<String> item, Parcel dest, int parcelFlags) {
141                 dest.writeStringList(item);
142             }
143 
144             @Override
unparcel(Parcel source)145             public List<String> unparcel(Parcel source) {
146                 ArrayList<String> list = source.createStringArrayList();
147                 if (list != null) {
148                     int size = list.size();
149                     for (int index = 0; index < size; index++) {
150                         list.set(index, list.get(index).intern());
151                     }
152                 }
153                 return (list == null) ? Collections.EMPTY_LIST : list;
154             }
155         }
156 
157         class ForInternedStringValueMap implements Parcelling<Map<String, String>> {
158             @Override
parcel(Map<String, String> item, Parcel dest, int parcelFlags)159             public void parcel(Map<String, String> item, Parcel dest, int parcelFlags) {
160                 dest.writeMap(item);
161             }
162 
163             @Override
unparcel(Parcel source)164             public Map<String, String> unparcel(Parcel source) {
165                 ArrayMap<String, String> map = new ArrayMap<>();
166                 source.readMap(map, String.class.getClassLoader());
167                 for (int index = 0; index < map.size(); index++) {
168                     map.setValueAt(index, TextUtils.safeIntern(map.valueAt(index)));
169                 }
170                 return map;
171             }
172         }
173 
174         class ForStringSet implements Parcelling<Set<String>> {
175             @Override
parcel(Set<String> item, Parcel dest, int parcelFlags)176             public void parcel(Set<String> item, Parcel dest, int parcelFlags) {
177                 if (item == null) {
178                     dest.writeInt(-1);
179                 } else {
180                     dest.writeInt(item.size());
181                     for (String string : item) {
182                         dest.writeString(string);
183                     }
184                 }
185             }
186 
187             @Override
unparcel(Parcel source)188             public Set<String> unparcel(Parcel source) {
189                 final int size = source.readInt();
190                 if (size < 0) {
191                     return emptySet();
192                 }
193                 Set<String> set = new ArraySet<>();
194                 for (int count = 0; count < size; count++) {
195                     set.add(source.readString());
196                 }
197                 return set;
198             }
199         }
200 
201         class ForInternedStringSet implements Parcelling<Set<String>> {
202             @Override
parcel(Set<String> item, Parcel dest, int parcelFlags)203             public void parcel(Set<String> item, Parcel dest, int parcelFlags) {
204                 if (item == null) {
205                     dest.writeInt(-1);
206                 } else {
207                     dest.writeInt(item.size());
208                     for (String string : item) {
209                         dest.writeString(string);
210                     }
211                 }
212             }
213 
214             @Override
unparcel(Parcel source)215             public Set<String> unparcel(Parcel source) {
216                 final int size = source.readInt();
217                 if (size < 0) {
218                     return emptySet();
219                 }
220                 Set<String> set = new ArraySet<>();
221                 for (int count = 0; count < size; count++) {
222                     set.add(TextUtils.safeIntern(source.readString()));
223                 }
224                 return set;
225             }
226         }
227 
228         class ForInternedStringArraySet implements Parcelling<ArraySet<String>> {
229             @Override
parcel(ArraySet<String> item, Parcel dest, int parcelFlags)230             public void parcel(ArraySet<String> item, Parcel dest, int parcelFlags) {
231                 if (item == null) {
232                     dest.writeInt(-1);
233                 } else {
234                     dest.writeInt(item.size());
235                     for (String string : item) {
236                         dest.writeString(string);
237                     }
238                 }
239             }
240 
241             @Override
unparcel(Parcel source)242             public ArraySet<String> unparcel(Parcel source) {
243                 final int size = source.readInt();
244                 if (size < 0) {
245                     return null;
246                 }
247                 ArraySet<String> set = new ArraySet<>();
248                 for (int count = 0; count < size; count++) {
249                     set.add(TextUtils.safeIntern(source.readString()));
250                 }
251                 return set;
252             }
253         }
254 
255         class ForBoolean implements Parcelling<Boolean> {
256             @Override
parcel(@ullable Boolean item, Parcel dest, int parcelFlags)257             public void parcel(@Nullable Boolean item, Parcel dest, int parcelFlags) {
258                 if (item == null) {
259                     // This writes 1 for null to mirror TypedArray.getInteger(booleanResId, 1)
260                     dest.writeInt(1);
261                 } else if (!item) {
262                     dest.writeInt(0);
263                 } else {
264                     dest.writeInt(-1);
265                 }
266             }
267 
268             @Nullable
269             @Override
unparcel(Parcel source)270             public Boolean unparcel(Parcel source) {
271                 switch (source.readInt()) {
272                     default:
273                         throw new IllegalStateException("Malformed Parcel reading Boolean: "
274                                 + source);
275                     case 1:
276                         return null;
277                     case 0:
278                         return Boolean.FALSE;
279                     case -1:
280                         return Boolean.TRUE;
281                 }
282             }
283         }
284 
285         class ForPattern implements Parcelling<Pattern> {
286 
287             @Override
parcel(Pattern item, Parcel dest, int parcelFlags)288             public void parcel(Pattern item, Parcel dest, int parcelFlags) {
289                 dest.writeString(item == null ? null : item.pattern());
290             }
291 
292             @Override
unparcel(Parcel source)293             public Pattern unparcel(Parcel source) {
294                 String s = source.readString();
295                 return s == null ? null : Pattern.compile(s);
296             }
297         }
298 
299         class ForUUID implements Parcelling<UUID> {
300 
301             @Override
parcel(UUID item, Parcel dest, int parcelFlags)302             public void parcel(UUID item, Parcel dest, int parcelFlags) {
303                 dest.writeString(item == null ? null : item.toString());
304             }
305 
306             @Override
unparcel(Parcel source)307             public UUID unparcel(Parcel source) {
308                 String string = source.readString();
309                 return string == null ? null : UUID.fromString(string);
310             }
311         }
312     }
313 }
314