• 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 
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