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