blob: 7c4f364661f8490be6c219f96b58d124eca99bbf [file] [log] [blame]
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tools.metalava.apilevels
import com.android.tools.metalava.ARG_ANDROID_JAR_PATTERN
import com.android.tools.metalava.ARG_CURRENT_VERSION
import com.android.tools.metalava.ARG_FIRST_VERSION
import com.android.tools.metalava.ARG_GENERATE_API_LEVELS
import com.android.tools.metalava.ARG_SDK_INFO_FILE
import com.android.tools.metalava.ARG_SDK_JAR_ROOT
import com.android.tools.metalava.doc.getApiLookup
import com.android.tools.metalava.doc.minApiLevel
import java.io.File
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test
class ExtractSystemApiLevelsTest : ApiGeneratorIntegrationTestBase() {
@Test
fun `Extract System API`() {
// These are the wrong jar paths but this test doesn't actually care what the
// content of the jar files, just checking the logic of starting the database
// at some higher number than 1
val androidJarPattern = "${platformJars.path}/%/public/android.jar"
val filter = File.createTempFile("filter", "txt")
filter.deleteOnExit()
filter.writeText(
"""
<sdk-extensions-info>
<!-- SDK definitions -->
<sdk shortname="R" name="R Extensions" id="30" reference="android/os/Build${'$'}VERSION_CODES${'$'}R" />
<sdk shortname="S" name="S Extensions" id="31" reference="android/os/Build${'$'}VERSION_CODES${'$'}S" />
<sdk shortname="T" name="T Extensions" id="33" reference="android/os/Build${'$'}VERSION_CODES${'$'}T" />
<!-- Rules -->
<symbol jar="art.module.public.api" pattern="*" sdks="R" />
<symbol jar="conscrypt.module.intra.core.api " pattern="" sdks="R" />
<symbol jar="conscrypt.module.platform.api" pattern="*" sdks="R" />
<symbol jar="conscrypt.module.public.api" pattern="*" sdks="R" />
<symbol jar="framework-mediaprovider" pattern="*" sdks="R" />
<symbol jar="framework-mediaprovider" pattern="android.provider.MediaStore#canManageMedia" sdks="T" />
<symbol jar="framework-permission-s" pattern="*" sdks="R" />
<symbol jar="framework-permission" pattern="*" sdks="R" />
<symbol jar="framework-sdkextensions" pattern="*" sdks="R" />
<symbol jar="framework-scheduling" pattern="*" sdks="R" />
<symbol jar="framework-statsd" pattern="*" sdks="R" />
<symbol jar="framework-tethering" pattern="*" sdks="R" />
<symbol jar="legacy.art.module.platform.api" pattern="*" sdks="R" />
<symbol jar="service-media-s" pattern="*" sdks="R" />
<symbol jar="service-permission" pattern="*" sdks="R" />
<!-- use framework-permissions-s to test the order of multiple SDKs is respected -->
<symbol jar="android.net.ipsec.ike" pattern="android.net.eap.EapAkaInfo" sdks="R,S,T" />
<symbol jar="android.net.ipsec.ike" pattern="android.net.eap.EapInfo" sdks="T,S,R" />
<symbol jar="android.net.ipsec.ike" pattern="*" sdks="R" />
<!-- framework-connectivity: only android.net.CaptivePortal should have the 'sdks' attribute -->
<symbol jar="framework-connectivity" pattern="android.net.CaptivePortal" sdks="R" />
<!-- framework-media explicitly omitted: nothing in this module should have the 'sdks' attribute -->
</sdk-extensions-info>
"""
.trimIndent()
)
val output = File.createTempFile("api-info", "xml")
output.deleteOnExit()
val outputPath = output.path
check(
extraArguments =
arrayOf(
ARG_GENERATE_API_LEVELS,
outputPath,
ARG_ANDROID_JAR_PATTERN,
androidJarPattern,
ARG_SDK_JAR_ROOT,
"$extensionSdkJars",
ARG_SDK_INFO_FILE,
filter.path,
ARG_FIRST_VERSION,
"21",
ARG_CURRENT_VERSION,
"33"
)
)
assertTrue(output.isFile)
val xml = output.readText(Charsets.UTF_8)
assertTrue(xml.contains("<api version=\"3\" min=\"21\">"))
assertTrue(
xml.contains(
"<sdk id=\"30\" shortname=\"R\" name=\"R Extensions\" reference=\"android/os/Build\$VERSION_CODES\$R\"/>"
)
)
assertTrue(
xml.contains(
"<sdk id=\"31\" shortname=\"S\" name=\"S Extensions\" reference=\"android/os/Build\$VERSION_CODES\$S\"/>"
)
)
assertTrue(
xml.contains(
"<sdk id=\"33\" shortname=\"T\" name=\"T Extensions\" reference=\"android/os/Build\$VERSION_CODES\$T\"/>"
)
)
assertTrue(xml.contains("<class name=\"android/Manifest\" since=\"21\">"))
assertTrue(xml.contains("<field name=\"showWhenLocked\" since=\"27\"/>"))
// top level class marked as since=21 and R=1, implemented in the framework-mediaprovider
// mainline module
assertTrue(
xml.contains(
"<class name=\"android/provider/MediaStore\" module=\"framework-mediaprovider\" since=\"21\" sdks=\"30:1,0:21\">"
)
)
// method with identical sdks attribute as containing class: sdks attribute should be
// omitted
assertTrue(xml.contains("<method name=\"getMediaScannerUri()Landroid/net/Uri;\"/>"))
// method with different sdks attribute than containing class
assertTrue(
xml.contains(
"<method name=\"canManageMedia(Landroid/content/Context;)Z\" since=\"31\" sdks=\"33:1,0:31\"/>"
)
)
val apiLookup = getApiLookup(output)
@Suppress("DEPRECATION") apiLookup.getClassVersion("android.v")
// This field was added in API level 5, but when we're starting the count higher
// (as in the system API), the first introduced API level is the one we use
@Suppress("DEPRECATION")
(assertEquals(
21,
apiLookup.getFieldVersion("android.Manifest\$permission", "AUTHENTICATE_ACCOUNTS")
))
@Suppress("DEPRECATION")
val methodVersion =
apiLookup.getMethodVersion("android/icu/util/CopticCalendar", "computeTime", "()")
assertEquals(24, methodVersion)
// The filter says 'framework-permission-s * R' so RoleManager should exist
// and should have a module/sdks attributes
assertTrue(apiLookup.containsClass("android/app/role/RoleManager"))
// The filter doesn't mention framework-media, so no class in that module should have a
// module/sdks attributes
assertTrue(xml.contains("<class name=\"android/media/MediaFeature\" since=\"31\">"))
// The filter only defines a single API in framework-connectivity: verify that only that API
// has the module/sdks attributes
assertTrue(
xml.contains(
"<class name=\"android/net/CaptivePortal\" module=\"framework-connectivity\" since=\"23\" sdks=\"30:1,0:23\">"
)
)
assertTrue(
xml.contains("<class name=\"android/net/ConnectivityDiagnosticsManager\" since=\"30\">")
)
// The order of the SDKs should be respected
// android.net.eap.EapAkaInfo R S T -> 0,30,31,33
assertTrue(
xml.contains(
"<class name=\"android/net/eap/EapAkaInfo\" module=\"android.net.ipsec.ike\" since=\"33\" sdks=\"30:3,31:3,33:3,0:33\">"
)
)
// android.net.eap.EapInfo T S R -> 0,33,31,30
assertTrue(
xml.contains(
"<class name=\"android/net/eap/EapInfo\" module=\"android.net.ipsec.ike\" since=\"33\" sdks=\"33:3,31:3,30:3,0:33\">"
)
)
// Verify historical backfill
assertEquals(30, apiLookup.getClassVersions("android/os/ext/SdkExtensions").minApiLevel())
assertEquals(
30,
apiLookup
.getMethodVersions("android/os/ext/SdkExtensions", "getExtensionVersion", "(I)I")
.minApiLevel()
)
assertEquals(
31,
apiLookup
.getMethodVersions(
"android/os/ext/SdkExtensions",
"getAllExtensionVersions",
"()Ljava/util/Map;"
)
.minApiLevel()
)
// Verify there's no extension versions listed for SdkExtensions
val sdkExtClassLine =
xml.lines().first { it.contains("<class name=\"android/os/ext/SdkExtensions\"") }
assertFalse(sdkExtClassLine.contains("sdks="))
}
}