You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
455 lines
13 KiB
455 lines
13 KiB
// 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 <assert.h>
|
|
#include <limits.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
|
|
#define D(...) VERBOSE_PRINT(init,__VA_ARGS__)
|
|
|
|
/* Return the path to the AVD's root configuration .ini file. it is located in
|
|
* ~/.android/avd/<name>.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;
|
|
}
|