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