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
29 struct 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 */
48 AUserConfig*
auserConfig_new(AvdInfo * info)49 auserConfig_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
153 uint64_t
auserConfig_getUUID(AUserConfig * uconfig)154 auserConfig_getUUID( AUserConfig* uconfig )
155 {
156 return uconfig->uuid;
157 }
158
159 void
auserConfig_getWindowPos(AUserConfig * uconfig,int * pX,int * pY)160 auserConfig_getWindowPos( AUserConfig* uconfig, int *pX, int *pY )
161 {
162 *pX = uconfig->windowX;
163 *pY = uconfig->windowY;
164 }
165
166
167 void
auserConfig_setWindowPos(AUserConfig * uconfig,int x,int y)168 auserConfig_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 */
179 void
auserConfig_save(AUserConfig * uconfig)180 auserConfig_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