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 111 return api; 112 } 113 processCodebase(Api api, int apiLevel)114 private void processCodebase(Api api, int apiLevel) { 115 if (mCodebase == null) { 116 return; 117 } 118 AddApisFromCodebaseKt.addApisFromCodebase(api, apiLevel, mCodebase); 119 } 120 readJar(Api api, int apiLevel, File jar)121 private void readJar(Api api, int apiLevel, File jar) throws IOException { 122 api.update(apiLevel); 123 124 FileInputStream fis = new FileInputStream(jar); 125 ZipInputStream zis = new ZipInputStream(fis); 126 ZipEntry entry = zis.getNextEntry(); 127 while (entry != null) { 128 String name = entry.getName(); 129 130 if (name.endsWith(SdkConstants.DOT_CLASS)) { 131 byte[] bytes = ByteStreams.toByteArray(zis); 132 ClassReader reader = new ClassReader(bytes); 133 ClassNode classNode = new ClassNode(Opcodes.ASM5); 134 reader.accept(classNode, 0 /*flags*/); 135 136 ApiClass theClass = api.addClass(classNode.name, apiLevel, 137 (classNode.access & Opcodes.ACC_DEPRECATED) != 0); 138 139 theClass.updateHidden(apiLevel, (classNode.access & Opcodes.ACC_PUBLIC) == 0); 140 141 // super class 142 if (classNode.superName != null) { 143 theClass.addSuperClass(classNode.superName, apiLevel); 144 } 145 146 // interfaces 147 for (Object interfaceName : classNode.interfaces) { 148 theClass.addInterface((String) interfaceName, apiLevel); 149 } 150 151 // fields 152 for (Object field : classNode.fields) { 153 FieldNode fieldNode = (FieldNode) field; 154 if (((fieldNode.access & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED)) == 0)) { 155 continue; 156 } 157 if (!fieldNode.name.startsWith("this$") && 158 !fieldNode.name.equals("$VALUES")) { 159 boolean deprecated = (fieldNode.access & Opcodes.ACC_DEPRECATED) != 0; 160 theClass.addField(fieldNode.name, apiLevel, deprecated); 161 } 162 } 163 164 // methods 165 for (Object method : classNode.methods) { 166 MethodNode methodNode = (MethodNode) method; 167 if (((methodNode.access & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED)) == 0)) { 168 continue; 169 } 170 if (!methodNode.name.equals("<clinit>")) { 171 boolean deprecated = (methodNode.access & Opcodes.ACC_DEPRECATED) != 0; 172 theClass.addMethod(methodNode.name + methodNode.desc, apiLevel, deprecated); 173 } 174 } 175 } 176 entry = zis.getNextEntry(); 177 } 178 179 Closeables.close(fis, true); 180 } 181 getAndroidJarFile(int apiLevel)182 private File getAndroidJarFile(int apiLevel) { 183 if (mApiLevels != null) { 184 return mApiLevels[apiLevel]; 185 } 186 for (String pattern : mPatterns) { 187 File f = new File(pattern.replace("%", Integer.toString(apiLevel))); 188 if (f.isFile()) { 189 return f; 190 } 191 } 192 return null; 193 } 194 } 195