// Copyright 2020 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "android/avd/keys.h" #include "android/avd/util.h" #include "android/android.h" #include "android/base/ArraySize.h" #include "android/emulation/bufprint_config_dirs.h" #include "android/utils/bufprint.h" #include "android/utils/debug.h" #include "android/utils/ini.h" #include "android/utils/panic.h" #include "android/utils/path.h" #include "android/utils/property_file.h" #include "android/utils/string.h" #include "android/utils/system.h" #include #include #include #include #include #define D(...) VERBOSE_PRINT(init,__VA_ARGS__) /* Return the path to the AVD's root configuration .ini file. it is located in * ~/.android/avd/.ini or Windows equivalent * * This file contains the path to the AVD's content directory, which * includes its own config.ini. */ char* path_getRootIniPath( const char* avdName ) { char temp[PATH_MAX], *p=temp, *end=p+sizeof(temp); p = bufprint_avd_home_path(temp, end); p = bufprint(p, end, PATH_SEP "%s.ini", avdName); if (p >= end) { return NULL; } if (!path_exists(temp)) { return NULL; } return ASTRDUP(temp); } char* path_getAvdContentPath(const char* avdName) { char temp[PATH_MAX], *p=temp, *end=p+sizeof(temp); CIniFile* ini = NULL; char* iniPath = path_getRootIniPath(avdName); char* avdPath = NULL; if (!iniPath) { return NULL; } ini = iniFile_newFromFile(iniPath); if (ini == NULL) { APANIC("Could not parse file: %s\n", iniPath); } AFREE(iniPath); avdPath = iniFile_getString(ini, ROOT_ABS_PATH_KEY, NULL); if (!path_is_dir(avdPath)) { // If the absolute path doesn't match an actual directory, try // the relative path if present. const char* relPath = iniFile_getString(ini, ROOT_REL_PATH_KEY, NULL); if (relPath != NULL) { p = bufprint_config_path(temp, end); p = bufprint(p, end, PATH_SEP "%s", relPath); if (p < end && path_is_dir(temp)) { AFREE(avdPath); avdPath = ASTRDUP(temp); } } } iniFile_free(ini); return avdPath; } char* propertyFile_getTargetAbi(const FileData* data) { char* abi = propertyFile_getValue((const char*)data->data, data->size, "ro.product.cpu.abi"); if (abi && !strcmp(abi, "mips")) { // mips32[r5|r6] images show cpu.abi as just mips, // but give correct abi in a dalvik property. char* abi2 = propertyFile_getValue((const char*)data->data, data->size, "dalvik.vm.isa.mips.variant"); if (abi2 && !strcmp(abi2, "mips32r6")) { AFREE(abi); abi = abi2; } else if (abi2 && !strcmp(abi2, "mips32r5")) { AFREE(abi); abi = abi2; } else { AFREE(abi2); } } return abi; } char* propertyFile_getTargetArch(const FileData* data) { char* ret = propertyFile_getTargetAbi(data); if (ret) { // Translate ABI name into architecture name. // By default, there are the same with a few exceptions. static const struct { const char* input; const char* output; } kData[] = { { "armeabi", "arm" }, { "armeabi-v7a", "arm" }, { "arm64-v8a", "arm64" }, { "mips32r5", "mips" }, { "mips32r6", "mips" }, }; size_t n; for (n = 0; n < sizeof(kData)/sizeof(kData[0]); ++n) { if (!strcmp(ret, kData[n].input)) { free(ret); ret = ASTRDUP(kData[n].output); break; } } } return ret; } int propertyFile_getInt(const FileData* data, const char* key, int _default, SearchResult* searchResult) { char* prop = propertyFile_getValue((const char*)data->data, data->size, key); if (!prop) { if (searchResult) { *searchResult = RESULT_NOT_FOUND; } return _default; } char* end; // long is only 32 bits on windows so it isn't enough to detect int overflow long long val = strtoll(prop, &end, 10); if (val < INT_MIN || val > INT_MAX || end == prop || *end != '\0') { D("Invalid int property: '%s:%s'", key, prop); AFREE(prop); if (searchResult) { *searchResult = RESULT_INVALID; } return _default; } AFREE(prop); if (searchResult) { *searchResult = RESULT_FOUND; } return (int)val; } int propertyFile_getApiLevel(const FileData* data) { const int kMinLevel = 3; const int kMaxLevel = 10000; SearchResult searchResult; int level = propertyFile_getInt(data, "ro.build.version.sdk", kMinLevel, &searchResult); if (searchResult == RESULT_NOT_FOUND) { level = kMaxLevel; D("Could not find SDK version in build.prop, default is: %d", level); } else if (searchResult == RESULT_INVALID || level < 0) { D("Defaulting to target API sdkVersion %d", level); } else { D("Found target API sdkVersion: %d\n", level); } return level; } #define IN_PRODUCT_NAME(data, names) \ propertyFile_findProductName( \ data, names, ARRAY_SIZE(names), false /*prefix*/) \ AvdFlavor propertyFile_getAvdFlavor(const FileData* data) { AvdFlavor res = AVD_OTHER; const char* phone_names[] = {"phone"}; const char* tv_names[] = {"atv"}; const char* wear_names[] = {"aw", "wear"}; const char* car_names[] = {"car"}; if (IN_PRODUCT_NAME(data, phone_names)) { res = AVD_PHONE; } else if (IN_PRODUCT_NAME(data, tv_names)) { res = AVD_TV; } else if (IN_PRODUCT_NAME(data, wear_names)) { res = AVD_WEAR; } else if (IN_PRODUCT_NAME(data, car_names)) { res = AVD_ANDROID_AUTO; } return res; } bool propertyFile_isGoogleApis(const FileData* data) { const char* google_names[] = {"sdk_google", "google_sdk", "sdk_gphone"}; return propertyFile_findProductName( data, google_names, ARRAY_SIZE(google_names), false /*prefix*/); } bool propertyFile_isUserBuild(const FileData* data) { bool isUser = false; char* prop = propertyFile_getValue((const char*)data->data, data->size, "ro.build.type"); if (!prop) { return false; } if (!strcmp(prop, "user")) { isUser = true; } free(prop); return isUser; } bool propertyFile_findProductName(const FileData* data, const char* productNames[], int count, bool prefix) { const char* props[] = {"ro.product.name", "ro.product.system.name", "ro.build.flavor"}; char *prop = propertyFile_getAnyValue((const char*)data->data, data->size, props, ARRAY_SIZE(props)); if (!prop) { return false; } if (!prefix) { int i; for (i = 0; i < count; i++) { if (strstr(prop, productNames[i])) { free(prop); return true; } } } else { int i; for (i = 0; i < count; i++) { int len = strlen(productNames[i]); if (strlen(prop) >= len && strncmp(productNames[i], prop, len) == 0) { free(prop); return true; } } } free(prop); return false; } char* path_getBuildBuildProp(const char* androidOut) { char temp[MAX_PATH], *p = temp, *end = p + sizeof(temp); p = bufprint(temp, end, "%s"PATH_SEP"system"PATH_SEP"build.prop", androidOut); if (p >= end) { D("ANDROID_BUILD_OUT is too long: %s\n", androidOut); return NULL; } if (!path_exists(temp)) { D("Cannot find build properties file: %s\n", temp); return NULL; } return ASTRDUP(temp); } char* path_getBuildBootProp(const char* androidOut) { char temp[MAX_PATH], *p = temp, *end = p + sizeof(temp); p = bufprint(temp, end, "%s"PATH_SEP"boot.prop", androidOut); if (p >= end) { D("ANDROID_BUILD_OUT is too long: %s\n", androidOut); return NULL; } if (!path_exists(temp)) { D("Cannot find boot properties file: %s\n", temp); return NULL; } return ASTRDUP(temp); } char* path_getBuildTargetArch(const char* androidOut) { char* buildPropPath = path_getBuildBuildProp(androidOut); if (!buildPropPath) { return NULL; } FileData buildProp[1]; fileData_initFromFile(buildProp, buildPropPath); char* ret = propertyFile_getTargetArch(buildProp); fileData_done(buildProp); AFREE(buildPropPath); return ret; } static char* _getAvdConfigValue(const char* avdPath, const char* key, const char* defaultValue) { CIniFile* ini; char* result = NULL; char temp[PATH_MAX], *p = temp, *end = p + sizeof(temp); p = bufprint(temp, end, "%s" PATH_SEP CORE_CONFIG_INI, avdPath); if (p >= end) { APANIC("AVD path too long: %s\n", avdPath); } ini = iniFile_newFromFile(temp); if (ini == NULL) { APANIC("Could not open AVD config file: %s\n", temp); } result = iniFile_getString(ini, key, defaultValue); iniFile_free(ini); return result; } char* path_getAvdTargetArch( const char* avdName ) { char* avdPath = path_getAvdContentPath(avdName); char* avdArch = _getAvdConfigValue(avdPath, "hw.cpu.arch", "arm"); char* avdTag = _getAvdConfigValue(avdPath, TAG_ID, "default"); AFREE(avdPath); /* Chrome OS images always are x86_64 arch even abi says it is x86. * We run 32 bits android inside 64 bits Chrome OS now. */ if (!strcmp(avdArch, "x86") && !strcmp(avdTag, TAG_ID_CHROMEOS)) { str_reset(&avdArch, "x86_64"); } AFREE(avdTag); return avdArch; } char* path_getAvdSnapshotPresent( const char* avdName ) { char* avdPath = path_getAvdContentPath(avdName); char* snapshotPresent = _getAvdConfigValue(avdPath, "snapshot.present", "no"); AFREE(avdPath); return snapshotPresent; } char* path_getAvdSystemPath(const char* avdName, const char* sdkRoot) { char* result = NULL; char* avdPath = path_getAvdContentPath(avdName); int nn; for (nn = 0; nn < MAX_SEARCH_PATHS; ++nn) { char searchKey[32]; snprintf(searchKey, sizeof(searchKey), "%s%d", SEARCH_PREFIX, nn + 1); char* searchPath = _getAvdConfigValue(avdPath, searchKey, NULL); if (!searchPath) { continue; } char temp[PATH_MAX], *p = temp, *end= p+sizeof temp; // Prefix sdkRoot if the path is not absolute if (path_is_absolute(searchPath)) { p = strncpy(temp, searchPath, sizeof(temp)); } else { p = bufprint(temp, end, "%s"PATH_SEP"%s", sdkRoot, searchPath); } free(searchPath); if (p >= end || !path_is_dir(temp)) { D(" Not a directory: %s\n", temp); continue; } D(" Found directory: %s\n", temp); result = ASTRDUP(temp); break; } AFREE(avdPath); return result; } char* path_getAvdGpuMode(const char* avdName) { char* avdPath = path_getAvdContentPath(avdName); char* gpuEnabled = _getAvdConfigValue(avdPath, "hw.gpu.enabled", "no"); bool enabled = !strcmp(gpuEnabled, "yes"); AFREE(gpuEnabled); char* gpuMode = NULL; if (enabled) { gpuMode = _getAvdConfigValue(avdPath, "hw.gpu.mode", "auto"); } AFREE(avdPath); return gpuMode; } const char* emulator_getBackendSuffix(const char* targetArch) { if (!targetArch) return NULL; static const struct { const char* avd_arch; const char* emulator_suffix; } kPairs[] = { { "arm", "arm" }, { "x86", "x86" }, { "x86_64", "x86" }, { "mips", "mips" }, { "arm64", "arm64" }, { "mips64", "mips64" }, // Add more if needed here. }; size_t n; for (n = 0; n < sizeof(kPairs)/sizeof(kPairs[0]); ++n) { if (!strcmp(targetArch, kPairs[n].avd_arch)) { return kPairs[n].emulator_suffix; } } return NULL; }