blob: 0d6054fdd88162024dafbc3cd43f71e74284736c [file] [log] [blame]
The Android Open Source Project52d4c302009-03-03 19:29:09 -08001//
2// Copyright 2007 The Android Open Source Project
3//
4// Property sever. Mimics behavior provided on the device by init(8) and
5// some code built into libc.
6//
7
8// For compilers that support precompilation, include "wx/wx.h".
9#include "wx/wxprec.h"
10
11// Otherwise, include all standard headers
12#ifndef WX_PRECOMP
13# include "wx/wx.h"
14#endif
15#include "wx/image.h"
16
17#include "PropertyServer.h"
18#include "MyApp.h"
19#include "Preferences.h"
20#include "MainFrame.h"
21#include "utils.h"
22
23#include <stdlib.h>
24#include <unistd.h>
25#include <string.h>
26#include <errno.h>
27#include <assert.h>
28#include <sys/types.h>
29#include <sys/socket.h>
30#include <sys/stat.h>
31#include <sys/un.h>
32
33
34using namespace android;
35
36const char* PropertyServer::kPropCheckJni = "ro.kernel.android.checkjni";
37
38/*
39 * Destructor.
40 */
41PropertyServer::~PropertyServer(void)
42{
43 if (IsRunning()) {
44 // TODO: cause thread to stop, then Wait for it
45 }
46 printf("Sim: in ~PropertyServer()\n");
47}
48
49/*
50 * Create and run the thread.
51 */
52bool PropertyServer::StartThread(void)
53{
54 if (Create() != wxTHREAD_NO_ERROR) {
55 fprintf(stderr, "Sim: ERROR: can't create PropertyServer thread\n");
56 return false;
57 }
58
59 Run();
60 return true;
61}
62
63
64/*
65 * Clear out the list.
66 */
67void PropertyServer::ClearProperties(void)
68{
69 typedef List<Property>::iterator PropIter;
70
71 for (PropIter pi = mPropList.begin(); pi != mPropList.end(); ++pi) {
72 pi = mPropList.erase(pi);
73 }
74}
75
76/*
77 * Set default values for several properties.
78 */
79void PropertyServer::SetDefaultProperties(void)
80{
81 static const struct {
82 const char* key;
83 const char* value;
84 } propList[] = {
85 { "net.bt.name", "Android" },
86 { "ro.kernel.mem", "60M" },
87 { "ro.kernel.board_sardine.version", "4" },
88 { "ro.kernel.console", "null" },
89 { "ro.build.id", "engineering" },
90 { "ro.build.date", "Wed Nov 28 07:44:14 PST 2007" },
91 { "ro.build.date.utc", "1196264654" },
92 { "ro.build.type", "eng" },
93 { "ro.product.device", "simulator" /*"sooner"*/ },
94 { "ro.product.brand", "generic" },
95 { "ro.build.user", "fadden" },
96 { "ro.build.host", "marathon" },
97 { "ro.config.nocheckin", "yes" },
98 { "ro.product.manufacturer", "" },
99 { "ro.radio.use-ppp", "no" },
100 { "ro.FOREGROUND_APP_ADJ", "0" },
101 { "ro.VISIBLE_APP_ADJ", "1" },
102 { "ro.SECONDARY_SERVER_ADJ", "2" },
103 { "ro.HIDDEN_APP_MIN_ADJ", "7" },
104 { "ro.CONTENT_PROVIDER_ADJ", "14" },
105 { "ro.EMPTY_APP_ADJ", "15" },
106 { "ro.FOREGROUND_APP_MEM", "1536" },
107 { "ro.VISIBLE_APP_MEM", "2048" },
108 { "ro.SECONDARY_SERVER_MEM", "4096" },
109 { "ro.HIDDEN_APP_MEM", "8192" },
110 { "ro.EMPTY_APP_MEM", "16384" },
111 //{ "init.svc.adbd", "running" }, // causes ADB-JDWP
112 { "init.svc.usbd", "running" },
113 { "init.svc.debuggerd", "running" },
114 { "init.svc.ril-daemon", "running" },
115 { "init.svc.zygote", "running" },
116 { "init.svc.runtime", "running" },
117 { "init.svc.dbus", "running" },
118 { "init.svc.pppd_gprs", "running" },
119 { "adb.connected", "0" },
120 //{ "use-adb-networking", "1" },
121 /*
122 { "status.battery.state", "Slow" },
123 { "status.battery.level", "5" },
124 { "status.battery.level_raw", "50" },
125 { "status.battery.level_scale", "9" },
126 */
127
128 /* disable the annoying setup wizard */
129 { "app.setupwizard.disable", "1" },
130
131 /* Dalvik options, set by AndroidRuntime */
132 { "dalvik.vm.stack-trace-file", "/data/anr/traces.txt" },
133 //{ "dalvik.vm.execution-mode", "int:portable" },
134 { "dalvik.vm.enableassertions", "all" }, // -ea
135 { "dalvik.vm.dexopt-flags", "" }, // e.g. "v=a,o=v,m=n"
136 { "dalvik.vm.deadlock-predict", "off" }, // -Xdeadlockpredict
137 //{ "dalvik.vm.jniopts", "forcecopy" }, // -Xjniopts
138 { "log.redirect-stdio", "false" }, // -Xlog-stdio
139
140 /* SurfaceFlinger options */
141 { "debug.sf.nobootanimation", "1" },
142 { "debug.sf.showupdates", "0" },
143 { "debug.sf.showcpu", "0" },
144 { "debug.sf.showbackground", "0" },
145 { "debug.sf.showfps", "0" },
146 };
147
148 for (int i = 0; i < NELEM(propList); i++)
149 SetProperty(propList[i].key, propList[i].value);
150
151 Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
152 bool doCheckJni = false;
153
154 pPrefs->GetBool("check-jni", &doCheckJni);
155 if (doCheckJni)
156 SetProperty(kPropCheckJni, "1");
157 else
158 SetProperty(kPropCheckJni, "0");
159}
160
161/*
162 * Get the value of a property.
163 *
164 * "valueBuf" must hold at least PROPERTY_VALUE_MAX bytes.
165 *
166 * Returns "true" if the property was found.
167 */
168bool PropertyServer::GetProperty(const char* key, char* valueBuf)
169{
170 typedef List<Property>::iterator PropIter;
171
172 assert(key != NULL);
173 assert(valueBuf != NULL);
174
175 for (PropIter pi = mPropList.begin(); pi != mPropList.end(); ++pi) {
176 Property& prop = *pi;
177 if (strcmp(prop.key, key) == 0) {
178 if (strlen(prop.value) >= PROPERTY_VALUE_MAX) {
179 fprintf(stderr,
180 "GLITCH: properties table holds '%s' '%s' (len=%d)\n",
181 prop.key, prop.value, strlen(prop.value));
182 abort();
183 }
184 assert(strlen(prop.value) < PROPERTY_VALUE_MAX);
185 strcpy(valueBuf, prop.value);
186 return true;
187 }
188 }
189
190 //printf("Prop: get [%s] not found\n", key);
191 return false;
192}
193
194/*
195 * Set the value of a property, replacing it if it already exists.
196 *
197 * If "value" is NULL, the property is removed.
198 *
199 * If the property is immutable, this returns "false" without doing
200 * anything. (Not implemented.)
201 */
202bool PropertyServer::SetProperty(const char* key, const char* value)
203{
204 typedef List<Property>::iterator PropIter;
205
206 assert(key != NULL);
207 assert(value != NULL);
208
209 for (PropIter pi = mPropList.begin(); pi != mPropList.end(); ++pi) {
210 Property& prop = *pi;
211 if (strcmp(prop.key, key) == 0) {
212 if (value != NULL) {
213 //printf("Prop: replacing [%s]: [%s] with [%s]\n",
214 // prop.key, prop.value, value);
215 strcpy(prop.value, value);
216 } else {
217 //printf("Prop: removing [%s]\n", prop.key);
218 mPropList.erase(pi);
219 }
220 return true;
221 }
222 }
223
224 //printf("Prop: adding [%s]: [%s]\n", key, value);
225 Property tmp;
226 strcpy(tmp.key, key);
227 strcpy(tmp.value, value);
228 mPropList.push_back(tmp);
229 return true;
230}
231
232/*
233 * Create a UNIX domain socket, carefully removing it if it already
234 * exists.
235 */
236bool PropertyServer::CreateSocket(const char* fileName)
237{
238 struct stat sb;
239 bool result = false;
240 int sock = -1;
241 int cc;
242
243 cc = stat(fileName, &sb);
244 if (cc < 0) {
245 if (errno != ENOENT) {
246 LOG(LOG_ERROR, "sim-prop",
247 "Unable to stat '%s' (errno=%d)\n", fileName, errno);
248 goto bail;
249 }
250 } else {
251 /* don't touch it if it's not a socket */
252 if (!(S_ISSOCK(sb.st_mode))) {
253 LOG(LOG_ERROR, "sim-prop",
254 "File '%s' exists and is not a socket\n", fileName);
255 goto bail;
256 }
257
258 /* remove the cruft */
259 if (unlink(fileName) < 0) {
260 LOG(LOG_ERROR, "sim-prop",
261 "Unable to remove '%s' (errno=%d)\n", fileName, errno);
262 goto bail;
263 }
264 }
265
266 struct sockaddr_un addr;
267
268 sock = ::socket(AF_UNIX, SOCK_STREAM, 0);
269 if (sock < 0) {
270 LOG(LOG_ERROR, "sim-prop",
271 "UNIX domain socket create failed (errno=%d)\n", errno);
272 goto bail;
273 }
274
275 /* bind the socket; this creates the file on disk */
276 strcpy(addr.sun_path, fileName); // max 108 bytes
277 addr.sun_family = AF_UNIX;
278 cc = ::bind(sock, (struct sockaddr*) &addr, SUN_LEN(&addr));
279 if (cc < 0) {
280 LOG(LOG_ERROR, "sim",
281 "AF_UNIX bind failed for '%s' (errno=%d)\n", fileName, errno);
282 goto bail;
283 }
284
285 cc = ::listen(sock, 5);
286 if (cc < 0) {
287 LOG(LOG_ERROR, "sim", "AF_UNIX listen failed (errno=%d)\n", errno);
288 goto bail;
289 }
290
291 mListenSock = sock;
292 sock = -1;
293 result = true;
294
295bail:
296 if (sock >= 0)
297 close(sock);
298 return result;
299}
300
301/*
302 * Handle a client request.
303 *
304 * Returns true on success, false if the fd should be closed.
305 */
306bool PropertyServer::HandleRequest(int fd)
307{
308 char reqBuf[PROPERTY_KEY_MAX + PROPERTY_VALUE_MAX];
309 char valueBuf[1 + PROPERTY_VALUE_MAX];
310 ssize_t actual;
311
312 memset(valueBuf, 'x', sizeof(valueBuf)); // placate valgrind
313
314 /* read the command byte; this determines the message length */
315 actual = read(fd, reqBuf, 1);
316 if (actual <= 0)
317 return false;
318
319 if (reqBuf[0] == kSystemPropertyGet) {
320 actual = read(fd, reqBuf, PROPERTY_KEY_MAX);
321
322 if (actual != PROPERTY_KEY_MAX) {
323 fprintf(stderr, "Bad read on get: %d of %d\n",
324 actual, PROPERTY_KEY_MAX);
325 return false;
326 }
327 if (GetProperty(reqBuf, valueBuf+1))
328 valueBuf[0] = 1;
329 else
330 valueBuf[0] = 0;
331 //printf("GET property [%s]: (found=%d) [%s]\n",
332 // reqBuf, valueBuf[0], valueBuf+1);
333 if (write(fd, valueBuf, sizeof(valueBuf)) != sizeof(valueBuf)) {
334 fprintf(stderr, "Bad write on get\n");
335 return false;
336 }
337 } else if (reqBuf[0] == kSystemPropertySet) {
338 actual = read(fd, reqBuf, PROPERTY_KEY_MAX + PROPERTY_VALUE_MAX);
339 if (actual != PROPERTY_KEY_MAX + PROPERTY_VALUE_MAX) {
340 fprintf(stderr, "Bad read on set: %d of %d\n",
341 actual, PROPERTY_KEY_MAX + PROPERTY_VALUE_MAX);
342 return false;
343 }
344 //printf("SET property '%s'\n", reqBuf);
345 if (SetProperty(reqBuf, reqBuf + PROPERTY_KEY_MAX))
346 valueBuf[0] = 1;
347 else
348 valueBuf[0] = 0;
349 if (write(fd, valueBuf, 1) != 1) {
350 fprintf(stderr, "Bad write on set\n");
351 return false;
352 }
353 } else if (reqBuf[0] == kSystemPropertyList) {
354 /* TODO */
355 assert(false);
356 } else {
357 fprintf(stderr, "Unexpected request %d from prop client\n", reqBuf[0]);
358 return false;
359 }
360
361 return true;
362}
363
364/*
365 * Serve up properties.
366 */
367void PropertyServer::ServeProperties(void)
368{
369 typedef List<int>::iterator IntIter;
370 fd_set readfds;
371 int maxfd;
372
373 while (true) {
374 int cc;
375
376 FD_ZERO(&readfds);
377 FD_SET(mListenSock, &readfds);
378 maxfd = mListenSock;
379
380 for (IntIter ii = mClientList.begin(); ii != mClientList.end(); ++ii) {
381 int fd = (*ii);
382
383 FD_SET(fd, &readfds);
384 if (maxfd < fd)
385 maxfd = fd;
386 }
387
388 cc = select(maxfd+1, &readfds, NULL, NULL, NULL);
389 if (cc < 0) {
390 if (errno == EINTR) {
391 printf("hiccup!\n");
392 continue;
393 }
394 return;
395 }
396 if (FD_ISSET(mListenSock, &readfds)) {
397 struct sockaddr_un from;
398 socklen_t fromlen;
399 int newSock;
400
401 fromlen = sizeof(from);
402 newSock = ::accept(mListenSock, (struct sockaddr*) &from, &fromlen);
403 if (newSock < 0) {
404 LOG(LOG_WARN, "sim",
405 "AF_UNIX accept failed (errno=%d)\n", errno);
406 } else {
407 //printf("new props connection on %d --> %d\n",
408 // mListenSock, newSock);
409
410 mClientList.push_back(newSock);
411 }
412 }
413
414 for (IntIter ii = mClientList.begin(); ii != mClientList.end(); ) {
415 int fd = (*ii);
416 bool ok = true;
417
418 if (FD_ISSET(fd, &readfds)) {
419 //printf("--- activity on %d\n", fd);
420
421 ok = HandleRequest(fd);
422 }
423
424 if (ok) {
425 ++ii;
426 } else {
427 //printf("--- closing %d\n", fd);
428 close(fd);
429 ii = mClientList.erase(ii);
430 }
431 }
432 }
433}
434
435/*
436 * Thread entry point.
437 *
438 * This just sits and waits for a new connection. It hands it off to the
439 * main thread and then goes back to waiting.
440 *
441 * There is currently no "polite" way to shut this down.
442 */
443void* PropertyServer::Entry(void)
444{
445 if (CreateSocket(SYSTEM_PROPERTY_PIPE_NAME)) {
446 assert(mListenSock >= 0);
447 SetDefaultProperties();
448
449 /* loop until it's time to exit or we fail */
450 ServeProperties();
451
452 ClearProperties();
453
454 /*
455 * Close listen socket and all clients.
456 */
457 LOG(LOG_INFO, "sim", "Cleaning up socket list\n");
458 typedef List<int>::iterator IntIter;
459 for (IntIter ii = mClientList.begin(); ii != mClientList.end(); ++ii)
460 close((*ii));
461 close(mListenSock);
462 }
463
464 LOG(LOG_INFO, "sim", "PropertyServer thread exiting\n");
465 return NULL;
466}
467