Marat Dukhan | 006461a | 2017-08-24 16:10:46 -0700 | [diff] [blame] | 1 | #include <stdbool.h> |
| 2 | #include <stdint.h> |
| 3 | #include <stdlib.h> |
| 4 | #include <stddef.h> |
| 5 | #include <string.h> |
| 6 | |
Marat Dukhan | d156525 | 2017-09-12 00:29:01 -0700 | [diff] [blame] | 7 | #include <dlfcn.h> |
| 8 | |
Marat Dukhan | 006461a | 2017-08-24 16:10:46 -0700 | [diff] [blame] | 9 | #include <linux/api.h> |
| 10 | #include <arm/android/api.h> |
| 11 | #include <arm/linux/api.h> |
| 12 | #include <log.h> |
| 13 | |
Marat Dukhan | d156525 | 2017-09-12 00:29:01 -0700 | [diff] [blame] | 14 | #if CPUINFO_MOCK |
| 15 | #include <cpuinfo-mock.h> |
| 16 | #endif |
| 17 | |
| 18 | |
Marat Dukhan | 006461a | 2017-08-24 16:10:46 -0700 | [diff] [blame] | 19 | /* |
| 20 | * Size, in chars, of the on-stack buffer used for parsing lines of /system/build.prop |
| 21 | * This is also the limit on the length of a single line. |
| 22 | */ |
| 23 | #define BUFFER_SIZE 1024 |
| 24 | |
Marat Dukhan | 006461a | 2017-08-24 16:10:46 -0700 | [diff] [blame] | 25 | /* |
| 26 | * Decode a single line of /system/build.prop information. |
| 27 | * Lines have format <key>=<value> |
| 28 | * An truncated example of /system/build.prop (from Galaxy S7): |
| 29 | * |
| 30 | * # ro.product.cpu.abi and ro.product.cpu.abi2 are obsolete, |
| 31 | * # use ro.product.cpu.abilist instead. |
| 32 | * ro.product.cpu.abi=arm64-v8a |
| 33 | * ro.product.cpu.abilist=arm64-v8a,armeabi-v7a,armeabi |
| 34 | * ro.product.cpu.abilist32=armeabi-v7a,armeabi |
| 35 | * ro.product.cpu.abilist64=arm64-v8a |
| 36 | * ro.product.manufacturer=samsung |
| 37 | */ |
| 38 | static bool parse_line( |
| 39 | const char* line_start, |
| 40 | const char* line_end, |
| 41 | struct cpuinfo_android_properties properties[restrict static 1], |
| 42 | uint64_t line_number) |
| 43 | { |
| 44 | /* Minimal valid line length is 3 characters: 1-character key, '=', 1-character value. */ |
| 45 | const size_t line_length = line_end - line_start; |
| 46 | if (line_length < 3) { |
| 47 | return true; |
| 48 | } |
| 49 | |
| 50 | /* Lines starting with '#' are comments */ |
| 51 | if (line_start[0] == '#') { |
| 52 | return true; |
| 53 | } |
| 54 | |
| 55 | /* Search for '=' on the line. */ |
| 56 | const char* separator = line_start; |
| 57 | for (; separator != line_end; separator++) { |
| 58 | if (*separator == '=') { |
| 59 | break; |
| 60 | } |
| 61 | } |
| 62 | /* Skip line if no '=' separator was found. */ |
| 63 | if (separator == line_end) { |
| 64 | return true; |
| 65 | } |
| 66 | |
| 67 | /* Skip leading spaces in value part. */ |
| 68 | const char* value_start = separator + 1; |
| 69 | for (; value_start != line_end; value_start++) { |
| 70 | if (*value_start != ' ') { |
| 71 | break; |
| 72 | } |
| 73 | } |
| 74 | /* Value part contains nothing but spaces. Skip line. */ |
| 75 | if (value_start == line_end) { |
| 76 | return true; |
| 77 | } |
| 78 | |
| 79 | /* Skip trailing spaces in value part (if any) */ |
| 80 | const char* value_end = line_end; |
| 81 | for (; value_end != value_start; value_end--) { |
| 82 | if (value_end[-1] != ' ') { |
| 83 | break; |
| 84 | } |
| 85 | } |
| 86 | |
| 87 | const size_t key_length = separator - line_start; |
| 88 | const size_t value_length = value_end - value_start; |
| 89 | const size_t limited_value_length = (value_length >= CPUINFO_BUILD_PROP_VALUE_MAX) ? |
| 90 | CPUINFO_BUILD_PROP_VALUE_MAX - 1 : value_length; |
| 91 | switch (key_length) { |
| 92 | case 11: |
| 93 | if (memcmp(line_start, "ro.chipname", key_length) == 0) { |
| 94 | memcpy(properties->ro_chipname, value_start, limited_value_length); |
| 95 | cpuinfo_log_debug("parsed ro.chipname = \"%s\"", |
| 96 | properties->ro_chipname); |
| 97 | } |
| 98 | break; |
| 99 | case 16: |
| 100 | if (memcmp(line_start, "ro.product.board", key_length) == 0) { |
| 101 | memcpy(properties->ro_product_board, value_start, limited_value_length); |
| 102 | cpuinfo_log_debug("parsed ro.product.board = \"%s\"", |
| 103 | properties->ro_product_board); |
| 104 | } |
| 105 | break; |
| 106 | case 17: |
Marat Dukhan | 7920b2b | 2017-09-11 10:51:58 -0700 | [diff] [blame] | 107 | if (memcmp(line_start, "ro.board.platform", key_length) == 0) { |
Marat Dukhan | 006461a | 2017-08-24 16:10:46 -0700 | [diff] [blame] | 108 | memcpy(properties->ro_board_platform, value_start, limited_value_length); |
| 109 | cpuinfo_log_debug("parsed ro.board.platform = \"%s\"", |
| 110 | properties->ro_board_platform); |
| 111 | } |
| 112 | break; |
Marat Dukhan | 7920b2b | 2017-09-11 10:51:58 -0700 | [diff] [blame] | 113 | case 20: |
| 114 | if (memcmp(line_start, "ro.mediatek.platform", key_length) == 0) { |
| 115 | memcpy(properties->ro_mediatek_platform, value_start, limited_value_length); |
| 116 | cpuinfo_log_debug("parsed ro.mediatek.platform = \"%s\"", |
| 117 | properties->ro_mediatek_platform); |
Marat Dukhan | 006461a | 2017-08-24 16:10:46 -0700 | [diff] [blame] | 118 | } |
| 119 | break; |
| 120 | } |
| 121 | return true; |
| 122 | } |
| 123 | |
Marat Dukhan | d156525 | 2017-09-12 00:29:01 -0700 | [diff] [blame] | 124 | #if CPUINFO_MOCK |
| 125 | static struct cpuinfo_mock_property* cpuinfo_mock_properties = NULL; |
| 126 | |
| 127 | void CPUINFO_ABI cpuinfo_mock_android_properties(struct cpuinfo_mock_property* properties) { |
| 128 | cpuinfo_log_info("Android properties mocking enabled"); |
| 129 | cpuinfo_mock_properties = properties; |
| 130 | } |
| 131 | |
| 132 | static int mock_system_property_get(const char* key, char* value) { |
| 133 | for (const struct cpuinfo_mock_property* prop = cpuinfo_mock_properties; prop->key != NULL; prop++) { |
| 134 | if (strncmp(key, prop->key, CPUINFO_BUILD_PROP_NAME_MAX) == 0) { |
| 135 | strncpy(value, prop->value, CPUINFO_BUILD_PROP_VALUE_MAX); |
| 136 | return (int) strnlen(prop->value, CPUINFO_BUILD_PROP_VALUE_MAX); |
| 137 | } |
| 138 | } |
| 139 | *value = '\0'; |
| 140 | return 0; |
| 141 | } |
| 142 | |
| 143 | static bool cpuinfo_arm_android_query_properties(struct cpuinfo_android_properties properties[restrict static 1]) { |
| 144 | if (cpuinfo_mock_properties == NULL) { |
| 145 | return false; |
| 146 | } |
| 147 | |
| 148 | const int ro_product_board_length = |
| 149 | mock_system_property_get("ro.product.board", properties->ro_product_board); |
| 150 | cpuinfo_log_debug("read ro.product.board = \"%.*s\"", |
| 151 | ro_product_board_length, properties->ro_product_board); |
| 152 | |
| 153 | const int ro_board_platform_length = |
| 154 | mock_system_property_get("ro.board.platform", properties->ro_board_platform); |
| 155 | cpuinfo_log_debug("read ro.board.platform = \"%.*s\"", |
| 156 | ro_board_platform_length, properties->ro_board_platform); |
| 157 | |
| 158 | const int ro_mediatek_platform_length = |
| 159 | mock_system_property_get("ro.mediatek.platform", properties->ro_mediatek_platform); |
| 160 | cpuinfo_log_debug("read ro.mediatek.platform = \"%.*s\"", |
| 161 | ro_mediatek_platform_length, properties->ro_mediatek_platform); |
| 162 | |
| 163 | const int ro_chipname_length = |
| 164 | mock_system_property_get("ro.chipname", properties->ro_chipname); |
| 165 | cpuinfo_log_debug("read ro.chipname = \"%.*s\"", |
| 166 | ro_chipname_length, properties->ro_chipname); |
| 167 | |
| 168 | return true; |
| 169 | } |
| 170 | #else |
| 171 | /* |
| 172 | * Function pointer declaration for |
| 173 | * int __system_property_get(const char *name, char *value) |
| 174 | * function from sys/system_properties.h header. Google's stance on whether function is part of a stable API is unclear: |
| 175 | * it was removed from public API in some NDK versions, and then added back. In practice, libc.so has this symbol, |
| 176 | * so we dynamically locate this function using dlopen + dlsym. |
| 177 | */ |
| 178 | typedef int (*system_property_get_ptr)(const char[], char[]); |
| 179 | |
| 180 | static bool cpuinfo_arm_android_query_properties(struct cpuinfo_android_properties properties[restrict static 1]) { |
| 181 | void* libc = dlopen("libc.so", RTLD_LAZY); |
| 182 | if (libc == NULL) { |
| 183 | cpuinfo_log_warning("failed to dlopen libc.so; fallback to reading /system/build.prop for Android properties"); |
| 184 | return false; |
| 185 | } |
| 186 | |
| 187 | system_property_get_ptr system_property_get = (system_property_get_ptr) dlsym(libc, "__system_property_get"); |
| 188 | if (system_property_get == NULL) { |
| 189 | cpuinfo_log_warning( |
| 190 | "could not find __system_property_get in libc.so: " |
| 191 | "fallback to reading /system/build.prop for Android properties"); |
| 192 | goto cleanup; |
| 193 | } |
| 194 | |
| 195 | const int ro_product_board_length = system_property_get("ro.product.board", properties->ro_product_board); |
| 196 | cpuinfo_log_debug("read ro.product.board = \"%.*s\"", ro_product_board_length, properties->ro_product_board); |
| 197 | |
| 198 | const int ro_board_platform_length = system_property_get("ro.board.platform", properties->ro_board_platform); |
| 199 | cpuinfo_log_debug("read ro.board.platform = \"%.*s\"", ro_board_platform_length, properties->ro_board_platform); |
| 200 | |
| 201 | const int ro_mediatek_platform_length = |
| 202 | system_property_get("ro.mediatek.platform", properties->ro_mediatek_platform); |
| 203 | cpuinfo_log_debug("read ro.mediatek.platform = \"%.*s\"", |
| 204 | ro_mediatek_platform_length, properties->ro_mediatek_platform); |
| 205 | |
| 206 | const int ro_chipname_length = system_property_get("ro.chipname", properties->ro_chipname); |
| 207 | cpuinfo_log_debug("read ro.chipname = \"%.*s\"", ro_chipname_length, properties->ro_chipname); |
| 208 | |
| 209 | cleanup: |
| 210 | dlclose(libc); |
| 211 | return system_property_get != NULL; |
| 212 | } |
| 213 | #endif |
| 214 | |
Marat Dukhan | 006461a | 2017-08-24 16:10:46 -0700 | [diff] [blame] | 215 | bool cpuinfo_arm_android_parse_properties(struct cpuinfo_android_properties properties[restrict static 1]) { |
| 216 | memset(properties, 0, sizeof(struct cpuinfo_android_properties)); |
Marat Dukhan | d156525 | 2017-09-12 00:29:01 -0700 | [diff] [blame] | 217 | |
| 218 | /* |
| 219 | * First, try to use __system_property_get, which is widely supported. Whether is it a part of public API is |
| 220 | * debatable: Android 7.0 release note suggest to use __system_property_get instead of property_get from |
| 221 | * libcutils.so, but it was removed from headers in some NDK versions on ARM64. |
| 222 | * If __system_property_get can't be located, fallback to parsing /system/build.prop file. This method is |
| 223 | * recommended by an Android engineer (see https://stackoverflow.com/a/28416743), but doesn't work on Android Oreo |
| 224 | * (where the /system/build.prop file is not readable for non-root users) and some Huawei devices (where |
| 225 | * /system/build.prop contains empty strings for some properties of interest). |
| 226 | */ |
| 227 | return cpuinfo_arm_android_query_properties(properties) || |
| 228 | cpuinfo_linux_parse_multiline_file("/system/build.prop", BUFFER_SIZE, |
| 229 | (cpuinfo_line_callback) parse_line, properties); |
Marat Dukhan | 006461a | 2017-08-24 16:10:46 -0700 | [diff] [blame] | 230 | } |