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 private static final String BUILD_TAGS_PROPERTY = "ro.build.tags"; 38 private static final String BUILD_TYPE_PROPERTY = "ro.build.type"; 39 private static final String MANUFACTURER_PROPERTY = "ro.product.manufacturer"; 40 private static final String TAG_DEV_KEYS = "dev-keys"; 41 private static final String VNDK_VERSION = "ro.vndk.version"; 42 43 /** Value to be returned by getPropertyInt() if property is not found */ 44 public static final int INT_VALUE_IF_UNSET = -1; 45 46 public static final String GOOGLE_SETTINGS_QUERY = 47 "content query --uri content://com.google.settings/partner"; 48 49 /** Returns whether the device build is a user build */ isUserBuild(ITestDevice device)50 public static boolean isUserBuild(ITestDevice device) throws DeviceNotAvailableException { 51 return propertyEquals(device, BUILD_TYPE_PROPERTY, "user"); 52 } 53 54 /** Returns whether this build is built with dev-keys */ isDevKeysBuild(ITestDevice device)55 public static boolean isDevKeysBuild(ITestDevice device) throws DeviceNotAvailableException { 56 String buildTags = device.getProperty(BUILD_TAGS_PROPERTY); 57 for (String tag : buildTags.split(",")) { 58 if (TAG_DEV_KEYS.equals(tag.trim())) { 59 return true; 60 } 61 } 62 return false; 63 } 64 65 /** 66 * Return the first API level for this product. If the read-only property is unset, 67 * this means the first API level is the current API level, and the current API level 68 * is returned. 69 */ getFirstApiLevel(ITestDevice device)70 public static int getFirstApiLevel(ITestDevice device) throws DeviceNotAvailableException { 71 String propString = device.getProperty(FIRST_API_LEVEL); 72 return (propString == null) ? device.getApiLevel() : Integer.parseInt(propString); 73 } 74 75 /** 76 * Return whether the SDK version of the vendor partiton is newer than the given API level. 77 * If the property is set to non-integer value, this means the vendor partition is using 78 * current API level and true is returned. 79 */ isVendorApiLevelNewerThan(ITestDevice device, int apiLevel)80 public static boolean isVendorApiLevelNewerThan(ITestDevice device, int apiLevel) 81 throws DeviceNotAvailableException { 82 int vendorApiLevel = getPropertyInt(device, VNDK_VERSION); 83 if (vendorApiLevel == INT_VALUE_IF_UNSET) { 84 return true; 85 } 86 return vendorApiLevel > apiLevel; 87 } 88 89 /** 90 * Return the manufacturer of this product. If unset, return null. 91 */ getManufacturer(ITestDevice device)92 public static String getManufacturer(ITestDevice device) throws DeviceNotAvailableException { 93 return device.getProperty(MANUFACTURER_PROPERTY); 94 } 95 96 /** Returns a mapping from client ID names to client ID values */ getClientIds(ITestDevice device)97 public static Map<String, String> getClientIds(ITestDevice device) 98 throws DeviceNotAvailableException { 99 Map<String,String> clientIds = new HashMap<>(); 100 String queryOutput = device.executeShellCommand(GOOGLE_SETTINGS_QUERY); 101 for (String line : queryOutput.split("[\\r?\\n]+")) { 102 // Expected line format: "Row: 1 _id=123, name=<property_name>, value=<property_value>" 103 Pattern pattern = Pattern.compile("name=([a-z_]*), value=(.*)$"); 104 Matcher matcher = pattern.matcher(line); 105 if (matcher.find()) { 106 String name = matcher.group(1); 107 String value = matcher.group(2); 108 if (name.contains("client_id")) { 109 clientIds.put(name, value); // only add name-value pair for client ids 110 } 111 } 112 } 113 return clientIds; 114 } 115 116 /** Returns whether the property exists on this device */ propertyExists(ITestDevice device, String property)117 public static boolean propertyExists(ITestDevice device, String property) 118 throws DeviceNotAvailableException { 119 return device.getProperty(property) != null; 120 } 121 122 /** Returns whether the property value is equal to a given string */ propertyEquals(ITestDevice device, String property, String value)123 public static boolean propertyEquals(ITestDevice device, String property, String value) 124 throws DeviceNotAvailableException { 125 if (value == null) { 126 return !propertyExists(device, property); // null value implies property does not exist 127 } 128 return value.equals(device.getProperty(property)); 129 } 130 131 /** 132 * Returns whether the property value matches a given regular expression. The method uses 133 * String.matches(), requiring a complete match (i.e. expression matches entire value string) 134 */ propertyMatches(ITestDevice device, String property, String regex)135 public static boolean propertyMatches(ITestDevice device, String property, String regex) 136 throws DeviceNotAvailableException { 137 if (regex == null || regex.isEmpty()) { 138 // null or empty pattern implies property does not exist 139 return !propertyExists(device, property); 140 } 141 String value = device.getProperty(property); 142 return (value == null) ? false : value.matches(regex); 143 } 144 145 /** 146 * Retrieves the desired integer property, returning INT_VALUE_IF_UNSET if not found. 147 */ getPropertyInt(ITestDevice device, String property)148 public static int getPropertyInt(ITestDevice device, String property) 149 throws DeviceNotAvailableException { 150 String value = device.getProperty(property); 151 if (value == null) { 152 return INT_VALUE_IF_UNSET; 153 } 154 try { 155 return Integer.parseInt(value); 156 } catch (NumberFormatException e) { 157 return INT_VALUE_IF_UNSET; 158 } 159 } 160 } 161