1 /* 2 * Copyright (C) 2021 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 17 package android.content.pm.parsing.cts.host 18 19 import android.content.pm.parsing.cts.generator.api.AndroidManifestXml 20 import android.content.pm.parsing.cts.generator.api.ApkGenerator 21 import android.platform.test.annotations.RequiresFlagsEnabled 22 import android.platform.test.flag.junit.CheckFlagsRule 23 import android.platform.test.flag.junit.host.HostFlagsValueProvider 24 import android.sdk.Flags.FLAG_MAJOR_MINOR_VERSIONING_SCHEME 25 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner 26 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test 27 import com.google.common.truth.Truth.assertThat 28 import com.google.common.truth.Truth.assertWithMessage 29 import org.junit.After 30 import org.junit.Before 31 import org.junit.BeforeClass 32 import org.junit.ClassRule 33 import org.junit.Rule 34 import org.junit.Test 35 import org.junit.rules.TemporaryFolder 36 import org.junit.runner.RunWith 37 38 @RunWith(DeviceJUnit4ClassRunner::class) 39 class UsesSdkTest : BaseHostJUnit4Test() { 40 41 companion object { 42 43 @JvmField 44 @ClassRule 45 val tempFolder = TemporaryFolder() 46 47 @JvmStatic 48 @BeforeClass preloadApksnull49 fun preloadApks() { 50 ApkGenerator.initialize(tempFolder) 51 } 52 } 53 54 @Rule 55 @JvmField 56 val mCheckFlagsRule: CheckFlagsRule = <lambda>null57 HostFlagsValueProvider.createCheckFlagsRule { this.device } 58 59 @Before 60 @After uninstallnull61 fun uninstall() { 62 ApkGenerator.uninstallAll(device, tempFolder) 63 } 64 65 @Test defaultsNoTagnull66 fun defaultsNoTag() { 67 // language=XML 68 @AndroidManifestXml 69 val xml = """ 70 <manifest> 71 <application/> 72 </manifest> 73 """ 74 val result = ApkGenerator.install(device, xml, tempFolder) 75 assertThat(result.error).isEmpty() 76 assertSdks(result, min = 1, target = 0) 77 } 78 79 @Test defaultsWithTagnull80 fun defaultsWithTag() { 81 // language=XML 82 @AndroidManifestXml 83 val xml = """ 84 <manifest> 85 <uses-sdk/> 86 <application/> 87 </manifest> 88 """ 89 val result = ApkGenerator.install(device, xml, tempFolder) 90 assertThat(result.error).isEmpty() 91 assertSdks(result, min = 1, target = 1) 92 } 93 94 @Test missingMinDefaultsnull95 fun missingMinDefaults() { 96 // language=XML 97 @AndroidManifestXml 98 val xml = """ 99 <manifest xmlns:android="http://schemas.android.com/apk/res/android"> 100 <uses-sdk android:targetSdkVersion="29"/> 101 <application/> 102 </manifest> 103 """ 104 val result = ApkGenerator.install(device, xml, tempFolder) 105 assertThat(result.error).isEmpty() 106 assertSdks(result, min = 1, target = 29) 107 } 108 109 @Test missingTargetCoercedToMinnull110 fun missingTargetCoercedToMin() { 111 // language=XML 112 @AndroidManifestXml 113 val xml = """ 114 <manifest xmlns:android="http://schemas.android.com/apk/res/android"> 115 <uses-sdk android:minSdkVersion="29"/> 116 <application/> 117 </manifest> 118 """ 119 val result = ApkGenerator.install(device, xml, tempFolder) 120 assertThat(result.error).isEmpty() 121 assertSdks(result, min = 29, target = 29) 122 } 123 124 @Test takeAndroidTargetSdknull125 fun takeAndroidTargetSdk() { 126 // language=XML 127 @AndroidManifestXml 128 val xml = """ 129 <manifest xmlns:android="http://schemas.android.com/apk/res/android"> 130 <uses-sdk targetSdkVersion="27" android:targetSdkVersion="27"> 131 <extension-sdk android:sdkVersion="30" android:minExtensionVersion="0"/> 132 </uses-sdk> 133 <uses-sdk android:targetSdkVersion="29" targetSdkVersion="27"> 134 <extension-sdk android:sdkVersion="31" android:minExtensionVersion="0"/> 135 </uses-sdk> 136 <application/> 137 </manifest> 138 """ 139 val result = ApkGenerator.install(device, xml, tempFolder) 140 assertThat(result.error).isEmpty() 141 assertSdks(result, target = 29, extensions = mapOf(31 to 0)) 142 } 143 144 @Test emptyUsesSdknull145 fun emptyUsesSdk() { 146 // language=XML 147 @AndroidManifestXml 148 val xml = """ 149 <manifest xmlns:android="http://schemas.android.com/apk/res/android"> 150 <uses-sdk android:targetSdkVersion="29"> 151 <extension-sdk android:sdkVersion="30" android:minExtensionVersion="0"/> 152 </uses-sdk> 153 <uses-sdk targetSdkVersion="27"/> 154 <application/> 155 </manifest> 156 """ 157 val result = ApkGenerator.install(device, xml, tempFolder) 158 assertThat(result.error).isEmpty() 159 assertSdks(result, min = 1, target = 1, extensions = emptyMap()) 160 } 161 162 @Test takeLastTargetSdknull163 fun takeLastTargetSdk() { 164 // language=XML 165 @AndroidManifestXml 166 val xml = """ 167 <manifest xmlns:android="http://schemas.android.com/apk/res/android"> 168 <uses-sdk android:targetSdkVersion="28"/> 169 <uses-sdk android:targetSdkVersion="29"/> 170 <application/> 171 </manifest> 172 """ 173 val result = ApkGenerator.install(device, xml, tempFolder) 174 assertThat(result.error).isEmpty() 175 assertSdks(result, target = 29) 176 } 177 178 @Test takeLastMinSdknull179 fun takeLastMinSdk() { 180 // language=XML 181 @AndroidManifestXml 182 val xml = """ 183 <manifest xmlns:android="http://schemas.android.com/apk/res/android"> 184 <uses-sdk android:minSdkVersion="28"/> 185 <uses-sdk android:minSdkVersion="29"/> 186 <application/> 187 </manifest> 188 """ 189 val result = ApkGenerator.install(device, xml, tempFolder) 190 assertThat(result.error).isEmpty() 191 // Target gets coerced to 29 since it's not possible to target below min 192 assertSdks(result, min = 29, target = 29) 193 } 194 195 @Test takeLastExtensionSdknull196 fun takeLastExtensionSdk() { 197 // language=XML 198 @AndroidManifestXml 199 val xml = """ 200 <manifest xmlns:android="http://schemas.android.com/apk/res/android"> 201 <uses-sdk> 202 <extension-sdk android:sdkVersion="30" android:minExtensionVersion="0"/> 203 </uses-sdk> 204 <uses-sdk> 205 <extension-sdk android:sdkVersion="31" android:minExtensionVersion="0"/> 206 </uses-sdk> 207 <application/> 208 </manifest> 209 """ 210 val result = ApkGenerator.install(device, xml, tempFolder) 211 assertThat(result.error).isEmpty() 212 assertSdks(result, min = 1, target = 1, extensions = mapOf(31 to 0)) 213 } 214 215 @Test lastDeclarationOverridesAllPreviousnull216 fun lastDeclarationOverridesAllPrevious() { 217 // language=XML 218 @AndroidManifestXml 219 val xml = """ 220 <manifest xmlns:android="http://schemas.android.com/apk/res/android"> 221 <uses-sdk android:minSdkVersion="21"> 222 <extension-sdk android:sdkVersion="30" android:minExtensionVersion="0"/> 223 </uses-sdk> 224 <uses-sdk android:targetSdkVersion="30"/> 225 <application/> 226 </manifest> 227 """ 228 val result = ApkGenerator.install(device, xml, tempFolder) 229 assertThat(result.error).isEmpty() 230 assertSdks(result, min = 1, target = 30, extensions = emptyMap()) 231 } 232 233 @Test takeLastAfterAppTagnull234 fun takeLastAfterAppTag() { 235 // language=XML 236 @AndroidManifestXml 237 val xml = """ 238 <manifest xmlns:android="http://schemas.android.com/apk/res/android"> 239 <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"> 240 <extension-sdk android:sdkVersion="30" android:minExtensionVersion="0"/> 241 </uses-sdk> 242 <application/> 243 <uses-sdk android:minSdkVersion="30" android:targetSdkVersion="30"> 244 <extension-sdk android:sdkVersion="31" android:minExtensionVersion="0"/> 245 </uses-sdk> 246 </manifest> 247 """ 248 val result = ApkGenerator.install(device, xml, tempFolder) 249 assertThat(result.error).isEmpty() 250 assertSdks(result, min = 30, target = 30, extensions = mapOf(31 to 0)) 251 } 252 253 @Test 254 @RequiresFlagsEnabled(FLAG_MAJOR_MINOR_VERSIONING_SCHEME) minSdkVersionMinorVersionImplicitlySetTo0null255 fun minSdkVersionMinorVersionImplicitlySetTo0() { 256 // language=XML 257 @AndroidManifestXml 258 val xml = """ 259 <manifest xmlns:android="http://schemas.android.com/apk/res/android"> 260 <uses-sdk android:minSdkVersionFull="35"/> 261 <application/> 262 </manifest> 263 """ 264 val result = ApkGenerator.install(device, xml, tempFolder) 265 assertThat(result.error).isEmpty() 266 } 267 268 @Test 269 @RequiresFlagsEnabled(FLAG_MAJOR_MINOR_VERSIONING_SCHEME) minSdkVersionMinorVersionSetTo0null270 fun minSdkVersionMinorVersionSetTo0() { 271 // language=XML 272 @AndroidManifestXml 273 val xml = """ 274 <manifest xmlns:android="http://schemas.android.com/apk/res/android"> 275 <uses-sdk android:minSdkVersionFull="35.0"/> 276 <application/> 277 </manifest> 278 """ 279 val result = ApkGenerator.install(device, xml, tempFolder) 280 assertThat(result.error).isEmpty() 281 } 282 283 @Test 284 @RequiresFlagsEnabled(FLAG_MAJOR_MINOR_VERSIONING_SCHEME) minSdkVersionMinorVersionSetTo1null285 fun minSdkVersionMinorVersionSetTo1() { 286 // language=XML 287 @AndroidManifestXml 288 val xml = """ 289 <manifest xmlns:android="http://schemas.android.com/apk/res/android"> 290 <uses-sdk android:minSdkVersionFull="35.1"/> 291 <application/> 292 </manifest> 293 """ 294 val result = ApkGenerator.install(device, xml, tempFolder) 295 assertThat(result.error) 296 .contains("Requires newer sdk version 35.1 (current version is 35.0)") 297 } 298 assertSdksnull299 private fun assertSdks( 300 result: ApkGenerator.InstallResult, 301 min: Int = 1, 302 target: Int = 1, 303 extensions: Map<Int, Int> = emptyMap() 304 ) { 305 val output = device.executeShellCommand("dumpsys package ${result.packageName}") 306 assertThat(output).contains("minSdk=$min") 307 assertThat(output).contains("targetSdk=$target") 308 309 val minExtensionVersions = output.lineSequence() 310 .dropWhile { !it.startsWith("Packages:") } 311 .map { it.trim() } 312 .first { it.startsWith("minExtensionVersions=") } 313 .removePrefix("minExtensionVersions=[") 314 .removeSuffix("]") 315 .trim() 316 .takeIf(String::isNotEmpty) 317 ?.split(",") 318 ?.associate { 319 it.split("=") 320 .map(String::trim) 321 .map(String::toInt) 322 .let { it.first() to it.last() } 323 } 324 ?: emptyMap() 325 326 assertWithMessage(output).that(minExtensionVersions).containsExactlyEntriesIn(extensions) 327 } 328 } 329