1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.tools.metalava.apilevels; 17 18 import com.android.SdkConstants; 19 import com.android.tools.metalava.model.Codebase; 20 import com.google.common.io.ByteStreams; 21 import com.google.common.io.Closeables; 22 import org.jetbrains.annotations.NotNull; 23 import org.jetbrains.annotations.Nullable; 24 import org.objectweb.asm.ClassReader; 25 import org.objectweb.asm.Opcodes; 26 import org.objectweb.asm.tree.ClassNode; 27 import org.objectweb.asm.tree.FieldNode; 28 import org.objectweb.asm.tree.MethodNode; 29 30 import java.io.File; 31 import java.io.FileInputStream; 32 import java.io.IOException; 33 import java.util.List; 34 import java.util.zip.ZipEntry; 35 import java.util.zip.ZipInputStream; 36 37 /** 38 * Reads all the android.jar files found in an SDK and generate a map of {@link ApiClass}. 39 */ 40 class AndroidJarReader { 41 private int mMinApi; 42 private int mCurrentApi; 43 private File mCurrentJar; 44 private List<String> mPatterns; 45 private File[] mApiLevels; 46 private final Codebase mCodebase; 47 AndroidJarReader(@otNull List<String> patterns, int minApi, @NotNull File currentJar, int currentApi, @Nullable Codebase codebase)48 AndroidJarReader(@NotNull List<String> patterns, 49 int minApi, 50 @NotNull File currentJar, 51 int currentApi, 52 @Nullable Codebase codebase) { 53 mPatterns = patterns; 54 mMinApi = minApi; 55 mCurrentJar = currentJar; 56 mCurrentApi = currentApi; 57 mCodebase = codebase; 58 } 59 AndroidJarReader(@otNull File[] apiLevels, @Nullable Codebase codebase)60 AndroidJarReader(@NotNull File[] apiLevels, @Nullable Codebase codebase) { 61 mApiLevels = apiLevels; 62 mCodebase = codebase; 63 } 64 getApi()65 public Api getApi() throws IOException { 66 Api api; 67 if (mApiLevels != null) { 68 int max = mApiLevels.length - 1; 69 if (mCodebase != null) { 70 max = mCodebase.getApiLevel(); 71 } 72 73 api = new Api(max); 74 for (int apiLevel = 1; apiLevel < mApiLevels.length; apiLevel++) { 75 File jar = getAndroidJarFile(apiLevel); 76 readJar(api, apiLevel, jar); 77 } 78 if (mCodebase != null) { 79 int apiLevel = mCodebase.getApiLevel(); 80 if (apiLevel != -1) { 81 processCodebase(api, apiLevel); 82 } 83 } 84 } else { 85 api = new Api(mCurrentApi); 86 // Get all the android.jar. They are in platforms-# 87 int apiLevel = mMinApi - 1; 88 while (true) { 89 apiLevel++; 90 File jar = null; 91 if (apiLevel == mCurrentApi) { 92 jar = mCurrentJar; 93 } 94 if (jar == null) { 95 jar = getAndroidJarFile(apiLevel); 96 } 97 if (jar == null || !jar.isFile()) { 98 if (mCodebase != null) { 99 processCodebase(api, apiLevel); 100 } 101 break; 102 } 103 readJar(api, apiLevel, jar); 104 } 105 } 106 107 api.inlineFromHiddenSuperClasses(); 108 api.removeImplicitInterfaces(); 109 api.removeOverridingMethods(); 110 api.prunePackagePrivateClasses(); 111 112 return api; 113 } 114 processCodebase(Api api, int apiLevel)115 private void processCodebase(Api api, int apiLevel) { 116 if (mCodebase == null) { 117 return; 118 } 119 AddApisFromCodebaseKt.addApisFromCodebase(api, apiLevel, mCodebase); 120 } 121 readJar(Api api, int apiLevel, File jar)122 private void readJar(Api api, int apiLevel, File jar) throws IOException { 123 api.update(apiLevel); 124 125 FileInputStream fis = new FileInputStream(jar); 126 ZipInputStream zis = new ZipInputStream(fis); 127 ZipEntry entry = zis.getNextEntry(); 128 while (entry != null) { 129 String name = entry.getName(); 130 131 if (name.endsWith(SdkConstants.DOT_CLASS)) { 132 byte[] bytes = ByteStreams.toByteArray(zis); 133 ClassReader reader = new ClassReader(bytes); 134 ClassNode classNode = new ClassNode(Opcodes.ASM5); 135 reader.accept(classNode, 0 /*flags*/); 136 137 ApiClass theClass = api.addClass(classNode.name, apiLevel, 138 (classNode.access & Opcodes.ACC_DEPRECATED) != 0); 139 140 theClass.updateHidden(apiLevel, (classNode.access & Opcodes.ACC_PUBLIC) == 0); 141 142 // super class 143 if (classNode.superName != null) { 144 theClass.addSuperClass(classNode.superName, apiLevel); 145 } 146 147 // interfaces 148 for (Object interfaceName : classNode.interfaces) { 149 theClass.addInterface((String) interfaceName, apiLevel); 150 } 151 152 // fields 153 for (Object field : classNode.fields) { 154 FieldNode fieldNode = (FieldNode) field; 155 if (((fieldNode.access & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED)) == 0)) { 156 continue; 157 } 158 if (!fieldNode.name.startsWith("this$") && 159 !fieldNode.name.equals("$VALUES")) { 160 boolean deprecated = (fieldNode.access & Opcodes.ACC_DEPRECATED) != 0; 161 theClass.addField(fieldNode.name, apiLevel, deprecated); 162 } 163 } 164 165 // methods 166 for (Object method : classNode.methods) { 167 MethodNode methodNode = (MethodNode) method; 168 if (((methodNode.access & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED)) == 0)) { 169 continue; 170 } 171 if (!methodNode.name.equals("<clinit>")) { 172 boolean deprecated = (methodNode.access & Opcodes.ACC_DEPRECATED) != 0; 173 theClass.addMethod(methodNode.name + methodNode.desc, apiLevel, deprecated); 174 } 175 } 176 } 177 entry = zis.getNextEntry(); 178 } 179 180 Closeables.close(fis, true); 181 } 182 getAndroidJarFile(int apiLevel)183 private File getAndroidJarFile(int apiLevel) { 184 if (mApiLevels != null) { 185 return mApiLevels[apiLevel]; 186 } 187 for (String pattern : mPatterns) { 188 File f = new File(pattern.replace("%", Integer.toString(apiLevel))); 189 if (f.isFile()) { 190 return f; 191 } 192 } 193 return null; 194 } 195 } 196