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.tools.metalava.SdkIdentifier
19 import java.io.PrintStream
20 import java.util.Collections
21 import java.util.TreeMap
22 import java.util.TreeSet
23 
24 /** Represents the whole Android API. */
25 class Api(private val mMin: Int) : ApiElement("Android API") {
26     private val mClasses: MutableMap<String, ApiClass> = HashMap()
27 
28     /**
29      * Prints the whole API definition to a stream.
30      *
31      * @param stream the stream to print the XML elements to
32      */
printnull33     fun print(stream: PrintStream, sdkIdentifiers: Set<SdkIdentifier>) {
34         stream.print("<api version=\"3\"")
35         if (mMin > 1) {
36             stream.print(" min=\"$mMin\"")
37         }
38         stream.println(">")
39         for ((id, shortname, name, reference) in sdkIdentifiers) {
40             stream.println(
41                 String.format(
42                     "\t<sdk id=\"%d\" shortname=\"%s\" name=\"%s\" reference=\"%s\"/>",
43                     id,
44                     shortname,
45                     name,
46                     reference
47                 )
48             )
49         }
50         print(mClasses.values, "class", "\t", stream)
51         printClosingTag("api", "", stream)
52     }
53 
54     /**
55      * Adds or updates a class.
56      *
57      * @param name the name of the class
58      * @param version an API version in which the class existed
59      * @param deprecated whether the class was deprecated in the API version
60      * @return the newly created or a previously existed class
61      */
addClassnull62     fun addClass(name: String, version: Int, deprecated: Boolean): ApiClass {
63         var classElement = mClasses[name]
64         if (classElement == null) {
65             classElement = ApiClass(name, version, deprecated)
66             mClasses[name] = classElement
67         } else {
68             classElement.update(version, deprecated)
69         }
70         return classElement
71     }
72 
findClassnull73     fun findClass(name: String?): ApiClass? {
74         return if (name == null) null else mClasses[name]
75     }
76 
77     /** Cleans up the API surface for printing after all elements have been added. */
cleannull78     fun clean() {
79         inlineFromHiddenSuperClasses()
80         removeImplicitInterfaces()
81         removeOverridingMethods()
82         prunePackagePrivateClasses()
83     }
84 
85     val classes: Collection<ApiClass>
86         get() = Collections.unmodifiableCollection(mClasses.values)
87 
backfillHistoricalFixesnull88     fun backfillHistoricalFixes() {
89         backfillSdkExtensions()
90     }
91 
backfillSdkExtensionsnull92     private fun backfillSdkExtensions() {
93         // SdkExtensions.getExtensionVersion was added in 30/R, but was a SystemApi
94         // to avoid publishing the versioning API publicly before there was any
95         // valid use for it.
96         // getAllExtensionsVersions was added as part of 31/S
97         // The class and its APIs were made public between S and T, but we pretend
98         // here like it was always public, for maximum backward compatibility.
99         val sdkExtensions = findClass("android/os/ext/SdkExtensions")
100         if (sdkExtensions != null && sdkExtensions.since != 30 && sdkExtensions.since != 33) {
101             throw AssertionError("Received unexpected historical data")
102         } else if (sdkExtensions == null || sdkExtensions.since == 30) {
103             // This is the system API db (30), or module-lib/system-server dbs (null)
104             // They don't need patching.
105             return
106         }
107         sdkExtensions.update(30, false)
108         sdkExtensions.addSuperClass("java/lang/Object", 30)
109         sdkExtensions.getMethod("getExtensionVersion(I)I")!!.update(30, false)
110         sdkExtensions.getMethod("getAllExtensionVersions()Ljava/util/Map;")!!.update(31, false)
111     }
112 
113     /**
114      * The bytecode visitor registers interfaces listed for a class. However, a class will **also**
115      * implement interfaces implemented by the super classes. This isn't available in the class
116      * file, so after all classes have been read in, we iterate through all classes, and for those
117      * that have interfaces, we check up the inheritance chain to see if it has already been
118      * introduced in a super class at an earlier API level.
119      */
removeImplicitInterfacesnull120     fun removeImplicitInterfaces() {
121         for (classElement in mClasses.values) {
122             classElement.removeImplicitInterfaces(mClasses)
123         }
124     }
125 
126     /** @see ApiClass.removeOverridingMethods */
removeOverridingMethodsnull127     fun removeOverridingMethods() {
128         for (classElement in mClasses.values) {
129             classElement.removeOverridingMethods(mClasses)
130         }
131     }
132 
inlineFromHiddenSuperClassesnull133     fun inlineFromHiddenSuperClasses() {
134         val hidden: MutableMap<String, ApiClass> = HashMap()
135         for (classElement in mClasses.values) {
136             if (
137                 classElement.hiddenUntil < 0
138             ) { // hidden in the .jar files? (mMax==codebase, -1: jar files)
139                 hidden[classElement.name] = classElement
140             }
141         }
142         for (classElement in mClasses.values) {
143             classElement.inlineFromHiddenSuperClasses(hidden)
144         }
145     }
146 
prunePackagePrivateClassesnull147     fun prunePackagePrivateClasses() {
148         for (cls in mClasses.values) {
149             cls.removeHiddenSuperClasses(mClasses)
150         }
151     }
152 
removeMissingClassesnull153     fun removeMissingClasses() {
154         for (cls in mClasses.values) {
155             cls.removeMissingClasses(mClasses)
156         }
157     }
158 
verifyNoMissingClassesnull159     fun verifyNoMissingClasses() {
160         val results: MutableMap<String?, MutableSet<String?>> = TreeMap()
161         for (cls in mClasses.values) {
162             val missing = cls.findMissingClasses(mClasses)
163             // Have the missing classes as keys, and the referencing classes as values.
164             for (missingClass in missing) {
165                 val missingName = missingClass.name
166                 if (!results.containsKey(missingName)) {
167                     results[missingName] = TreeSet()
168                 }
169                 results[missingName]!!.add(cls.name)
170             }
171         }
172         if (results.isNotEmpty()) {
173             var message = ""
174             for ((key, value) in results) {
175                 message += """
176   $key referenced by:"""
177                 for (referencer in value) {
178                     message += "\n    $referencer"
179                 }
180             }
181             throw IllegalStateException(
182                 "There are classes in this API that reference other " +
183                     "classes that do not exist in this API. " +
184                     "This can happen when an api is provided by an apex, but referenced " +
185                     "from non-updatable platform code. Use --remove-missing-classes-in-api-levels to " +
186                     "make metalava remove these references instead of erroring out." +
187                     message
188             )
189         }
190     }
191 }
192