• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2023 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.app.appsearch.safeparcel;
18 
19 import static java.lang.Math.max;
20 import static java.util.Comparator.comparing;
21 import static java.util.stream.Collectors.toList;
22 
23 import androidx.annotation.RequiresApi;
24 
25 import com.google.clearsilver.jsilver.JSilver;
26 import com.google.clearsilver.jsilver.JSilverOptions;
27 import com.google.clearsilver.jsilver.autoescape.EscapeMode;
28 import com.google.clearsilver.jsilver.data.Data;
29 import com.google.clearsilver.jsilver.resourceloader.ClassLoaderResourceLoader;
30 
31 import java.io.IOException;
32 import java.io.PrintWriter;
33 import java.util.ArrayList;
34 import java.util.Collections;
35 import java.util.HashMap;
36 import java.util.HashSet;
37 import java.util.List;
38 import java.util.Locale;
39 import java.util.Objects;
40 import java.util.Set;
41 
42 import javax.annotation.processing.AbstractProcessor;
43 import javax.annotation.processing.Messager;
44 import javax.annotation.processing.ProcessingEnvironment;
45 import javax.annotation.processing.RoundEnvironment;
46 import javax.annotation.processing.SupportedAnnotationTypes;
47 import javax.lang.model.SourceVersion;
48 import javax.lang.model.element.Element;
49 import javax.lang.model.element.ElementKind;
50 import javax.lang.model.element.ExecutableElement;
51 import javax.lang.model.element.Modifier;
52 import javax.lang.model.element.PackageElement;
53 import javax.lang.model.element.TypeElement;
54 import javax.lang.model.element.VariableElement;
55 import javax.lang.model.type.ArrayType;
56 import javax.lang.model.type.DeclaredType;
57 import javax.lang.model.type.TypeKind;
58 import javax.lang.model.type.TypeMirror;
59 import javax.lang.model.util.ElementFilter;
60 import javax.lang.model.util.Elements;
61 import javax.lang.model.util.Types;
62 import javax.tools.Diagnostic;
63 import javax.tools.JavaFileObject;
64 
65 /**
66  * Annotation Processor for {@link SafeParcelable}.
67  *
68  * @hide
69  */
70 // Include the SafeParcel source code directly in AppSearch until it gets officially open-sourced.
71 @SupportedAnnotationTypes({SafeParcelProcessor.CLASS_ANNOTATION_NAME})
72 public class SafeParcelProcessor extends AbstractProcessor {
73     @Override
getSupportedSourceVersion()74     public SourceVersion getSupportedSourceVersion() {
75         return SourceVersion.latestSupported();
76     }
77 
78     public static final String SAFE_PARCELABLE_NAME =
79             "android.app.appsearch.safeparcel.SafeParcelable";
80     public static final String REFLECTED_PARCELABLE_NAME =
81             "android.app.appsearch.safeparcel.ReflectedParcelable";
82     public static final String CLASS_ANNOTATION_NAME = SAFE_PARCELABLE_NAME + ".Class";
83     public static final String FIELD_ANNOTATION_NAME = SAFE_PARCELABLE_NAME + ".Field";
84     public static final String LOCAL_VARIABLE_PREFIX = "_local_safe_0a1b_";
85 
86     public static final int INDICATOR_FIELD_ID = -1;
87 
88     Elements mElements;
89     Types mTypes;
90     Messager mMessager;
91 
92     TypeMirror mStringType;
93     TypeMirror mListType;
94     TypeMirror mSetType;
95     TypeMirror mHashSetType;
96     TypeMirror mArrayListType;
97     TypeMirror mBigIntegerType;
98     TypeMirror mBigDecimalType;
99     TypeMirror mBooleanType;
100     TypeMirror mIntegerType;
101     TypeMirror mLongType;
102     TypeMirror mFloatType;
103     TypeMirror mDoubleType;
104     TypeMirror mCharacterType;
105     TypeMirror mByteType;
106     TypeMirror mShortType;
107 
108     TypeMirror mParcelableType;
109     TypeMirror mParcelableCreatorType;
110     TypeMirror mIBinderType;
111     TypeMirror mBundleType;
112     TypeMirror mParcelType;
113     TypeMirror mSparseArrayType;
114     TypeMirror mSparseBooleanArrayType;
115     TypeMirror mSparseIntArrayType;
116     TypeMirror mSparseLongArrayType;
117 
118     TypeMirror mSafeParcelableType;
119     TypeMirror mReflectedParcelableType;
120 
121     static class ParcelableField {
122         VariableElement mVariable;
123         int mId;
124         String mName;
125         String mReadName;
126         TypeMirror mType;
127         String mGetter;
128         SerializationMethods mSm;
129         String mDefaultValue;
130 
ParcelableField(VariableElement variable)131         ParcelableField(VariableElement variable) {
132             this.mVariable = variable;
133         }
134     }
135 
136     static class ParcelableConstructor {
137         ArrayList<ParcelableField> mParameters = new ArrayList<>();
138     }
139 
140     class ParcelableClass {
141         TypeElement mParcelableClass;
142         SafeParcelable.Class mAnnotation;
143         String mQualifiedName;
144         String mGeneratedClassName;
145         HashMap<Integer, ParcelableField> mFields = new HashMap<>();
146         ParcelableConstructor mConstructor;
147         ParcelableField mIndicatorField;
148         Integer mRequiresApi = null;
149 
ParcelableClass(TypeElement parcelableClass)150         ParcelableClass(TypeElement parcelableClass) {
151             this.mParcelableClass = parcelableClass;
152             this.mQualifiedName = parcelableClass.getQualifiedName().toString();
153             this.mAnnotation = parcelableClass.getAnnotation(SafeParcelable.Class.class);
154             RequiresApi requiresApiAnnotation = parcelableClass.getAnnotation(RequiresApi.class);
155             if (requiresApiAnnotation != null) {
156                 mRequiresApi = max(requiresApiAnnotation.value(), requiresApiAnnotation.api());
157             }
158 
159             PackageElement parcelablePackage = mElements.getPackageOf(parcelableClass);
160             this.mGeneratedClassName =
161                     parcelablePackage.getQualifiedName() + "." + this.mAnnotation.creator();
162         }
163     }
164 
165     static class SerializationMethods {
166         String mWrite;
167         boolean mWriteWithFlags;
168         boolean mIsAssignment;
169         String mRead;
170         String mCreator;
171         boolean mHasWriteNull;
172 
SerializationMethods( String write, boolean writeWithFlags, boolean isAssignment, String read, String creator, Boolean hasWriteNull)173         SerializationMethods(
174                 String write,
175                 boolean writeWithFlags,
176                 boolean isAssignment,
177                 String read,
178                 String creator,
179                 Boolean hasWriteNull) {
180             this.mWrite = write;
181             this.mWriteWithFlags = writeWithFlags;
182             this.mIsAssignment = isAssignment;
183             this.mRead = read;
184             this.mCreator = creator;
185             this.mHasWriteNull = hasWriteNull;
186         }
187     }
188 
189     HashMap<String, ParcelableClass> mParcelableClasses;
190 
SafeParcelProcessor()191     public SafeParcelProcessor() {}
192 
193     @Override
init(ProcessingEnvironment processingEnv)194     public synchronized void init(ProcessingEnvironment processingEnv) {
195         super.init(processingEnv);
196 
197         mElements = processingEnv.getElementUtils();
198         mTypes = processingEnv.getTypeUtils();
199         mMessager = processingEnv.getMessager();
200 
201         // Built in java ones
202         mStringType = mElements.getTypeElement("java.lang.String").asType();
203         mListType = mElements.getTypeElement("java.util.List").asType();
204         mArrayListType = mElements.getTypeElement("java.util.ArrayList").asType();
205         mSetType = mElements.getTypeElement("java.util.Set").asType();
206         mHashSetType = mElements.getTypeElement("java.util.HashSet").asType();
207         mBigIntegerType = mElements.getTypeElement("java.math.BigInteger").asType();
208         mBigDecimalType = mElements.getTypeElement("java.math.BigDecimal").asType();
209         mBooleanType = mElements.getTypeElement("java.lang.Boolean").asType();
210         mIntegerType = mElements.getTypeElement("java.lang.Integer").asType();
211         mLongType = mElements.getTypeElement("java.lang.Long").asType();
212         mFloatType = mElements.getTypeElement("java.lang.Float").asType();
213         mDoubleType = mElements.getTypeElement("java.lang.Double").asType();
214         mByteType = mElements.getTypeElement("java.lang.Byte").asType();
215         mShortType = mElements.getTypeElement("java.lang.Short").asType();
216         mCharacterType = mElements.getTypeElement("java.lang.Character").asType();
217 
218         // Android classes
219         mParcelableType = loadTypeOrFail("android.os.Parcelable");
220         mParcelableCreatorType = loadTypeOrFail("android.os.Parcelable.Creator");
221         mIBinderType = loadTypeOrFail("android.os.IBinder");
222         mBundleType = loadTypeOrFail("android.os.Bundle");
223         mParcelType = loadTypeOrFail("android.os.Parcel");
224         mSparseArrayType = loadTypeOrFail("android.util.SparseArray");
225         mSparseBooleanArrayType = loadTypeOrFail("android.util.SparseBooleanArray");
226         mSparseIntArrayType = loadTypeOrFail("android.util.SparseIntArray");
227         mSparseLongArrayType = loadTypeOrFail("android.util.SparseLongArray");
228         mSafeParcelableType = loadTypeOrFail(SAFE_PARCELABLE_NAME);
229         mReflectedParcelableType = loadTypeOrFail(REFLECTED_PARCELABLE_NAME);
230     }
231 
loadTypeOrFail(String qualified)232     private TypeMirror loadTypeOrFail(String qualified) {
233         Element e = mElements.getTypeElement(qualified);
234         if (e == null) {
235 
236             // Check if the string refers to a primitive type or an array of a primitive type
237             final boolean isArray;
238             final String typeName;
239             if (qualified.endsWith("[]")) {
240                 isArray = true;
241                 typeName = qualified.substring(0, qualified.length() - 2).toUpperCase();
242             } else {
243                 isArray = false;
244                 typeName = qualified.toUpperCase();
245             }
246 
247             // Find the TypeKind of the typeName
248             TypeKind primitiveKind = null;
249             try {
250                 primitiveKind = TypeKind.valueOf(typeName);
251             } catch (IllegalArgumentException ignored) {
252                 // continue to error reporting below
253             }
254             if (primitiveKind != null && primitiveKind.isPrimitive()) {
255                 TypeMirror type = mTypes.getPrimitiveType(primitiveKind);
256                 if (isArray) {
257                     return mTypes.getArrayType(type);
258                 }
259                 return type;
260             }
261             throw new IllegalArgumentException("Can't find class " + qualified + " in classpath.");
262         }
263         return e.asType();
264     }
265 
266     @Override
process(Set<? extends TypeElement> elements, RoundEnvironment env)267     public boolean process(Set<? extends TypeElement> elements, RoundEnvironment env) {
268         mParcelableClasses = new HashMap<>();
269 
270         // Gather all the parcelable classes.
271         for (TypeElement e : elements) {
272             if (e.getQualifiedName().contentEquals(CLASS_ANNOTATION_NAME)) {
273                 Set<? extends Element> annotatedElements = env.getElementsAnnotatedWith(e);
274                 for (Element annotatedElement : annotatedElements) {
275                     // Workaround for open-jdk bug https://bugs.openjdk.java.net/browse/JDK-8030049:
276                     // we need to explicitly check that the annotated type is a TypeElement and is
277                     // annotated with SafeParcelable.Class.class
278                     if (annotatedElement instanceof TypeElement
279                             && annotatedElement.getAnnotation(SafeParcelable.Class.class) != null) {
280                         processParcelableClass((TypeElement) annotatedElement);
281                     } else {
282                         mMessager.printMessage(
283                                 Diagnostic.Kind.WARNING,
284                                 "Undefined annotation used on "
285                                         + annotatedElement.getSimpleName()
286                                         + "\nA non-class type "
287                                         + annotatedElement.asType().getKind()
288                                         + " is returned by RoundEnvironment"
289                                         + ".getElementsAnnotatedWith.");
290                     }
291                 }
292             }
293         }
294 
295         // Generate the code
296         for (ParcelableClass cl : mParcelableClasses.values()) {
297             generateParser(cl);
298         }
299         return false;
300     }
301 
processParcelableClass(TypeElement parcelableClass)302     private boolean processParcelableClass(TypeElement parcelableClass) {
303         ParcelableClass cl = new ParcelableClass(parcelableClass);
304         boolean isOkay = true;
305 
306         // Check that the class implements SafeParcelable
307         if (!mTypes.isAssignable(parcelableClass.asType(), mSafeParcelableType)) {
308             mMessager.printMessage(
309                     Diagnostic.Kind.ERROR,
310                     "Class tagged @SafeParcelable.Class does not implement SafeParcelable",
311                     parcelableClass);
312             isOkay = false;
313         }
314 
315         // Check that if the annotation has validate=true the class has a validateContents() method.
316         if (cl.mAnnotation.validate()) {
317             if (!hasValidateContents(parcelableClass)) {
318                 mMessager.printMessage(
319                         Diagnostic.Kind.ERROR,
320                         "Class tagged @SafeParcelable.Class has validate=true but no"
321                                 + " void validateContents() method.",
322                         parcelableClass);
323                 isOkay = false;
324             }
325         }
326 
327         // Check that the class has a CREATOR method.
328         if (!hasCreator(parcelableClass, cl.mGeneratedClassName)) {
329             mMessager.printMessage(
330                     Diagnostic.Kind.ERROR,
331                     "Class tagged @SafeParcelable.Class must have a public static final "
332                             + cl.mGeneratedClassName
333                             + " CREATOR field.",
334                     parcelableClass);
335             isOkay = false;
336         }
337 
338         // If this is an inner class, it must be static.
339         if (parcelableClass.getEnclosingElement().getKind() == ElementKind.CLASS) {
340             Set<Modifier> modifiers = parcelableClass.getModifiers();
341             if (!modifiers.contains(Modifier.STATIC)) {
342                 mMessager.printMessage(
343                         Diagnostic.Kind.ERROR,
344                         "Inner class tagged @SafeParcelable.Class must be declared static.",
345                         parcelableClass);
346                 isOkay = false;
347             }
348         }
349 
350         // Check that the class is public or package private
351         final Set<Modifier> modifiers = parcelableClass.getModifiers();
352         if (modifiers.contains(Modifier.PRIVATE) || modifiers.contains(Modifier.PROTECTED)) {
353             mMessager.printMessage(
354                     Diagnostic.Kind.ERROR,
355                     "SafeParcelable class must be public or package private.",
356                     parcelableClass);
357             isOkay = false;
358         }
359 
360         // Enforce final on the SafeParcelable class
361         // TODO: Uncomment this after finalizing this constraint with all teams.
362         // if (!modifiers.contains(Modifier.FINAL)) {
363         //    mMessager.printMessage(Diagnostic.Kind.ERROR, "SafeParcelable class must be final.",
364         //            parcelableClass);
365         // }
366 
367         // Check whether there is exactly one constructor annotated with @Constructor for the
368         // SafeParcelable.  This check needs to happen before parsing all of the fields.
369         final ExecutableElement constructor = findAnnotatedConstructor(parcelableClass);
370         if (constructor == null) {
371             mMessager.printMessage(
372                     Diagnostic.Kind.ERROR,
373                     "SafeParcelable class must have exactly"
374                             + " one constructor annotated with @Constructor, which is used to "
375                             + "construct"
376                             + " this SafeParcelable implementing object from a Parcel.");
377             isOkay = false;
378         }
379 
380         // Get ids that have been marked as reserved
381         Set<Integer> reservedIds = getReservedIds(parcelableClass);
382 
383         // Get all annotated fields for serialization/deserialization
384         boolean versionFieldFound = false;
385         boolean indicatorFieldFound = false;
386         List<VariableElement> fields =
387                 ElementFilter.fieldsIn(mElements.getAllMembers(parcelableClass));
388         // usedIds tracks which id's have been used to prevent duplicates
389         Set<Integer> usedIds = new HashSet<>();
390         for (VariableElement field : fields) {
391             SafeParcelable.Field fieldInfo = field.getAnnotation(SafeParcelable.Field.class);
392             SafeParcelable.VersionField versionFieldInfo =
393                     field.getAnnotation(SafeParcelable.VersionField.class);
394             SafeParcelable.Indicator indicatorInfo =
395                     field.getAnnotation(SafeParcelable.Indicator.class);
396             checkAtMostOneFieldAnnotation(
397                     field, fieldInfo != null, versionFieldInfo != null, indicatorInfo != null);
398 
399             if (fieldInfo != null && versionFieldInfo != null) {
400                 // This field has both a Field and a VersionField annotation.  This is illegal.
401                 mMessager.printMessage(
402                         Diagnostic.Kind.ERROR,
403                         "A member cannot be annotated with both Field and a VersionField.",
404                         field);
405                 isOkay = false;
406             } else if (fieldInfo != null) {
407                 // This is a regular field.
408                 if (usedIds.contains(fieldInfo.id())) {
409                     // Found a duplicate field id
410                     mMessager.printMessage(
411                             Diagnostic.Kind.ERROR,
412                             "Found a duplicate field id=" + fieldInfo.id() + ".",
413                             field);
414                     isOkay = false;
415                 } else if (reservedIds.contains(fieldInfo.id())) {
416                     // Found a field with a reserved id
417                     mMessager.printMessage(
418                             Diagnostic.Kind.ERROR,
419                             "Found a reserved field id=" + fieldInfo.id() + ".",
420                             field);
421                     isOkay = false;
422                 } else if (cl.mAnnotation.doNotParcelTypeDefaultValues()
423                         && !fieldInfo.defaultValue().equals(SafeParcelable.NULL)) {
424                     mMessager.printMessage(
425                             Diagnostic.Kind.ERROR,
426                             "You cannot set defaultValue on "
427                                     + fieldInfo.id()
428                                     + " when doNotParcelTypeDefaultValues is true.",
429                             field);
430                 } else {
431                     usedIds.add(fieldInfo.id());
432                     parseField(
433                             cl,
434                             field,
435                             fieldInfo.id(),
436                             fieldInfo.getter(),
437                             fieldInfo.type(),
438                             fieldInfo.defaultValue(),
439                             fieldInfo.defaultValueUnchecked(),
440                             false);
441                 }
442             } else if (versionFieldInfo != null) {
443                 // This is the version field.
444                 // Ensure that a version field was not already specified.
445                 if (versionFieldFound) {
446                     mMessager.printMessage(
447                             Diagnostic.Kind.ERROR,
448                             "More than one field has been"
449                                     + " annotated with VersionField.  One and only one member may"
450                                     + " be"
451                                     + " annotated with VersionField.",
452                             field);
453                     isOkay = false;
454                 }
455                 if (usedIds.contains(versionFieldInfo.id())) {
456                     // Found a duplicate field id
457                     mMessager.printMessage(
458                             Diagnostic.Kind.ERROR,
459                             "Found a duplicate field id=" + versionFieldInfo.id() + ".",
460                             field);
461                     isOkay = false;
462                 } else if (reservedIds.contains(versionFieldInfo.id())) {
463                     // Found a field with a reserved id
464                     mMessager.printMessage(
465                             Diagnostic.Kind.ERROR,
466                             "Found a reserved field id=" + versionFieldInfo.id() + ".",
467                             field);
468                     isOkay = false;
469                 } else {
470                     usedIds.add(versionFieldInfo.id());
471                     parseField(
472                             cl,
473                             field,
474                             versionFieldInfo.id(),
475                             versionFieldInfo.getter(),
476                             versionFieldInfo.type(),
477                             null,
478                             null,
479                             true);
480                 }
481                 versionFieldFound = true;
482             } else if (indicatorInfo != null) {
483                 // Ensure that there is only one field that has been annotated with @Indicator
484                 if (indicatorFieldFound) {
485                     mMessager.printMessage(
486                             Diagnostic.Kind.ERROR,
487                             "More than one field has been"
488                                     + " annotated with Indicator.  Only one member may be "
489                                     + "annotated with"
490                                     + " Indicator.",
491                             field);
492                     isOkay = false;
493                 }
494                 indicatorFieldFound = true;
495                 isOkay &= parseIndicatorField(cl, field, indicatorInfo.getter());
496             }
497         }
498 
499         // Process the constructor parameters.
500         // Note: the following must be executed after all annotated fields have been processed.
501         // If the constructor is null, then an error message has already been printed out, so no
502         // need to print another message.
503         if (constructor != null) {
504             isOkay &= processParcelableConstructor(cl, constructor, reservedIds);
505         }
506 
507         if (isOkay) {
508             mParcelableClasses.put(cl.mGeneratedClassName, cl);
509         } else {
510             mMessager.printMessage(
511                     Diagnostic.Kind.NOTE,
512                     "Errors prevented the SafeParcelable "
513                             + parcelableClass.getQualifiedName()
514                             + " from being processed. Look above this line in the log to find the"
515                             + " error.");
516         }
517 
518         return isOkay;
519     }
520 
521     /** A field can be annotated with one of Field, VersionField, Indicator, or nothing. */
checkAtMostOneFieldAnnotation( VariableElement field, boolean fieldAnnotation, boolean versionFieldAnnotation, boolean indicatorAnnotation)522     private void checkAtMostOneFieldAnnotation(
523             VariableElement field,
524             boolean fieldAnnotation,
525             boolean versionFieldAnnotation,
526             boolean indicatorAnnotation) {
527         int numFieldAnnotations =
528                 (fieldAnnotation ? 1 : 0)
529                         + (versionFieldAnnotation ? 1 : 0)
530                         + (indicatorAnnotation ? 1 : 0);
531         if (numFieldAnnotations > 1) {
532             mMessager.printMessage(
533                     Diagnostic.Kind.ERROR,
534                     "Field has multiple "
535                             + "annotations:"
536                             + (fieldAnnotation ? " @Field" : "")
537                             + (versionFieldAnnotation ? " @VersionField" : "")
538                             + (indicatorAnnotation ? " @Indicator" : ""),
539                     field);
540         }
541     }
542 
543     /**
544      * Loop over all constructors, looking for the annotated one. Also, check if more than one
545      * constructor was annotated and print an error message if so.
546      */
findAnnotatedConstructor(TypeElement parcelableClass)547     private ExecutableElement findAnnotatedConstructor(TypeElement parcelableClass) {
548         ExecutableElement annotatedConstructor = null;
549         // Loop over all constructors, looking for the annotated one.  Also, check if more
550         // than one constructor was annotated and print an error message if so.
551         List<ExecutableElement> constructors =
552                 ElementFilter.constructorsIn(parcelableClass.getEnclosedElements());
553         for (ExecutableElement constructor : constructors) {
554             SafeParcelable.Constructor constructorAnnotation =
555                     constructor.getAnnotation(SafeParcelable.Constructor.class);
556             if (constructorAnnotation != null) {
557                 if (annotatedConstructor != null) {
558                     // An annotated constructor was already found before.  This is an error.
559                     mMessager.printMessage(
560                             Diagnostic.Kind.ERROR,
561                             "More than one constructor was" + " annotated with @Constructor.",
562                             constructor);
563                 } else {
564                     annotatedConstructor = constructor;
565                 }
566             }
567         }
568         return annotatedConstructor;
569     }
570 
getReservedIds(TypeElement parcelableClass)571     private Set<Integer> getReservedIds(TypeElement parcelableClass) {
572         SafeParcelable.Reserved annotation =
573                 parcelableClass.getAnnotation(SafeParcelable.Reserved.class);
574         if (annotation != null) {
575             Set<Integer> ids = new HashSet<>();
576             for (int i : annotation.value()) {
577                 ids.add(i);
578             }
579             return Collections.unmodifiableSet(ids);
580         }
581         return Collections.emptySet();
582     }
583 
processParcelableConstructor( ParcelableClass cl, ExecutableElement constructor, Set<Integer> reservedIds)584     private boolean processParcelableConstructor(
585             ParcelableClass cl, ExecutableElement constructor, Set<Integer> reservedIds) {
586         boolean isOkay = true;
587 
588         // Iterate through all of the formal parameters of the constructor, and record the field
589         // id of each parameter.  All formal parameters of this constructor must be annotated with
590         // Param.
591         ParcelableConstructor parcelableConstructor = new ParcelableConstructor();
592 
593         // Check that the constructor is either public or package private
594         Set<Modifier> modifiers = constructor.getModifiers();
595         if (modifiers.contains(Modifier.PRIVATE) || modifiers.contains(Modifier.PROTECTED)) {
596             mMessager.printMessage(
597                     Diagnostic.Kind.ERROR,
598                     "A SafeParcelable class constructor"
599                             + " annotated with @Constructor must be either public or package "
600                             + "private.",
601                     constructor);
602             isOkay = false;
603         }
604 
605         boolean foundIndicator = false;
606         List<? extends VariableElement> parameters = constructor.getParameters();
607         Set<Integer> usedParamIds = new HashSet<>();
608         for (VariableElement parameter : parameters) {
609             SafeParcelable.Param paramInfo = parameter.getAnnotation(SafeParcelable.Param.class);
610             if (paramInfo == null) {
611                 if (cl.mIndicatorField != null) {
612                     // Check if the parameter has an Indicator annotation.
613                     SafeParcelable.Indicator indicatorInfo =
614                             parameter.getAnnotation(SafeParcelable.Indicator.class);
615                     if (indicatorInfo != null) {
616                         // Found an indicator
617                         if (foundIndicator) {
618                             // Already found an indicator, so this is an error
619                             mMessager.printMessage(
620                                     Diagnostic.Kind.ERROR,
621                                     "Found more than two"
622                                             + " parameters in the constructor that have been "
623                                             + "annoatated"
624                                             + " with @Indicator",
625                                     parameter);
626                             isOkay = false;
627                         }
628                         foundIndicator = true;
629                         parcelableConstructor.mParameters.add(cl.mIndicatorField);
630                     } else {
631                         // Parameter has no annotation
632                         mMessager.printMessage(
633                                 Diagnostic.Kind.ERROR,
634                                 "All parameters of the constructor"
635                                         + " annotated with @Constructor must be annotated with "
636                                         + "@Param,"
637                                         + " @RemovedParam, or @Indicator.",
638                                 parameter);
639                         isOkay = false;
640                     }
641                 } else {
642                     SafeParcelable.RemovedParam removedParamInfo =
643                             parameter.getAnnotation(SafeParcelable.RemovedParam.class);
644                     if (removedParamInfo != null) {
645                         int paramId = removedParamInfo.id();
646                         // Check that this parameter id has not been used before.
647                         if (!usedParamIds.add(paramId)) {
648                             mMessager.printMessage(
649                                     Diagnostic.Kind.ERROR,
650                                     "@RemovedParam(id="
651                                             + paramId
652                                             + ") has"
653                                             + " already been used.  Each parameter must map to a "
654                                             + "distinct field"
655                                             + " id.",
656                                     parameter);
657                             isOkay = false;
658                         } else {
659                             if (!reservedIds.contains(paramId)) {
660                                 mMessager.printMessage(
661                                         Diagnostic.Kind.ERROR,
662                                         "@RemovedParam(id="
663                                                 + paramId
664                                                 + ") not found as a reserved id. Every "
665                                                 + "@RemovedParam must be listed as a "
666                                                 + "@Reserved id as well.",
667                                         parameter);
668                                 isOkay = false;
669                             } else {
670                                 parseField(
671                                         cl,
672                                         parameter,
673                                         paramId,
674                                         null,
675                                         null,
676                                         removedParamInfo.defaultValue(),
677                                         removedParamInfo.defaultValueUnchecked(),
678                                         false);
679                                 ParcelableField field =
680                                         Objects.requireNonNull(cl.mFields.get(paramId));
681                                 // ensure field will not participate in writeToParcel()
682                                 field.mSm.mWrite = null;
683                                 parcelableConstructor.mParameters.add(field);
684                                 cl.mFields.put(paramId, field);
685                             }
686                         }
687                     } else {
688                         // A parameter in this constructor does not have a Param annotation. This
689                         // is an error.
690                         mMessager.printMessage(
691                                 Diagnostic.Kind.ERROR,
692                                 "All parameters of the"
693                                         + " constructor annotated with @Constructor must be "
694                                         + "annotated with"
695                                         + " @Param or @RemovedParam.",
696                                 parameter);
697                         isOkay = false;
698                     }
699                 }
700             } else {
701                 int paramId = paramInfo.id();
702                 // Check that this parameter id has not been used before.
703                 if (!usedParamIds.add(paramId)) {
704                     // This id has been used before.
705                     mMessager.printMessage(
706                             Diagnostic.Kind.ERROR,
707                             "@Param(id="
708                                     + paramId
709                                     + ") has"
710                                     + " already been used.  Each parameter must map to a distinct"
711                                     + " field"
712                                     + " id.",
713                             parameter);
714                     isOkay = false;
715                 } else {
716                     ParcelableField field = cl.mFields.get(paramId);
717                     if (field == null) {
718                         // There is no field corresponding to the given parameter id.
719                         mMessager.printMessage(
720                                 Diagnostic.Kind.ERROR,
721                                 "There is no field annotated" + " with @Field(id=" + paramId + ").",
722                                 parameter);
723                         isOkay = false;
724                     } else {
725                         parcelableConstructor.mParameters.add(field);
726                     }
727                 }
728             }
729         }
730 
731         // Check that all fields are in the constructor list
732         if ((cl.mIndicatorField == null
733                         && parcelableConstructor.mParameters.size() != cl.mFields.size())
734                 || (cl.mIndicatorField != null
735                         && parcelableConstructor.mParameters.size() != (cl.mFields.size() + 1))) {
736             mMessager.printMessage(
737                     Diagnostic.Kind.ERROR,
738                     "Not all fields have been included in"
739                             + " the formal parameter list for this SafeParcelable constructor.",
740                     constructor);
741             isOkay = false;
742         } else {
743             cl.mConstructor = parcelableConstructor;
744         }
745 
746         return isOkay;
747     }
748 
parseIndicatorField(ParcelableClass cl, VariableElement field, String getter)749     private boolean parseIndicatorField(ParcelableClass cl, VariableElement field, String getter) {
750         boolean isOkay = true;
751 
752         // Convert getter to null if it is set to SafeParcelable.NULL since we can't have
753         // actual null values as a default value in an annotation.
754         if (getter.equals(SafeParcelable.NULL)) {
755             getter = null;
756         }
757 
758         // Check that the field type is assignable to a Set<Integer>
759         TypeMirror fieldType = field.asType();
760         if (!isSetOfInteger(fieldType)) {
761             mMessager.printMessage(
762                     Diagnostic.Kind.ERROR,
763                     "Field annotated with Indicator is the"
764                             + " wrong type.  Should be a type assignable to Set<Integer>.",
765                     field);
766         }
767 
768         // Check visibility
769         final Set<Modifier> fieldModifiers = field.getModifiers();
770         if (fieldModifiers.contains(Modifier.PROTECTED)
771                 || fieldModifiers.contains(Modifier.PRIVATE)) {
772             // field must have a getter
773             if (getter == null) {
774                 mMessager.printMessage(
775                         Diagnostic.Kind.ERROR,
776                         "A private or protected indicator" + " field must have a getter.",
777                         field);
778                 isOkay = false;
779             }
780         }
781 
782         ParcelableField indicatorField = new ParcelableField(field);
783         indicatorField.mId = INDICATOR_FIELD_ID;
784         indicatorField.mName = field.getSimpleName().toString();
785         indicatorField.mReadName = LOCAL_VARIABLE_PREFIX + indicatorField.mName;
786         indicatorField.mType = fieldType;
787         indicatorField.mGetter = getter;
788         indicatorField.mSm = null;
789         cl.mIndicatorField = indicatorField;
790 
791         return isOkay;
792     }
793 
isSetOfInteger(TypeMirror type)794     private boolean isSetOfInteger(TypeMirror type) {
795         if (mTypes.isSameType(mTypes.erasure(type), mTypes.erasure(mSetType))
796                 || mTypes.isSameType(mTypes.erasure(type), mTypes.erasure(mHashSetType))) {
797             final DeclaredType dt = (DeclaredType) type;
798             List<? extends TypeMirror> typeArgs = dt.getTypeArguments();
799             if (typeArgs.size() == 1) {
800                 TypeMirror typeArg = typeArgs.get(0);
801                 if (mTypes.isSameType(typeArg, mIntegerType)) {
802                     return true;
803                 }
804             }
805         }
806         return false;
807     }
808 
809     /**
810      * Parses each field annotated with @Field or @VersionField in a SafeParcelable class.
811      *
812      * @param id from the @Field or @VersionField annotation
813      * @param getter from the @Field or the @VersionField annotation
814      * @param type from the @Field or the @VersionField annotation
815      * @param defaultValue from the @Field or null for the @VersionField annotation
816      * @param isVersionField indicates that this field was annotated with @VersionField
817      */
parseField( ParcelableClass cl, VariableElement field, int id, String getter, String type, String defaultValue, String defaultValueUnchecked, boolean isVersionField)818     private void parseField(
819             ParcelableClass cl,
820             VariableElement field,
821             int id,
822             String getter,
823             String type,
824             String defaultValue,
825             String defaultValueUnchecked,
826             boolean isVersionField) {
827         // Validate the id value, which can be between 1 and 0xffff inclusive
828         if (id < 0x00000001 || id > 0x0000ffff) {
829             mMessager.printMessage(
830                     Diagnostic.Kind.ERROR,
831                     "id in Field annotation must be a value between 1 and 65535 inclusive.",
832                     field);
833         }
834 
835         // Convert getter to null if it is set to SafeParcelable.NULL since we can't have
836         // actual null values as a default value in an annotation.
837         if (SafeParcelable.NULL.equals(getter)) {
838             getter = null;
839         }
840         if (SafeParcelable.NULL.equals(type)) {
841             type = null;
842         }
843         if (SafeParcelable.NULL.equals(defaultValue)) {
844             defaultValue = null;
845         }
846         if (SafeParcelable.NULL.equals(defaultValueUnchecked)) {
847             defaultValueUnchecked = null;
848         }
849 
850         // Check visibility
851         final Set<Modifier> fieldModifiers = field.getModifiers();
852         if (isVersionField) {
853             // Version field must be final
854             if (!fieldModifiers.contains(Modifier.FINAL)) {
855                 mMessager.printMessage(
856                         Diagnostic.Kind.ERROR, "Version field must be final.", field);
857             }
858         }
859         if (fieldModifiers.contains(Modifier.PROTECTED)
860                 || fieldModifiers.contains(Modifier.PRIVATE)) {
861             // field must have a getter
862             if (getter == null) {
863                 mMessager.printMessage(
864                         Diagnostic.Kind.ERROR,
865                         "A private or protected field" + " must have a getter.",
866                         field);
867             }
868         }
869 
870         TypeMirror fieldType = type != null ? loadTypeOrFail(type) : field.asType();
871         final String resolvedDefaultValue;
872         if (defaultValue != null) {
873             if (defaultValueUnchecked != null) {
874                 // Both defaultValue and defaultValueUnchecked have been specified.  Only one is
875                 // allowed to be specified.
876                 mMessager.printMessage(
877                         Diagnostic.Kind.ERROR,
878                         "Both defaultValue and"
879                                 + " defaultValueUnchecked have been specified in the Field "
880                                 + "annotation.  You"
881                                 + " can specify at most only one of these.");
882                 resolvedDefaultValue = null;
883             } else {
884                 // Only defaultValue has been specified, so do checks and set things
885                 if (!allowsDefaultValue(fieldType)) {
886                     mMessager.printMessage(
887                             Diagnostic.Kind.ERROR,
888                             "defaultValue in Field annotation"
889                                     + " is allowed only for primitive types, primitive type "
890                                     + "object wrappers"
891                                     + " and String.",
892                             field);
893                     resolvedDefaultValue = null;
894                 } else {
895                     resolvedDefaultValue = convertForType(defaultValue, fieldType);
896                     if (resolvedDefaultValue == null) {
897                         mMessager.printMessage(
898                                 Diagnostic.Kind.ERROR,
899                                 "defaultValue in Field"
900                                         + " annotation must be a proper value of annotated "
901                                         + "field's type.",
902                                 field);
903                     }
904                 }
905             }
906         } else if (defaultValueUnchecked != null) {
907             // Only defaultValueUnchecked has been specified.
908             resolvedDefaultValue = defaultValueUnchecked;
909         } else {
910             // neither defaultValue nor defaultValueUnchecked has been specified.
911             resolvedDefaultValue = null;
912         }
913         SerializationMethods sm = getSerializationMethod(field, fieldType);
914         if (sm != null) {
915             ParcelableField f = new ParcelableField(field);
916             f.mId = id;
917             f.mName = field.getSimpleName().toString();
918             f.mReadName = LOCAL_VARIABLE_PREFIX + f.mName;
919             f.mType = fieldType;
920             f.mGetter = getter;
921             f.mSm = sm;
922             f.mDefaultValue = resolvedDefaultValue;
923 
924             // Special case of generic list
925             if (sm.mWrite.equals("writeList")) {
926                 // When instantiating a List, this will be an ArrayList
927                 f.mType = mTypes.erasure(f.mType);
928             }
929 
930             cl.mFields.put(f.mId, f);
931         } else {
932             mMessager.printMessage(
933                     Diagnostic.Kind.ERROR,
934                     fieldType
935                             + " field tagged @SafeParcelable.Field is not supported in a"
936                             + " SafeParcelable.  The field must be a primitive type, a concrete"
937                             + " class implementing SafeParcelable, or a Parcelable class in the "
938                             + "Android"
939                             + " framework.",
940                     field);
941         }
942     }
943 
944     /**
945      * Returns true if @Field's defaultValue parameter is allowed for specified field type.
946      * defaultValue parameter is allowed only in @Field annotating field with one of following
947      * types: primitive type, primitive type object wrapper or String.
948      */
allowsDefaultValue(TypeMirror type)949     private boolean allowsDefaultValue(TypeMirror type) {
950         if (type.getKind().isPrimitive()) {
951             return true;
952         }
953         if (type.getKind() != TypeKind.DECLARED) {
954             return false;
955         }
956         return mTypes.isSameType(type, mStringType)
957                 || mTypes.isSameType(type, mIntegerType)
958                 || mTypes.isSameType(type, mLongType)
959                 || mTypes.isSameType(type, mFloatType)
960                 || mTypes.isSameType(type, mDoubleType)
961                 || mTypes.isSameType(type, mByteType)
962                 || mTypes.isSameType(type, mShortType)
963                 || mTypes.isSameType(type, mCharacterType)
964                 || mTypes.isSameType(type, mBooleanType);
965     }
966 
967     /**
968      * Converts @Field's defaultValue parameter value to value of specific type. Returns null if
969      * trying to convert invalid value.
970      */
convertForType(String value, TypeMirror type)971     private String convertForType(String value, TypeMirror type) {
972         TypeKind kind = type.getKind();
973         if (kind == TypeKind.DECLARED && mTypes.isSameType(type, mStringType)) {
974             return "\"" + value + "\"";
975         } else if (kind == TypeKind.CHAR
976                 || (kind == TypeKind.DECLARED && mTypes.isSameType(type, mCharacterType))) {
977             if (" ".equals(value)) {
978                 return "' '";
979             }
980             value = value.trim();
981             return value.length() == 1 ? "'" + value + "'" : null;
982         } else {
983             // Ignoring leading and trailing whitespace for non-string types
984             value = value.trim();
985             if (kind == TypeKind.BOOLEAN
986                     || (kind == TypeKind.DECLARED && mTypes.isSameType(type, mBooleanType))) {
987                 return "true".equals(value) || "false".equals(value) ? value : null;
988             } else {
989                 try {
990                     if (kind == TypeKind.DOUBLE
991                             || (kind == TypeKind.DECLARED
992                                     && mTypes.isSameType(type, mDoubleType))) {
993                         Double.parseDouble(value);
994                     } else if (kind == TypeKind.FLOAT
995                             || (kind == TypeKind.DECLARED && mTypes.isSameType(type, mFloatType))) {
996                         Float.parseFloat(value);
997                     } else if (kind == TypeKind.BYTE
998                             || (kind == TypeKind.DECLARED && mTypes.isSameType(type, mByteType))) {
999                         Byte.parseByte(value);
1000                     } else if (kind == TypeKind.INT
1001                             || (kind == TypeKind.DECLARED
1002                                     && mTypes.isSameType(type, mIntegerType))) {
1003                         Integer.parseInt(value);
1004                     } else if (kind == TypeKind.LONG
1005                             || (kind == TypeKind.DECLARED && mTypes.isSameType(type, mLongType))) {
1006                         Long.parseLong(value);
1007                     } else if (kind == TypeKind.SHORT
1008                             || (kind == TypeKind.DECLARED && mTypes.isSameType(type, mShortType))) {
1009                         Short.parseShort(value);
1010                     } else {
1011                         return null;
1012                     }
1013                 } catch (NumberFormatException e) {
1014                     return null;
1015                 }
1016                 return value;
1017             }
1018         }
1019     }
1020 
getSerializationMethod(VariableElement field, TypeMirror type)1021     private SerializationMethods getSerializationMethod(VariableElement field, TypeMirror type) {
1022         switch (type.getKind()) {
1023             case BOOLEAN:
1024                 return new SerializationMethods(
1025                         "writeBoolean", false, true, "readBoolean", null, false);
1026             case BYTE:
1027                 return new SerializationMethods("writeByte", false, true, "readByte", null, false);
1028             case CHAR:
1029                 return new SerializationMethods("writeChar", false, true, "readChar", null, false);
1030             case DOUBLE:
1031                 return new SerializationMethods(
1032                         "writeDouble", false, true, "readDouble", null, false);
1033             case FLOAT:
1034                 return new SerializationMethods(
1035                         "writeFloat", false, true, "readFloat", null, false);
1036             case INT:
1037                 return new SerializationMethods("writeInt", false, true, "readInt", null, false);
1038             case LONG:
1039                 return new SerializationMethods("writeLong", false, true, "readLong", null, false);
1040             case SHORT:
1041                 return new SerializationMethods(
1042                         "writeShort", false, true, "readShort", null, false);
1043             case DECLARED:
1044                 {
1045                     final DeclaredType dt = (DeclaredType) type;
1046                     if (mTypes.isSameType(type, mStringType)) {
1047                         return new SerializationMethods(
1048                                 "writeString", false, true, "createString", null, true);
1049                     } else if (mTypes.isSameType(type, mBigIntegerType)) {
1050                         return new SerializationMethods(
1051                                 "writeBigInteger", false, true, "createBigInteger", null, true);
1052                     } else if (mTypes.isSameType(type, mBigDecimalType)) {
1053                         return new SerializationMethods(
1054                                 "writeBigDecimal", false, true, "createBigDecimal", null, true);
1055                     } else if (mTypes.isSameType(type, mBooleanType)) {
1056                         return new SerializationMethods(
1057                                 "writeBooleanObject", false, true, "readBooleanObject", null, true);
1058                     } else if (mTypes.isSameType(type, mIntegerType)) {
1059                         return new SerializationMethods(
1060                                 "writeIntegerObject", false, true, "readIntegerObject", null, true);
1061                     } else if (mTypes.isSameType(type, mLongType)) {
1062                         return new SerializationMethods(
1063                                 "writeLongObject", false, true, "readLongObject", null, true);
1064                     } else if (mTypes.isSameType(type, mFloatType)) {
1065                         return new SerializationMethods(
1066                                 "writeFloatObject", false, true, "readFloatObject", null, true);
1067                     } else if (mTypes.isSameType(type, mDoubleType)) {
1068                         return new SerializationMethods(
1069                                 "writeDoubleObject", false, true, "readDoubleObject", null, true);
1070                     } else if (mTypes.isAssignable(type, mIBinderType)) {
1071                         return new SerializationMethods(
1072                                 "writeIBinder", false, true, "readIBinder", null, true);
1073                     } else if (mTypes.isSameType(type, mBundleType)) {
1074                         return new SerializationMethods(
1075                                 "writeBundle", false, true, "createBundle", null, true);
1076                     } else if (mTypes.isSameType(type, mParcelType)) {
1077                         return new SerializationMethods(
1078                                 "writeParcel", false, true, "createParcel", null, true);
1079                     } else if (mTypes.isAssignable(
1080                             mTypes.erasure(type), mTypes.erasure(mListType))) {
1081                         List<? extends TypeMirror> typeArgs = dt.getTypeArguments();
1082                         if (typeArgs.isEmpty() || mTypes.isSameType(mListType, type)) {
1083                             if (isListSafeForReflection((DeclaredType) field.asType())) {
1084                                 return new SerializationMethods(
1085                                         "writeList",
1086                                         false,
1087                                         false,
1088                                         "readList",
1089                                         "getClass().getClassLoader()",
1090                                         true);
1091                             } else {
1092                                 mMessager.printMessage(
1093                                         Diagnostic.Kind.ERROR,
1094                                         "Use a type parameter for java.util.List fields.",
1095                                         field);
1096 
1097                                 return null;
1098                             }
1099                         } else if (typeArgs.size() == 1) {
1100                             TypeMirror typeArg = typeArgs.get(0);
1101                             if (mTypes.isAssignable(typeArg, mBooleanType)) {
1102                                 return new SerializationMethods(
1103                                         "writeBooleanList",
1104                                         false,
1105                                         true,
1106                                         "createBooleanList",
1107                                         null,
1108                                         true);
1109                             } else if (mTypes.isAssignable(typeArg, mIntegerType)) {
1110                                 return new SerializationMethods(
1111                                         "writeIntegerList",
1112                                         false,
1113                                         true,
1114                                         "createIntegerList",
1115                                         null,
1116                                         true);
1117                             } else if (mTypes.isAssignable(typeArg, mLongType)) {
1118                                 return new SerializationMethods(
1119                                         "writeLongList", false, true, "createLongList", null, true);
1120                             } else if (mTypes.isAssignable(typeArg, mFloatType)) {
1121                                 return new SerializationMethods(
1122                                         "writeFloatList",
1123                                         false,
1124                                         true,
1125                                         "createFloatList",
1126                                         null,
1127                                         true);
1128                             } else if (mTypes.isAssignable(typeArg, mDoubleType)) {
1129                                 return new SerializationMethods(
1130                                         "writeDoubleList",
1131                                         false,
1132                                         true,
1133                                         "createDoubleList",
1134                                         null,
1135                                         true);
1136                             } else if (mTypes.isAssignable(typeArg, mStringType)) {
1137                                 return new SerializationMethods(
1138                                         "writeStringList",
1139                                         false,
1140                                         true,
1141                                         "createStringList",
1142                                         null,
1143                                         true);
1144                             } else if (mTypes.isSameType(typeArg, mParcelType)) {
1145                                 return new SerializationMethods(
1146                                         "writeParcelList",
1147                                         false,
1148                                         true,
1149                                         "createParcelList",
1150                                         null,
1151                                         true);
1152                             } else if (mTypes.isAssignable(typeArg, mIBinderType)) {
1153                                 return new SerializationMethods(
1154                                         "writeIBinderList",
1155                                         false,
1156                                         true,
1157                                         "createIBinderList",
1158                                         null,
1159                                         true);
1160                             } else if (mTypes.isAssignable(typeArg, mParcelableType)) {
1161                                 return new SerializationMethods(
1162                                         "writeTypedList",
1163                                         false,
1164                                         true,
1165                                         "createTypedList",
1166                                         mTypes.erasure(typeArg) + ".CREATOR",
1167                                         true);
1168                             }
1169                         }
1170                     } else if (mTypes.isAssignable(type, mParcelableType)) {
1171                         if (isSafeForSafeParcelable(dt)) {
1172                             return new SerializationMethods(
1173                                     "writeParcelable",
1174                                     true,
1175                                     true,
1176                                     "createParcelable",
1177                                     mTypes.erasure(type) + ".CREATOR",
1178                                     true);
1179                         }
1180                     } else if (mTypes.isAssignable(type, mSparseBooleanArrayType)) {
1181                         return new SerializationMethods(
1182                                 "writeSparseBooleanArray",
1183                                 false,
1184                                 true,
1185                                 "createSparseBooleanArray",
1186                                 null,
1187                                 true);
1188                     } else if (mTypes.isAssignable(type, mSparseIntArrayType)) {
1189                         return new SerializationMethods(
1190                                 "writeSparseIntArray",
1191                                 false,
1192                                 true,
1193                                 "createSparseIntArray",
1194                                 null,
1195                                 true);
1196                     } else if (mTypes.isAssignable(type, mSparseLongArrayType)) {
1197                         return new SerializationMethods(
1198                                 "writeSparseLongArray",
1199                                 false,
1200                                 true,
1201                                 "createSparseLongArray",
1202                                 null,
1203                                 true);
1204                     } else if (mTypes.isAssignable(
1205                             mTypes.erasure(type), mTypes.erasure(mSparseArrayType))) {
1206                         List<? extends TypeMirror> typeArgs = dt.getTypeArguments();
1207                         if (typeArgs.isEmpty() || mTypes.isSameType(mSparseArrayType, type)) {
1208                             return null; // Do not support generic SparseArray.
1209                         } else if (typeArgs.size() == 1) {
1210                             TypeMirror typeArg = typeArgs.get(0);
1211                             if (mTypes.isAssignable(typeArg, mBooleanType)) {
1212                                 mMessager.printMessage(
1213                                         Diagnostic.Kind.ERROR,
1214                                         "please use " + "android.util.SparseBooleanArray instead.",
1215                                         field);
1216                                 return null;
1217                             } else if (mTypes.isAssignable(typeArg, mIntegerType)) {
1218                                 mMessager.printMessage(
1219                                         Diagnostic.Kind.ERROR,
1220                                         "please use " + "android.util.SparseIntArray instead.",
1221                                         field);
1222                                 return null;
1223                             } else if (mTypes.isAssignable(typeArg, mLongType)) {
1224                                 mMessager.printMessage(
1225                                         Diagnostic.Kind.ERROR,
1226                                         "please use " + "android.util.SparseLongArray instead.",
1227                                         field);
1228                                 return null;
1229                             } else if (mTypes.isAssignable(typeArg, mFloatType)) {
1230                                 return new SerializationMethods(
1231                                         "writeFloatSparseArray",
1232                                         false,
1233                                         true,
1234                                         "createFloatSparseArray",
1235                                         null,
1236                                         true);
1237                             } else if (mTypes.isAssignable(typeArg, mDoubleType)) {
1238                                 return new SerializationMethods(
1239                                         "writeDoubleSparseArray",
1240                                         false,
1241                                         true,
1242                                         "createDoubleSparseArray",
1243                                         null,
1244                                         true);
1245                             } else if (mTypes.isAssignable(typeArg, mStringType)) {
1246                                 return new SerializationMethods(
1247                                         "writeStringSparseArray",
1248                                         false,
1249                                         true,
1250                                         "createStringSparseArray",
1251                                         null,
1252                                         true);
1253                             } else if (mTypes.isSameType(typeArg, mParcelType)) {
1254                                 return new SerializationMethods(
1255                                         "writeParcelSparseArray",
1256                                         false,
1257                                         true,
1258                                         "createParcelSparseArray",
1259                                         null,
1260                                         true);
1261                             } else if (mTypes.isAssignable(typeArg, mIBinderType)) {
1262                                 return new SerializationMethods(
1263                                         "writeIBinderSparseArray",
1264                                         false,
1265                                         true,
1266                                         "createIBinderSparseArray",
1267                                         null,
1268                                         true);
1269                             } else if (mTypes.isAssignable(typeArg, mParcelableType)) {
1270                                 DeclaredType dtTypeArg = (DeclaredType) typeArg;
1271                                 if (isSafeForSafeParcelable(dtTypeArg)) {
1272                                     return new SerializationMethods(
1273                                             "writeTypedSparseArray",
1274                                             false,
1275                                             true,
1276                                             "createTypedSparseArray",
1277                                             mTypes.erasure(typeArg) + ".CREATOR",
1278                                             true);
1279                                 }
1280                             } else if (typeArg.getKind() == TypeKind.ARRAY) {
1281                                 final TypeMirror internalComponentType =
1282                                         ((ArrayType) typeArg).getComponentType();
1283                                 switch (internalComponentType.getKind()) {
1284                                     case BYTE:
1285                                         return new SerializationMethods(
1286                                                 "writeByteArraySparseArray",
1287                                                 false,
1288                                                 true,
1289                                                 "createByteArraySparseArray",
1290                                                 null,
1291                                                 true);
1292                                     default:
1293                                         return null;
1294                                 }
1295                             }
1296                         }
1297                     }
1298                     return null;
1299                 }
1300             case ARRAY:
1301                 {
1302                     // The list of array types supported by Parcel is much shorter.
1303                     // We could certainly implement more here, but if Parcel has gotten
1304                     // this far without them, it's low priority.
1305                     final TypeMirror componentType = ((ArrayType) type).getComponentType();
1306                     switch (componentType.getKind()) {
1307                         case BOOLEAN:
1308                             return new SerializationMethods(
1309                                     "writeBooleanArray",
1310                                     false,
1311                                     true,
1312                                     "createBooleanArray",
1313                                     null,
1314                                     true);
1315                         case BYTE:
1316                             return new SerializationMethods(
1317                                     "writeByteArray", false, true, "createByteArray", null, true);
1318                         case CHAR:
1319                             return new SerializationMethods(
1320                                     "writeCharArray", false, true, "createCharArray", null, true);
1321                         case INT:
1322                             return new SerializationMethods(
1323                                     "writeIntArray", false, true, "createIntArray", null, true);
1324                         case LONG:
1325                             return new SerializationMethods(
1326                                     "writeLongArray", false, true, "createLongArray", null, true);
1327                         case FLOAT:
1328                             return new SerializationMethods(
1329                                     "writeFloatArray", false, true, "createFloatArray", null, true);
1330                         case DOUBLE:
1331                             return new SerializationMethods(
1332                                     "writeDoubleArray",
1333                                     false,
1334                                     true,
1335                                     "createDoubleArray",
1336                                     null,
1337                                     true);
1338                         case DECLARED:
1339                             {
1340                                 final DeclaredType dt = (DeclaredType) componentType;
1341                                 if (mTypes.isSameType(componentType, mStringType)) {
1342                                     return new SerializationMethods(
1343                                             "writeStringArray",
1344                                             false,
1345                                             true,
1346                                             "createStringArray",
1347                                             null,
1348                                             true);
1349                                 } else if (mTypes.isSameType(componentType, mParcelType)) {
1350                                     return new SerializationMethods(
1351                                             "writeParcelArray",
1352                                             false,
1353                                             true,
1354                                             "createParcelArray",
1355                                             null,
1356                                             true);
1357                                 } else if (mTypes.isSameType(componentType, mBigIntegerType)) {
1358                                     return new SerializationMethods(
1359                                             "writeBigIntegerArray",
1360                                             false,
1361                                             true,
1362                                             "createBigIntegerArray",
1363                                             null,
1364                                             true);
1365                                 } else if (mTypes.isSameType(componentType, mBigDecimalType)) {
1366                                     return new SerializationMethods(
1367                                             "writeBigDecimalArray",
1368                                             false,
1369                                             true,
1370                                             "createBigDecimalArray",
1371                                             null,
1372                                             true);
1373                                 } else if (mTypes.isAssignable(componentType, mIBinderType)) {
1374                                     return new SerializationMethods(
1375                                             "writeIBinderArray",
1376                                             false,
1377                                             true,
1378                                             "createIBinderArray",
1379                                             null,
1380                                             true);
1381                                 } else if (mTypes.isAssignable(componentType, mParcelableType)) {
1382                                     if (isSafeForSafeParcelable(dt)) {
1383                                         return new SerializationMethods(
1384                                                 "writeTypedArray",
1385                                                 true,
1386                                                 true,
1387                                                 "createTypedArray",
1388                                                 mTypes.erasure(componentType) + ".CREATOR",
1389                                                 true);
1390                                     }
1391                                 }
1392                                 return null;
1393                             }
1394                         case ARRAY:
1395                             {
1396                                 final TypeMirror internalComponentType =
1397                                         ((ArrayType) componentType).getComponentType();
1398                                 switch (internalComponentType.getKind()) {
1399                                     case BYTE:
1400                                         return new SerializationMethods(
1401                                                 "writeByteArrayArray",
1402                                                 false,
1403                                                 true,
1404                                                 "createByteArrayArray",
1405                                                 null,
1406                                                 true);
1407                                     default:
1408                                         return null;
1409                                 }
1410                             }
1411                         default:
1412                             return null;
1413                     }
1414                 }
1415             default:
1416                 return null;
1417         }
1418     }
1419 
1420     /** Check that the Parcelable type can be included as a field for a SafeParcelable. */
isSafeForSafeParcelable(DeclaredType type)1421     private boolean isSafeForSafeParcelable(DeclaredType type) {
1422         // Include any types that have been annotated with SafeParcelable
1423         if (type.asElement().getAnnotation(SafeParcelable.Class.class) != null) {
1424             return true;
1425         }
1426 
1427         // Include any types under android.*
1428         PackageElement typePackage = mElements.getPackageOf(type.asElement());
1429         if (typePackage.getQualifiedName().toString().startsWith("android.")) {
1430             return true;
1431         }
1432         return false;
1433     }
1434 
isListSafeForReflection(DeclaredType fieldType)1435     private boolean isListSafeForReflection(DeclaredType fieldType) {
1436         if (fieldType.getTypeArguments().size() == 1) {
1437             // List<?>
1438             DeclaredType typeArg = (DeclaredType) fieldType.getTypeArguments().get(0);
1439             if (mTypes.isAssignable(mTypes.erasure(typeArg), mTypes.erasure(mListType))) {
1440                 // List<List>
1441                 if (typeArg.getTypeArguments().size() == 1) {
1442                     TypeMirror innerArg = typeArg.getTypeArguments().get(0);
1443                     if (mTypes.isAssignable(innerArg, mReflectedParcelableType)) {
1444                         // List<List<? exends ReflectedParcelable>>
1445                         return true;
1446                     }
1447                 }
1448             } else if (mTypes.isAssignable(typeArg, mReflectedParcelableType)) {
1449                 // List<? extends ReflectedParcelable>
1450                 return true;
1451             } else if (mTypes.isAssignable(typeArg, mIntegerType)) {
1452                 // List<Integer> for legacy reasons
1453                 return true;
1454             }
1455         }
1456 
1457         return false;
1458     }
1459 
hasValidateContents(TypeElement clazz)1460     private boolean hasValidateContents(TypeElement clazz) {
1461         while (clazz != null) {
1462             List<ExecutableElement> methods = ElementFilter.methodsIn(clazz.getEnclosedElements());
1463             for (ExecutableElement method : methods) {
1464                 if ("validateContents".equals(method.getSimpleName().toString())
1465                         && method.getReturnType().getKind() == TypeKind.VOID
1466                         && method.getParameters().isEmpty()) {
1467                     // Check that the method is either public or package private.
1468                     Set<Modifier> modifiers = method.getModifiers();
1469                     if (modifiers.contains(Modifier.PRIVATE)
1470                             || modifiers.contains(Modifier.PROTECTED)) {
1471                         mMessager.printMessage(
1472                                 Diagnostic.Kind.ERROR,
1473                                 "validateContents must be public or package private",
1474                                 method);
1475                     }
1476                     return true;
1477                 }
1478             }
1479             clazz = (TypeElement) mTypes.asElement(clazz.getSuperclass());
1480         }
1481         return false;
1482     }
1483 
hasCreator(TypeElement parcelableClass, String generatedClassName)1484     private boolean hasCreator(TypeElement parcelableClass, String generatedClassName) {
1485         List<VariableElement> fields =
1486                 ElementFilter.fieldsIn(parcelableClass.getEnclosedElements());
1487         for (VariableElement field : fields) {
1488             if ("CREATOR".equals(field.getSimpleName().toString())) {
1489                 // If the type of the CREATOR field is the custom creator class, then this would
1490                 // be the type name.  Note that since the creator class is generated, the creator
1491                 // class is not a type that is known by the annotation processor.  Hence, we must
1492                 // manually qualify the package.  The expected name of the CREATOR class is the
1493                 // value of generatedClassName.
1494                 String detectedCreatorTypeName =
1495                         mElements.getPackageOf(parcelableClass).getQualifiedName()
1496                                 + "."
1497                                 + field.asType();
1498                 // If the type of the CREATOR field is the generic form, Parcelable.Creator<T>
1499                 // then we don't need to manually qualify the type.
1500                 String detectedAlternativeCreatorTypeName = field.asType().toString();
1501 
1502                 // This represents the expected type of the CREATOR object in the alternative form.
1503                 // The expected name is in this form is the value of
1504                 // expectedAlternativeCreatorTypeName.
1505                 String expectedAlternativeCreatorTypeName =
1506                         mTypes.getDeclaredType(
1507                                         (TypeElement) mTypes.asElement(mParcelableCreatorType),
1508                                         parcelableClass.asType())
1509                                 .toString();
1510                 TypeMirror parcelableType = parcelableClass.asType();
1511                 if (parcelableType instanceof DeclaredType) {
1512                     DeclaredType declaredType = (DeclaredType) parcelableType; // Parcel<T>
1513                     if (!declaredType.getTypeArguments().isEmpty()) {
1514                         // If the ParcelableType is generic (ex: Parcelable.Creator<Parcel<T>>),
1515                         // then expectedAlternativeCreatorTypeName needs to trim <T> part as
1516                         // detectedAlternativeCreatorTypeName would only return Parcel resulting
1517                         // in an incorrect ParcelCreatorType failure.
1518                         String type = declaredType.getTypeArguments().get(0).toString(); // T
1519                         expectedAlternativeCreatorTypeName =
1520                                 expectedAlternativeCreatorTypeName.replace("<" + type + ">", "");
1521                     }
1522                 }
1523                 if (generatedClassName.equals(detectedCreatorTypeName)
1524                         || expectedAlternativeCreatorTypeName.equals(
1525                                 detectedAlternativeCreatorTypeName)) {
1526                     Set<Modifier> modifiers = field.getModifiers();
1527                     if (modifiers.contains(Modifier.PUBLIC)
1528                             && modifiers.contains(Modifier.STATIC)
1529                             && modifiers.contains(Modifier.FINAL)) {
1530                         return true;
1531                     }
1532                 }
1533             }
1534         }
1535         return false;
1536     }
1537 
generateParser(ParcelableClass cl)1538     private void generateParser(ParcelableClass cl) {
1539         PrintWriter writer = null;
1540         try {
1541             JavaFileObject file =
1542                     this.processingEnv
1543                             .getFiler()
1544                             .createSourceFile(cl.mGeneratedClassName, cl.mParcelableClass);
1545             writer = new PrintWriter(file.openOutputStream());
1546 
1547             JSilver jSilver =
1548                     new JSilver(
1549                             new ClassLoaderResourceLoader(getClass().getClassLoader(), "templates"),
1550                             new JSilverOptions().setEscapeMode(EscapeMode.ESCAPE_NONE));
1551 
1552             Data data = jSilver.createData();
1553 
1554             Data annotations = data.createChild("annotations");
1555             if (cl.mRequiresApi != null) {
1556                 annotations.setValue(
1557                         "0",
1558                         String.format(
1559                                 Locale.ROOT,
1560                                 "@androidx.annotation.RequiresApi(%d)",
1561                                 cl.mRequiresApi));
1562             }
1563 
1564             // Creator class name
1565             int index = cl.mGeneratedClassName.lastIndexOf('.');
1566             if (index > 0) {
1567                 data.setValue("creator_package", cl.mGeneratedClassName.substring(0, index));
1568                 data.setValue("creator_name", cl.mGeneratedClassName.substring(index + 1));
1569             } else {
1570                 data.setValue("creator_name", cl.mGeneratedClassName);
1571             }
1572 
1573             if (cl.mAnnotation.creatorIsFinal()) {
1574                 data.setValue("creatorIsFinal", "true");
1575             }
1576 
1577             // Data class name
1578             data.setValue("class", cl.mQualifiedName);
1579 
1580             // Set the constructor parameters
1581             data.setValue("params", generateFormalParameters(cl.mConstructor));
1582 
1583             // Call validate on the object
1584             if (cl.mAnnotation.validate()) {
1585                 data.setValue("call_validateContents", "true");
1586             }
1587 
1588             if (cl.mAnnotation.doNotParcelTypeDefaultValues()) {
1589                 data.setValue("doNotParcelTypeDefaultValues", "true");
1590             }
1591 
1592             if (cl.mIndicatorField != null) {
1593                 data.setValue("indicator.read_name", cl.mIndicatorField.mReadName);
1594                 if (cl.mIndicatorField.mGetter == null) {
1595                     data.setValue("indicator.write_name", cl.mIndicatorField.mName);
1596                 } else {
1597                     data.setValue("indicator.write_name", cl.mIndicatorField.mGetter + "()");
1598                 }
1599             }
1600 
1601             // temporary variable declarations
1602             Data declarations = data.createChild("declarations");
1603             int i = 0;
1604             for (ParcelableField f : cl.mConstructor.mParameters) {
1605                 Data declaration = declarations.createChild(Integer.toString(i++));
1606                 declaration.setValue("type", typeOrSuper(f.mType));
1607                 declaration.setValue("var_name", f.mReadName);
1608                 declaration.setValue("initial_value", getDefaultValueForField(f));
1609             }
1610 
1611             // reading the fields
1612             Data fields = data.createChild("fields");
1613             i = 0;
1614             for (ParcelableField f :
1615                     cl.mFields.values().stream().sorted(comparing(f -> f.mId)).collect(toList())) {
1616                 SerializationMethods sm = f.mSm;
1617                 Data field = fields.createChild(Integer.toString(i++));
1618                 field.setValue("id", Integer.toString(f.mId));
1619                 if (sm.mIsAssignment) {
1620                     field.setValue("is_assignment", "true");
1621                 }
1622                 field.setValue("read_name", f.mReadName);
1623                 if (f.mGetter == null) {
1624                     field.setValue("write_name", f.mName);
1625                 } else {
1626                     field.setValue("write_name", f.mGetter + "()");
1627                 }
1628                 field.setValue("write", sm.mWrite);
1629                 if (sm.mWriteWithFlags) {
1630                     field.setValue("writeWithFlags", "1");
1631                 }
1632                 field.setValue("create", sm.mRead);
1633                 if (sm.mCreator != null) {
1634                     field.setValue("creator", sm.mCreator);
1635                 }
1636                 if (sm.mHasWriteNull) {
1637                     field.setValue("hasWriteNull", "1");
1638                 }
1639             }
1640 
1641             // Write to the file.
1642             if (cl.mIndicatorField == null) {
1643                 jSilver.render("template.cs", data, writer);
1644             } else {
1645                 jSilver.render("templateWithIndicator.cs", data, writer);
1646             }
1647         } catch (IOException ex) {
1648             mMessager.printMessage(
1649                     Diagnostic.Kind.ERROR,
1650                     "Error writing class file for "
1651                             + cl.mGeneratedClassName
1652                             + ": "
1653                             + ex.getMessage());
1654         } finally {
1655             if (writer != null) {
1656                 writer.close();
1657             }
1658         }
1659     }
1660 
typeOrSuper(TypeMirror type)1661     private String typeOrSuper(TypeMirror type) {
1662         if (type.getKind() == TypeKind.DECLARED) {
1663             DeclaredType dt = (DeclaredType) type;
1664             if (mTypes.isAssignable(mTypes.erasure(type), mTypes.erasure(mListType))
1665                     && !mTypes.isAssignable(mTypes.erasure(type), mTypes.erasure(mArrayListType))) {
1666                 List<? extends TypeMirror> typeArgs = dt.getTypeArguments();
1667                 if (typeArgs.isEmpty()) {
1668                     return mTypes.erasure(mListType).toString();
1669                 }
1670                 return mTypes.erasure(mListType) + "<" + typeArgs.get(0) + ">";
1671             }
1672         }
1673         return type.toString();
1674     }
1675 
getDefaultValueForField(ParcelableField field)1676     private String getDefaultValueForField(ParcelableField field) {
1677         if (field.mDefaultValue != null) {
1678             return field.mDefaultValue;
1679         }
1680         TypeMirror type = field.mType;
1681         switch (type.getKind()) {
1682             case BOOLEAN:
1683                 return "false";
1684             case BYTE:
1685                 // fallthrough
1686             case CHAR:
1687                 // fallthrough
1688             case SHORT:
1689                 // fallthrough
1690             case INT:
1691                 return "0";
1692             case DOUBLE:
1693                 return "0.0";
1694             case FLOAT:
1695                 return "0.0f";
1696             case LONG:
1697                 return "0L";
1698             case DECLARED:
1699                 if (mTypes.isAssignable(mTypes.erasure(type), mTypes.erasure(mListType))) {
1700                     final DeclaredType dt = (DeclaredType) type;
1701                     if (dt.getTypeArguments().isEmpty()) {
1702                         // This is a generic list, so create this.
1703                         return "new java.util.ArrayList()";
1704                     }
1705                 }
1706                 return "null";
1707             default:
1708                 return "null";
1709         }
1710     }
1711 
generateFormalParameters(ParcelableConstructor constructor)1712     private static String generateFormalParameters(ParcelableConstructor constructor) {
1713         StringBuffer sb = new StringBuffer();
1714         ArrayList<ParcelableField> parameters = constructor.mParameters;
1715         for (int i = 0; i < parameters.size(); i++) {
1716             sb.append(parameters.get(i).mReadName);
1717             if (i < parameters.size() - 1) {
1718                 sb.append(", ");
1719             }
1720         }
1721         return sb.toString();
1722     }
1723 }
1724