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 android.os.Build.VERSION.CODENAME; 20 import static android.os.Build.VERSION_CODES.CUR_DEVELOPMENT; 21 22 import android.os.Build; 23 import android.platform.test.annotations.RestrictedBuildTest; 24 25 import dalvik.system.VMRuntime; 26 27 import junit.framework.TestCase; 28 29 import java.io.IOException; 30 import java.lang.reflect.Field; 31 import java.lang.reflect.Modifier; 32 import java.util.Arrays; 33 import java.util.List; 34 import java.util.Scanner; 35 import java.util.regex.Pattern; 36 37 public class BuildTest extends TestCase { 38 39 private static final String RO_PRODUCT_CPU_ABILIST = "ro.product.cpu.abilist"; 40 private static final String RO_PRODUCT_CPU_ABILIST32 = "ro.product.cpu.abilist32"; 41 private static final String RO_PRODUCT_CPU_ABILIST64 = "ro.product.cpu.abilist64"; 42 43 /** Tests that check the values of {@link Build#CPU_ABI} and {@link Build#CPU_ABI2}. */ 44 public void testCpuAbi() throws Exception { 45 runTestCpuAbiCommon(); 46 if (VMRuntime.getRuntime().is64Bit()) { 47 runTestCpuAbi64(); 48 } else { 49 runTestCpuAbi32(); 50 } 51 } 52 53 private void runTestCpuAbiCommon() throws Exception { 54 // The build property must match Build.SUPPORTED_ABIS exactly. 55 final String[] abiListProperty = getStringList(RO_PRODUCT_CPU_ABILIST); 56 assertEquals(Arrays.toString(abiListProperty), Arrays.toString(Build.SUPPORTED_ABIS)); 57 58 List<String> abiList = Arrays.asList(abiListProperty); 59 60 // Every device must support at least one 32 bit ABI. 61 assertTrue(Build.SUPPORTED_32_BIT_ABIS.length > 0); 62 63 // Every supported 32 bit ABI must be present in Build.SUPPORTED_ABIS. 64 for (String abi : Build.SUPPORTED_32_BIT_ABIS) { 65 assertTrue(abiList.contains(abi)); 66 assertFalse(VMRuntime.is64BitAbi(abi)); 67 } 68 69 // Every supported 64 bit ABI must be present in Build.SUPPORTED_ABIS. 70 for (String abi : Build.SUPPORTED_64_BIT_ABIS) { 71 assertTrue(abiList.contains(abi)); 72 assertTrue(VMRuntime.is64BitAbi(abi)); 73 } 74 75 // Build.CPU_ABI and Build.CPU_ABI2 must be present in Build.SUPPORTED_ABIS. 76 assertTrue(abiList.contains(Build.CPU_ABI)); 77 if (!Build.CPU_ABI2.isEmpty()) { 78 assertTrue(abiList.contains(Build.CPU_ABI2)); 79 } 80 } 81 82 private void runTestCpuAbi32() throws Exception { 83 List<String> abi32 = Arrays.asList(Build.SUPPORTED_32_BIT_ABIS); 84 assertTrue(abi32.contains(Build.CPU_ABI)); 85 86 if (!Build.CPU_ABI2.isEmpty()) { 87 assertTrue(abi32.contains(Build.CPU_ABI2)); 88 } 89 } 90 91 private void runTestCpuAbi64() { 92 List<String> abi64 = Arrays.asList(Build.SUPPORTED_64_BIT_ABIS); 93 assertTrue(abi64.contains(Build.CPU_ABI)); 94 95 if (!Build.CPU_ABI2.isEmpty()) { 96 assertTrue(abi64.contains(Build.CPU_ABI2)); 97 } 98 } 99 100 private String[] getStringList(String property) throws IOException { 101 String value = getProperty(property); 102 if (value.isEmpty()) { 103 return new String[0]; 104 } else { 105 return value.split(","); 106 } 107 } 108 109 /** 110 * @param property name passed to getprop 111 */ 112 static String getProperty(String property) 113 throws IOException { 114 Process process = new ProcessBuilder("getprop", property).start(); 115 Scanner scanner = null; 116 String line = ""; 117 try { 118 scanner = new Scanner(process.getInputStream()); 119 line = scanner.nextLine(); 120 } finally { 121 if (scanner != null) { 122 scanner.close(); 123 } 124 } 125 return line; 126 } 127 /** 128 * @param message shown when the test fails 129 * @param property name passed to getprop 130 * @param expected value of the property 131 */ 132 private void assertProperty(String message, String property, String expected) 133 throws IOException { 134 Process process = new ProcessBuilder("getprop", property).start(); 135 Scanner scanner = null; 136 try { 137 scanner = new Scanner(process.getInputStream()); 138 String line = scanner.nextLine(); 139 assertEquals(message + " Value found: " + line , expected, line); 140 assertFalse(scanner.hasNext()); 141 } finally { 142 if (scanner != null) { 143 scanner.close(); 144 } 145 } 146 } 147 148 /** 149 * Check that a property is not set by scanning through the list of properties returned by 150 * getprop, since calling getprop on an property set to "" and on a non-existent property 151 * yields the same output. 152 * 153 * @param message shown when the test fails 154 * @param property name passed to getprop 155 */ 156 private void assertNoPropertySet(String message, String property) throws IOException { 157 Process process = new ProcessBuilder("getprop").start(); 158 Scanner scanner = null; 159 try { 160 scanner = new Scanner(process.getInputStream()); 161 while (scanner.hasNextLine()) { 162 String line = scanner.nextLine(); 163 assertFalse(message + "Property found: " + line, 164 line.startsWith("[" + property + "]")); 165 } 166 } finally { 167 if (scanner != null) { 168 scanner.close(); 169 } 170 } 171 } 172 173 private static final Pattern BOARD_PATTERN = 174 Pattern.compile("^([0-9A-Za-z._-]+)$"); 175 private static final Pattern BRAND_PATTERN = 176 Pattern.compile("^([0-9A-Za-z._-]+)$"); 177 private static final Pattern DEVICE_PATTERN = 178 Pattern.compile("^([0-9A-Za-z._-]+)$"); 179 private static final Pattern ID_PATTERN = 180 Pattern.compile("^([0-9A-Za-z._-]+)$"); 181 private static final Pattern HARDWARE_PATTERN = 182 Pattern.compile("^([0-9A-Za-z.,_-]+)$"); 183 private static final Pattern PRODUCT_PATTERN = 184 Pattern.compile("^([0-9A-Za-z._-]+)$"); 185 private static final Pattern SERIAL_NUMBER_PATTERN = 186 Pattern.compile("^([0-9A-Za-z]{6,20})$"); 187 private static final Pattern TAGS_PATTERN = 188 Pattern.compile("^([0-9A-Za-z.,_-]+)$"); 189 private static final Pattern TYPE_PATTERN = 190 Pattern.compile("^([0-9A-Za-z._-]+)$"); 191 192 /** Tests that check for valid values of constants in Build. */ 193 public void testBuildConstants() { 194 // Build.VERSION.* constants tested by BuildVersionTest 195 196 assertTrue(BOARD_PATTERN.matcher(Build.BOARD).matches()); 197 198 assertTrue(BRAND_PATTERN.matcher(Build.BRAND).matches()); 199 200 assertTrue(DEVICE_PATTERN.matcher(Build.DEVICE).matches()); 201 202 // Build.FINGERPRINT tested by BuildVersionTest 203 204 assertTrue(HARDWARE_PATTERN.matcher(Build.HARDWARE).matches()); 205 206 assertNotEmpty(Build.HOST); 207 208 assertTrue(ID_PATTERN.matcher(Build.ID).matches()); 209 210 assertNotEmpty(Build.MANUFACTURER); 211 212 assertNotEmpty(Build.MODEL); 213 214 assertTrue(PRODUCT_PATTERN.matcher(Build.PRODUCT).matches()); 215 216 assertTrue(SERIAL_NUMBER_PATTERN.matcher(Build.SERIAL).matches()); 217 218 assertTrue(TAGS_PATTERN.matcher(Build.TAGS).matches()); 219 220 // No format requirements stated in CDD for Build.TIME 221 222 assertTrue(TYPE_PATTERN.matcher(Build.TYPE).matches()); 223 224 assertNotEmpty(Build.USER); 225 226 // CUR_DEVELOPMENT must be larger than any released version. 227 Field[] fields = Build.VERSION_CODES.class.getDeclaredFields(); 228 for (Field field : fields) { 229 if (field.getType().equals(int.class) && Modifier.isStatic(field.getModifiers())) { 230 String fieldName = field.getName(); 231 final int fieldValue; 232 try { 233 fieldValue = field.getInt(null); 234 } catch (IllegalAccessException e) { 235 throw new AssertionError(e.getMessage()); 236 } 237 if (fieldName.equals("CUR_DEVELOPMENT")) { 238 // It should be okay to change the value of this constant in future, but it 239 // should at least be a conscious decision. 240 assertEquals(10000, fieldValue); 241 } else if (fieldName.equals(CODENAME) && !CODENAME.equals("REL")) { 242 // This is the current development version. Note that fieldName can 243 // become < CUR_DEVELOPMENT before CODENAME becomes "REL", so we 244 // can't assertEquals(CUR_DEVELOPMENT, fieldValue) here. 245 assertTrue("Expected " + fieldName + " value to be <= " + CUR_DEVELOPMENT 246 + ", got " + fieldValue, fieldValue <= CUR_DEVELOPMENT); 247 } else { 248 assertTrue("Expected " + fieldName + " value to be < " + CUR_DEVELOPMENT 249 + ", got " + fieldValue, fieldValue < CUR_DEVELOPMENT); 250 } 251 } 252 } 253 } 254 255 /** 256 * Verify that SDK versions are bounded by both high and low expected 257 * values. 258 */ 259 public void testSdkInt() { 260 assertTrue( 261 "Current SDK version " + Build.VERSION.SDK_INT 262 + " is invalid; must be at least VERSION_CODES.BASE", 263 Build.VERSION.SDK_INT >= Build.VERSION_CODES.BASE); 264 assertTrue( 265 "First SDK version " + Build.VERSION.FIRST_SDK_INT 266 + " is invalid; must be at least VERSION_CODES.BASE", 267 Build.VERSION.FIRST_SDK_INT >= Build.VERSION_CODES.BASE); 268 assertTrue( 269 "Current SDK version " + Build.VERSION.SDK_INT 270 + " must be at least first SDK version " + Build.VERSION.FIRST_SDK_INT, 271 Build.VERSION.SDK_INT >= Build.VERSION.FIRST_SDK_INT); 272 } 273 274 static final String RO_DEBUGGABLE = "ro.debuggable"; 275 private static final String RO_SECURE = "ro.secure"; 276 277 /** 278 * Assert that the device is a secure, not debuggable user build. 279 * 280 * Debuggable devices allow adb root and have the su command, allowing 281 * escalations to root and unauthorized access to application data. 282 * 283 * Note: This test will fail on userdebug / eng devices, but should pass 284 * on production (user) builds. 285 */ 286 @RestrictedBuildTest 287 public void testIsSecureUserBuild() throws IOException { 288 assertEquals("Must be a user build", "user", Build.TYPE); 289 assertProperty("Must be a non-debuggable build", RO_DEBUGGABLE, "0"); 290 assertProperty("Must be a secure build", RO_SECURE, "1"); 291 } 292 293 private void assertNotEmpty(String value) { 294 assertNotNull(value); 295 assertFalse(value.isEmpty()); 296 } 297 } 298