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