• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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