blob: a55037db5d7c53ebe64d9abf69af0a402fc52845 [file] [log] [blame]
Marat Dukhan006461a2017-08-24 16:10:46 -07001#include <stdbool.h>
2#include <stdint.h>
3#include <stdlib.h>
4#include <stddef.h>
5#include <string.h>
6
Marat Dukhand1565252017-09-12 00:29:01 -07007#include <dlfcn.h>
8
Marat Dukhan006461a2017-08-24 16:10:46 -07009#include <linux/api.h>
10#include <arm/android/api.h>
11#include <arm/linux/api.h>
12#include <log.h>
13
Marat Dukhand1565252017-09-12 00:29:01 -070014#if CPUINFO_MOCK
15 #include <cpuinfo-mock.h>
16#endif
17
18
Marat Dukhan006461a2017-08-24 16:10:46 -070019/*
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 Dukhan006461a2017-08-24 16:10:46 -070025/*
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 */
38static 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 Dukhan7920b2b2017-09-11 10:51:58 -0700107 if (memcmp(line_start, "ro.board.platform", key_length) == 0) {
Marat Dukhan006461a2017-08-24 16:10:46 -0700108 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 Dukhan7920b2b2017-09-11 10:51:58 -0700113 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 Dukhan006461a2017-08-24 16:10:46 -0700118 }
119 break;
120 }
121 return true;
122}
123
Marat Dukhand1565252017-09-12 00:29:01 -0700124#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 Dukhan006461a2017-08-24 16:10:46 -0700215bool cpuinfo_arm_android_parse_properties(struct cpuinfo_android_properties properties[restrict static 1]) {
216 memset(properties, 0, sizeof(struct cpuinfo_android_properties));
Marat Dukhand1565252017-09-12 00:29:01 -0700217
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 Dukhan006461a2017-08-24 16:10:46 -0700230}