1 /* 2 * Copyright (C) 2017 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 package com.android.compatibility.common.util; 17 18 import com.android.tradefed.device.DeviceNotAvailableException; 19 import com.android.tradefed.device.ITestDevice; 20 21 import java.util.HashMap; 22 import java.util.Map; 23 import java.util.regex.Matcher; 24 import java.util.regex.Pattern; 25 26 /** 27 * Host-side utility class for reading properties and gathering information for testing 28 * Android device compatibility. 29 */ 30 public class PropertyUtil { 31 32 /** 33 * Name of read-only property detailing the first API level for which the product was 34 * shipped. Property should be undefined for factory ROM products. 35 */ 36 public static final String FIRST_API_LEVEL = "ro.product.first_api_level"; 37 38 private static final String BOARD_API_LEVEL = "ro.board.api_level"; 39 private static final String BOARD_FIRST_API_LEVEL = "ro.board.first_api_level"; 40 private static final String BUILD_TAGS_PROPERTY = "ro.build.tags"; 41 private static final String BUILD_TYPE_PROPERTY = "ro.build.type"; 42 private static final String MANUFACTURER_PROPERTY = "ro.product.manufacturer"; 43 private static final String TAG_DEV_KEYS = "dev-keys"; 44 private static final String VENDOR_API_LEVEL = "ro.vendor.api_level"; 45 private static final String VNDK_VERSION = "ro.vndk.version"; 46 47 /** Value to be returned by getPropertyInt() if property is not found */ 48 public static final int INT_VALUE_IF_UNSET = -1; 49 50 /** API level for current in development */ 51 public static final int API_LEVEL_CURRENT = 10000; 52 53 public static final String GOOGLE_SETTINGS_QUERY = 54 "content query --uri content://com.google.settings/partner"; 55 PropertyUtil()56 private PropertyUtil() {} 57 58 /** Returns whether the device build is a user build */ isUserBuild(ITestDevice device)59 public static boolean isUserBuild(ITestDevice device) throws DeviceNotAvailableException { 60 return propertyEquals(device, BUILD_TYPE_PROPERTY, "user"); 61 } 62 63 /** Returns whether this build is built with dev-keys */ isDevKeysBuild(ITestDevice device)64 public static boolean isDevKeysBuild(ITestDevice device) throws DeviceNotAvailableException { 65 String buildTags = device.getProperty(BUILD_TAGS_PROPERTY); 66 for (String tag : buildTags.split(",")) { 67 if (TAG_DEV_KEYS.equals(tag.trim())) { 68 return true; 69 } 70 } 71 return false; 72 } 73 74 /** 75 * Return the first API level for this product. If the read-only property is unset, this means 76 * the first API level is the current API level, and the current API level is returned. 77 */ getFirstApiLevel(ITestDevice device)78 public static int getFirstApiLevel(ITestDevice device) throws DeviceNotAvailableException { 79 int firstApiLevel = getPropertyInt(device, FIRST_API_LEVEL); 80 return (firstApiLevel == INT_VALUE_IF_UNSET) ? device.getApiLevel() : firstApiLevel; 81 } 82 83 /** 84 * Return the API level that the VSR requirement must be fulfilled. It reads 85 * ro.vendor.api_level. If not provided for old devices, read ro.product.first_api_level, 86 * ro.board.api_level and ro.board.first_api_level to find the minimum required VSR api level of 87 * the DUT. 88 */ getVsrApiLevel(ITestDevice device)89 public static int getVsrApiLevel(ITestDevice device) throws DeviceNotAvailableException { 90 int vendorApiLevel = getPropertyInt(device, VENDOR_API_LEVEL); 91 if (vendorApiLevel != INT_VALUE_IF_UNSET) { 92 return vendorApiLevel; 93 } 94 // Fallback to api level calculation for old devices. 95 String[] boardApiLevelProps = {BOARD_API_LEVEL, BOARD_FIRST_API_LEVEL}; 96 for (String apiLevelProp : boardApiLevelProps) { 97 int apiLevel = getPropertyInt(device, apiLevelProp); 98 if (apiLevel != INT_VALUE_IF_UNSET) { 99 return Math.min(apiLevel, getFirstApiLevel(device)); 100 } 101 } 102 return getFirstApiLevel(device); 103 } 104 105 /** 106 * Return the API level of the vendor partition. It will read the following properties in order 107 * and returns the value of the first defined property. If none of them are defined, or the 108 * value is a VERSION CODENAME, returns the current API level which is defined in 109 * API_LEVEL_CURRENT. 110 * 111 * <ul> 112 * <li>ro.board.api_level 113 * <li>ro.board.first_api_level 114 * <li>ro.vndk.version 115 * </ul> 116 */ getVendorApiLevel(ITestDevice device)117 public static int getVendorApiLevel(ITestDevice device) throws DeviceNotAvailableException { 118 String[] vendorApiLevelProps = { 119 // Use the properties in order. 120 BOARD_API_LEVEL, BOARD_FIRST_API_LEVEL, VNDK_VERSION, 121 }; 122 for (String prop : vendorApiLevelProps) { 123 int apiLevel = getPropertyInt(device, prop); 124 if (apiLevel != INT_VALUE_IF_UNSET) { 125 return apiLevel; 126 } 127 } 128 return API_LEVEL_CURRENT; 129 } 130 131 /** Return whether the API level of the vendor partition is newer than the given API level. */ isVendorApiLevelNewerThan(ITestDevice device, int apiLevel)132 public static boolean isVendorApiLevelNewerThan(ITestDevice device, int apiLevel) 133 throws DeviceNotAvailableException { 134 return getVendorApiLevel(device) > apiLevel; 135 } 136 137 /** 138 * Return whether the API level of the vendor partition is same or newer than the given API 139 * level. 140 */ isVendorApiLevelAtLeast(ITestDevice device, int apiLevel)141 public static boolean isVendorApiLevelAtLeast(ITestDevice device, int apiLevel) 142 throws DeviceNotAvailableException { 143 return getVendorApiLevel(device) >= apiLevel; 144 } 145 146 /** 147 * Return the manufacturer of this product. If unset, return null. 148 */ getManufacturer(ITestDevice device)149 public static String getManufacturer(ITestDevice device) throws DeviceNotAvailableException { 150 return device.getProperty(MANUFACTURER_PROPERTY); 151 } 152 153 /** Returns a mapping from client ID names to client ID values */ getClientIds(ITestDevice device)154 public static Map<String, String> getClientIds(ITestDevice device) 155 throws DeviceNotAvailableException { 156 Map<String,String> clientIds = new HashMap<>(); 157 String queryOutput = device.executeShellCommand(GOOGLE_SETTINGS_QUERY); 158 for (String line : queryOutput.split("[\\r?\\n]+")) { 159 // Expected line format: "Row: 1 _id=123, name=<property_name>, value=<property_value>" 160 Pattern pattern = Pattern.compile("name=([a-z_]*), value=(.*)$"); 161 Matcher matcher = pattern.matcher(line); 162 if (matcher.find()) { 163 String name = matcher.group(1); 164 String value = matcher.group(2); 165 if (name.contains("client_id")) { 166 clientIds.put(name, value); // only add name-value pair for client ids 167 } 168 } 169 } 170 return clientIds; 171 } 172 173 /** Returns whether the property exists on this device */ propertyExists(ITestDevice device, String property)174 public static boolean propertyExists(ITestDevice device, String property) 175 throws DeviceNotAvailableException { 176 return device.getProperty(property) != null; 177 } 178 179 /** Returns whether the property value is equal to a given string */ propertyEquals(ITestDevice device, String property, String value)180 public static boolean propertyEquals(ITestDevice device, String property, String value) 181 throws DeviceNotAvailableException { 182 if (value == null) { 183 return !propertyExists(device, property); // null value implies property does not exist 184 } 185 return value.equals(device.getProperty(property)); 186 } 187 188 /** 189 * Returns whether the property value matches a given regular expression. The method uses 190 * String.matches(), requiring a complete match (i.e. expression matches entire value string) 191 */ propertyMatches(ITestDevice device, String property, String regex)192 public static boolean propertyMatches(ITestDevice device, String property, String regex) 193 throws DeviceNotAvailableException { 194 if (regex == null || regex.isEmpty()) { 195 // null or empty pattern implies property does not exist 196 return !propertyExists(device, property); 197 } 198 String value = device.getProperty(property); 199 return (value == null) ? false : value.matches(regex); 200 } 201 202 /** 203 * Retrieves the desired integer property, returning INT_VALUE_IF_UNSET if not found. 204 */ getPropertyInt(ITestDevice device, String property)205 public static int getPropertyInt(ITestDevice device, String property) 206 throws DeviceNotAvailableException { 207 String value = device.getProperty(property); 208 if (value == null) { 209 return INT_VALUE_IF_UNSET; 210 } 211 try { 212 return Integer.parseInt(value); 213 } catch (NumberFormatException e) { 214 return INT_VALUE_IF_UNSET; 215 } 216 } 217 } 218