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 java.util.Collections 19 import java.util.TreeMap 20 import java.util.TreeSet 21 22 /** 23 * Represents the whole Android API. 24 * 25 * @param useInternalNames `true` if JVM internal names should be used, `false` otherwise. 26 */ 27 class Api(val useInternalNames: Boolean) : ParentApiElement { 28 /** 29 * This has to behave as if it exists since before any specific version (so that every class 30 * always specifies its `since` attribute. 31 */ 32 override val since: ApiVersion = ApiVersion.LOWEST 33 34 override var lastPresentIn = since 35 private set 36 37 override val sdks: String? = null 38 39 override val deprecatedIn: ApiVersion? = null 40 41 private val mClasses: MutableMap<String, ApiClass> = HashMap() 42 43 /** 44 * Updates this with information for a specific API version. 45 * 46 * @param apiVersion an API version that this contains. 47 */ updatenull48 fun update(apiVersion: ApiVersion) { 49 // Track the last version added to this. 50 if (lastPresentIn < apiVersion) { 51 lastPresentIn = apiVersion 52 } 53 } 54 toStringnull55 override fun toString() = "Android Api" 56 57 /** 58 * Updates the [ApiClass] for the class called [name], creating and adding one if necessary. 59 * 60 * @param name the name of the class 61 * @param updater the [ApiHistoryUpdater] that will update the element with information about 62 * the version to which it belongs. 63 * @param deprecated whether the class was deprecated in the API version 64 * @return the newly created or a previously existed class 65 */ 66 fun updateClass( 67 name: String, 68 updater: ApiHistoryUpdater, 69 deprecated: Boolean, 70 ): ApiClass { 71 val existing = mClasses[name] 72 val classElement = existing ?: ApiClass(name).apply { mClasses[name] = this } 73 updater.update(classElement, deprecated) 74 return classElement 75 } 76 findClassnull77 fun findClass(name: String?): ApiClass? { 78 return if (name == null) null else mClasses[name] 79 } 80 81 /** Cleans up the API surface for printing after all elements have been added. */ cleannull82 fun clean() { 83 inlineFromHiddenSuperClasses() 84 removeImplicitInterfaces() 85 removeOverridingMethods() 86 prunePackagePrivateClasses() 87 } 88 89 val classes: Collection<ApiClass> 90 get() = Collections.unmodifiableCollection(mClasses.values) 91 92 /** 93 * Patch up the `android.os.ext.SdkExtensions` history to improve backward compatibility. 94 * 95 * This does nothing if the class is not defined in this [Api]. 96 */ patchSdkExtensionsHistorynull97 fun patchSdkExtensionsHistory() { 98 val sdkExtensions = 99 findClass("android/os/ext/SdkExtensions") 100 // This is either for the module-lib/system-server (null) or for a non-Android API. 101 // Either way it does not need patching. 102 ?: return 103 104 val sdk30 = ApiVersion.fromLevel(30) 105 val sdk31 = ApiVersion.fromLevel(31) 106 val sdk33 = ApiVersion.fromLevel(33) 107 val sdkExtensionsSince = sdkExtensions.since 108 if (sdkExtensionsSince != sdk30 && sdkExtensionsSince != sdk33) { 109 throw AssertionError("Received unexpected historical data") 110 } else if (sdkExtensionsSince == sdk30) { 111 // This is the system API db (30). The class does not need patching but the members do. 112 // Drop through. 113 } else { 114 // The class was added in 30/R, but was a SystemApi to avoid publishing the versioning 115 // API publicly before there was any valid use for it. It was made public between S and 116 // T, but we pretend here like it was always public, for maximum backward compatibility. 117 sdkExtensions.update(sdk30, false) 118 } 119 120 val sdk30Updater = ApiHistoryUpdater.forApiVersion(sdk30) 121 val sdk31Updater = ApiHistoryUpdater.forApiVersion(sdk31) 122 123 // Remove the sdks attribute from the extends for public and system. 124 sdkExtensions.updateSuperClass("java/lang/Object", sdk30Updater).apply { 125 // Pretend this was not added in any extension. 126 clearSdkExtensionInfo() 127 } 128 129 // getExtensionVersion was added in 30/R along with the class, and just like the class we 130 // pretend it was always public. 131 sdkExtensions.updateMethod("getExtensionVersion(I)I", sdk30Updater, false) 132 133 // getAllExtensionsVersions was added as part of 31/S SystemApi. Just like for the class 134 // we pretend it was always public. 135 sdkExtensions 136 .updateMethod("getAllExtensionVersions()Ljava/util/Map;", sdk31Updater, false) 137 .apply { 138 // Pretend this was not added in any extension. 139 clearSdkExtensionInfo() 140 } 141 } 142 143 /** 144 * The bytecode visitor registers interfaces listed for a class. However, a class will **also** 145 * implement interfaces implemented by the super classes. This isn't available in the class 146 * file, so after all classes have been read in, we iterate through all classes, and for those 147 * that have interfaces, we check up the inheritance chain to see if it has already been 148 * introduced in a super class at an earlier API level. 149 */ removeImplicitInterfacesnull150 private fun removeImplicitInterfaces() { 151 for (classElement in mClasses.values) { 152 classElement.removeImplicitInterfaces(mClasses) 153 } 154 } 155 156 /** @see ApiClass.removeOverridingMethods */ removeOverridingMethodsnull157 private fun removeOverridingMethods() { 158 for (classElement in mClasses.values) { 159 classElement.removeOverridingMethods(mClasses) 160 } 161 } 162 inlineFromHiddenSuperClassesnull163 private fun inlineFromHiddenSuperClasses() { 164 val hidden: MutableMap<String, ApiClass> = HashMap() 165 for (classElement in mClasses.values) { 166 if (classElement.alwaysHidden) { 167 // hidden in the .jar files? (mMax==codebase, -1: jar files) 168 hidden[classElement.name] = classElement 169 } 170 } 171 for (classElement in mClasses.values) { 172 classElement.inlineFromHiddenSuperClasses(hidden) 173 } 174 } 175 prunePackagePrivateClassesnull176 private fun prunePackagePrivateClasses() { 177 for (cls in mClasses.values) { 178 cls.removeHiddenSuperClasses(mClasses) 179 } 180 } 181 removeMissingClassesnull182 fun removeMissingClasses() { 183 for (cls in mClasses.values) { 184 cls.removeMissingClasses(mClasses) 185 } 186 } 187 verifyNoMissingClassesnull188 fun verifyNoMissingClasses() { 189 val results: MutableMap<String?, MutableSet<String?>> = TreeMap() 190 for (cls in mClasses.values) { 191 val missing = cls.findMissingClasses(mClasses) 192 // Have the missing classes as keys, and the referencing classes as values. 193 for (missingClass in missing) { 194 val missingName = missingClass.name 195 if (!results.containsKey(missingName)) { 196 results[missingName] = TreeSet() 197 } 198 results[missingName]!!.add(cls.name) 199 } 200 } 201 if (results.isNotEmpty()) { 202 var message = "" 203 for ((key, value) in results) { 204 message += """ 205 $key referenced by:""" 206 for (referencer in value) { 207 message += "\n $referencer" 208 } 209 } 210 throw IllegalStateException( 211 "There are classes in this API that reference other " + 212 "classes that do not exist in this API. " + 213 "This can happen when an api is provided by an apex, but referenced " + 214 "from non-updatable platform code. Use --remove-missing-classes-in-api-levels to " + 215 "make metalava remove these references instead of erroring out." + 216 message 217 ) 218 } 219 } 220 } 221