1 /* 2 * Copyright (C) 2020 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 com.android.tests.fastboot_getvar; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertNotEquals; 21 import static org.junit.Assert.assertNotNull; 22 import static org.junit.Assert.assertTrue; 23 import static org.junit.Assert.fail; 24 25 import android.platform.test.annotations.RequiresDevice; 26 import com.android.tradefed.device.DeviceProperties; 27 import com.android.tradefed.device.ITestDevice; 28 import com.android.tradefed.invoker.TestInformation; 29 import com.android.tradefed.log.LogUtil.CLog; 30 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; 31 import com.android.tradefed.testtype.junit4.AfterClassWithInfo; 32 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; 33 import com.android.tradefed.testtype.junit4.BeforeClassWithInfo; 34 import java.text.ParseException; 35 import java.text.SimpleDateFormat; 36 import java.util.Arrays; 37 import java.util.HashSet; 38 import java.util.concurrent.ConcurrentHashMap; 39 import java.util.regex.Matcher; 40 import java.util.regex.Pattern; 41 import org.junit.Assert; 42 import org.junit.Assume; 43 import org.junit.Before; 44 import org.junit.Test; 45 import org.junit.runner.RunWith; 46 47 /* VTS test to verify userspace fastboot getvar information. */ 48 @RequiresDevice 49 @RunWith(DeviceJUnit4ClassRunner.class) 50 public class FastbootGetvarUserspaceTest extends BaseHostJUnit4Test { 51 private static final int PLATFORM_API_LEVEL_R = 30; 52 private static final int ANDROID_RELEASE_VERSION_R = 11; 53 54 private static String executeShellKernelARM64 = 55 "cat /proc/config.gz | gzip -d | grep CONFIG_ARM64=y"; 56 57 // IMPORTANT: Multiple instances of this class will be created while sharding 58 // the tests across multiple devices. So it needs to use ConcurrentHashMap to 59 // make these static variables thread-safe. 60 private static ConcurrentHashMap<ITestDevice, String> sDeviceCodeName = 61 new ConcurrentHashMap<>(); 62 private static ConcurrentHashMap<ITestDevice, Boolean> sDeviceIsGKI10 = 63 new ConcurrentHashMap<>(); 64 65 @BeforeClassWithInfo setUpClass(TestInformation testInfo)66 public static void setUpClass(TestInformation testInfo) throws Exception { 67 // Collects information while adb is available, prior to rebooting into fastbootd. 68 boolean isKernelARM64 = testInfo.getDevice() 69 .executeShellCommand(executeShellKernelARM64) 70 .contains("CONFIG_ARM64"); 71 boolean isGKI10 = false; 72 if (isKernelARM64) { 73 String output = testInfo.getDevice().executeShellCommand("uname -r"); 74 Pattern p = Pattern.compile("^(\\d+)\\.(\\d+)"); 75 Matcher m1 = p.matcher(output); 76 assertTrue(m1.find()); 77 isGKI10 = (Integer.parseInt(m1.group(1)) == 5 && Integer.parseInt(m1.group(2)) == 4); 78 } 79 80 // Gets the code name via adb first. The following test cases might 81 // assert different values based on if the build is a final release build 82 // or not, where the value of the code name will be "REL" in this case. 83 String codeName = testInfo.getDevice().getProperty(DeviceProperties.BUILD_CODENAME); 84 assertNotNull(codeName); 85 codeName = codeName.trim(); 86 87 // Saves the local variables to static variables for later use, because adb 88 // is not available in the following tests. 89 sDeviceIsGKI10.put(testInfo.getDevice(), isGKI10); 90 sDeviceCodeName.put(testInfo.getDevice(), codeName); 91 92 // Transfers from adb to fastbootd. 93 if (!isGKI10) { 94 testInfo.getDevice().rebootIntoFastbootd(); 95 } 96 } 97 98 @Before setUp()99 public void setUp() throws Exception { 100 Assume.assumeFalse("Skipping test for fastbootd on GKI 1.0", 101 sDeviceIsGKI10.get(getTestInformation().getDevice())); 102 } 103 104 @AfterClassWithInfo tearDownClass(TestInformation testInfo)105 public static void tearDownClass(TestInformation testInfo) throws Exception { 106 if (!sDeviceIsGKI10.get(testInfo.getDevice())) { 107 testInfo.getDevice().reboot(); // reboot from fastbootd to adb. 108 } 109 sDeviceIsGKI10.remove(testInfo.getDevice()); 110 sDeviceCodeName.remove(testInfo.getDevice()); 111 } 112 113 /* Devices launching in R and after must export cpu-abi. */ 114 @Test testCpuAbiInfo()115 public void testCpuAbiInfo() throws Exception { 116 final HashSet<String> allCpuAbis = new HashSet<String>( 117 Arrays.asList("armeabi-v7a", "arm64-v8a", "mips", "mips64", "x86", "x86_64")); 118 String cpuAbi = getTestInformation().getDevice().getFastbootVariable("cpu-abi"); 119 CLog.d("cpuAbi: '%s'", cpuAbi); 120 assertTrue(allCpuAbis.contains(cpuAbi)); 121 } 122 123 /* Devices launching in R and after must export version-os. */ 124 @Test testOsVersion()125 public void testOsVersion() throws Exception { 126 String osVersion = getTestInformation().getDevice().getFastbootVariable("version-os"); 127 CLog.d("os version: '%s'", osVersion); 128 // The value of osVersion is derived from "ro.build.version.release", 129 // which is a user-visible version string. The value does not have 130 // has any particular structure. See https://r.android.com/657597 for 131 // details. 132 assertNotNull(osVersion); 133 } 134 135 /* Devices launching in R and after must export version-vndk. */ 136 @Test testVndkVersion()137 public void testVndkVersion() throws Exception { 138 String vndkVersion = getTestInformation().getDevice().getFastbootVariable("version-vndk"); 139 String codeName = sDeviceCodeName.get(getTestInformation().getDevice()); 140 CLog.d("vndk version: '%s', code name: '%s'", vndkVersion, codeName); 141 // The value of vndkVersion might be a letter or a string on pre-release builds, 142 // e.g., R or Tiramisu. 143 // And it is a number representing the API level on final release builds, e.g., 30. 144 if ("REL".equals(codeName)) { 145 try { 146 int intVndkVersion = Integer.parseInt(vndkVersion); 147 assertTrue(intVndkVersion >= PLATFORM_API_LEVEL_R); 148 } catch (NumberFormatException nfe) { 149 fail("ro.vndk.version should be a number. But the current value is " + vndkVersion); 150 } 151 } else { 152 assertNotNull(vndkVersion); 153 } 154 } 155 156 /* Devices launching in R and after must export dynamic-partition. */ 157 @Test testDynamicPartition()158 public void testDynamicPartition() throws Exception { 159 String dynamic_partition = 160 getTestInformation().getDevice().getFastbootVariable("dynamic-partition"); 161 CLog.d("dynamic_partition: '%s'", dynamic_partition); 162 assertTrue(dynamic_partition.equals("true")); 163 } 164 165 /* Devices launching in R and after must export treble-enabled. */ 166 @Test testTrebleEnable()167 public void testTrebleEnable() throws Exception { 168 String treble_enabled = 169 getTestInformation().getDevice().getFastbootVariable("treble-enabled"); 170 CLog.d("treble_enabled: '%s'", treble_enabled); 171 assertTrue(treble_enabled.equals("true") || treble_enabled.equals("false")); 172 } 173 174 /* Devices launching in R and after must export first-api-level. */ 175 @Test testFirstApiLevel()176 public void testFirstApiLevel() throws Exception { 177 String first_api_level = 178 getTestInformation().getDevice().getFastbootVariable("first-api-level"); 179 CLog.d("first_api_level: '%s'", first_api_level); 180 try { 181 int api_level = Integer.parseInt(first_api_level); 182 assertTrue(api_level >= PLATFORM_API_LEVEL_R); 183 } catch (NumberFormatException nfe) { 184 fail("Failed to parse first-api-level: " + first_api_level); 185 } 186 } 187 188 /* Devices launching in R and after must export security-patch-level. */ 189 @Test testSecurityPatchLevel()190 public void testSecurityPatchLevel() throws Exception { 191 String SPL = getTestInformation().getDevice().getFastbootVariable("security-patch-level"); 192 CLog.d("SPL: '%s'", SPL); 193 try { 194 SimpleDateFormat template = new SimpleDateFormat("yyyy-MM-dd"); 195 template.parse(SPL); 196 } catch (ParseException e) { 197 fail("Failed to parse security-patch-level: " + SPL); 198 } 199 } 200 201 /* Devices launching in R and after must export system-fingerprint. */ 202 @Test testSystemFingerprint()203 public void testSystemFingerprint() throws Exception { 204 String systemFingerprint = 205 getTestInformation().getDevice().getFastbootVariable("system-fingerprint"); 206 CLog.d("system fingerprint: '%s'", systemFingerprint); 207 verifyFingerprint(systemFingerprint); 208 } 209 210 /* Devices launching in R and after must export vendor-fingerprint. */ 211 @Test testVendorFingerprint()212 public void testVendorFingerprint() throws Exception { 213 String vendorFingerprint = 214 getTestInformation().getDevice().getFastbootVariable("vendor-fingerprint"); 215 CLog.d("vendor fingerprint: '%s'", vendorFingerprint); 216 verifyFingerprint(vendorFingerprint); 217 } 218 219 /* 220 * Verifies the fingerprint defined in CDD. 221 * https://source.android.com/compatibility/cdd 222 * 223 * The fingerprint should be of format: 224 * $(BRAND)/$(PRODUCT)/$(DEVICE):$(VERSION.RELEASE)/$(ID)/$(VERSION.INCREMENTAL):$(TYPE)/$(TAGS). 225 */ verifyFingerprint(String fingerprint)226 private void verifyFingerprint(String fingerprint) { 227 final HashSet<String> allBuildVariants = 228 new HashSet<String>(Arrays.asList("user", "userdebug", "eng")); 229 230 final HashSet<String> allTags = 231 new HashSet<String>(Arrays.asList("release-keys", "dev-keys", "test-keys")); 232 233 verifyFingerprintStructure(fingerprint); 234 235 String[] fingerprintSegs = fingerprint.split("/"); 236 assertTrue(fingerprintSegs[0].matches("^[a-zA-Z0-9_-]+$")); // BRAND 237 assertTrue(fingerprintSegs[1].matches("^[a-zA-Z0-9_-]+$")); // PRODUCT 238 239 String[] devicePlatform = fingerprintSegs[2].split(":"); 240 assertEquals(2, devicePlatform.length); 241 assertTrue(devicePlatform[0].matches("^[a-zA-Z0-9_-]+$")); // DEVICE 242 243 assertTrue(fingerprintSegs[3].matches("^[a-zA-Z0-9._-]+$")); // ID 244 245 String[] buildNumberVariant = fingerprintSegs[4].split(":"); 246 assertTrue(buildNumberVariant[0].matches("^[^ :\\/~]+$")); // VERSION.INCREMENTAL 247 assertTrue(allBuildVariants.contains(buildNumberVariant[1])); // TYPE 248 249 assertTrue(allTags.contains(fingerprintSegs[5])); // TAG 250 } 251 verifyFingerprintStructure(String fingerprint)252 private void verifyFingerprintStructure(String fingerprint) { 253 assertEquals("Build fingerprint must not include whitespace", -1, fingerprint.indexOf(' ')); 254 255 String[] segments = fingerprint.split("/"); 256 assertEquals("Build fingerprint does not match expected format", 6, segments.length); 257 258 String[] devicePlatform = segments[2].split(":"); 259 assertEquals(2, devicePlatform.length); 260 261 assertTrue(segments[4].contains(":")); 262 String buildVariant = segments[4].split(":")[1]; 263 assertTrue(buildVariant.length() > 0); 264 } 265 } 266