• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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