blob: 91223ef4063a934374455aeb0c3ceb1f97800159 [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" },
Marco Nelissen51d41d52010-05-24 15:19:31 -070093 { "ro.build.version.sdk", "8" },
Marco Nelissen47017b32010-10-06 13:24:23 -070094 { "ro.build.version.codename", "Froyo" },
95 { "ro.build.version.release", "Froyo" },
The Android Open Source Project52d4c302009-03-03 19:29:09 -080096 { "ro.product.device", "simulator" /*"sooner"*/ },
97 { "ro.product.brand", "generic" },
98 { "ro.build.user", "fadden" },
99 { "ro.build.host", "marathon" },
100 { "ro.config.nocheckin", "yes" },
101 { "ro.product.manufacturer", "" },
102 { "ro.radio.use-ppp", "no" },
103 { "ro.FOREGROUND_APP_ADJ", "0" },
104 { "ro.VISIBLE_APP_ADJ", "1" },
Marco Nelissenf6bf5802010-06-21 12:32:29 -0700105 { "ro.PERCEPTIBLE_APP_ADJ", "2" },
106 { "ro.HEAVY_WEIGHT_APP_ADJ", "3" },
The Android Open Source Project52d4c302009-03-03 19:29:09 -0800107 { "ro.SECONDARY_SERVER_ADJ", "2" },
108 { "ro.HIDDEN_APP_MIN_ADJ", "7" },
109 { "ro.CONTENT_PROVIDER_ADJ", "14" },
110 { "ro.EMPTY_APP_ADJ", "15" },
111 { "ro.FOREGROUND_APP_MEM", "1536" },
112 { "ro.VISIBLE_APP_MEM", "2048" },
Marco Nelissenf6bf5802010-06-21 12:32:29 -0700113 { "ro.PERCEPTIBLE_APP_MEM", "4096" },
114 { "ro.HEAVY_WEIGHT_APP_MEM", "4096" },
The Android Open Source Project52d4c302009-03-03 19:29:09 -0800115 { "ro.SECONDARY_SERVER_MEM", "4096" },
116 { "ro.HIDDEN_APP_MEM", "8192" },
117 { "ro.EMPTY_APP_MEM", "16384" },
The Android Open Source Project692ab022009-03-09 11:52:11 -0700118 { "ro.HOME_APP_ADJ", "4" },
119 { "ro.HOME_APP_MEM", "4096" },
Andy McFadden3c5497c2009-06-08 10:08:18 -0700120 { "ro.BACKUP_APP_ADJ", "2" },
121 { "ro.BACKUP_APP_MEM", "4096" },
Brian Carlstromd275b622010-08-24 18:02:43 -0700122 { "ro.PERCEPTIBLE_APP_ADJ", "2" },
123 { "ro.PERCEPTIBLE_APP_MEM", "4096" },
124 { "ro.HEAVY_WEIGHT_APP_ADJ", "3" },
125 { "ro.HEAVY_WEIGHT_APP_MEM", "4096" },
The Android Open Source Project52d4c302009-03-03 19:29:09 -0800126 //{ "init.svc.adbd", "running" }, // causes ADB-JDWP
127 { "init.svc.usbd", "running" },
128 { "init.svc.debuggerd", "running" },
129 { "init.svc.ril-daemon", "running" },
130 { "init.svc.zygote", "running" },
131 { "init.svc.runtime", "running" },
132 { "init.svc.dbus", "running" },
133 { "init.svc.pppd_gprs", "running" },
134 { "adb.connected", "0" },
The Android Open Source Project52d4c302009-03-03 19:29:09 -0800135 /*
136 { "status.battery.state", "Slow" },
137 { "status.battery.level", "5" },
138 { "status.battery.level_raw", "50" },
139 { "status.battery.level_scale", "9" },
140 */
141
142 /* disable the annoying setup wizard */
143 { "app.setupwizard.disable", "1" },
144
145 /* Dalvik options, set by AndroidRuntime */
146 { "dalvik.vm.stack-trace-file", "/data/anr/traces.txt" },
147 //{ "dalvik.vm.execution-mode", "int:portable" },
148 { "dalvik.vm.enableassertions", "all" }, // -ea
149 { "dalvik.vm.dexopt-flags", "" }, // e.g. "v=a,o=v,m=n"
150 { "dalvik.vm.deadlock-predict", "off" }, // -Xdeadlockpredict
151 //{ "dalvik.vm.jniopts", "forcecopy" }, // -Xjniopts
152 { "log.redirect-stdio", "false" }, // -Xlog-stdio
153
154 /* SurfaceFlinger options */
Marco Nelissen51d41d52010-05-24 15:19:31 -0700155 { "ro.sf.lcd_density", "160" },
The Android Open Source Project52d4c302009-03-03 19:29:09 -0800156 { "debug.sf.nobootanimation", "1" },
157 { "debug.sf.showupdates", "0" },
158 { "debug.sf.showcpu", "0" },
159 { "debug.sf.showbackground", "0" },
160 { "debug.sf.showfps", "0" },
Marco Nelissenbbabb3a2009-09-23 09:52:22 -0700161 { "default", "default" },
Marco Nelissen0eae51d2010-01-12 09:44:39 -0800162
Andreas Huber791be382010-02-03 13:55:53 -0800163 /* Stagefright options */
Marco Nelissen0eae51d2010-01-12 09:44:39 -0800164 { "media.stagefright.enable-player", "true" },
Andreas Huber791be382010-02-03 13:55:53 -0800165 { "media.stagefright.enable-meta", "true" },
166 { "media.stagefright.enable-scan", "true" },
167 { "media.stagefright.enable-http", "true" },
The Android Open Source Project52d4c302009-03-03 19:29:09 -0800168 };
169
170 for (int i = 0; i < NELEM(propList); i++)
171 SetProperty(propList[i].key, propList[i].value);
172
173 Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
174 bool doCheckJni = false;
175
176 pPrefs->GetBool("check-jni", &doCheckJni);
177 if (doCheckJni)
178 SetProperty(kPropCheckJni, "1");
179 else
180 SetProperty(kPropCheckJni, "0");
181}
182
183/*
184 * Get the value of a property.
185 *
186 * "valueBuf" must hold at least PROPERTY_VALUE_MAX bytes.
187 *
188 * Returns "true" if the property was found.
189 */
190bool PropertyServer::GetProperty(const char* key, char* valueBuf)
191{
192 typedef List<Property>::iterator PropIter;
193
194 assert(key != NULL);
195 assert(valueBuf != NULL);
196
197 for (PropIter pi = mPropList.begin(); pi != mPropList.end(); ++pi) {
198 Property& prop = *pi;
199 if (strcmp(prop.key, key) == 0) {
200 if (strlen(prop.value) >= PROPERTY_VALUE_MAX) {
201 fprintf(stderr,
202 "GLITCH: properties table holds '%s' '%s' (len=%d)\n",
Andy McFadden3c5497c2009-06-08 10:08:18 -0700203 prop.key, prop.value, (int) strlen(prop.value));
The Android Open Source Project52d4c302009-03-03 19:29:09 -0800204 abort();
205 }
206 assert(strlen(prop.value) < PROPERTY_VALUE_MAX);
207 strcpy(valueBuf, prop.value);
208 return true;
209 }
210 }
211
212 //printf("Prop: get [%s] not found\n", key);
213 return false;
214}
215
216/*
217 * Set the value of a property, replacing it if it already exists.
218 *
219 * If "value" is NULL, the property is removed.
220 *
221 * If the property is immutable, this returns "false" without doing
222 * anything. (Not implemented.)
223 */
224bool PropertyServer::SetProperty(const char* key, const char* value)
225{
226 typedef List<Property>::iterator PropIter;
227
228 assert(key != NULL);
229 assert(value != NULL);
230
231 for (PropIter pi = mPropList.begin(); pi != mPropList.end(); ++pi) {
232 Property& prop = *pi;
233 if (strcmp(prop.key, key) == 0) {
234 if (value != NULL) {
235 //printf("Prop: replacing [%s]: [%s] with [%s]\n",
236 // prop.key, prop.value, value);
237 strcpy(prop.value, value);
238 } else {
239 //printf("Prop: removing [%s]\n", prop.key);
240 mPropList.erase(pi);
241 }
242 return true;
243 }
244 }
245
246 //printf("Prop: adding [%s]: [%s]\n", key, value);
247 Property tmp;
248 strcpy(tmp.key, key);
249 strcpy(tmp.value, value);
250 mPropList.push_back(tmp);
251 return true;
252}
253
254/*
255 * Create a UNIX domain socket, carefully removing it if it already
256 * exists.
257 */
258bool PropertyServer::CreateSocket(const char* fileName)
259{
260 struct stat sb;
261 bool result = false;
262 int sock = -1;
263 int cc;
264
265 cc = stat(fileName, &sb);
266 if (cc < 0) {
267 if (errno != ENOENT) {
268 LOG(LOG_ERROR, "sim-prop",
269 "Unable to stat '%s' (errno=%d)\n", fileName, errno);
270 goto bail;
271 }
272 } else {
273 /* don't touch it if it's not a socket */
274 if (!(S_ISSOCK(sb.st_mode))) {
275 LOG(LOG_ERROR, "sim-prop",
276 "File '%s' exists and is not a socket\n", fileName);
277 goto bail;
278 }
279
280 /* remove the cruft */
281 if (unlink(fileName) < 0) {
282 LOG(LOG_ERROR, "sim-prop",
283 "Unable to remove '%s' (errno=%d)\n", fileName, errno);
284 goto bail;
285 }
286 }
287
288 struct sockaddr_un addr;
289
290 sock = ::socket(AF_UNIX, SOCK_STREAM, 0);
291 if (sock < 0) {
292 LOG(LOG_ERROR, "sim-prop",
293 "UNIX domain socket create failed (errno=%d)\n", errno);
294 goto bail;
295 }
296
297 /* bind the socket; this creates the file on disk */
298 strcpy(addr.sun_path, fileName); // max 108 bytes
299 addr.sun_family = AF_UNIX;
300 cc = ::bind(sock, (struct sockaddr*) &addr, SUN_LEN(&addr));
301 if (cc < 0) {
302 LOG(LOG_ERROR, "sim",
303 "AF_UNIX bind failed for '%s' (errno=%d)\n", fileName, errno);
304 goto bail;
305 }
306
307 cc = ::listen(sock, 5);
308 if (cc < 0) {
309 LOG(LOG_ERROR, "sim", "AF_UNIX listen failed (errno=%d)\n", errno);
310 goto bail;
311 }
312
313 mListenSock = sock;
314 sock = -1;
315 result = true;
316
317bail:
318 if (sock >= 0)
319 close(sock);
320 return result;
321}
322
323/*
324 * Handle a client request.
325 *
326 * Returns true on success, false if the fd should be closed.
327 */
328bool PropertyServer::HandleRequest(int fd)
329{
330 char reqBuf[PROPERTY_KEY_MAX + PROPERTY_VALUE_MAX];
331 char valueBuf[1 + PROPERTY_VALUE_MAX];
332 ssize_t actual;
333
334 memset(valueBuf, 'x', sizeof(valueBuf)); // placate valgrind
335
336 /* read the command byte; this determines the message length */
337 actual = read(fd, reqBuf, 1);
338 if (actual <= 0)
339 return false;
340
341 if (reqBuf[0] == kSystemPropertyGet) {
342 actual = read(fd, reqBuf, PROPERTY_KEY_MAX);
343
344 if (actual != PROPERTY_KEY_MAX) {
345 fprintf(stderr, "Bad read on get: %d of %d\n",
Andy McFadden3c5497c2009-06-08 10:08:18 -0700346 (int) actual, PROPERTY_KEY_MAX);
The Android Open Source Project52d4c302009-03-03 19:29:09 -0800347 return false;
348 }
349 if (GetProperty(reqBuf, valueBuf+1))
350 valueBuf[0] = 1;
351 else
352 valueBuf[0] = 0;
353 //printf("GET property [%s]: (found=%d) [%s]\n",
354 // reqBuf, valueBuf[0], valueBuf+1);
355 if (write(fd, valueBuf, sizeof(valueBuf)) != sizeof(valueBuf)) {
356 fprintf(stderr, "Bad write on get\n");
357 return false;
358 }
359 } else if (reqBuf[0] == kSystemPropertySet) {
360 actual = read(fd, reqBuf, PROPERTY_KEY_MAX + PROPERTY_VALUE_MAX);
361 if (actual != PROPERTY_KEY_MAX + PROPERTY_VALUE_MAX) {
362 fprintf(stderr, "Bad read on set: %d of %d\n",
Andy McFadden3c5497c2009-06-08 10:08:18 -0700363 (int) actual, PROPERTY_KEY_MAX + PROPERTY_VALUE_MAX);
The Android Open Source Project52d4c302009-03-03 19:29:09 -0800364 return false;
365 }
366 //printf("SET property '%s'\n", reqBuf);
367 if (SetProperty(reqBuf, reqBuf + PROPERTY_KEY_MAX))
368 valueBuf[0] = 1;
369 else
370 valueBuf[0] = 0;
371 if (write(fd, valueBuf, 1) != 1) {
372 fprintf(stderr, "Bad write on set\n");
373 return false;
374 }
375 } else if (reqBuf[0] == kSystemPropertyList) {
376 /* TODO */
377 assert(false);
378 } else {
379 fprintf(stderr, "Unexpected request %d from prop client\n", reqBuf[0]);
380 return false;
381 }
382
383 return true;
384}
385
386/*
387 * Serve up properties.
388 */
389void PropertyServer::ServeProperties(void)
390{
391 typedef List<int>::iterator IntIter;
392 fd_set readfds;
393 int maxfd;
394
395 while (true) {
396 int cc;
397
398 FD_ZERO(&readfds);
399 FD_SET(mListenSock, &readfds);
400 maxfd = mListenSock;
401
402 for (IntIter ii = mClientList.begin(); ii != mClientList.end(); ++ii) {
403 int fd = (*ii);
404
405 FD_SET(fd, &readfds);
406 if (maxfd < fd)
407 maxfd = fd;
408 }
409
410 cc = select(maxfd+1, &readfds, NULL, NULL, NULL);
411 if (cc < 0) {
412 if (errno == EINTR) {
413 printf("hiccup!\n");
414 continue;
415 }
416 return;
417 }
418 if (FD_ISSET(mListenSock, &readfds)) {
419 struct sockaddr_un from;
420 socklen_t fromlen;
421 int newSock;
422
423 fromlen = sizeof(from);
424 newSock = ::accept(mListenSock, (struct sockaddr*) &from, &fromlen);
425 if (newSock < 0) {
426 LOG(LOG_WARN, "sim",
427 "AF_UNIX accept failed (errno=%d)\n", errno);
428 } else {
429 //printf("new props connection on %d --> %d\n",
430 // mListenSock, newSock);
431
432 mClientList.push_back(newSock);
433 }
434 }
435
436 for (IntIter ii = mClientList.begin(); ii != mClientList.end(); ) {
437 int fd = (*ii);
438 bool ok = true;
439
440 if (FD_ISSET(fd, &readfds)) {
441 //printf("--- activity on %d\n", fd);
442
443 ok = HandleRequest(fd);
444 }
445
446 if (ok) {
447 ++ii;
448 } else {
449 //printf("--- closing %d\n", fd);
450 close(fd);
451 ii = mClientList.erase(ii);
452 }
453 }
454 }
455}
456
457/*
458 * Thread entry point.
459 *
460 * This just sits and waits for a new connection. It hands it off to the
461 * main thread and then goes back to waiting.
462 *
463 * There is currently no "polite" way to shut this down.
464 */
465void* PropertyServer::Entry(void)
466{
467 if (CreateSocket(SYSTEM_PROPERTY_PIPE_NAME)) {
468 assert(mListenSock >= 0);
469 SetDefaultProperties();
470
471 /* loop until it's time to exit or we fail */
472 ServeProperties();
473
474 ClearProperties();
475
476 /*
477 * Close listen socket and all clients.
478 */
479 LOG(LOG_INFO, "sim", "Cleaning up socket list\n");
480 typedef List<int>::iterator IntIter;
481 for (IntIter ii = mClientList.begin(); ii != mClientList.end(); ++ii)
482 close((*ii));
483 close(mListenSock);
484 }
485
486 LOG(LOG_INFO, "sim", "PropertyServer thread exiting\n");
487 return NULL;
488}
489