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