1 // Copyright 2020 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 #include "android/avd/keys.h"
15 #include "android/avd/util.h"
16
17 #include "android/android.h"
18 #include "android/base/ArraySize.h"
19 #include "android/emulation/bufprint_config_dirs.h"
20 #include "android/utils/bufprint.h"
21 #include "android/utils/debug.h"
22 #include "android/utils/ini.h"
23 #include "android/utils/panic.h"
24 #include "android/utils/path.h"
25 #include "android/utils/property_file.h"
26 #include "android/utils/string.h"
27 #include "android/utils/system.h"
28
29 #include <assert.h>
30 #include <limits.h>
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <errno.h>
34
35 #define D(...) VERBOSE_PRINT(init,__VA_ARGS__)
36
37 /* Return the path to the AVD's root configuration .ini file. it is located in
38 * ~/.android/avd/<name>.ini or Windows equivalent
39 *
40 * This file contains the path to the AVD's content directory, which
41 * includes its own config.ini.
42 */
43 char*
path_getRootIniPath(const char * avdName)44 path_getRootIniPath( const char* avdName )
45 {
46 char temp[PATH_MAX], *p=temp, *end=p+sizeof(temp);
47
48 p = bufprint_avd_home_path(temp, end);
49 p = bufprint(p, end, PATH_SEP "%s.ini", avdName);
50 if (p >= end) {
51 return NULL;
52 }
53 if (!path_exists(temp)) {
54 return NULL;
55 }
56 return ASTRDUP(temp);
57 }
58
59 char*
path_getAvdContentPath(const char * avdName)60 path_getAvdContentPath(const char* avdName)
61 {
62 char temp[PATH_MAX], *p=temp, *end=p+sizeof(temp);
63 CIniFile* ini = NULL;
64 char* iniPath = path_getRootIniPath(avdName);
65 char* avdPath = NULL;
66
67 if (!iniPath) {
68 return NULL;
69 }
70
71 ini = iniFile_newFromFile(iniPath);
72 if (ini == NULL) {
73 APANIC("Could not parse file: %s\n", iniPath);
74 }
75 AFREE(iniPath);
76
77 avdPath = iniFile_getString(ini, ROOT_ABS_PATH_KEY, NULL);
78
79 if (!path_is_dir(avdPath)) {
80 // If the absolute path doesn't match an actual directory, try
81 // the relative path if present.
82 const char* relPath = iniFile_getString(ini, ROOT_REL_PATH_KEY, NULL);
83 if (relPath != NULL) {
84 p = bufprint_config_path(temp, end);
85 p = bufprint(p, end, PATH_SEP "%s", relPath);
86 if (p < end && path_is_dir(temp)) {
87 AFREE(avdPath);
88 avdPath = ASTRDUP(temp);
89 }
90 }
91 }
92
93 iniFile_free(ini);
94
95 return avdPath;
96 }
97
98 char*
propertyFile_getTargetAbi(const FileData * data)99 propertyFile_getTargetAbi(const FileData* data) {
100 char* abi = propertyFile_getValue((const char*)data->data,
101 data->size,
102 "ro.product.cpu.abi");
103 if (abi && !strcmp(abi, "mips")) {
104 // mips32[r5|r6] images show cpu.abi as just mips,
105 // but give correct abi in a dalvik property.
106 char* abi2 = propertyFile_getValue((const char*)data->data,
107 data->size,
108 "dalvik.vm.isa.mips.variant");
109 if (abi2 && !strcmp(abi2, "mips32r6")) {
110 AFREE(abi);
111 abi = abi2;
112 } else if (abi2 && !strcmp(abi2, "mips32r5")) {
113 AFREE(abi);
114 abi = abi2;
115 } else {
116 AFREE(abi2);
117 }
118 }
119 return abi;
120 }
121
122 char*
propertyFile_getTargetArch(const FileData * data)123 propertyFile_getTargetArch(const FileData* data) {
124 char* ret = propertyFile_getTargetAbi(data);
125 if (ret) {
126 // Translate ABI name into architecture name.
127 // By default, there are the same with a few exceptions.
128 static const struct {
129 const char* input;
130 const char* output;
131 } kData[] = {
132 { "armeabi", "arm" },
133 { "armeabi-v7a", "arm" },
134 { "arm64-v8a", "arm64" },
135 { "mips32r5", "mips" },
136 { "mips32r6", "mips" },
137 };
138 size_t n;
139 for (n = 0; n < sizeof(kData)/sizeof(kData[0]); ++n) {
140 if (!strcmp(ret, kData[n].input)) {
141 free(ret);
142 ret = ASTRDUP(kData[n].output);
143 break;
144 }
145 }
146 }
147 return ret;
148 }
149
150
151 int
propertyFile_getInt(const FileData * data,const char * key,int _default,SearchResult * searchResult)152 propertyFile_getInt(const FileData* data, const char* key, int _default,
153 SearchResult* searchResult) {
154 char* prop = propertyFile_getValue((const char*)data->data,
155 data->size,
156 key);
157 if (!prop) {
158 if (searchResult) {
159 *searchResult = RESULT_NOT_FOUND;
160 }
161 return _default;
162 }
163
164 char* end;
165 // long is only 32 bits on windows so it isn't enough to detect int overflow
166 long long val = strtoll(prop, &end, 10);
167 if (val < INT_MIN || val > INT_MAX ||
168 end == prop || *end != '\0') {
169 D("Invalid int property: '%s:%s'", key, prop);
170 AFREE(prop);
171 if (searchResult) {
172 *searchResult = RESULT_INVALID;
173 }
174 return _default;
175 }
176
177 AFREE(prop);
178
179 if (searchResult) {
180 *searchResult = RESULT_FOUND;
181 }
182 return (int)val;
183 }
184
185 int
propertyFile_getApiLevel(const FileData * data)186 propertyFile_getApiLevel(const FileData* data) {
187 const int kMinLevel = 3;
188 const int kMaxLevel = 10000;
189 SearchResult searchResult;
190 int level = propertyFile_getInt(data, "ro.build.version.sdk", kMinLevel,
191 &searchResult);
192 if (searchResult == RESULT_NOT_FOUND) {
193 level = kMaxLevel;
194 D("Could not find SDK version in build.prop, default is: %d", level);
195 } else if (searchResult == RESULT_INVALID || level < 0) {
196 D("Defaulting to target API sdkVersion %d", level);
197 } else {
198 D("Found target API sdkVersion: %d\n", level);
199 }
200 return level;
201 }
202
203 #define IN_PRODUCT_NAME(data, names) \
204 propertyFile_findProductName( \
205 data, names, ARRAY_SIZE(names), false /*prefix*/) \
206
propertyFile_getAvdFlavor(const FileData * data)207 AvdFlavor propertyFile_getAvdFlavor(const FileData* data) {
208 AvdFlavor res = AVD_OTHER;
209
210 const char* phone_names[] = {"phone"};
211 const char* tv_names[] = {"atv"};
212 const char* wear_names[] = {"aw", "wear"};
213 const char* car_names[] = {"car"};
214
215 if (IN_PRODUCT_NAME(data, phone_names)) {
216 res = AVD_PHONE;
217 } else if (IN_PRODUCT_NAME(data, tv_names)) {
218 res = AVD_TV;
219 } else if (IN_PRODUCT_NAME(data, wear_names)) {
220 res = AVD_WEAR;
221 } else if (IN_PRODUCT_NAME(data, car_names)) {
222 res = AVD_ANDROID_AUTO;
223 }
224
225 return res;
226 }
227
propertyFile_isGoogleApis(const FileData * data)228 bool propertyFile_isGoogleApis(const FileData* data) {
229 const char* google_names[] = {"sdk_google", "google_sdk", "sdk_gphone"};
230 return propertyFile_findProductName(
231 data, google_names, ARRAY_SIZE(google_names), false /*prefix*/);
232 }
233
propertyFile_isUserBuild(const FileData * data)234 bool propertyFile_isUserBuild(const FileData* data) {
235 bool isUser = false;
236 char* prop = propertyFile_getValue((const char*)data->data, data->size,
237 "ro.build.type");
238 if (!prop) {
239 return false;
240 }
241 if (!strcmp(prop, "user")) {
242 isUser = true;
243 }
244 free(prop);
245 return isUser;
246 }
247
propertyFile_findProductName(const FileData * data,const char * productNames[],int count,bool prefix)248 bool propertyFile_findProductName(const FileData* data,
249 const char* productNames[],
250 int count,
251 bool prefix) {
252 const char* props[] = {"ro.product.name", "ro.product.system.name", "ro.build.flavor"};
253 char *prop = propertyFile_getAnyValue((const char*)data->data, data->size,
254 props, ARRAY_SIZE(props));
255
256 if (!prop) {
257 return false;
258 }
259 if (!prefix) {
260 int i;
261 for (i = 0; i < count; i++) {
262 if (strstr(prop, productNames[i])) {
263 free(prop);
264 return true;
265 }
266 }
267 } else {
268 int i;
269 for (i = 0; i < count; i++) {
270 int len = strlen(productNames[i]);
271 if (strlen(prop) >= len &&
272 strncmp(productNames[i], prop, len) == 0) {
273 free(prop);
274 return true;
275 }
276 }
277 }
278
279 free(prop);
280 return false;
281 }
282
path_getBuildBuildProp(const char * androidOut)283 char* path_getBuildBuildProp(const char* androidOut) {
284 char temp[MAX_PATH], *p = temp, *end = p + sizeof(temp);
285 p = bufprint(temp, end, "%s"PATH_SEP"system"PATH_SEP"build.prop", androidOut);
286 if (p >= end) {
287 D("ANDROID_BUILD_OUT is too long: %s\n", androidOut);
288 return NULL;
289 }
290 if (!path_exists(temp)) {
291 D("Cannot find build properties file: %s\n", temp);
292 return NULL;
293 }
294 return ASTRDUP(temp);
295 }
296
297
path_getBuildBootProp(const char * androidOut)298 char* path_getBuildBootProp(const char* androidOut) {
299 char temp[MAX_PATH], *p = temp, *end = p + sizeof(temp);
300 p = bufprint(temp, end, "%s"PATH_SEP"boot.prop", androidOut);
301 if (p >= end) {
302 D("ANDROID_BUILD_OUT is too long: %s\n", androidOut);
303 return NULL;
304 }
305 if (!path_exists(temp)) {
306 D("Cannot find boot properties file: %s\n", temp);
307 return NULL;
308 }
309 return ASTRDUP(temp);
310 }
311
312
313 char*
path_getBuildTargetArch(const char * androidOut)314 path_getBuildTargetArch(const char* androidOut) {
315 char* buildPropPath = path_getBuildBuildProp(androidOut);
316 if (!buildPropPath) {
317 return NULL;
318 }
319
320 FileData buildProp[1];
321 fileData_initFromFile(buildProp, buildPropPath);
322 char* ret = propertyFile_getTargetArch(buildProp);
323 fileData_done(buildProp);
324 AFREE(buildPropPath);
325 return ret;
326 }
327
328
329 static char*
_getAvdConfigValue(const char * avdPath,const char * key,const char * defaultValue)330 _getAvdConfigValue(const char* avdPath,
331 const char* key,
332 const char* defaultValue)
333 {
334 CIniFile* ini;
335 char* result = NULL;
336 char temp[PATH_MAX], *p = temp, *end = p + sizeof(temp);
337 p = bufprint(temp, end, "%s" PATH_SEP CORE_CONFIG_INI, avdPath);
338 if (p >= end) {
339 APANIC("AVD path too long: %s\n", avdPath);
340 }
341 ini = iniFile_newFromFile(temp);
342 if (ini == NULL) {
343 APANIC("Could not open AVD config file: %s\n", temp);
344 }
345 result = iniFile_getString(ini, key, defaultValue);
346 iniFile_free(ini);
347
348 return result;
349 }
350
351 char*
path_getAvdTargetArch(const char * avdName)352 path_getAvdTargetArch( const char* avdName )
353 {
354 char* avdPath = path_getAvdContentPath(avdName);
355 char* avdArch = _getAvdConfigValue(avdPath, "hw.cpu.arch", "arm");
356 char* avdTag = _getAvdConfigValue(avdPath, TAG_ID, "default");
357 AFREE(avdPath);
358
359 /* Chrome OS images always are x86_64 arch even abi says it is x86.
360 * We run 32 bits android inside 64 bits Chrome OS now. */
361 if (!strcmp(avdArch, "x86") && !strcmp(avdTag, TAG_ID_CHROMEOS)) {
362 str_reset(&avdArch, "x86_64");
363 }
364
365 AFREE(avdTag);
366 return avdArch;
367 }
368
369 char*
path_getAvdSnapshotPresent(const char * avdName)370 path_getAvdSnapshotPresent( const char* avdName )
371 {
372 char* avdPath = path_getAvdContentPath(avdName);
373 char* snapshotPresent = _getAvdConfigValue(avdPath, "snapshot.present", "no");
374 AFREE(avdPath);
375
376 return snapshotPresent;
377 }
378
379 char*
path_getAvdSystemPath(const char * avdName,const char * sdkRoot)380 path_getAvdSystemPath(const char* avdName,
381 const char* sdkRoot) {
382 char* result = NULL;
383 char* avdPath = path_getAvdContentPath(avdName);
384 int nn;
385 for (nn = 0; nn < MAX_SEARCH_PATHS; ++nn) {
386 char searchKey[32];
387 snprintf(searchKey, sizeof(searchKey), "%s%d", SEARCH_PREFIX, nn + 1);
388 char* searchPath = _getAvdConfigValue(avdPath, searchKey, NULL);
389 if (!searchPath) {
390 continue;
391 }
392
393 char temp[PATH_MAX], *p = temp, *end= p+sizeof temp;
394 // Prefix sdkRoot if the path is not absolute
395 if (path_is_absolute(searchPath)) {
396 p = strncpy(temp, searchPath, sizeof(temp));
397 } else {
398 p = bufprint(temp, end, "%s"PATH_SEP"%s", sdkRoot, searchPath);
399 }
400 free(searchPath);
401 if (p >= end || !path_is_dir(temp)) {
402 D(" Not a directory: %s\n", temp);
403 continue;
404 }
405 D(" Found directory: %s\n", temp);
406 result = ASTRDUP(temp);
407 break;
408 }
409 AFREE(avdPath);
410 return result;
411 }
412
413 char*
path_getAvdGpuMode(const char * avdName)414 path_getAvdGpuMode(const char* avdName)
415 {
416 char* avdPath = path_getAvdContentPath(avdName);
417 char* gpuEnabled = _getAvdConfigValue(avdPath, "hw.gpu.enabled", "no");
418 bool enabled = !strcmp(gpuEnabled, "yes");
419 AFREE(gpuEnabled);
420
421 char* gpuMode = NULL;
422 if (enabled) {
423 gpuMode = _getAvdConfigValue(avdPath, "hw.gpu.mode", "auto");
424 }
425 AFREE(avdPath);
426 return gpuMode;
427 }
428
429 const char*
emulator_getBackendSuffix(const char * targetArch)430 emulator_getBackendSuffix(const char* targetArch)
431 {
432 if (!targetArch)
433 return NULL;
434
435 static const struct {
436 const char* avd_arch;
437 const char* emulator_suffix;
438 } kPairs[] = {
439 { "arm", "arm" },
440 { "x86", "x86" },
441 { "x86_64", "x86" },
442 { "mips", "mips" },
443 { "arm64", "arm64" },
444 { "mips64", "mips64" },
445 // Add more if needed here.
446 };
447 size_t n;
448 for (n = 0; n < sizeof(kPairs)/sizeof(kPairs[0]); ++n) {
449 if (!strcmp(targetArch, kPairs[n].avd_arch)) {
450 return kPairs[n].emulator_suffix;
451 }
452 }
453 return NULL;
454 }
455