1 /*
2  * Copyright (C) 2010 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.os.cts;
18 
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertFalse;
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.os.Build;
26 import android.os.SystemProperties;
27 import android.platform.test.annotations.AppModeSdkSandbox;
28 import android.platform.test.ravenwood.RavenwoodRule;
29 
30 import com.android.compatibility.common.util.CddTest;
31 
32 import org.junit.Before;
33 import org.junit.Test;
34 
35 import java.io.IOException;
36 import java.util.Arrays;
37 import java.util.HashSet;
38 import java.util.List;
39 import java.util.Scanner;
40 import java.util.Set;
41 import java.util.regex.Pattern;
42 
43 /**
44  * CTS for the {@link Build} class.
45  *
46  * This class contains tests that must pass without having a {@link RavenwoodRule},
47  * so do not add one in this class. {@link #setUp()} has a check to ensure it.
48  *
49  * For tests that do require a {@link RavenwoodRule}, use {@link BuildExtTest} instead.
50  */
51 @AppModeSdkSandbox(reason = "Allow test in the SDK sandbox (does not prevent other modes).")
52 public class BuildTest {
53 
54     static final String RO_PRODUCT_CPU_ABILIST = "ro.product.cpu.abilist";
55     static final String RO_PRODUCT_CPU_ABILIST32 = "ro.product.cpu.abilist32";
56     static final String RO_PRODUCT_CPU_ABILIST64 = "ro.product.cpu.abilist64";
57     static final String DEVICE = "ro.product.device";
58     static final String MANUFACTURER = "ro.product.manufacturer";
59     static final String MODEL = "ro.product.model";
60 
61     @Before
setUp()62     public void setUp() {
63         // Ensure this class doesn't have a RavenwoodRule.
64         for (var field : this.getClass().getFields()) {
65             if (field.getType() == RavenwoodRule.class) {
66                 fail("This clsas is not supposed to have a RavenwoodRule. See the class javadoc.");
67             }
68         }
69     }
70 
71     /**
72      * Check if minimal properties are set (note that these might come from either
73      * /system/build.props or /oem/oem.props.
74      */
75     @Test
76     @CddTest(requirements = {"3.2.2/C-0-1"})
testBuildProperties()77     public void testBuildProperties() throws Exception {
78         assertNotNull("Build.DEVICE should be defined", Build.DEVICE);
79         assertNotNull("Build.MANUFACTURER should be defined", Build.MANUFACTURER);
80         assertNotNull("Build.MODEL should be defined", Build.MODEL);
81     }
82 
83     /**
84      * Verify that the CPU ABI fields on device match the permitted ABIs defined by CDD.
85      */
86     @Test
87     @CddTest(requirements = {"3.3.1/C-0-6"})
testCpuAbi_valuesMatchPermitted()88     public void testCpuAbi_valuesMatchPermitted() throws Exception {
89         for (String abi : Build.SUPPORTED_ABIS) {
90             if (abi.endsWith("-hwasan")) {
91                 // HWASan builds are not official builds and support *-hwasan ABIs.
92                 return;
93             }
94         }
95         // The permitted ABIs are listed in https://developer.android.com/ndk/guides/abis.
96         Set<String> just32 = new HashSet<>(Arrays.asList("armeabi", "armeabi-v7a", "x86"));
97         Set<String> just64 = new HashSet<>(Arrays.asList("x86_64", "arm64-v8a", "riscv64"));
98         Set<String> all = new HashSet<>();
99         all.addAll(just32);
100         all.addAll(just64);
101         Set<String> allAndEmpty = new HashSet<>(all);
102         allAndEmpty.add("");
103 
104         // The cpu abi fields on the device must match the permitted values.
105         assertValueIsAllowed(all, Build.CPU_ABI);
106         // CPU_ABI2 will be empty when the device does not support a secondary CPU architecture.
107         assertValueIsAllowed(allAndEmpty, Build.CPU_ABI2);
108 
109         // The supported abi fields on the device must match the permitted values.
110         assertValuesAreAllowed(all, Build.SUPPORTED_ABIS);
111         assertValuesAreAllowed(just32, Build.SUPPORTED_32_BIT_ABIS);
112         assertValuesAreAllowed(just64, Build.SUPPORTED_64_BIT_ABIS);
113     }
114 
runTestCpuAbiCommon()115     static void runTestCpuAbiCommon() throws Exception {
116         // The build property must match Build.SUPPORTED_ABIS exactly.
117         final String[] abiListProperty = getStringList(RO_PRODUCT_CPU_ABILIST);
118         assertEquals(Arrays.toString(abiListProperty), Arrays.toString(Build.SUPPORTED_ABIS));
119 
120         List<String> abiList = Arrays.asList(abiListProperty);
121 
122         // Every supported 32 bit ABI must be present in Build.SUPPORTED_ABIS.
123         for (String abi : Build.SUPPORTED_32_BIT_ABIS) {
124             assertTrue(abiList.contains(abi));
125             assertFalse(Build.is64BitAbi(abi));
126         }
127 
128         // Every supported 64 bit ABI must be present in Build.SUPPORTED_ABIS.
129         for (String abi : Build.SUPPORTED_64_BIT_ABIS) {
130             assertTrue(abiList.contains(abi));
131             assertTrue(Build.is64BitAbi(abi));
132         }
133 
134         // Build.CPU_ABI and Build.CPU_ABI2 must be present in Build.SUPPORTED_ABIS.
135         assertTrue(abiList.contains(Build.CPU_ABI));
136         if (!Build.CPU_ABI2.isEmpty()) {
137             assertTrue(abiList.contains(Build.CPU_ABI2));
138         }
139     }
140 
runTestCpuAbi32()141     static void runTestCpuAbi32() throws Exception {
142         List<String> abi32 = Arrays.asList(Build.SUPPORTED_32_BIT_ABIS);
143         assertTrue(abi32.contains(Build.CPU_ABI));
144 
145         if (!Build.CPU_ABI2.isEmpty()) {
146             assertTrue(abi32.contains(Build.CPU_ABI2));
147         }
148     }
149 
runTestCpuAbi64()150     static void runTestCpuAbi64() {
151         List<String> abi64 = Arrays.asList(Build.SUPPORTED_64_BIT_ABIS);
152         assertTrue(abi64.contains(Build.CPU_ABI));
153 
154         if (!Build.CPU_ABI2.isEmpty()) {
155             assertTrue(abi64.contains(Build.CPU_ABI2));
156         }
157     }
158 
getStringList(String property)159     static String[] getStringList(String property) throws IOException {
160         String value = getProperty(property);
161         if (value.isEmpty()) {
162             return new String[0];
163         } else {
164             return value.split(",");
165         }
166     }
167 
168     /**
169      * @param property name passed to getprop
170      */
getProperty(String property)171     static String getProperty(String property)
172             throws IOException {
173         Process process = new ProcessBuilder("getprop", property).start();
174         Scanner scanner = null;
175         String line = "";
176         try {
177             scanner = new Scanner(process.getInputStream());
178             line = scanner.nextLine();
179         } finally {
180             if (scanner != null) {
181                 scanner.close();
182             }
183         }
184         return line;
185     }
186 
assertValueIsAllowed(Set<String> allowedValues, String actualValue)187     private static void assertValueIsAllowed(Set<String> allowedValues, String actualValue) {
188         assertTrue("Expected one of " + allowedValues + ", but was: '" + actualValue + "'",
189                 allowedValues.contains(actualValue));
190     }
191 
assertValuesAreAllowed(Set<String> allowedValues, String[] actualValues)192     private static void assertValuesAreAllowed(Set<String> allowedValues, String[] actualValues) {
193         for (String actualValue : actualValues) {
194             assertValueIsAllowed(allowedValues, actualValue);
195         }
196     }
197 
198     private static final Pattern BOARD_PATTERN =
199         Pattern.compile("^([0-9A-Za-z._-]+)$");
200     private static final Pattern BRAND_PATTERN =
201         Pattern.compile("^([0-9A-Za-z._-]+)$");
202     private static final Pattern DEVICE_PATTERN =
203         Pattern.compile("^([0-9A-Za-z._-]+)$");
204     private static final Pattern ID_PATTERN =
205         Pattern.compile("^([0-9A-Za-z._-]+)$");
206     private static final Pattern HARDWARE_PATTERN =
207         Pattern.compile("^([0-9A-Za-z.,_-]+)$");
208     private static final Pattern PRODUCT_PATTERN =
209         Pattern.compile("^([0-9A-Za-z._-]+)$");
210     private static final Pattern SOC_MANUFACTURER_PATTERN =
211         Pattern.compile("^([0-9A-Za-z ]+)$");
212     private static final Pattern SOC_MODEL_PATTERN =
213         Pattern.compile("^([0-9A-Za-z ._/+-]+)$");
214     private static final Pattern SERIAL_NUMBER_PATTERN =
215         Pattern.compile("^([0-9A-Za-z]{6,20})$");
216     private static final Pattern SKU_PATTERN =
217         Pattern.compile("^([0-9A-Za-z.,_-]+)$");
218     private static final Pattern TAGS_PATTERN =
219         Pattern.compile("^([0-9A-Za-z.,_-]+)$");
220     private static final Pattern TYPE_PATTERN =
221         Pattern.compile("^([0-9A-Za-z._-]+)$");
222 
223     /** Tests that check for valid values of constants in Build. */
224     @Test
testBuildConstants()225     public void testBuildConstants() {
226         // Build.VERSION.* constants tested by BuildVersionTest
227 
228         assertTrue(BOARD_PATTERN.matcher(Build.BOARD).matches());
229 
230         assertTrue(BRAND_PATTERN.matcher(Build.BRAND).matches());
231 
232         assertTrue(DEVICE_PATTERN.matcher(Build.DEVICE).matches());
233 
234         // Build.FINGERPRINT tested by BuildVersionTest
235 
236         assertTrue(HARDWARE_PATTERN.matcher(Build.HARDWARE).matches());
237 
238         assertNotEmpty(Build.HOST);
239 
240         assertTrue(ID_PATTERN.matcher(Build.ID).matches());
241 
242         assertNotEmpty(Build.MANUFACTURER);
243 
244         assertNotEmpty(Build.MODEL);
245 
246         assertEquals(Build.SOC_MANUFACTURER, Build.SOC_MANUFACTURER.trim());
247         assertTrue(SOC_MANUFACTURER_PATTERN.matcher(Build.SOC_MANUFACTURER).matches());
248         if (getVendorPartitionVersion() > Build.VERSION_CODES.R) {
249             assertFalse(Build.SOC_MANUFACTURER.equals(Build.UNKNOWN));
250         }
251 
252         assertEquals(Build.SOC_MODEL, Build.SOC_MODEL.trim());
253         assertTrue(SOC_MODEL_PATTERN.matcher(Build.SOC_MODEL).matches());
254         if (getVendorPartitionVersion() > Build.VERSION_CODES.R) {
255             assertFalse(Build.SOC_MODEL.equals(Build.UNKNOWN));
256         }
257 
258         assertTrue(PRODUCT_PATTERN.matcher(Build.PRODUCT).matches());
259 
260         assertTrue(SERIAL_NUMBER_PATTERN.matcher(Build.SERIAL).matches());
261 
262         assertTrue(SKU_PATTERN.matcher(Build.SKU).matches());
263 
264         assertTrue(SKU_PATTERN.matcher(Build.ODM_SKU).matches());
265 
266         assertTrue(TAGS_PATTERN.matcher(Build.TAGS).matches());
267 
268         // No format requirements stated in CDD for Build.TIME
269 
270         assertTrue(TYPE_PATTERN.matcher(Build.TYPE).matches());
271 
272         assertNotEmpty(Build.USER);
273     }
274 
275     /**
276      * Verify that SDK versions are bounded by both high and low expected
277      * values.
278      */
279     @Test
testSdkInt()280     public void testSdkInt() {
281         assertTrue(
282                 "Current SDK version " + Build.VERSION.SDK_INT
283                         + " is invalid; must be at least VERSION_CODES.BASE",
284                 Build.VERSION.SDK_INT >= Build.VERSION_CODES.BASE);
285         assertTrue(
286                 "First SDK version " + Build.VERSION.DEVICE_INITIAL_SDK_INT
287                         + " is invalid; must be at least VERSION_CODES.BASE",
288                 Build.VERSION.DEVICE_INITIAL_SDK_INT >= Build.VERSION_CODES.BASE);
289 
290         // During development of a new release SDK_INT is less than DEVICE_INITIAL_SDK_INT
291         if (Build.VERSION.CODENAME.equals("REL")) {
292             assertTrue(
293                     "Current SDK version " + Build.VERSION.SDK_INT
294                             + " must be at least first SDK version "
295                             + Build.VERSION.DEVICE_INITIAL_SDK_INT,
296                     Build.VERSION.SDK_INT >= Build.VERSION.DEVICE_INITIAL_SDK_INT);
297         }
298     }
299 
300     /**
301      * Verify that MEDIA_PERFORMANCE_CLASS are bounded by both high and low expected values.
302      */
303     @Test
testMediaPerformanceClass()304     public void testMediaPerformanceClass() {
305         // media performance class value of 0 is valid
306         if (Build.VERSION.MEDIA_PERFORMANCE_CLASS == 0) {
307             return;
308         }
309 
310         assertTrue(
311                 "Media Performance Class " + Build.VERSION.MEDIA_PERFORMANCE_CLASS
312                         + " is invalid; must be at least VERSION_CODES.R",
313                 Build.VERSION.MEDIA_PERFORMANCE_CLASS >= Build.VERSION_CODES.R);
314         assertTrue(
315                 "Media Performance Class " + Build.VERSION.MEDIA_PERFORMANCE_CLASS
316                         + " is invalid; must be at most VERSION.SDK_INT",
317                 // we use RESOURCES_SDK_INT to account for active development versions
318                 Build.VERSION.MEDIA_PERFORMANCE_CLASS <= Build.VERSION.RESOURCES_SDK_INT);
319     }
320 
assertNotEmpty(String value)321     private void assertNotEmpty(String value) {
322         assertNotNull(value);
323         assertFalse(value.isEmpty());
324     }
325 
getVendorPartitionVersion()326     private int getVendorPartitionVersion() {
327         String version = SystemProperties.get("ro.vndk.version");
328         try {
329             return Integer.parseInt(version);
330         } catch (NumberFormatException ignore) {
331             return Build.VERSION_CODES.CUR_DEVELOPMENT;
332         }
333     }
334 }
335