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