1/* Copyright (C) 2009 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 "android/user-config.h"
13#include "android/utils/bufprint.h"
14#include "android/utils/debug.h"
15#include "android/utils/system.h"
16#include "android/utils/path.h"
17#include <stdlib.h>
18#include <errno.h>
19#include <sys/time.h>
20
21#define  D(...)   VERBOSE_PRINT(init,__VA_ARGS__)
22
23#if 0 /* set to 1 for more debugging */
24#  define  DD(...)  D(__VA_ARGS__)
25#else
26#  define  DD(...)  ((void)0)
27#endif
28
29struct AUserConfig {
30    ABool      changed;
31    int        windowX;
32    int        windowY;
33    uint64_t   uuid;
34    char*      iniPath;
35};
36
37/* Name of the user-config file */
38#define  USER_CONFIG_FILE  "emulator-user.ini"
39
40#define  KEY_WINDOW_X  "window.x"
41#define  KEY_WINDOW_Y  "window.y"
42#define  KEY_UUID      "uuid"
43
44#define  DEFAULT_X  100
45#define  DEFAULT_Y  100
46
47/* Create a new AUserConfig object from a given AvdInfo */
48AUserConfig*
49auserConfig_new( AvdInfo*  info )
50{
51    AUserConfig*  uc;
52    char          inAndroidBuild = avdInfo_inAndroidBuild(info);
53    char          needUUID = 1;
54    char          temp[PATH_MAX], *p=temp, *end=p+sizeof(temp);
55    char*         parentPath;
56    IniFile*      ini = NULL;
57
58    ANEW0(uc);
59
60    /* If we are in the Android build system, store the configuration
61     * in ~/.android/emulator-user.ini. otherwise, store it in the file
62     * emulator-user.ini in the AVD's content directory.
63     */
64    if (inAndroidBuild) {
65        p = bufprint_config_file(temp, end, USER_CONFIG_FILE);
66    } else {
67        p = bufprint(temp, end, "%s/%s", avdInfo_getContentPath(info),
68                     USER_CONFIG_FILE);
69    }
70
71    /* handle the unexpected */
72    if (p >= end) {
73        /* Hmmm, something is weird, let's use a temporary file instead */
74        p = bufprint_temp_file(temp, end, USER_CONFIG_FILE);
75        if (p >= end) {
76            derror("Weird: Cannot create temporary user-config file?");
77            exit(2);
78        }
79        dwarning("Weird: Content path too long, using temporary user-config.");
80    }
81
82    uc->iniPath = ASTRDUP(temp);
83    DD("looking user-config in: %s", uc->iniPath);
84
85
86    /* ensure that the parent directory exists */
87    parentPath = path_parent(uc->iniPath, 1);
88    if (parentPath == NULL) {
89        derror("Weird: Can't find parent of user-config file: %s",
90               uc->iniPath);
91        exit(2);
92    }
93
94    if (!path_exists(parentPath)) {
95        if (!inAndroidBuild) {
96            derror("Weird: No content path for this AVD: %s", parentPath);
97            exit(2);
98        }
99        DD("creating missing directory: %s", parentPath);
100        if (path_mkdir_if_needed(parentPath, 0755) < 0) {
101            derror("Using empty user-config, can't create %s: %s",
102                   parentPath, strerror(errno));
103            exit(2);
104        }
105    }
106
107    if (path_exists(uc->iniPath)) {
108        DD("reading user-config file");
109        ini = iniFile_newFromFile(uc->iniPath);
110        if (ini == NULL) {
111            dwarning("Can't read user-config file: %s\nUsing default values",
112                     uc->iniPath);
113        }
114    }
115
116    if (ini != NULL) {
117        uc->windowX = iniFile_getInteger(ini, KEY_WINDOW_X, DEFAULT_X);
118        DD("    found %s = %d", KEY_WINDOW_X, uc->windowX);
119
120        uc->windowY = iniFile_getInteger(ini, KEY_WINDOW_Y, DEFAULT_Y);
121        DD("    found %s = %d", KEY_WINDOW_Y, uc->windowY);
122
123        if (iniFile_getValue(ini, KEY_UUID) != NULL) {
124            uc->uuid = (uint64_t) iniFile_getInt64(ini, KEY_UUID, 0LL);
125            needUUID = 0;
126            DD("    found %s = %lld", KEY_UUID, uc->uuid);
127        }
128
129        iniFile_free(ini);
130    }
131    else {
132        uc->windowX = DEFAULT_X;
133        uc->windowY = DEFAULT_Y;
134        uc->changed = 1;
135    }
136
137    /* Generate a 64-bit UUID if necessary. We simply take the
138     * current time, which avoids any privacy-related value.
139     */
140    if (needUUID) {
141        struct timeval  tm;
142
143        gettimeofday( &tm, NULL );
144        uc->uuid    = (uint64_t)tm.tv_sec*1000 + tm.tv_usec/1000;
145        uc->changed = 1;
146        DD("    Generated UUID = %lld", uc->uuid);
147    }
148
149    return uc;
150}
151
152
153uint64_t
154auserConfig_getUUID( AUserConfig*  uconfig )
155{
156    return uconfig->uuid;
157}
158
159void
160auserConfig_getWindowPos( AUserConfig*  uconfig, int  *pX, int  *pY )
161{
162    *pX = uconfig->windowX;
163    *pY = uconfig->windowY;
164}
165
166
167void
168auserConfig_setWindowPos( AUserConfig*  uconfig, int  x, int  y )
169{
170    if (x != uconfig->windowX || y != uconfig->windowY) {
171        uconfig->windowX = x;
172        uconfig->windowY = y;
173        uconfig->changed = 1;
174    }
175}
176
177/* Save the user configuration back to the content directory.
178 * Should be used in an atexit() handler */
179void
180auserConfig_save( AUserConfig*  uconfig )
181{
182    IniFile*   ini;
183    char       temp[256];
184
185    if (uconfig->changed == 0) {
186        D("User-config was not changed.");
187        return;
188    }
189
190    bufprint(temp, temp+sizeof(temp),
191             "%s = %d\n"
192             "%s = %d\n"
193             "%s = %lld\n",
194             KEY_WINDOW_X, uconfig->windowX,
195             KEY_WINDOW_Y, uconfig->windowY,
196             KEY_UUID,     uconfig->uuid );
197
198    DD("Generated user-config file:\n%s", temp);
199
200    ini = iniFile_newFromMemory(temp, uconfig->iniPath);
201    if (ini == NULL) {
202        D("Weird: can't create user-config iniFile?");
203        return;
204    }
205    if (iniFile_saveToFile(ini, uconfig->iniPath) < 0) {
206        dwarning("could not save user configuration: %s: %s",
207                 uconfig->iniPath, strerror(errno));
208    } else {
209        D("User configuration saved to %s", uconfig->iniPath);
210    }
211    iniFile_free(ini);
212}
213