1 package com.android.class2nonsdklist; 2 3 import com.android.annotationvisitor.AnnotatedClassContext; 4 import com.android.annotationvisitor.AnnotatedMemberContext; 5 import com.android.annotationvisitor.AnnotationConsumer; 6 import com.android.annotationvisitor.AnnotationContext; 7 import com.android.annotationvisitor.AnnotationHandler; 8 9 import com.google.common.base.Preconditions; 10 import com.google.common.collect.ImmutableSet; 11 12 import org.apache.bcel.classfile.AnnotationEntry; 13 import org.apache.bcel.classfile.ElementValuePair; 14 import org.apache.bcel.classfile.Method; 15 16 import java.util.Locale; 17 import java.util.Set; 18 19 /** 20 * Handles {@code CovariantReturnType} annotations, generating whitelist 21 * entries from them. 22 * 23 * <p>A whitelist entry is generated with the same descriptor as the original 24 * method, but with the return type replaced with than specified by the 25 * {@link #RETURN_TYPE} property. 26 * 27 * <p>Methods are also validated against the public API list, to assert that 28 * the annotated method is already a public API. 29 */ 30 public class CovariantReturnTypeHandler extends AnnotationHandler { 31 32 private static final String SHORT_NAME = "CovariantReturnType"; 33 public static final String ANNOTATION_NAME = "Ldalvik/annotation/codegen/CovariantReturnType;"; 34 public static final String REPEATED_ANNOTATION_NAME = 35 "Ldalvik/annotation/codegen/CovariantReturnType$CovariantReturnTypes;"; 36 37 private static final String RETURN_TYPE = "returnType"; 38 39 private final AnnotationConsumer mAnnotationConsumer; 40 private final Set<String> mPublicApis; 41 private final String mHiddenapiFlag; 42 CovariantReturnTypeHandler(AnnotationConsumer consumer, Set<String> publicApis, String hiddenapiFlag)43 public CovariantReturnTypeHandler(AnnotationConsumer consumer, Set<String> publicApis, 44 String hiddenapiFlag) { 45 mAnnotationConsumer = consumer; 46 mPublicApis = publicApis; 47 mHiddenapiFlag = hiddenapiFlag; 48 } 49 50 @Override handleAnnotation(AnnotationEntry annotation, AnnotationContext context)51 public void handleAnnotation(AnnotationEntry annotation, AnnotationContext context) { 52 if (context instanceof AnnotatedClassContext) { 53 return; 54 } 55 handleAnnotation(annotation, (AnnotatedMemberContext) context); 56 } 57 handleAnnotation(AnnotationEntry annotation, AnnotatedMemberContext context)58 private void handleAnnotation(AnnotationEntry annotation, AnnotatedMemberContext context) { 59 // Verify that the annotation has been applied to what we expect, and 60 // has the right form. Note, this should not strictly be necessary, as 61 // the annotation has a target of just 'method' and the property 62 // returnType does not have a default value, but checking makes the code 63 // less brittle to future changes. 64 if (!(context.member instanceof Method)) { 65 context.reportError("Cannot specify %s on a field", RETURN_TYPE); 66 return; 67 } 68 String returnType = findReturnType(annotation); 69 if (returnType == null) { 70 context.reportError("No %s set on @%s", RETURN_TYPE, SHORT_NAME); 71 return; 72 } 73 if (!mPublicApis.contains(context.getMemberDescriptor())) { 74 context.reportError("Found @%s on non-SDK method", SHORT_NAME); 75 return; 76 } 77 78 // Generate the signature of overload that we expect the annotation will 79 // cause the platform dexer to create. 80 String typeSignature = context.member.getSignature(); 81 int closingBrace = typeSignature.indexOf(')'); 82 Preconditions.checkState(closingBrace != -1, 83 "No ) found in method type signature %s", typeSignature); 84 typeSignature = new StringBuilder() 85 .append(typeSignature.substring(0, closingBrace + 1)) 86 .append(returnType) 87 .toString(); 88 String signature = String.format(Locale.US, context.signatureFormatString, 89 context.getClassDescriptor(), context.member.getName(), typeSignature); 90 91 if (mPublicApis.contains(signature)) { 92 context.reportError("Signature %s generated from @%s already exists as a public API", 93 signature, SHORT_NAME); 94 return; 95 } 96 97 mAnnotationConsumer.consume(signature, stringifyAnnotationProperties(annotation), 98 ImmutableSet.of(mHiddenapiFlag)); 99 } 100 findReturnType(AnnotationEntry a)101 private String findReturnType(AnnotationEntry a) { 102 for (ElementValuePair property : a.getElementValuePairs()) { 103 if (property.getNameString().equals(RETURN_TYPE)) { 104 return property.getValue().stringifyValue(); 105 } 106 } 107 // not found 108 return null; 109 } 110 } 111