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