android系统源码编译报错问题分析处理--持续更新

一、build/make/core/base_rules.mk:232: error: packages/services/Car/service: LOCAL_BUILT_MODULE and LOCAL_INSTALLED_MODULE must not be defined by component makefiles

出现此错误时,需要检查Android.mk中include $(CLEAR_VARS)及include $(BUILD_XXX...)是否一一对应了,如果有类似下边的写法,就需要修正

错误:
......
include $(CLEAR_VARS)
......
include $(BUILD_MULTI_PREBUILT)
......
include $(BUILD_STATIC_JAVA_LIBRARY)
......
include $(BUILD_PACKAGE)
include $(CLEAR_VARS)

正确:
include $(CLEAR_VARS)
......
include $(BUILD_MULTI_PREBUILT)

include $(CLEAR_VARS)
......
include $(BUILD_STATIC_JAVA_LIBRARY)

include $(CLEAR_VARS)
......
include $(BUILD_PACKAGE)

 

二、错误: 程序包androidx.annotation不存在,需要在 Android.mk 中添加如下,根据自己的依赖进行添加

LOCAL_STATIC_ANDROID_LIBRARIES := \
androidx.recyclerview_recyclerview \
androidx.preference_preference \
androidx.appcompat_appcompat \
androidx.annotation_annotation \
androidx.legacy_legacy-preference-v14 \
androidx.leanback_leanback-preference \
androidx.leanback_leanback \

 

三、javadoc: 错误 - 在 doclet 类com.google.doclava.Doclava中, 方法start已抛出异常错误java.lang.reflect.InvocationTargetException

java.lang.IllegalArgumentException: Unable to find IBtMusicCmd.java. This is usually because doclava has been asked to generate stubs for a file that isn't present in the list of input source files but exists in the input classpath.

IBtMusicCmd是我的aidl文件,代码都编译通过了,没有出现依赖问题,但是最后报出这个错,查了两天才发现这个问题应该是javadoc的版本太低了,我找到了这个系统文件进行了暴力修改后编译正常,但是是否存在隐患还不确定

解决办法:
直接找到external\doclava\src\com\google\doclava\Stubs.java,然后在53行添加if(true) return;如下!目的就是不执行此文件的操作
public class Stubs {
  public static void writeStubsAndApi(String stubsDir, String apiFile, String dexApiFile,
      String keepListFile, String removedApiFile, String removedDexApiFile, String exactApiFile,
      String privateApiFile, String privateDexApiFile, HashSet<String> stubPackages,
      HashSet<String> stubImportPackages, boolean stubSourceOnly) {
    if(true) return;

如果为了安全一点,也可以按照下边的方式,只判断是自己的这个模块就跳过就好了,源代码里三个地方添加以下代码,“android.car.xxxx.bt”需要替换为自己冲突的模块包名

if(cl.containingPackage().name().contains("android.car.xxxx.bt")){
        System.out.println("=======android.car.xxxx.bt=============continue");
        continue;
      }

 

   1 /*
   2  * Copyright (C) 2010 Google Inc.
   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 com.google.doclava;
  18 
  19 import java.io.BufferedOutputStream;
  20 import java.io.BufferedReader;
  21 import java.io.ByteArrayOutputStream;
  22 import java.io.File;
  23 import java.io.FileInputStream;
  24 import java.io.FileNotFoundException;
  25 import java.io.FileOutputStream;
  26 import java.io.IOException;
  27 import java.io.InputStream;
  28 import java.io.InputStreamReader;
  29 import java.io.PrintStream;
  30 import java.nio.charset.StandardCharsets;
  31 import java.nio.file.Files;
  32 import java.nio.file.Paths;
  33 import java.util.ArrayList;
  34 import java.util.Arrays;
  35 import java.util.Collection;
  36 import java.util.Collections;
  37 import java.util.HashMap;
  38 import java.util.HashSet;
  39 import java.util.Iterator;
  40 import java.util.List;
  41 import java.util.Map;
  42 import java.util.Scanner;
  43 import java.util.Set;
  44 import java.util.function.Predicate;
  45 import java.util.regex.Pattern;
  46 import java.util.stream.Collectors;
  47 
  48 public class Stubs {
  49   public static void writeStubsAndApi(String stubsDir, String apiFile, String dexApiFile,
  50       String keepListFile, String removedApiFile, String removedDexApiFile, String exactApiFile,
  51       String privateApiFile, String privateDexApiFile, HashSet<String> stubPackages,
  52       HashSet<String> stubImportPackages, boolean stubSourceOnly) {
  53     //if(true) return;
  54     // figure out which classes we need
  55     final HashSet<ClassInfo> notStrippable = new HashSet<ClassInfo>();
  56     Collection<ClassInfo> all = Converter.allClasses();
  57     Map<PackageInfo, List<ClassInfo>> allClassesByPackage = null;
  58     PrintStream apiWriter = null;
  59     PrintStream dexApiWriter = null;
  60     PrintStream keepListWriter = null;
  61     PrintStream removedApiWriter = null;
  62     PrintStream removedDexApiWriter = null;
  63     PrintStream exactApiWriter = null;
  64     PrintStream privateApiWriter = null;
  65     PrintStream privateDexApiWriter = null;
  66 
  67     if (apiFile != null) {
  68       try {
  69         File xml = new File(apiFile);
  70         xml.getParentFile().mkdirs();
  71         apiWriter = new PrintStream(new BufferedOutputStream(new FileOutputStream(xml)));
  72       } catch (FileNotFoundException e) {
  73         Errors.error(Errors.IO_ERROR, new SourcePositionInfo(apiFile, 0, 0),
  74             "Cannot open file for write.");
  75       }
  76     }
  77     if (dexApiFile != null) {
  78       try {
  79         File dexApi = new File(dexApiFile);
  80         dexApi.getParentFile().mkdirs();
  81         dexApiWriter = new PrintStream(new BufferedOutputStream(new FileOutputStream(dexApi)));
  82       } catch (FileNotFoundException e) {
  83         Errors.error(Errors.IO_ERROR, new SourcePositionInfo(dexApiFile, 0, 0),
  84             "Cannot open file for write.");
  85       }
  86     }
  87     if (keepListFile != null) {
  88       try {
  89         File keepList = new File(keepListFile);
  90         keepList.getParentFile().mkdirs();
  91         keepListWriter = new PrintStream(new BufferedOutputStream(new FileOutputStream(keepList)));
  92       } catch (FileNotFoundException e) {
  93         Errors.error(Errors.IO_ERROR, new SourcePositionInfo(keepListFile, 0, 0),
  94             "Cannot open file for write.");
  95       }
  96     }
  97     if (removedApiFile != null) {
  98       try {
  99         File removedApi = new File(removedApiFile);
 100         removedApi.getParentFile().mkdirs();
 101         removedApiWriter = new PrintStream(
 102             new BufferedOutputStream(new FileOutputStream(removedApi)));
 103       } catch (FileNotFoundException e) {
 104         Errors.error(Errors.IO_ERROR, new SourcePositionInfo(removedApiFile, 0, 0),
 105             "Cannot open file for write");
 106       }
 107     }
 108     if (removedDexApiFile != null) {
 109       try {
 110         File removedDexApi = new File(removedDexApiFile);
 111         removedDexApi.getParentFile().mkdirs();
 112         removedDexApiWriter = new PrintStream(
 113             new BufferedOutputStream(new FileOutputStream(removedDexApi)));
 114       } catch (FileNotFoundException e) {
 115         Errors.error(Errors.IO_ERROR, new SourcePositionInfo(removedDexApiFile, 0, 0),
 116             "Cannot open file for write");
 117       }
 118     }
 119     if (exactApiFile != null) {
 120       try {
 121         File exactApi = new File(exactApiFile);
 122         exactApi.getParentFile().mkdirs();
 123         exactApiWriter = new PrintStream(
 124             new BufferedOutputStream(new FileOutputStream(exactApi)));
 125       } catch (FileNotFoundException e) {
 126         Errors.error(Errors.IO_ERROR, new SourcePositionInfo(exactApiFile, 0, 0),
 127             "Cannot open file for write");
 128       }
 129     }
 130     if (privateApiFile != null) {
 131       try {
 132         File privateApi = new File(privateApiFile);
 133         privateApi.getParentFile().mkdirs();
 134         privateApiWriter = new PrintStream(
 135             new BufferedOutputStream(new FileOutputStream(privateApi)));
 136       } catch (FileNotFoundException e) {
 137         Errors.error(Errors.IO_ERROR, new SourcePositionInfo(privateApiFile, 0, 0),
 138             "Cannot open file for write");
 139       }
 140     }
 141     if (privateDexApiFile != null) {
 142       try {
 143         File privateDexApi = new File(privateDexApiFile);
 144         privateDexApi.getParentFile().mkdirs();
 145         privateDexApiWriter = new PrintStream(
 146             new BufferedOutputStream(new FileOutputStream(privateDexApi)));
 147       } catch (FileNotFoundException e) {
 148         Errors.error(Errors.IO_ERROR, new SourcePositionInfo(privateDexApiFile, 0, 0),
 149             "Cannot open file for write");
 150       }
 151     }
 152     // If a class is public or protected, not hidden, not imported and marked as included,
 153     // then we can't strip it
 154     for (ClassInfo cl : all) {
 155       if(cl.containingPackage().name().contains("android.car.xxxx.bt")){
 156         System.out.println("=======android.car.xxxx.bt=============continue");
 157         continue;
 158       }
 159       if (cl.checkLevel() && cl.isIncluded()) {
 160         cantStripThis(cl, notStrippable, "0:0", stubImportPackages);
 161       }
 162     }
 163 
 164     // complain about anything that looks includeable but is not supposed to
 165     // be written, e.g. hidden things
 166     for (ClassInfo cl : notStrippable) {
 167       if(cl.containingPackage().name().contains("android.car.xxxx.bt")){
 168         System.out.println("=======android.car.xxxx.bt=============continue");
 169         continue;
 170       }
 171       if (!cl.isHiddenOrRemoved()) {
 172         for (MethodInfo m : cl.selfMethods()) {
 173           if (m.isHiddenOrRemoved()) {
 174             Errors.error(Errors.UNAVAILABLE_SYMBOL, m.position(), "Reference to unavailable method "
 175                 + m.name());
 176           } else if (m.isDeprecated()) {
 177             // don't bother reporting deprecated methods
 178             // unless they are public
 179             Errors.error(Errors.DEPRECATED, m.position(), "Method " + cl.qualifiedName() + "."
 180                 + m.name() + " is deprecated");
 181           }
 182 
 183           ClassInfo hiddenClass = findHiddenClasses(m.returnType(), stubImportPackages);
 184           if (null != hiddenClass) {
 185             if (hiddenClass.qualifiedName() == m.returnType().asClassInfo().qualifiedName()) {
 186               // Return type is hidden
 187               Errors.error(Errors.UNAVAILABLE_SYMBOL, m.position(), "Method " + cl.qualifiedName()
 188                   + "." + m.name() + " returns unavailable type " + hiddenClass.name());
 189             } else {
 190               // Return type contains a generic parameter
 191               Errors.error(Errors.HIDDEN_TYPE_PARAMETER, m.position(), "Method " + cl.qualifiedName()
 192                   + "." + m.name() + " returns unavailable type " + hiddenClass.name()
 193                   + " as a type parameter");
 194             }
 195           }
 196 
 197           for (ParameterInfo p :  m.parameters()) {
 198             TypeInfo t = p.type();
 199             if (!t.isPrimitive()) {
 200               hiddenClass = findHiddenClasses(t, stubImportPackages);
 201               if (null != hiddenClass) {
 202                 if (hiddenClass.qualifiedName() == t.asClassInfo().qualifiedName()) {
 203                   // Parameter type is hidden
 204                   Errors.error(Errors.UNAVAILABLE_SYMBOL, m.position(),
 205                       "Parameter of unavailable type " + t.fullName() + " in " + cl.qualifiedName()
 206                       + "." + m.name() + "()");
 207                 } else {
 208                   // Parameter type contains a generic parameter
 209                   Errors.error(Errors.HIDDEN_TYPE_PARAMETER, m.position(),
 210                       "Parameter uses type parameter of unavailable type " + t.fullName() + " in "
 211                       + cl.qualifiedName() + "." + m.name() + "()");
 212                 }
 213               }
 214             }
 215           }
 216         }
 217 
 218         // annotations are handled like methods
 219         for (MethodInfo m : cl.annotationElements()) {
 220           if (m.isHiddenOrRemoved()) {
 221             Errors.error(Errors.UNAVAILABLE_SYMBOL, m.position(), "Reference to unavailable annotation "
 222                 + m.name());
 223           }
 224 
 225           ClassInfo returnClass = m.returnType().asClassInfo();
 226           if (returnClass != null && returnClass.isHiddenOrRemoved()) {
 227             Errors.error(Errors.UNAVAILABLE_SYMBOL, m.position(), "Annotation '" + m.name()
 228                 + "' returns unavailable type " + returnClass.name());
 229           }
 230 
 231           for (ParameterInfo p :  m.parameters()) {
 232             TypeInfo t = p.type();
 233             if (!t.isPrimitive()) {
 234               if (t.asClassInfo().isHiddenOrRemoved()) {
 235                 Errors.error(Errors.UNAVAILABLE_SYMBOL, p.position(),
 236                     "Reference to unavailable annotation class " + t.fullName());
 237               }
 238             }
 239           }
 240         }
 241       } else if (cl.isDeprecated()) {
 242         // not hidden, but deprecated
 243         Errors.error(Errors.DEPRECATED, cl.position(), "Class " + cl.qualifiedName()
 244             + " is deprecated");
 245       }
 246     }
 247 
 248     // packages contains all the notStrippable classes mapped by their containing packages
 249     HashMap<PackageInfo, List<ClassInfo>> packages = new HashMap<PackageInfo, List<ClassInfo>>();
 250     final HashSet<Pattern> stubPackageWildcards = extractWildcards(stubPackages);
 251     for (ClassInfo cl : notStrippable) {
 252       if(cl.containingPackage().name().contains("android.car.xxxx.bt")){
 253         System.out.println("=======android.car.xxxx.bt=============continue");
 254         continue;
 255       }
 256       if (!cl.isDocOnly()) {
 257         if (stubSourceOnly && !Files.exists(Paths.get(cl.position().file))) {
 258           continue;
 259         }
 260         if (shouldWriteStub(cl.containingPackage().name(), stubPackages, stubPackageWildcards)) {
 261           // write out the stubs
 262           if (stubsDir != null) {
 263             writeClassFile(stubsDir, notStrippable, cl);
 264           }
 265           // build class list for api file or keep list file
 266           if (apiWriter != null || dexApiWriter != null || keepListWriter != null) {
 267             if (packages.containsKey(cl.containingPackage())) {
 268               packages.get(cl.containingPackage()).add(cl);
 269             } else {
 270               ArrayList<ClassInfo> classes = new ArrayList<ClassInfo>();
 271               classes.add(cl);
 272               packages.put(cl.containingPackage(), classes);
 273             }
 274           }
 275         }
 276       }
 277     }
 278 
 279     if (privateApiWriter != null || privateDexApiWriter != null || removedApiWriter != null
 280             || removedDexApiWriter != null) {
 281       allClassesByPackage = Converter.allClasses().stream()
 282           // Make sure that the files only contains information from the required packages.
 283           .filter(ci -> stubPackages == null
 284               || stubPackages.contains(ci.containingPackage().qualifiedName()))
 285           .collect(Collectors.groupingBy(ClassInfo::containingPackage));
 286     }
 287 
 288     final boolean ignoreShown = Doclava.showUnannotated;
 289 
 290     Predicate<MemberInfo> memberIsNotCloned = (x -> !x.isCloned());
 291 
 292     FilterPredicate apiFilter = new FilterPredicate(new ApiPredicate().setIgnoreShown(ignoreShown));
 293     ApiPredicate apiReference = new ApiPredicate().setIgnoreShown(true);
 294     Predicate<MemberInfo> apiEmit = apiFilter.and(new ElidingPredicate(apiReference));
 295     Predicate<MemberInfo> dexApiEmit = memberIsNotCloned.and(apiFilter);
 296 
 297     Predicate<MemberInfo> privateEmit = memberIsNotCloned.and(apiFilter.negate());
 298     Predicate<MemberInfo> privateReference = (x -> true);
 299 
 300     FilterPredicate removedFilter =
 301         new FilterPredicate(new ApiPredicate().setIgnoreShown(ignoreShown).setMatchRemoved(true));
 302     ApiPredicate removedReference = new ApiPredicate().setIgnoreShown(true).setIgnoreRemoved(true);
 303     Predicate<MemberInfo> removedEmit = removedFilter.and(new ElidingPredicate(removedReference));
 304     Predicate<MemberInfo> removedDexEmit = memberIsNotCloned.and(removedFilter);
 305 
 306     // Write out the current API
 307     if (apiWriter != null) {
 308       writeApi(apiWriter, packages, apiEmit, apiReference);
 309       apiWriter.close();
 310     }
 311 
 312     // Write out the current DEX API
 313     if (dexApiWriter != null) {
 314       writeDexApi(dexApiWriter, packages, dexApiEmit);
 315       dexApiWriter.close();
 316     }
 317 
 318     // Write out the keep list
 319     if (keepListWriter != null) {
 320       writeKeepList(keepListWriter, packages, notStrippable);
 321       keepListWriter.close();
 322     }
 323 
 324     // Write out the private API
 325     if (privateApiWriter != null) {
 326       writeApi(privateApiWriter, allClassesByPackage, privateEmit, privateReference);
 327       privateApiWriter.close();
 328     }
 329 
 330     // Write out the private API
 331     if (privateDexApiWriter != null) {
 332       writeDexApi(privateDexApiWriter, allClassesByPackage, privateEmit);
 333       privateDexApiWriter.close();
 334     }
 335 
 336     // Write out the removed API
 337     if (removedApiWriter != null) {
 338       writeApi(removedApiWriter, allClassesByPackage, removedEmit, removedReference);
 339       removedApiWriter.close();
 340     }
 341 
 342     // Write out the removed DEX API
 343     if (removedDexApiWriter != null) {
 344       writeDexApi(removedDexApiWriter, allClassesByPackage, removedDexEmit);
 345       removedDexApiWriter.close();
 346     }
 347   }
 348 
 349   private static boolean shouldWriteStub(final String packageName,
 350           final HashSet<String> stubPackages, final HashSet<Pattern> stubPackageWildcards) {
 351     if (stubPackages == null) {
 352       // There aren't any stub packages set, write all stubs
 353       return true;
 354     }
 355     if (stubPackages.contains(packageName)) {
 356       // Stub packages contains package, return true
 357       return true;
 358     }
 359     if (stubPackageWildcards != null) {
 360       // Else, we will iterate through the wildcards to see if there's a match
 361       for (Pattern wildcard : stubPackageWildcards) {
 362         if (wildcard.matcher(packageName).matches()) {
 363           return true;
 364         }
 365       }
 366     }
 367     return false;
 368   }
 369 
 370   private static HashSet<Pattern> extractWildcards(HashSet<String> stubPackages) {
 371     HashSet<Pattern> wildcards = null;
 372     if (stubPackages != null) {
 373       for (Iterator<String> i = stubPackages.iterator(); i.hasNext();) {
 374         final String pkg = i.next();
 375         if (pkg.indexOf('*') != -1) {
 376           if (wildcards == null) {
 377             wildcards = new HashSet<Pattern>();
 378           }
 379           // Add the compiled wildcard, replacing * with the regex equivalent
 380           wildcards.add(Pattern.compile(pkg.replace("*", ".*?")));
 381           // And remove the raw wildcard from the packages
 382           i.remove();
 383         }
 384       }
 385     }
 386     return wildcards;
 387   }
 388 
 389   /**
 390    * Find references to hidden classes.
 391    *
 392    * <p>This finds hidden classes that are used by public parts of the API in order to ensure the
 393    * API is self consistent and does not reference classes that are not included in
 394    * the stubs. Any such references cause an error to be reported.
 395    *
 396    * <p>A reference to an imported class is not treated as an error, even though imported classes
 397    * are hidden from the stub generation. That is because imported classes are, by definition,
 398    * excluded from the set of classes for which stubs are required.
 399    *
 400    * @param ti the type information to examine for references to hidden classes.
 401    * @param stubImportPackages the possibly null set of imported package names.
 402    * @return a reference to a hidden class or null if there are none
 403    */
 404   private static ClassInfo findHiddenClasses(TypeInfo ti, HashSet<String> stubImportPackages) {
 405     ClassInfo ci = ti.asClassInfo();
 406     if (ci == null) return null;
 407     if (stubImportPackages != null
 408         && stubImportPackages.contains(ci.containingPackage().qualifiedName())) {
 409       return null;
 410     }
 411     if (ci.isHiddenOrRemoved()) return ci;
 412     if (ti.typeArguments() != null) {
 413       for (TypeInfo tii : ti.typeArguments()) {
 414         // Avoid infinite recursion in the case of Foo<T extends Foo>
 415         if (tii.qualifiedTypeName() != ti.qualifiedTypeName()) {
 416           ClassInfo hiddenClass = findHiddenClasses(tii, stubImportPackages);
 417           if (hiddenClass != null) return hiddenClass;
 418         }
 419       }
 420     }
 421     return null;
 422   }
 423 
 424   public static void cantStripThis(ClassInfo cl, HashSet<ClassInfo> notStrippable, String why,
 425       HashSet<String> stubImportPackages) {
 426 
 427     if (stubImportPackages != null
 428         && stubImportPackages.contains(cl.containingPackage().qualifiedName())) {
 429       // if the package is imported then it does not need stubbing.
 430       return;
 431     }
 432 
 433     if (!notStrippable.add(cl)) {
 434       // slight optimization: if it already contains cl, it already contains
 435       // all of cl's parents
 436       return;
 437     }
 438     cl.setReasonIncluded(why);
 439 
 440     // cant strip annotations
 441     /*
 442      * if (cl.annotations() != null){ for (AnnotationInstanceInfo ai : cl.annotations()){ if
 443      * (ai.type() != null){ cantStripThis(ai.type(), notStrippable, "1:" + cl.qualifiedName()); } }
 444      * }
 445      */
 446     // cant strip any public fields or their generics
 447     if (cl.selfFields() != null) {
 448       for (FieldInfo fInfo : cl.selfFields()) {
 449         if (fInfo.type() != null) {
 450           if (fInfo.type().asClassInfo() != null) {
 451             cantStripThis(fInfo.type().asClassInfo(), notStrippable, "2:" + cl.qualifiedName(),
 452                 stubImportPackages);
 453           }
 454           if (fInfo.type().typeArguments() != null) {
 455             for (TypeInfo tTypeInfo : fInfo.type().typeArguments()) {
 456               if (tTypeInfo.asClassInfo() != null) {
 457                 cantStripThis(tTypeInfo.asClassInfo(), notStrippable, "3:" + cl.qualifiedName(),
 458                     stubImportPackages);
 459               }
 460             }
 461           }
 462         }
 463       }
 464     }
 465     // cant strip any of the type's generics
 466     if (cl.asTypeInfo() != null) {
 467       if (cl.asTypeInfo().typeArguments() != null) {
 468         for (TypeInfo tInfo : cl.asTypeInfo().typeArguments()) {
 469           if (tInfo.asClassInfo() != null) {
 470             cantStripThis(tInfo.asClassInfo(), notStrippable, "4:" + cl.qualifiedName(),
 471                 stubImportPackages);
 472           }
 473         }
 474       }
 475     }
 476     // cant strip any of the annotation elements
 477     // cantStripThis(cl.annotationElements(), notStrippable);
 478     // take care of methods
 479     cantStripThis(cl.allSelfMethods(), notStrippable, stubImportPackages);
 480     cantStripThis(cl.allConstructors(), notStrippable, stubImportPackages);
 481     // blow the outer class open if this is an inner class
 482     if (cl.containingClass() != null) {
 483       cantStripThis(cl.containingClass(), notStrippable, "5:" + cl.qualifiedName(),
 484           stubImportPackages);
 485     }
 486     // blow open super class and interfaces
 487     ClassInfo supr = cl.realSuperclass();
 488     if (supr != null) {
 489       if (supr.isHiddenOrRemoved()) {
 490         // cl is a public class declared as extending a hidden superclass.
 491         // this is not a desired practice but it's happened, so we deal
 492         // with it by finding the first super class which passes checklevel for purposes of
 493         // generating the doc & stub information, and proceeding normally.
 494         ClassInfo publicSuper = cl.superclass();
 495         cl.init(cl.asTypeInfo(), cl.realInterfaces(), cl.realInterfaceTypes(), cl.innerClasses(),
 496             cl.allConstructors(), cl.allSelfMethods(), cl.annotationElements(), cl.allSelfFields(),
 497             cl.enumConstants(), cl.containingPackage(), cl.containingClass(),
 498             publicSuper, publicSuper.asTypeInfo(), cl.annotations());
 499         Errors.error(Errors.HIDDEN_SUPERCLASS, cl.position(), "Public class " + cl.qualifiedName()
 500             + " stripped of unavailable superclass " + supr.qualifiedName());
 501       } else {
 502         cantStripThis(supr, notStrippable, "6:" + cl.realSuperclass().name() + cl.qualifiedName(),
 503             stubImportPackages);
 504         if (supr.isPrivate()) {
 505           Errors.error(Errors.PRIVATE_SUPERCLASS, cl.position(), "Public class "
 506               + cl.qualifiedName() + " extends private class " + supr.qualifiedName());
 507         }
 508       }
 509     }
 510   }
 511 
 512   private static void cantStripThis(ArrayList<MethodInfo> mInfos, HashSet<ClassInfo> notStrippable,
 513       HashSet<String> stubImportPackages) {
 514     // for each method, blow open the parameters, throws and return types. also blow open their
 515     // generics
 516     if (mInfos != null) {
 517       for (MethodInfo mInfo : mInfos) {
 518         if (mInfo.getTypeParameters() != null) {
 519           for (TypeInfo tInfo : mInfo.getTypeParameters()) {
 520             if (tInfo.asClassInfo() != null) {
 521               cantStripThis(tInfo.asClassInfo(), notStrippable, "8:"
 522                   + mInfo.realContainingClass().qualifiedName() + ":" + mInfo.name(),
 523                   stubImportPackages);
 524             }
 525           }
 526         }
 527         if (mInfo.parameters() != null) {
 528           for (ParameterInfo pInfo : mInfo.parameters()) {
 529             if (pInfo.type() != null && pInfo.type().asClassInfo() != null) {
 530               cantStripThis(pInfo.type().asClassInfo(), notStrippable, "9:"
 531                   + mInfo.realContainingClass().qualifiedName() + ":" + mInfo.name(),
 532                   stubImportPackages);
 533               if (pInfo.type().typeArguments() != null) {
 534                 for (TypeInfo tInfoType : pInfo.type().typeArguments()) {
 535                   if (tInfoType.asClassInfo() != null) {
 536                     ClassInfo tcl = tInfoType.asClassInfo();
 537                     if (tcl.isHiddenOrRemoved()) {
 538                       Errors
 539                           .error(Errors.UNAVAILABLE_SYMBOL, mInfo.position(),
 540                               "Parameter of hidden type " + tInfoType.fullName() + " in "
 541                                   + mInfo.containingClass().qualifiedName() + '.' + mInfo.name()
 542                                   + "()");
 543                     } else {
 544                       cantStripThis(tcl, notStrippable, "10:"
 545                           + mInfo.realContainingClass().qualifiedName() + ":" + mInfo.name(),
 546                           stubImportPackages);
 547                     }
 548                   }
 549                 }
 550               }
 551             }
 552           }
 553         }
 554         for (ClassInfo thrown : mInfo.thrownExceptions()) {
 555           cantStripThis(thrown, notStrippable, "11:" + mInfo.realContainingClass().qualifiedName()
 556               + ":" + mInfo.name(), stubImportPackages);
 557         }
 558         if (mInfo.returnType() != null && mInfo.returnType().asClassInfo() != null) {
 559           cantStripThis(mInfo.returnType().asClassInfo(), notStrippable, "12:"
 560               + mInfo.realContainingClass().qualifiedName() + ":" + mInfo.name(),
 561               stubImportPackages);
 562           if (mInfo.returnType().typeArguments() != null) {
 563             for (TypeInfo tyInfo : mInfo.returnType().typeArguments()) {
 564               if (tyInfo.asClassInfo() != null) {
 565                 cantStripThis(tyInfo.asClassInfo(), notStrippable, "13:"
 566                     + mInfo.realContainingClass().qualifiedName() + ":" + mInfo.name(),
 567                     stubImportPackages);
 568               }
 569             }
 570           }
 571         }
 572       }
 573     }
 574   }
 575 
 576   static String javaFileName(ClassInfo cl) {
 577     String dir = "";
 578     PackageInfo pkg = cl.containingPackage();
 579     if (pkg != null) {
 580       dir = pkg.name();
 581       dir = dir.replace('.', '/') + '/';
 582     }
 583     return dir + cl.name() + ".java";
 584   }
 585 
 586   static void writeClassFile(String stubsDir, HashSet<ClassInfo> notStrippable, ClassInfo cl) {
 587     // inner classes are written by their containing class
 588     if (cl.containingClass() != null) {
 589       return;
 590     }
 591 
 592     // Work around the bogus "Array" class we invent for
 593     // Arrays.copyOf's Class<? extends T[]> newType parameter. (http://b/2715505)
 594     if (cl.containingPackage() != null
 595         && cl.containingPackage().name().equals(PackageInfo.DEFAULT_PACKAGE)) {
 596       return;
 597     }
 598 
 599     String filename = stubsDir + '/' + javaFileName(cl);
 600     File file = new File(filename);
 601     ClearPage.ensureDirectory(file);
 602 
 603     PrintStream stream = null;
 604     try {
 605       stream = new PrintStream(new BufferedOutputStream(new FileOutputStream(file)));
 606       writeClassFile(stream, notStrippable, cl);
 607     } catch (FileNotFoundException e) {
 608       System.err.println("error writing file: " + filename);
 609     } finally {
 610       if (stream != null) {
 611         stream.close();
 612       }
 613     }
 614   }
 615 
 616   static void writeClassFile(PrintStream stream, HashSet<ClassInfo> notStrippable, ClassInfo cl) {
 617     PackageInfo pkg = cl.containingPackage();
 618     if (cl.containingClass() == null) {
 619         stream.print(parseLicenseHeader(cl.position()));
 620     }
 621     if (pkg != null) {
 622       stream.println("package " + pkg.name() + ";");
 623     }
 624     writeClass(stream, notStrippable, cl);
 625   }
 626 
 627   private static String parseLicenseHeader(/* @Nonnull */ SourcePositionInfo positionInfo) {
 628     if (positionInfo == null) {
 629       throw new NullPointerException("positionInfo == null");
 630     }
 631 
 632     try {
 633       final File sourceFile = new File(positionInfo.file);
 634       if (!sourceFile.exists()) {
 635         throw new IllegalArgumentException("Unable to find " + sourceFile +
 636                 ". This is usually because doclava has been asked to generate stubs for a file " +
 637                 "that isn't present in the list of input source files but exists in the input " +
 638                 "classpath.");
 639       }
 640       return parseLicenseHeader(new FileInputStream(sourceFile));
 641     } catch (IOException ioe) {
 642       throw new RuntimeException("Unable to parse license header for: " + positionInfo.file, ioe);
 643     }
 644   }
 645 
 646   /* @VisibleForTesting */
 647   static String parseLicenseHeader(InputStream input) throws IOException {
 648     StringBuilder builder = new StringBuilder(8192);
 649     try (Scanner scanner  = new Scanner(
 650           new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8)))) {
 651       String line;
 652       while (scanner.hasNextLine()) {
 653         line = scanner.nextLine().trim();
 654         // Use an extremely simple strategy for parsing license headers : assume that
 655         // all file content before the first "package " or "import " directive is a license
 656         // header. In some cases this might contain more than just the license header, but we
 657         // don't care.
 658         if (line.startsWith("package ") || line.startsWith("import ")) {
 659           break;
 660         }
 661         builder.append(line);
 662         builder.append("\n");
 663       }
 664 
 665       // We've reached the end of the file without reaching any package or import
 666       // directives.
 667       if (!scanner.hasNextLine()) {
 668         throw new IOException("Unable to parse license header");
 669       }
 670     }
 671 
 672     return builder.toString();
 673   }
 674 
 675   static void writeClass(PrintStream stream, HashSet<ClassInfo> notStrippable, ClassInfo cl) {
 676     writeAnnotations(stream, cl.annotations(), cl.isDeprecated());
 677 
 678     stream.print(cl.scope() + " ");
 679     if (cl.isAbstract() && !cl.isAnnotation() && !cl.isInterface()) {
 680       stream.print("abstract ");
 681     }
 682     if (cl.isStatic()) {
 683       stream.print("static ");
 684     }
 685     if (cl.isFinal() && !cl.isEnum()) {
 686       stream.print("final ");
 687     }
 688     if (false) {
 689       stream.print("strictfp ");
 690     }
 691 
 692     HashSet<String> classDeclTypeVars = new HashSet();
 693     String leafName = cl.asTypeInfo().fullName(classDeclTypeVars);
 694     int bracket = leafName.indexOf('<');
 695     if (bracket < 0) bracket = leafName.length() - 1;
 696     int period = leafName.lastIndexOf('.', bracket);
 697     if (period < 0) period = -1;
 698     leafName = leafName.substring(period + 1);
 699 
 700     String kind = cl.kind();
 701     stream.println(kind + " " + leafName);
 702 
 703     TypeInfo base = cl.superclassType();
 704 
 705     if (!"enum".equals(kind)) {
 706       if (base != null && !"java.lang.Object".equals(base.qualifiedTypeName())) {
 707         stream.println("  extends " + base.fullName(classDeclTypeVars));
 708       }
 709     }
 710 
 711     List<TypeInfo> usedInterfaces = new ArrayList<TypeInfo>();
 712     for (TypeInfo iface : cl.realInterfaceTypes()) {
 713       if (notStrippable.contains(iface.asClassInfo()) && !iface.asClassInfo().isDocOnly()) {
 714         usedInterfaces.add(iface);
 715       }
 716     }
 717     if (usedInterfaces.size() > 0 && !cl.isAnnotation()) {
 718       // can java annotations extend other ones?
 719       if (cl.isInterface() || cl.isAnnotation()) {
 720         stream.print("  extends ");
 721       } else {
 722         stream.print("  implements ");
 723       }
 724       String comma = "";
 725       for (TypeInfo iface : usedInterfaces) {
 726         stream.print(comma + iface.fullName(classDeclTypeVars));
 727         comma = ", ";
 728       }
 729       stream.println();
 730     }
 731 
 732     stream.println("{");
 733 
 734     ArrayList<FieldInfo> enumConstants = cl.enumConstants();
 735     int N = enumConstants.size();
 736     int i = 0;
 737     for (FieldInfo field : enumConstants) {
 738       writeAnnotations(stream, field.annotations(), field.isDeprecated());
 739       if (!field.constantLiteralValue().equals("null")) {
 740         stream.println(field.name() + "(" + field.constantLiteralValue()
 741             + (i == N - 1 ? ");" : "),"));
 742       } else {
 743         stream.println(field.name() + "(" + (i == N - 1 ? ");" : "),"));
 744       }
 745       i++;
 746     }
 747 
 748     for (ClassInfo inner : cl.getRealInnerClasses()) {
 749       if (notStrippable.contains(inner) && !inner.isDocOnly()) {
 750         writeClass(stream, notStrippable, inner);
 751       }
 752     }
 753 
 754 
 755     for (MethodInfo method : cl.constructors()) {
 756       if (!method.isDocOnly()) {
 757         writeMethod(stream, method, true);
 758       }
 759     }
 760 
 761     boolean fieldNeedsInitialization = false;
 762     boolean staticFieldNeedsInitialization = false;
 763     for (FieldInfo field : cl.selfFields()) {
 764       if (!field.isDocOnly()) {
 765         if (!field.isStatic() && field.isFinal() && !fieldIsInitialized(field)) {
 766           fieldNeedsInitialization = true;
 767         }
 768         if (field.isStatic() && field.isFinal() && !fieldIsInitialized(field)) {
 769           staticFieldNeedsInitialization = true;
 770         }
 771       }
 772     }
 773 
 774     // The compiler includes a default public constructor that calls the super classes
 775     // default constructor in the case where there are no written constructors.
 776     // So, if we hide all the constructors, java may put in a constructor
 777     // that calls a nonexistent super class constructor. So, if there are no constructors,
 778     // and the super class doesn't have a default constructor, write in a private constructor
 779     // that works. TODO -- we generate this as protected, but we really should generate
 780     // it as private unless it also exists in the real code.
 781     if ((cl.constructors().isEmpty() && (!cl.getNonWrittenConstructors().isEmpty() ||
 782         fieldNeedsInitialization)) && !cl.isAnnotation() && !cl.isInterface() && !cl.isEnum()) {
 783       // Errors.error(Errors.HIDDEN_CONSTRUCTOR,
 784       // cl.position(), "No constructors " +
 785       // "found and superclass has no parameterless constructor.  A constructor " +
 786       // "that calls an appropriate superclass constructor " +
 787       // "was automatically written to stubs.\n");
 788       stream.println(cl.leafName() + "() { " + superCtorCall(cl, null) + "throw new"
 789           + " RuntimeException(\"Stub!\"); }");
 790     }
 791 
 792     for (MethodInfo method : cl.allSelfMethods()) {
 793       if (cl.isEnum()) {
 794         if (("values".equals(method.name()) && "()".equals(method.signature())) ||
 795             ("valueOf".equals(method.name()) &&
 796             "(java.lang.String)".equals(method.signature()))) {
 797           // skip these two methods on enums, because they're synthetic,
 798           // although for some reason javadoc doesn't mark them as synthetic,
 799           // maybe because they still want them documented
 800           continue;
 801         }
 802       }
 803       if (!method.isDocOnly()) {
 804         writeMethod(stream, method, false);
 805       }
 806     }
 807     // Write all methods that are hidden or removed, but override abstract methods or interface methods.
 808     // These can't be hidden.
 809     List<MethodInfo> hiddenAndRemovedMethods = cl.getHiddenMethods();
 810     hiddenAndRemovedMethods.addAll(cl.getRemovedMethods());
 811     for (MethodInfo method : hiddenAndRemovedMethods) {
 812       MethodInfo overriddenMethod =
 813           method.findRealOverriddenMethod(method.name(), method.signature(), notStrippable);
 814       ClassInfo classContainingMethod =
 815           method.findRealOverriddenClass(method.name(), method.signature());
 816       if (overriddenMethod != null && !overriddenMethod.isHiddenOrRemoved() &&
 817           !overriddenMethod.isDocOnly() &&
 818           (overriddenMethod.isAbstract() || overriddenMethod.containingClass().isInterface())) {
 819         method.setReason("1:" + classContainingMethod.qualifiedName());
 820         cl.addMethod(method);
 821         writeMethod(stream, method, false);
 822       }
 823     }
 824 
 825     for (MethodInfo element : cl.annotationElements()) {
 826       if (!element.isDocOnly()) {
 827         writeAnnotationElement(stream, element);
 828       }
 829     }
 830 
 831     for (FieldInfo field : cl.selfFields()) {
 832       if (!field.isDocOnly()) {
 833         writeField(stream, field);
 834       }
 835     }
 836 
 837     if (staticFieldNeedsInitialization) {
 838       stream.print("static { ");
 839       for (FieldInfo field : cl.selfFields()) {
 840         if (!field.isDocOnly() && field.isStatic() && field.isFinal() && !fieldIsInitialized(field)
 841             && field.constantValue() == null) {
 842           stream.print(field.name() + " = " + field.type().defaultValue() + "; ");
 843         }
 844       }
 845       stream.println("}");
 846     }
 847 
 848     stream.println("}");
 849   }
 850 
 851   static void writeMethod(PrintStream stream, MethodInfo method, boolean isConstructor) {
 852     String comma;
 853 
 854     writeAnnotations(stream, method.annotations(), method.isDeprecated());
 855 
 856     if (method.isDefault()) {
 857       stream.print("default ");
 858     }
 859     stream.print(method.scope() + " ");
 860     if (method.isStatic()) {
 861       stream.print("static ");
 862     }
 863     if (method.isFinal()) {
 864       stream.print("final ");
 865     }
 866     if (method.isAbstract()) {
 867       stream.print("abstract ");
 868     }
 869     if (method.isSynchronized()) {
 870       stream.print("synchronized ");
 871     }
 872     if (method.isNative()) {
 873       stream.print("native ");
 874     }
 875     if (false /* method.isStictFP() */) {
 876       stream.print("strictfp ");
 877     }
 878 
 879     stream.print(method.typeArgumentsName(new HashSet()) + " ");
 880 
 881     if (!isConstructor) {
 882       stream.print(method.returnType().fullName(method.typeVariables()) + " ");
 883     }
 884     String n = method.name();
 885     int pos = n.lastIndexOf('.');
 886     if (pos >= 0) {
 887       n = n.substring(pos + 1);
 888     }
 889     stream.print(n + "(");
 890     comma = "";
 891     int count = 1;
 892     int size = method.parameters().size();
 893     for (ParameterInfo param : method.parameters()) {
 894       stream.print(comma);
 895       writeAnnotations(stream, param.annotations(), false);
 896       stream.print(fullParameterTypeName(method, param.type(), count == size) + " "
 897           + param.name());
 898       comma = ", ";
 899       count++;
 900     }
 901     stream.print(")");
 902 
 903     comma = "";
 904     if (method.thrownExceptions().size() > 0) {
 905       stream.print(" throws ");
 906       for (ClassInfo thrown : method.thrownExceptions()) {
 907         stream.print(comma + thrown.qualifiedName());
 908         comma = ", ";
 909       }
 910     }
 911     if (method.isAbstract() || method.isNative() || (method.containingClass().isInterface() && (!method.isDefault() && !method.isStatic()))) {
 912       stream.println(";");
 913     } else {
 914       stream.print(" { ");
 915       if (isConstructor) {
 916         stream.print(superCtorCall(method.containingClass(), method.thrownExceptions()));
 917       }
 918       stream.println("throw new RuntimeException(\"Stub!\"); }");
 919     }
 920   }
 921 
 922   static void writeField(PrintStream stream, FieldInfo field) {
 923     writeAnnotations(stream, field.annotations(), field.isDeprecated());
 924 
 925     stream.print(field.scope() + " ");
 926     if (field.isStatic()) {
 927       stream.print("static ");
 928     }
 929     if (field.isFinal()) {
 930       stream.print("final ");
 931     }
 932     if (field.isTransient()) {
 933       stream.print("transient ");
 934     }
 935     if (field.isVolatile()) {
 936       stream.print("volatile ");
 937     }
 938 
 939     stream.print(field.type().fullName());
 940     stream.print(" ");
 941     stream.print(field.name());
 942 
 943     if (fieldIsInitialized(field)) {
 944       stream.print(" = " + field.constantLiteralValue());
 945     }
 946 
 947     stream.println(";");
 948   }
 949 
 950   static boolean fieldIsInitialized(FieldInfo field) {
 951     return (field.isFinal() && field.constantValue() != null)
 952         || !field.type().dimension().equals("") || field.containingClass().isInterface();
 953   }
 954 
 955   static boolean canCallMethod(ClassInfo from, MethodInfo m) {
 956     if (m.isPublic() || m.isProtected()) {
 957       return true;
 958     }
 959     if (m.isPackagePrivate()) {
 960       String fromPkg = from.containingPackage().name();
 961       String pkg = m.containingClass().containingPackage().name();
 962       if (fromPkg.equals(pkg)) {
 963         return true;
 964       }
 965     }
 966     return false;
 967   }
 968 
 969   // call a constructor, any constructor on this class's superclass.
 970   static String superCtorCall(ClassInfo cl, ArrayList<ClassInfo> thrownExceptions) {
 971     ClassInfo base = cl.realSuperclass();
 972     if (base == null) {
 973       return "";
 974     }
 975     HashSet<String> exceptionNames = new HashSet<String>();
 976     if (thrownExceptions != null) {
 977       for (ClassInfo thrown : thrownExceptions) {
 978         exceptionNames.add(thrown.name());
 979       }
 980     }
 981     ArrayList<MethodInfo> ctors = base.constructors();
 982     MethodInfo ctor = null;
 983     // bad exception indicates that the exceptions thrown by the super constructor
 984     // are incompatible with the constructor we're using for the sub class.
 985     Boolean badException = false;
 986     for (MethodInfo m : ctors) {
 987       if (canCallMethod(cl, m)) {
 988         if (m.thrownExceptions() != null) {
 989           for (ClassInfo thrown : m.thrownExceptions()) {
 990             if (thrownExceptions != null && !exceptionNames.contains(thrown.name())) {
 991               badException = true;
 992             }
 993           }
 994         }
 995         if (badException) {
 996           badException = false;
 997           continue;
 998         }
 999         // if it has no args, we're done
1000         if (m.parameters().isEmpty()) {
1001           return "";
1002         }
1003         ctor = m;
1004       }
1005     }
1006     if (ctor != null) {
1007       String result = "";
1008       result += "super(";
1009       ArrayList<ParameterInfo> params = ctor.parameters();
1010       for (ParameterInfo param : params) {
1011         TypeInfo t = param.type();
1012         if (t.isPrimitive() && t.dimension().equals("")) {
1013           String n = t.simpleTypeName();
1014           if (("byte".equals(n) || "short".equals(n) || "int".equals(n) || "long".equals(n)
1015               || "float".equals(n) || "double".equals(n))
1016               && t.dimension().equals("")) {
1017             result += "0";
1018           } else if ("char".equals(n)) {
1019             result += "'\\0'";
1020           } else if ("boolean".equals(n)) {
1021             result += "false";
1022           } else {
1023             result += "<<unknown-" + n + ">>";
1024           }
1025         } else {
1026           // put null in each super class method. Cast null to the correct type
1027           // to avoid collisions with other constructors. If the type is generic
1028           // don't cast it
1029           result +=
1030               (!t.isTypeVariable() ? "(" + t.qualifiedTypeName() + t.dimension() + ")" : "")
1031                   + "null";
1032         }
1033         if (param != params.get(params.size()-1)) {
1034           result += ",";
1035         }
1036       }
1037       result += "); ";
1038       return result;
1039     } else {
1040       return "";
1041     }
1042   }
1043 
1044     /**
1045      * Write out the given list of annotations. If the {@code isDeprecated}
1046      * flag is true also write out a {@code @Deprecated} annotation if it did not
1047      * already appear in the list of annotations. (This covers APIs that mention
1048      * {@code @deprecated} in their documentation but fail to add
1049      * {@code @Deprecated} as an annotation.
1050      * <p>
1051      * {@code @Override} annotations are deliberately skipped.
1052      */
1053   static void writeAnnotations(PrintStream stream, List<AnnotationInstanceInfo> annotations,
1054           boolean isDeprecated) {
1055     assert annotations != null;
1056     for (AnnotationInstanceInfo ann : annotations) {
1057       // Skip @Override annotations: the stubs do not need it and in some cases it leads
1058       // to compilation errors with the way the stubs are generated
1059       if (ann.type() != null && ann.type().qualifiedName().equals("java.lang.Override")) {
1060         continue;
1061       }
1062       if (!ann.type().isHiddenOrRemoved()) {
1063         stream.println(ann.toString());
1064         if (isDeprecated && ann.type() != null
1065             && ann.type().qualifiedName().equals("java.lang.Deprecated")) {
1066           isDeprecated = false; // Prevent duplicate annotations
1067         }
1068       }
1069     }
1070     if (isDeprecated) {
1071       stream.println("@Deprecated");
1072     }
1073   }
1074 
1075   static void writeAnnotationElement(PrintStream stream, MethodInfo ann) {
1076     stream.print(ann.returnType().fullName());
1077     stream.print(" ");
1078     stream.print(ann.name());
1079     stream.print("()");
1080     AnnotationValueInfo def = ann.defaultAnnotationElementValue();
1081     if (def != null) {
1082       stream.print(" default ");
1083       stream.print(def.valueString());
1084     }
1085     stream.println(";");
1086   }
1087 
1088   public static void writeXml(PrintStream xmlWriter, Collection<PackageInfo> pkgs, boolean strip) {
1089     if (strip) {
1090       Stubs.writeXml(xmlWriter, pkgs);
1091     } else {
1092       Stubs.writeXml(xmlWriter, pkgs, c -> true);
1093     }
1094   }
1095 
1096   public static void writeXml(PrintStream xmlWriter, Collection<PackageInfo> pkgs,
1097       Predicate<ClassInfo> notStrippable) {
1098 
1099     final PackageInfo[] packages = pkgs.toArray(new PackageInfo[pkgs.size()]);
1100     Arrays.sort(packages, PackageInfo.comparator);
1101 
1102     xmlWriter.println("<api>");
1103     for (PackageInfo pkg: packages) {
1104       writePackageXML(xmlWriter, pkg, pkg.allClasses().values(), notStrippable);
1105     }
1106     xmlWriter.println("</api>");
1107   }
1108 
1109   public static void writeXml(PrintStream xmlWriter, Collection<PackageInfo> pkgs) {
1110     HashSet<ClassInfo> allClasses = new HashSet<>();
1111     for (PackageInfo pkg: pkgs) {
1112       allClasses.addAll(pkg.allClasses().values());
1113     }
1114     Predicate<ClassInfo> notStrippable = allClasses::contains;
1115     writeXml(xmlWriter, pkgs, notStrippable);
1116   }
1117 
1118   static void writePackageXML(PrintStream xmlWriter, PackageInfo pack,
1119       Collection<ClassInfo> classList, Predicate<ClassInfo> notStrippable) {
1120     ClassInfo[] classes = classList.toArray(new ClassInfo[classList.size()]);
1121     Arrays.sort(classes, ClassInfo.comparator);
1122     // Work around the bogus "Array" class we invent for
1123     // Arrays.copyOf's Class<? extends T[]> newType parameter. (http://b/2715505)
1124     if (pack.name().equals(PackageInfo.DEFAULT_PACKAGE)) {
1125       return;
1126     }
1127     xmlWriter.println("<package name=\"" + pack.name() + "\"\n"
1128     // + " source=\"" + pack.position() + "\"\n"
1129         + ">");
1130     for (ClassInfo cl : classes) {
1131       writeClassXML(xmlWriter, cl, notStrippable);
1132     }
1133     xmlWriter.println("</package>");
1134 
1135 
1136   }
1137 
1138   static void writeClassXML(PrintStream xmlWriter, ClassInfo cl, Predicate<ClassInfo> notStrippable) {
1139     String scope = cl.scope();
1140     String deprecatedString = "";
1141     String declString = (cl.isInterface()) ? "interface" : "class";
1142     if (cl.isDeprecated()) {
1143       deprecatedString = "deprecated";
1144     } else {
1145       deprecatedString = "not deprecated";
1146     }
1147     xmlWriter.println("<" + declString + " name=\"" + cl.name() + "\"");
1148     if (!cl.isInterface() && !cl.qualifiedName().equals("java.lang.Object")) {
1149       xmlWriter.println(" extends=\""
1150           + ((cl.realSuperclass() == null) ? "java.lang.Object" : cl.realSuperclass()
1151               .qualifiedName()) + "\"");
1152     }
1153     xmlWriter.println(" abstract=\"" + cl.isAbstract() + "\"\n" + " static=\"" + cl.isStatic()
1154         + "\"\n" + " final=\"" + cl.isFinal() + "\"\n" + " deprecated=\"" + deprecatedString
1155         + "\"\n" + " visibility=\"" + scope + "\"\n"
1156         // + " source=\"" + cl.position() + "\"\n"
1157         + ">");
1158 
1159     ArrayList<ClassInfo> interfaces = cl.realInterfaces();
1160     Collections.sort(interfaces, ClassInfo.comparator);
1161     for (ClassInfo iface : interfaces) {
1162       if (notStrippable.test(iface)) {
1163         xmlWriter.println("<implements name=\"" + iface.qualifiedName() + "\">");
1164         xmlWriter.println("</implements>");
1165       }
1166     }
1167 
1168     ArrayList<MethodInfo> constructors = cl.constructors();
1169     Collections.sort(constructors, MethodInfo.comparator);
1170     for (MethodInfo mi : constructors) {
1171       writeConstructorXML(xmlWriter, mi);
1172     }
1173 
1174     ArrayList<MethodInfo> methods = cl.allSelfMethods();
1175     Collections.sort(methods, MethodInfo.comparator);
1176     for (MethodInfo mi : methods) {
1177       writeMethodXML(xmlWriter, mi);
1178     }
1179 
1180     ArrayList<FieldInfo> fields = cl.selfFields();
1181     Collections.sort(fields, FieldInfo.comparator);
1182     for (FieldInfo fi : fields) {
1183       writeFieldXML(xmlWriter, fi);
1184     }
1185     xmlWriter.println("</" + declString + ">");
1186 
1187   }
1188 
1189   static void writeMethodXML(PrintStream xmlWriter, MethodInfo mi) {
1190     String scope = mi.scope();
1191 
1192     String deprecatedString = "";
1193     if (mi.isDeprecated()) {
1194       deprecatedString = "deprecated";
1195     } else {
1196       deprecatedString = "not deprecated";
1197     }
1198     xmlWriter.println("<method name=\""
1199         + mi.name()
1200         + "\"\n"
1201         + ((mi.returnType() != null) ? " return=\""
1202             + makeXMLcompliant(fullParameterTypeName(mi, mi.returnType(), false)) + "\"\n" : "")
1203         + " abstract=\"" + mi.isAbstract() + "\"\n" + " native=\"" + mi.isNative() + "\"\n"
1204         + " synchronized=\"" + mi.isSynchronized() + "\"\n" + " static=\"" + mi.isStatic() + "\"\n"
1205         + " final=\"" + mi.isFinal() + "\"\n" + " deprecated=\"" + deprecatedString + "\"\n"
1206         + " visibility=\"" + scope + "\"\n"
1207         // + " source=\"" + mi.position() + "\"\n"
1208         + ">");
1209 
1210     // write parameters in declaration order
1211     int numParameters = mi.parameters().size();
1212     int count = 0;
1213     for (ParameterInfo pi : mi.parameters()) {
1214       count++;
1215       writeParameterXML(xmlWriter, mi, pi, count == numParameters);
1216     }
1217 
1218     // but write exceptions in canonicalized order
1219     ArrayList<ClassInfo> exceptions = mi.thrownExceptions();
1220     Collections.sort(exceptions, ClassInfo.comparator);
1221     for (ClassInfo pi : exceptions) {
1222       xmlWriter.println("<exception name=\"" + pi.name() + "\" type=\"" + pi.qualifiedName()
1223           + "\">");
1224       xmlWriter.println("</exception>");
1225     }
1226     xmlWriter.println("</method>");
1227   }
1228 
1229   static void writeConstructorXML(PrintStream xmlWriter, MethodInfo mi) {
1230     String scope = mi.scope();
1231     String deprecatedString = "";
1232     if (mi.isDeprecated()) {
1233       deprecatedString = "deprecated";
1234     } else {
1235       deprecatedString = "not deprecated";
1236     }
1237     xmlWriter.println("<constructor name=\"" + mi.name() + "\"\n" + " type=\""
1238         + mi.containingClass().qualifiedName() + "\"\n" + " static=\"" + mi.isStatic() + "\"\n"
1239         + " final=\"" + mi.isFinal() + "\"\n" + " deprecated=\"" + deprecatedString + "\"\n"
1240         + " visibility=\"" + scope + "\"\n"
1241         // + " source=\"" + mi.position() + "\"\n"
1242         + ">");
1243 
1244     int numParameters = mi.parameters().size();
1245     int count = 0;
1246     for (ParameterInfo pi : mi.parameters()) {
1247       count++;
1248       writeParameterXML(xmlWriter, mi, pi, count == numParameters);
1249     }
1250 
1251     ArrayList<ClassInfo> exceptions = mi.thrownExceptions();
1252     Collections.sort(exceptions, ClassInfo.comparator);
1253     for (ClassInfo pi : exceptions) {
1254       xmlWriter.println("<exception name=\"" + pi.name() + "\" type=\"" + pi.qualifiedName()
1255           + "\">");
1256       xmlWriter.println("</exception>");
1257     }
1258     xmlWriter.println("</constructor>");
1259   }
1260 
1261   static void writeParameterXML(PrintStream xmlWriter, MethodInfo method, ParameterInfo pi,
1262       boolean isLast) {
1263     xmlWriter.println("<parameter name=\"" + pi.name() + "\" type=\""
1264         + makeXMLcompliant(fullParameterTypeName(method, pi.type(), isLast)) + "\">");
1265     xmlWriter.println("</parameter>");
1266   }
1267 
1268   static void writeFieldXML(PrintStream xmlWriter, FieldInfo fi) {
1269     String scope = fi.scope();
1270     String deprecatedString = "";
1271     if (fi.isDeprecated()) {
1272       deprecatedString = "deprecated";
1273     } else {
1274       deprecatedString = "not deprecated";
1275     }
1276     // need to make sure value is valid XML
1277     String value = makeXMLcompliant(fi.constantLiteralValue());
1278 
1279     String fullTypeName = makeXMLcompliant(fi.type().fullName());
1280 
1281     xmlWriter.println("<field name=\"" + fi.name() + "\"\n" + " type=\"" + fullTypeName + "\"\n"
1282         + " transient=\"" + fi.isTransient() + "\"\n" + " volatile=\"" + fi.isVolatile() + "\"\n"
1283         + (fieldIsInitialized(fi) ? " value=\"" + value + "\"\n" : "") + " static=\""
1284         + fi.isStatic() + "\"\n" + " final=\"" + fi.isFinal() + "\"\n" + " deprecated=\""
1285         + deprecatedString + "\"\n" + " visibility=\"" + scope + "\"\n"
1286         // + " source=\"" + fi.position() + "\"\n"
1287         + ">");
1288     xmlWriter.println("</field>");
1289   }
1290 
1291   static String makeXMLcompliant(String s) {
1292     String returnString = "";
1293     returnString = s.replaceAll("&", "&");
1294     returnString = returnString.replaceAll("<", "<");
1295     returnString = returnString.replaceAll(">", ">");
1296     returnString = returnString.replaceAll("\"", """);
1297     returnString = returnString.replaceAll("'", "&apos;");
1298     return returnString;
1299   }
1300 
1301   /**
1302    * Predicate that decides if the given member should be considered part of an
1303    * API surface area. To make the most accurate decision, it searches for
1304    * signals on the member, all containing classes, and all containing packages.
1305    */
1306   public static class ApiPredicate implements Predicate<MemberInfo> {
1307     public boolean ignoreShown;
1308     public boolean ignoreRemoved;
1309     public boolean matchRemoved;
1310 
1311     /**
1312      * Set if the value of {@link MemberInfo#hasShowAnnotation()} should be
1313      * ignored. That is, this predicate will assume that all encountered members
1314      * match the "shown" requirement.
1315      * <p>
1316      * This is typically useful when generating "current.txt", when no
1317      * {@link Doclava#showAnnotations} have been defined.
1318      */
1319     public ApiPredicate setIgnoreShown(boolean ignoreShown) {
1320       this.ignoreShown = ignoreShown;
1321       return this;
1322     }
1323 
1324     /**
1325      * Set if the value of {@link MemberInfo#isRemoved()} should be ignored.
1326      * That is, this predicate will assume that all encountered members match
1327      * the "removed" requirement.
1328      * <p>
1329      * This is typically useful when generating "removed.txt", when it's okay to
1330      * reference both current and removed APIs.
1331      */
1332     public ApiPredicate setIgnoreRemoved(boolean ignoreRemoved) {
1333       this.ignoreRemoved = ignoreRemoved;
1334       return this;
1335     }
1336 
1337     /**
1338      * Set what the value of {@link MemberInfo#isRemoved()} must be equal to in
1339      * order for a member to match.
1340      * <p>
1341      * This is typically useful when generating "removed.txt", when you only
1342      * want to match members that have actually been removed.
1343      */
1344     public ApiPredicate setMatchRemoved(boolean matchRemoved) {
1345       this.matchRemoved = matchRemoved;
1346       return this;
1347     }
1348 
1349     private static PackageInfo containingPackage(PackageInfo pkg) {
1350       String name = pkg.name();
1351       final int lastDot = name.lastIndexOf('.');
1352       if (lastDot == -1) {
1353         return null;
1354       } else {
1355         name = name.substring(0, lastDot);
1356         return Converter.obtainPackage(name);
1357       }
1358     }
1359 
1360     @Override
1361     public boolean test(MemberInfo member) {
1362       boolean visible = member.isPublic() || member.isProtected();
1363       boolean hasShowAnnotation = member.hasShowAnnotation();
1364       boolean hidden = member.isHidden();
1365       boolean docOnly = member.isDocOnly();
1366       boolean removed = member.isRemoved();
1367 
1368       ClassInfo clazz = member.containingClass();
1369       if (clazz != null) {
1370         PackageInfo pkg = clazz.containingPackage();
1371         while (pkg != null) {
1372           hidden |= pkg.isHidden();
1373           docOnly |= pkg.isDocOnly();
1374           removed |= pkg.isRemoved();
1375           pkg = containingPackage(pkg);
1376         }
1377       }
1378       while (clazz != null) {
1379         visible &= clazz.isPublic() || clazz.isProtected();
1380         hasShowAnnotation |= clazz.hasShowAnnotation();
1381         hidden |= clazz.isHidden();
1382         docOnly |= clazz.isDocOnly();
1383         removed |= clazz.isRemoved();
1384         clazz = clazz.containingClass();
1385       }
1386 
1387       if (ignoreShown) {
1388         hasShowAnnotation = true;
1389       }
1390       if (ignoreRemoved) {
1391         removed = matchRemoved;
1392       }
1393 
1394       return visible && hasShowAnnotation && !hidden && !docOnly && (removed == matchRemoved);
1395     }
1396   }
1397 
1398   /**
1399    * Filter that will elide exact duplicate members that are already included
1400    * in another superclass/interfaces.
1401    */
1402   public static class ElidingPredicate implements Predicate<MemberInfo> {
1403     private final Predicate<MemberInfo> wrapped;
1404 
1405     public ElidingPredicate(Predicate<MemberInfo> wrapped) {
1406       this.wrapped = wrapped;
1407     }
1408 
1409     @Override
1410     public boolean test(MemberInfo member) {
1411       // This member should be included, but if it's an exact duplicate
1412       // override then we can elide it.
1413       if (member instanceof MethodInfo) {
1414         MethodInfo method = (MethodInfo) member;
1415         if (method.returnType() != null) {  // not a constructor
1416           String methodRaw = writeMethodApiWithoutDefault(method);
1417           return (method.findPredicateOverriddenMethod(new Predicate<MemberInfo>() {
1418             @Override
1419             public boolean test(MemberInfo test) {
1420               // We're looking for included and perfect signature
1421               return (wrapped.test(test)
1422                   && writeMethodApiWithoutDefault((MethodInfo) test).equals(methodRaw));
1423             }
1424           }) == null);
1425         }
1426       }
1427       return true;
1428     }
1429   }
1430 
1431   public static class FilterPredicate implements Predicate<MemberInfo> {
1432     private final Predicate<MemberInfo> wrapped;
1433 
1434     public FilterPredicate(Predicate<MemberInfo> wrapped) {
1435       this.wrapped = wrapped;
1436     }
1437 
1438     @Override
1439     public boolean test(MemberInfo member) {
1440       if (wrapped.test(member)) {
1441         return true;
1442       } else if (member instanceof MethodInfo) {
1443         MethodInfo method = (MethodInfo) member;
1444         return method.returnType() != null &&  // not a constructor
1445                method.findPredicateOverriddenMethod(wrapped) != null;
1446       } else {
1447         return false;
1448       }
1449     }
1450   }
1451 
1452   static void writeApi(PrintStream apiWriter, Map<PackageInfo, List<ClassInfo>> classesByPackage,
1453       Predicate<MemberInfo> filterEmit, Predicate<MemberInfo> filterReference) {
1454     for (PackageInfo pkg : classesByPackage.keySet().stream().sorted(PackageInfo.comparator)
1455         .collect(Collectors.toList())) {
1456       if (pkg.name().equals(PackageInfo.DEFAULT_PACKAGE)) continue;
1457 
1458       boolean hasWrittenPackageHead = false;
1459       for (ClassInfo clazz : classesByPackage.get(pkg).stream().sorted(ClassInfo.comparator)
1460           .collect(Collectors.toList())) {
1461         hasWrittenPackageHead = writeClassApi(apiWriter, clazz, filterEmit, filterReference,
1462             hasWrittenPackageHead);
1463       }
1464 
1465       if (hasWrittenPackageHead) {
1466         apiWriter.print("}\n\n");
1467       }
1468     }
1469   }
1470 
1471   static void writeDexApi(PrintStream apiWriter, Map<PackageInfo, List<ClassInfo>> classesByPackage,
1472       Predicate<MemberInfo> filterEmit) {
1473     for (PackageInfo pkg : classesByPackage.keySet().stream().sorted(PackageInfo.comparator)
1474         .collect(Collectors.toList())) {
1475       if (pkg.name().equals(PackageInfo.DEFAULT_PACKAGE)) continue;
1476 
1477       for (ClassInfo clazz : classesByPackage.get(pkg).stream().sorted(ClassInfo.comparator)
1478           .collect(Collectors.toList())) {
1479         writeClassDexApi(apiWriter, clazz, filterEmit);
1480       }
1481     }
1482   }
1483 
1484   /**
1485    * Write the removed members of the class to removed.txt
1486    */
1487   private static boolean writeClassApi(PrintStream apiWriter, ClassInfo cl,
1488       Predicate<MemberInfo> filterEmit, Predicate<MemberInfo> filterReference,
1489       boolean hasWrittenPackageHead) {
1490 
1491     List<MethodInfo> constructors = cl.getExhaustiveConstructors().stream().filter(filterEmit)
1492         .sorted(MethodInfo.comparator).collect(Collectors.toList());
1493     List<MethodInfo> methods = cl.getExhaustiveMethods().stream().filter(filterEmit)
1494         .sorted(MethodInfo.comparator).collect(Collectors.toList());
1495     List<FieldInfo> enums = cl.getExhaustiveEnumConstants().stream().filter(filterEmit)
1496         .sorted(FieldInfo.comparator).collect(Collectors.toList());
1497     List<FieldInfo> fields = cl.filteredFields(filterEmit).stream()
1498         .sorted(FieldInfo.comparator).collect(Collectors.toList());
1499 
1500     final boolean classEmpty = (constructors.isEmpty() && methods.isEmpty() && enums.isEmpty()
1501         && fields.isEmpty());
1502     final boolean emit;
1503     if (filterEmit.test(cl.asMemberInfo())) {
1504       emit = true;
1505     } else if (!classEmpty) {
1506       emit = filterReference.test(cl.asMemberInfo());
1507     } else {
1508       emit = false;
1509     }
1510     if (!emit) {
1511       return hasWrittenPackageHead;
1512     }
1513 
1514     // Look for Android @SystemApi exposed outside the normal SDK; we require
1515     // that they're protected with a system permission.
1516     if (Doclava.android && Doclava.showAnnotations.contains("android.annotation.SystemApi")) {
1517       boolean systemService = "android.content.pm.PackageManager".equals(cl.qualifiedName());
1518       for (AnnotationInstanceInfo a : cl.annotations()) {
1519         if (a.type().qualifiedNameMatches("android", "annotation.SystemService")) {
1520           systemService = true;
1521         }
1522       }
1523       if (systemService) {
1524         for (MethodInfo mi : methods) {
1525           checkSystemPermissions(mi);
1526         }
1527       }
1528     }
1529 
1530     for (MethodInfo method : methods) {
1531       checkHiddenTypes(method, filterReference);
1532     }
1533     for (FieldInfo field : fields) {
1534       checkHiddenTypes(field, filterReference);
1535     }
1536 
1537     if (!hasWrittenPackageHead) {
1538       hasWrittenPackageHead = true;
1539       apiWriter.print("package ");
1540       apiWriter.print(cl.containingPackage().qualifiedName());
1541       apiWriter.print(" {\n\n");
1542     }
1543 
1544     apiWriter.print("  ");
1545     apiWriter.print(cl.scope());
1546     if (cl.isStatic()) {
1547       apiWriter.print(" static");
1548     }
1549     if (cl.isFinal()) {
1550       apiWriter.print(" final");
1551     }
1552     if (cl.isAbstract()) {
1553       apiWriter.print(" abstract");
1554     }
1555     if (cl.isDeprecated()) {
1556       apiWriter.print(" deprecated");
1557     }
1558     apiWriter.print(" ");
1559     apiWriter.print(cl.isInterface() ? "interface" : "class");
1560     apiWriter.print(" ");
1561     apiWriter.print(cl.name());
1562     if (cl.hasTypeParameters()) {
1563       apiWriter.print(TypeInfo.typeArgumentsName(cl.asTypeInfo().typeArguments(),
1564           new HashSet<String>()));
1565     }
1566 
1567     if (!cl.isInterface()
1568         && !"java.lang.Object".equals(cl.qualifiedName())) {
1569       final ClassInfo superclass = cl.filteredSuperclass(filterReference);
1570       if (superclass != null && !"java.lang.Object".equals(superclass.qualifiedName())) {
1571         apiWriter.print(" extends ");
1572         apiWriter.print(superclass.qualifiedName());
1573       }
1574     }
1575 
1576     List<ClassInfo> interfaces = cl.filteredInterfaces(filterReference).stream()
1577         .sorted(ClassInfo.comparator).collect(Collectors.toList());
1578     boolean first = true;
1579     for (ClassInfo iface : interfaces) {
1580       if (first) {
1581         apiWriter.print(" implements");
1582         first = false;
1583       }
1584       apiWriter.print(" ");
1585       apiWriter.print(iface.qualifiedName());
1586     }
1587 
1588     apiWriter.print(" {\n");
1589 
1590     for (MethodInfo mi : constructors) {
1591       writeConstructorApi(apiWriter, mi);
1592     }
1593     for (MethodInfo mi : methods) {
1594       writeMethodApi(apiWriter, mi);
1595     }
1596     for (FieldInfo fi : enums) {
1597       writeFieldApi(apiWriter, fi, "enum_constant");
1598     }
1599     for (FieldInfo fi : fields) {
1600       writeFieldApi(apiWriter, fi, "field");
1601     }
1602 
1603     apiWriter.print("  }\n\n");
1604     return hasWrittenPackageHead;
1605   }
1606 
1607   private static void writeClassDexApi(PrintStream apiWriter, ClassInfo cl,
1608       Predicate<MemberInfo> filterEmit) {
1609     if (filterEmit.test(cl.asMemberInfo())) {
1610       apiWriter.print(toSlashFormat(cl.qualifiedName()));
1611       apiWriter.print("\n");
1612     }
1613 
1614     List<MethodInfo> constructors = cl.getExhaustiveConstructors().stream().filter(filterEmit)
1615         .sorted(MethodInfo.comparator).collect(Collectors.toList());
1616     List<MethodInfo> methods = cl.getExhaustiveMethods().stream().filter(filterEmit)
1617         .sorted(MethodInfo.comparator).collect(Collectors.toList());
1618     List<FieldInfo> enums = cl.getExhaustiveEnumConstants().stream().filter(filterEmit)
1619         .sorted(FieldInfo.comparator).collect(Collectors.toList());
1620     List<FieldInfo> fields = cl.getExhaustiveFields().stream().filter(filterEmit)
1621         .sorted(FieldInfo.comparator).collect(Collectors.toList());
1622 
1623     for (MethodInfo mi : constructors) {
1624       writeMethodDexApi(apiWriter, cl, mi);
1625     }
1626     for (MethodInfo mi : methods) {
1627       writeMethodDexApi(apiWriter, cl, mi);
1628     }
1629     for (FieldInfo fi : enums) {
1630       writeFieldDexApi(apiWriter, cl, fi);
1631     }
1632     for (FieldInfo fi : fields) {
1633       writeFieldDexApi(apiWriter, cl, fi);
1634     }
1635   }
1636 
1637   private static void checkSystemPermissions(MethodInfo mi) {
1638     boolean hasAnnotation = false;
1639     for (AnnotationInstanceInfo a : mi.annotations()) {
1640       if (a.type().qualifiedNameMatches("android", "annotation.RequiresPermission")) {
1641         hasAnnotation = true;
1642         for (AnnotationValueInfo val : a.elementValues()) {
1643           ArrayList<AnnotationValueInfo> values = new ArrayList<>();
1644           boolean any = false;
1645           switch (val.element().name()) {
1646             case "value":
1647               values.add(val);
1648               break;
1649             case "allOf":
1650               values = (ArrayList<AnnotationValueInfo>) val.value();
1651               break;
1652             case "anyOf":
1653               any = true;
1654               values = (ArrayList<AnnotationValueInfo>) val.value();
1655               break;
1656           }
1657           if (values.isEmpty()) continue;
1658 
1659           ArrayList<String> system = new ArrayList<>();
1660           ArrayList<String> nonSystem = new ArrayList<>();
1661           for (AnnotationValueInfo value : values) {
1662             final String perm = String.valueOf(value.value());
1663             final String level = Doclava.manifestPermissions.getOrDefault(perm, null);
1664             if (level == null) {
1665               Errors.error(Errors.REMOVED_FIELD, mi.position(),
1666                   "Permission '" + perm + "' is not defined by AndroidManifest.xml.");
1667               continue;
1668             }
1669             if (level.contains("normal") || level.contains("dangerous")
1670                 || level.contains("ephemeral")) {
1671               nonSystem.add(perm);
1672             } else {
1673               system.add(perm);
1674             }
1675           }
1676 
1677           if (system.isEmpty() && nonSystem.isEmpty()) {
1678             hasAnnotation = false;
1679           } else if ((any && !nonSystem.isEmpty()) || (!any && system.isEmpty())) {
1680             Errors.error(Errors.REQUIRES_PERMISSION, mi, "Method '" + mi.name()
1681                 + "' must be protected with a system permission; it currently"
1682                 + " allows non-system callers holding " + nonSystem.toString());
1683           }
1684         }
1685       }
1686     }
1687     if (!hasAnnotation) {
1688       Errors.error(Errors.REQUIRES_PERMISSION, mi, "Method '" + mi.name()
1689         + "' must be protected with a system permission.");
1690     }
1691   }
1692 
1693   private static void checkHiddenTypes(MethodInfo method, Predicate<MemberInfo> filterReference) {
1694     checkHiddenTypes(method.returnType(), method, filterReference);
1695     List<ParameterInfo> params = method.parameters();
1696     if (params != null) {
1697       for (ParameterInfo param : params) {
1698         checkHiddenTypes(param.type(), method, filterReference);
1699       }
1700     }
1701   }
1702 
1703   private static void checkHiddenTypes(FieldInfo field, Predicate<MemberInfo> filterReference) {
1704     checkHiddenTypes(field.type(), field, filterReference);
1705   }
1706 
1707   private static void checkHiddenTypes(TypeInfo type, MemberInfo member,
1708       Predicate<MemberInfo> filterReference) {
1709     if (type == null || type.isPrimitive()) {
1710       return;
1711     }
1712 
1713     ClassInfo clazz = type.asClassInfo();
1714     if (clazz == null || !filterReference.test(clazz.asMemberInfo())) {
1715       Errors.error(Errors.HIDDEN_TYPE_PARAMETER, member.position(),
1716           "Member " + member + " references hidden type " + type.qualifiedTypeName() + ".");
1717     }
1718 
1719     List<TypeInfo> args = type.typeArguments();
1720     if (args != null) {
1721       for (TypeInfo arg : args) {
1722         checkHiddenTypes(arg, member, filterReference);
1723       }
1724     }
1725   }
1726 
1727   static void writeConstructorApi(PrintStream apiWriter, MethodInfo mi) {
1728     apiWriter.print("    ctor ");
1729     apiWriter.print(mi.scope());
1730     if (mi.isDeprecated()) {
1731       apiWriter.print(" deprecated");
1732     }
1733     apiWriter.print(" ");
1734     apiWriter.print(mi.name());
1735 
1736     writeParametersApi(apiWriter, mi, mi.parameters());
1737     writeThrowsApi(apiWriter, mi.thrownExceptions());
1738     apiWriter.print(";\n");
1739   }
1740 
1741   static String writeMethodApiWithoutDefault(MethodInfo mi) {
1742     final ByteArrayOutputStream out = new ByteArrayOutputStream();
1743     writeMethodApi(new PrintStream(out), mi, false);
1744     return out.toString();
1745   }
1746 
1747   static void writeMethodApi(PrintStream apiWriter, MethodInfo mi) {
1748     writeMethodApi(apiWriter, mi, true);
1749   }
1750 
1751   static void writeMethodApi(PrintStream apiWriter, MethodInfo mi, boolean withDefault) {
1752     apiWriter.print("    method ");
1753     apiWriter.print(mi.scope());
1754     if (mi.isDefault() && withDefault) {
1755       apiWriter.print(" default");
1756     }
1757     if (mi.isStatic()) {
1758       apiWriter.print(" static");
1759     }
1760     if (mi.isFinal()) {
1761       apiWriter.print(" final");
1762     }
1763     if (mi.isAbstract()) {
1764       apiWriter.print(" abstract");
1765     }
1766     if (mi.isDeprecated()) {
1767       apiWriter.print(" deprecated");
1768     }
1769     if (mi.isSynchronized()) {
1770       apiWriter.print(" synchronized");
1771     }
1772     if (mi.hasTypeParameters()) {
1773       apiWriter.print(" " + mi.typeArgumentsName(new HashSet<String>()));
1774     }
1775     apiWriter.print(" ");
1776     if (mi.returnType() == null) {
1777       apiWriter.print("void");
1778     } else {
1779       apiWriter.print(fullParameterTypeName(mi, mi.returnType(), false));
1780     }
1781     apiWriter.print(" ");
1782     apiWriter.print(mi.name());
1783 
1784     writeParametersApi(apiWriter, mi, mi.parameters());
1785     writeThrowsApi(apiWriter, mi.thrownExceptions());
1786 
1787     apiWriter.print(";\n");
1788   }
1789 
1790   static void writeMethodDexApi(PrintStream apiWriter, ClassInfo cl, MethodInfo mi) {
1791     apiWriter.print(toSlashFormat(cl.qualifiedName()));
1792     apiWriter.print("->");
1793     if (mi.returnType() == null) {
1794       apiWriter.print("<init>");
1795     } else {
1796       apiWriter.print(mi.name());
1797     }
1798     writeParametersDexApi(apiWriter, mi, mi.parameters());
1799     if (mi.returnType() == null) {  // constructor
1800       apiWriter.print("V");
1801     } else {
1802       apiWriter.print(toSlashFormat(mi.returnType().dexName()));
1803     }
1804     apiWriter.print("\n");
1805   }
1806 
1807   static void writeParametersApi(PrintStream apiWriter, MethodInfo method,
1808       ArrayList<ParameterInfo> params) {
1809     apiWriter.print("(");
1810 
1811     for (ParameterInfo pi : params) {
1812       if (pi != params.get(0)) {
1813         apiWriter.print(", ");
1814       }
1815       apiWriter.print(fullParameterTypeName(method, pi.type(), pi == params.get(params.size()-1)));
1816       // turn on to write the names too
1817       if (false) {
1818         apiWriter.print(" ");
1819         apiWriter.print(pi.name());
1820       }
1821     }
1822 
1823     apiWriter.print(")");
1824   }
1825 
1826   static void writeParametersDexApi(PrintStream apiWriter, MethodInfo method,
1827       ArrayList<ParameterInfo> params) {
1828     apiWriter.print("(");
1829     for (ParameterInfo pi : params) {
1830       String typeName = pi.type().dexName();
1831       if (method.isVarArgs() && pi == params.get(params.size() - 1)) {
1832         typeName += "[]";
1833       }
1834       apiWriter.print(toSlashFormat(typeName));
1835     }
1836     apiWriter.print(")");
1837   }
1838 
1839   static void writeThrowsApi(PrintStream apiWriter, ArrayList<ClassInfo> exceptions) {
1840     // write in a canonical order
1841     exceptions = (ArrayList<ClassInfo>) exceptions.clone();
1842     Collections.sort(exceptions, ClassInfo.comparator);
1843     //final int N = exceptions.length;
1844     boolean first = true;
1845     for (ClassInfo ex : exceptions) {
1846       // Turn this off, b/c we need to regenrate the old xml files.
1847       if (true || !"java.lang.RuntimeException".equals(ex.qualifiedName())
1848           && !ex.isDerivedFrom("java.lang.RuntimeException")) {
1849         if (first) {
1850           apiWriter.print(" throws ");
1851           first = false;
1852         } else {
1853           apiWriter.print(", ");
1854         }
1855         apiWriter.print(ex.qualifiedName());
1856       }
1857     }
1858   }
1859 
1860   static void writeFieldApi(PrintStream apiWriter, FieldInfo fi, String label) {
1861     apiWriter.print("    ");
1862     apiWriter.print(label);
1863     apiWriter.print(" ");
1864     apiWriter.print(fi.scope());
1865     if (fi.isStatic()) {
1866       apiWriter.print(" static");
1867     }
1868     if (fi.isFinal()) {
1869       apiWriter.print(" final");
1870     }
1871     if (fi.isDeprecated()) {
1872       apiWriter.print(" deprecated");
1873     }
1874     if (fi.isTransient()) {
1875       apiWriter.print(" transient");
1876     }
1877     if (fi.isVolatile()) {
1878       apiWriter.print(" volatile");
1879     }
1880 
1881     apiWriter.print(" ");
1882     apiWriter.print(fi.type().fullName(fi.typeVariables()));
1883 
1884     apiWriter.print(" ");
1885     apiWriter.print(fi.name());
1886 
1887     Object val = null;
1888     if (fi.isConstant() && fieldIsInitialized(fi)) {
1889       apiWriter.print(" = ");
1890       apiWriter.print(fi.constantLiteralValue());
1891       val = fi.constantValue();
1892     }
1893 
1894     apiWriter.print(";");
1895 
1896     if (val != null) {
1897       if (val instanceof Integer && "char".equals(fi.type().qualifiedTypeName())) {
1898         apiWriter.format(" // 0x%04x '%s'", val,
1899             FieldInfo.javaEscapeString("" + ((char)((Integer)val).intValue())));
1900       } else if (val instanceof Byte || val instanceof Short || val instanceof Integer) {
1901         apiWriter.format(" // 0x%x", val);
1902       } else if (val instanceof Long) {
1903         apiWriter.format(" // 0x%xL", val);
1904       }
1905     }
1906 
1907     apiWriter.print("\n");
1908   }
1909 
1910   static void writeFieldDexApi(PrintStream apiWriter, ClassInfo cl, FieldInfo fi) {
1911     apiWriter.print(toSlashFormat(cl.qualifiedName()));
1912     apiWriter.print("->");
1913     apiWriter.print(fi.name());
1914     apiWriter.print(":");
1915     apiWriter.print(toSlashFormat(fi.type().dexName()));
1916     apiWriter.print("\n");
1917   }
1918 
1919   static void writeKeepList(PrintStream keepListWriter,
1920       HashMap<PackageInfo, List<ClassInfo>> allClasses, HashSet<ClassInfo> notStrippable) {
1921     // extract the set of packages, sort them by name, and write them out in that order
1922     Set<PackageInfo> allClassKeys = allClasses.keySet();
1923     PackageInfo[] allPackages = allClassKeys.toArray(new PackageInfo[allClassKeys.size()]);
1924     Arrays.sort(allPackages, PackageInfo.comparator);
1925 
1926     for (PackageInfo pack : allPackages) {
1927       writePackageKeepList(keepListWriter, pack, allClasses.get(pack), notStrippable);
1928     }
1929   }
1930 
1931   static void writePackageKeepList(PrintStream keepListWriter, PackageInfo pack,
1932       Collection<ClassInfo> classList, HashSet<ClassInfo> notStrippable) {
1933     // Work around the bogus "Array" class we invent for
1934     // Arrays.copyOf's Class<? extends T[]> newType parameter. (http://b/2715505)
1935     if (pack.name().equals(PackageInfo.DEFAULT_PACKAGE)) {
1936       return;
1937     }
1938 
1939     ClassInfo[] classes = classList.toArray(new ClassInfo[classList.size()]);
1940     Arrays.sort(classes, ClassInfo.comparator);
1941     for (ClassInfo cl : classes) {
1942       writeClassKeepList(keepListWriter, cl, notStrippable);
1943     }
1944   }
1945 
1946   static void writeClassKeepList(PrintStream keepListWriter, ClassInfo cl,
1947       HashSet<ClassInfo> notStrippable) {
1948     keepListWriter.print("-keep class ");
1949     keepListWriter.print(to$Class(cl.qualifiedName()));
1950 
1951     keepListWriter.print(" {\n");
1952 
1953     ArrayList<MethodInfo> constructors = cl.constructors();
1954     Collections.sort(constructors, MethodInfo.comparator);
1955     for (MethodInfo mi : constructors) {
1956       writeConstructorKeepList(keepListWriter, mi);
1957     }
1958 
1959     keepListWriter.print("\n");
1960 
1961     ArrayList<MethodInfo> methods = cl.allSelfMethods();
1962     Collections.sort(methods, MethodInfo.comparator);
1963     for (MethodInfo mi : methods) {
1964       // allSelfMethods is the non-hidden and visible methods. See Doclava.checkLevel.
1965       writeMethodKeepList(keepListWriter, mi);
1966     }
1967 
1968     keepListWriter.print("\n");
1969 
1970     ArrayList<FieldInfo> enums = cl.enumConstants();
1971     Collections.sort(enums, FieldInfo.comparator);
1972     for (FieldInfo fi : enums) {
1973       writeFieldKeepList(keepListWriter, fi);
1974     }
1975 
1976     keepListWriter.print("\n");
1977 
1978     ArrayList<FieldInfo> fields = cl.selfFields();
1979     Collections.sort(fields, FieldInfo.comparator);
1980     for (FieldInfo fi : fields) {
1981       writeFieldKeepList(keepListWriter, fi);
1982     }
1983 
1984     keepListWriter.print("}\n\n");
1985   }
1986 
1987   static void writeConstructorKeepList(PrintStream keepListWriter, MethodInfo mi) {
1988     keepListWriter.print("    ");
1989     keepListWriter.print("<init>");
1990 
1991     writeParametersKeepList(keepListWriter, mi, mi.parameters());
1992     keepListWriter.print(";\n");
1993   }
1994 
1995   static void writeMethodKeepList(PrintStream keepListWriter, MethodInfo mi) {
1996     keepListWriter.print("    ");
1997     keepListWriter.print(mi.scope());
1998     if (mi.isStatic()) {
1999       keepListWriter.print(" static");
2000     }
2001     if (mi.isAbstract()) {
2002       keepListWriter.print(" abstract");
2003     }
2004     if (mi.isSynchronized()) {
2005       keepListWriter.print(" synchronized");
2006     }
2007     keepListWriter.print(" ");
2008     if (mi.returnType() == null) {
2009       keepListWriter.print("void");
2010     } else {
2011       keepListWriter.print(getCleanTypeName(mi.returnType()));
2012     }
2013     keepListWriter.print(" ");
2014     keepListWriter.print(mi.name());
2015 
2016     writeParametersKeepList(keepListWriter, mi, mi.parameters());
2017 
2018     keepListWriter.print(";\n");
2019   }
2020 
2021   static void writeParametersKeepList(PrintStream keepListWriter, MethodInfo method,
2022       ArrayList<ParameterInfo> params) {
2023     keepListWriter.print("(");
2024 
2025     for (ParameterInfo pi : params) {
2026       if (pi != params.get(0)) {
2027         keepListWriter.print(", ");
2028       }
2029       keepListWriter.print(getCleanTypeName(pi.type()));
2030     }
2031 
2032     keepListWriter.print(")");
2033   }
2034 
2035   static void writeFieldKeepList(PrintStream keepListWriter, FieldInfo fi) {
2036     keepListWriter.print("    ");
2037     keepListWriter.print(fi.scope());
2038     if (fi.isStatic()) {
2039       keepListWriter.print(" static");
2040     }
2041     if (fi.isTransient()) {
2042       keepListWriter.print(" transient");
2043     }
2044     if (fi.isVolatile()) {
2045       keepListWriter.print(" volatile");
2046     }
2047 
2048     keepListWriter.print(" ");
2049     keepListWriter.print(getCleanTypeName(fi.type()));
2050 
2051     keepListWriter.print(" ");
2052     keepListWriter.print(fi.name());
2053 
2054     keepListWriter.print(";\n");
2055   }
2056 
2057   static String fullParameterTypeName(MethodInfo method, TypeInfo type, boolean isLast) {
2058     String fullTypeName = type.fullName(method.typeVariables());
2059     if (isLast && method.isVarArgs()) {
2060       // TODO: note that this does not attempt to handle hypothetical
2061       // vararg methods whose last parameter is a list of arrays, e.g.
2062       // "Object[]...".
2063       fullTypeName = type.fullNameNoDimension(method.typeVariables()) + "...";
2064     }
2065     return fullTypeName;
2066   }
2067 
2068   static String to$Class(String name) {
2069     int pos = 0;
2070     while ((pos = name.indexOf('.', pos)) > 0) {
2071       String n = name.substring(0, pos);
2072       if (Converter.obtainClass(n) != null) {
2073         return n + (name.substring(pos).replace('.', '$'));
2074       }
2075       pos = pos + 1;
2076     }
2077     return name;
2078   }
2079 
2080   static String toSlashFormat(String name) {
2081     String dimension = "";
2082     while (name.endsWith("[]")) {
2083       dimension += "[";
2084       name = name.substring(0, name.length() - 2);
2085     }
2086 
2087     final String base;
2088     if (name.equals("void")) {
2089       base = "V";
2090     } else if (name.equals("byte")) {
2091       base = "B";
2092     } else if (name.equals("boolean")) {
2093       base = "Z";
2094     } else if (name.equals("char")) {
2095       base = "C";
2096     } else if (name.equals("short")) {
2097       base = "S";
2098     } else if (name.equals("int")) {
2099       base = "I";
2100     } else if (name.equals("long")) {
2101       base = "J";
2102     } else if (name.equals("float")) {
2103       base = "F";
2104     } else if (name.equals("double")) {
2105       base = "D";
2106     } else {
2107       base = "L" + to$Class(name).replace(".", "/") + ";";
2108     }
2109 
2110     return dimension + base;
2111   }
2112 
2113   static String getCleanTypeName(TypeInfo t) {
2114       return t.isPrimitive() ? t.simpleTypeName() + t.dimension() :
2115               to$Class(t.asClassInfo().qualifiedName() + t.dimension());
2116   }
2117 }
View Code

 

posted on 2022-02-15 16:44  Ants_hu  阅读(1090)  评论(0编辑  收藏  举报

导航