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