1/* Copyright (C) 2011 The Android Open Source Project
2**
3** This software is licensed under the terms of the GNU General Public
4** License version 2, as published by the Free Software Foundation, and
5** may be copied, distributed, and modified under those terms.
6**
7** This program is distributed in the hope that it will be useful,
8** but WITHOUT ANY WARRANTY; without even the implied warranty of
9** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10** GNU General Public License for more details.
11*/
12#include <stdlib.h>
13#include <stdio.h>
14#include <errno.h>
15#include "android/utils/debug.h"
16#include "android/utils/bufprint.h"
17#include "android/utils/ini.h"
18#include "android/utils/panic.h"
19#include "android/utils/path.h"
20#include "android/utils/system.h"
21#include "android/avd/util.h"
22
23#define D(...) VERBOSE_PRINT(init,__VA_ARGS__)
24
25/* this is the subdirectory of $HOME/.android where all
26 * root configuration files (and default content directories)
27 * are located.
28 */
29#define  ANDROID_AVD_DIR    "avd"
30
31
32/* Return the path to the Android SDK root installation.
33 *
34 * (*pFromEnv) will be set to 1 if it comes from the $ANDROID_SDK_ROOT
35 * environment variable, or 0 otherwise.
36 *
37 * Caller must free() returned string.
38 */
39char*
40path_getSdkRoot( char *pFromEnv )
41{
42    const char*  env;
43    char*        sdkPath;
44    char         temp[PATH_MAX], *p=temp, *end=p+sizeof(temp);
45
46    /* If ANDROID_SDK_ROOT is defined is must point to a directory
47     * containing a valid SDK installation.
48     */
49#define  SDK_ROOT_ENV  "ANDROID_SDK_ROOT"
50
51    env = getenv(SDK_ROOT_ENV);
52    if (env != NULL && env[0] != 0) {
53        if (path_exists(env)) {
54            D("found " SDK_ROOT_ENV ": %s", env);
55            *pFromEnv = 1;
56            return ASTRDUP(env);
57        }
58        D(SDK_ROOT_ENV " points to unknown directory: %s", env);
59    }
60
61    *pFromEnv = 0;
62
63    /* We assume the emulator binary is under tools/ so use its
64     * parent as the Android SDK root.
65     */
66    (void) bufprint_app_dir(temp, end);
67    sdkPath = path_parent(temp, 1);
68    if (sdkPath == NULL) {
69        derror("can't find root of SDK directory");
70        return NULL;
71    }
72    D("found SDK root at %s", sdkPath);
73    return sdkPath;
74}
75
76
77/* Return the path to the AVD's root configuration .ini file. it is located in
78 * ~/.android/avd/<name>.ini or Windows equivalent
79 *
80 * This file contains the path to the AVD's content directory, which
81 * includes its own config.ini.
82 */
83char*
84path_getRootIniPath( const char*  avdName )
85{
86    char temp[PATH_MAX], *p=temp, *end=p+sizeof(temp);
87
88    p = bufprint_config_path(temp, end);
89    p = bufprint(p, end, "/" ANDROID_AVD_DIR "/%s.ini", avdName);
90    if (p >= end) {
91        return NULL;
92    }
93    if (!path_exists(temp)) {
94        return NULL;
95    }
96    return ASTRDUP(temp);
97}
98
99
100char*
101path_getSdkHome(void)
102{
103    char temp[PATH_MAX], *p=temp, *end=p+sizeof(temp);
104    p = bufprint_config_path(temp, end);
105    if (p >= end) {
106        APANIC("User path too long!: %s\n", temp);
107    }
108    return strdup(temp);
109}
110
111
112static char*
113_getAvdContentPath(const char* avdName)
114{
115    char*    sdkHome = path_getSdkHome();
116    char*    avdPath = NULL;
117    IniFile* ini;
118    char     temp[PATH_MAX], *p=temp, *end=p+sizeof(temp);
119
120    /* Look for the root .ini file */
121    p = bufprint(temp, end, "%s/avd/%s.ini", sdkHome, avdName);
122    if (p >= end) {
123        APANIC("AVD Name too long: %s\n", avdName);
124    }
125
126    ini = iniFile_newFromFile(temp);
127    if (ini == NULL) {
128        APANIC("Could not open: %s", temp);
129    }
130
131    avdPath = iniFile_getString(ini, "path", NULL);
132
133    iniFile_free(ini);
134    AFREE(sdkHome);
135
136    return avdPath;
137}
138
139
140static char*
141_getAvdTargetArch(const char* avdPath)
142{
143    IniFile* ini;
144    char*    targetArch = NULL;
145    char     temp[PATH_MAX], *p=temp, *end=p+sizeof(temp);
146    p = bufprint(temp, end, "%s/config.ini", avdPath);
147    if (p >= end) {
148        APANIC("AVD path too long: %s\n", avdPath);
149    }
150    ini = iniFile_newFromFile(temp);
151    if (ini == NULL) {
152        APANIC("Could not open AVD config file: %s", temp);
153    }
154    targetArch = iniFile_getString(ini, "hw.cpu.arch", "arm");
155    iniFile_free(ini);
156
157    return targetArch;
158}
159
160char*
161path_getAvdTargetArch( const char* avdName )
162{
163    char*  avdPath = _getAvdContentPath(avdName);
164    char*  avdArch = _getAvdTargetArch(avdPath);
165
166    return avdArch;
167}
168
169/* Retrieves the value of a given system property defined in a .prop
170 * file. This is a text file that contains definitions of the format:
171 * <name>=<value>
172 *
173 * Returns NULL if property <name> is undefined or empty.
174 * Returned string must be freed by the caller.
175 */
176static char*
177_getSystemProperty( const char* propFile, const char* propName )
178{
179    FILE*  file;
180    char   temp[PATH_MAX], *p=temp, *end=p+sizeof(temp);
181    int    propNameLen = strlen(propName);
182    char*  result = NULL;
183
184    file = fopen(propFile, "rb");
185    if (file == NULL) {
186        D("Could not open file: %s: %s", temp, strerror(errno));
187        return NULL;
188    }
189
190    while (fgets(temp, sizeof temp, file) != NULL) {
191        /* Trim trailing newlines, if any */
192        p = memchr(temp, '\0', sizeof temp);
193        if (p == NULL)
194            p = end;
195        if (p > temp && p[-1] == '\n') {
196            *--p = '\0';
197        }
198        if (p > temp && p[-1] == '\r') {
199            *--p = '\0';
200        }
201        /* force zero-termination in case of full-buffer */
202        if (p == end)
203            *--p = '\0';
204
205        /* check that the line starts with the property name */
206        if (memcmp(temp, propName, propNameLen) != 0) {
207            continue;
208        }
209        p = temp + propNameLen;
210
211        /* followed by an equal sign */
212        if (p >= end || *p != '=')
213            continue;
214        p++;
215
216        /* followed by something */
217        if (p >= end || !*p)
218            break;
219
220        result = ASTRDUP(p);
221        break;
222    }
223    fclose(file);
224    return result;
225}
226
227static char*
228_getBuildProperty( const char* androidOut, const char* propName )
229{
230    char temp[PATH_MAX], *p=temp, *end=p+sizeof(temp);
231
232    p = bufprint(temp, end, "%s/system/build.prop", androidOut);
233    if (p >= end) {
234        D("%s: ANDROID_PRODUCT_OUT too long: %s", __FUNCTION__, androidOut);
235        return NULL;
236    }
237    return _getSystemProperty(temp, propName);
238}
239
240char*
241path_getBuildTargetArch( const char* androidOut )
242{
243    const char* defaultArch = "arm";
244    char*       result = NULL;
245    char*       cpuAbi = _getBuildProperty(androidOut, "ro.product.cpu.abi");
246
247    if (cpuAbi == NULL) {
248        D("Coult not find CPU ABI in build properties!");
249        D("Default target architecture=%s", defaultArch);
250        result = ASTRDUP(defaultArch);
251    } else {
252        /* Translate ABI to cpu arch if necessary */
253        if (!strcmp("armeabi",cpuAbi))
254            result = "arm";
255        else if (!strcmp("armeabi-v7a", cpuAbi))
256            result = "arm";
257        else
258            result = cpuAbi;
259
260        D("Found target ABI=%s, architecture=%s", cpuAbi, result);
261        result = ASTRDUP(result);
262        AFREE(cpuAbi);
263    }
264    return result;
265}
266
267char*
268path_getBuildTargetAbi( const char* androidOut )
269{
270    const char* defaultAbi = "armeabi";
271    char*       result = NULL;
272    char*       cpuAbi = _getBuildProperty(androidOut, "ro.product.cpu.abi");
273
274    if (cpuAbi == NULL) {
275        D("Coult not find CPU ABI in build properties!");
276        D("Default target ABI: %s", defaultAbi);
277        result = ASTRDUP(defaultAbi);
278    } else {
279        D("Found target ABI=%s", cpuAbi);
280        result = cpuAbi;
281    }
282    return result;
283}
284
285
286int
287path_getBuildTargetApiLevel( const char* androidOut )
288{
289    const int  defaultLevel = 1000;
290    int        level        = defaultLevel;
291    char*      sdkVersion = _getBuildProperty(androidOut, "ro.build.version.sdk");
292
293    if (sdkVersion != NULL) {
294        long  value;
295        char* end;
296        value = strtol(sdkVersion, &end, 10);
297        if (end == NULL || *end != '\0' || value != (int)value) {
298            D("Invalid SDK version build property: '%s'", sdkVersion);
299            D("Defaulting to target API level %d", level);
300        } else {
301            level = (int)value;
302            /* Sanity check, the Android SDK doesn't support anything
303             * before Android 1.5, a.k.a API level 3 */
304            if (level < 3)
305                level = 3;
306            D("Found target API level: %d", level);
307        }
308        AFREE(sdkVersion);
309    } else {
310        D("Could not find target API level / SDK version in build properties!");
311        D("Default target API level: %d", level);
312    }
313    return level;
314}
315
316int
317path_getAdbdCommunicationMode( const char* androidOut )
318{
319    char* prop = _getBuildProperty(androidOut, "ro.adb.qemud");
320    if (prop != NULL) {
321        long val = 0;
322        char* end;
323        val = strtol(prop, &end, 10);
324        if (end == NULL || *end != '\0' || val != (int)val) {
325            D("Invalid ro.adb.qemud build property: '%s'", prop);
326            val = 0;
327        } else {
328            D("Found ro.adb.qemud build property: %d", val);
329        }
330        AFREE(prop);
331        return (int)val;
332    } else {
333        /* Missing ro.adb.qemud means "legacy" ADBD. */
334        return 0;
335    }
336}
337