diff options
88 files changed, 6634 insertions, 2432 deletions
diff --git a/CleanSpec.mk b/CleanSpec.mk index 0254bd2..e78fc88 100644 --- a/CleanSpec.mk +++ b/CleanSpec.mk @@ -53,3 +53,4 @@ $(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/init.rc) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/reboot) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/default.prop) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/default.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/lmkd_intermediates/import_includes) diff --git a/adb/Android.mk b/adb/Android.mk index 80c1f9c..bba5365 100644 --- a/adb/Android.mk +++ b/adb/Android.mk @@ -168,7 +168,7 @@ LOCAL_C_INCLUDES += external/openssl/include LOCAL_MODULE := adb -LOCAL_STATIC_LIBRARIES := libzipfile libunz libcutils +LOCAL_STATIC_LIBRARIES := libzipfile libunz libcutils liblog LOCAL_SHARED_LIBRARIES := libcrypto @@ -17,6 +17,9 @@ #ifndef __ADB_H #define __ADB_H +#if !ADB_HOST +#include <android/log.h> +#endif #include <limits.h> #include "transport.h" /* readx(), writex() */ @@ -36,7 +39,7 @@ #define ADB_VERSION_MAJOR 1 // Used for help/version information #define ADB_VERSION_MINOR 0 // Used for help/version information -#define ADB_SERVER_VERSION 31 // Increment this when we want to force users to start a new adb server +#define ADB_SERVER_VERSION 32 // Increment this when we want to force users to start a new adb server typedef struct amessage amessage; typedef struct apacket apacket; @@ -382,7 +385,27 @@ void adb_qemu_trace(const char* fmt, ...); # define ADB_TRACING ((adb_trace_mask & (1 << TRACE_TAG)) != 0) - /* you must define TRACE_TAG before using this macro */ +/* you must define TRACE_TAG before using this macro */ +#if !ADB_HOST +# define D(...) \ + do { \ + if (ADB_TRACING) { \ + __android_log_print( \ + ANDROID_LOG_INFO, \ + __FUNCTION__, \ + __VA_ARGS__ ); \ + } \ + } while (0) +# define DR(...) \ + do { \ + if (ADB_TRACING) { \ + __android_log_print( \ + ANDROID_LOG_INFO, \ + __FUNCTION__, \ + __VA_ARGS__ ); \ + } \ + } while (0) +#else # define D(...) \ do { \ if (ADB_TRACING) { \ @@ -409,6 +432,7 @@ void adb_qemu_trace(const char* fmt, ...); errno = save_errno; \ } \ } while (0) +#endif #else # define D(...) ((void)0) # define DR(...) ((void)0) diff --git a/adb/adb_client.c b/adb/adb_client.c index 1e47486..eb1720d 100644 --- a/adb/adb_client.c +++ b/adb/adb_client.c @@ -279,7 +279,7 @@ int adb_connect(const char *service) fd = _adb_connect(service); if(fd == -1) { - fprintf(stderr,"error: %s\n", __adb_error); + D("_adb_connect error: %s\n", __adb_error); } else if(fd == -2) { fprintf(stderr,"** daemon still not running\n"); } @@ -296,6 +296,7 @@ int adb_command(const char *service) { int fd = adb_connect(service); if(fd < 0) { + fprintf(stderr, "error: %s\n", adb_error()); return -1; } diff --git a/adb/commandline.c b/adb/commandline.c index 1ba6049..e1ff856 100644 --- a/adb/commandline.c +++ b/adb/commandline.c @@ -41,8 +41,9 @@ static int do_cmd(transport_type ttype, char* serial, char *cmd, ...); void get_my_path(char *s, size_t maxLen); int find_sync_dirs(const char *srcarg, - char **android_srcdir_out, char **data_srcdir_out); + char **android_srcdir_out, char **data_srcdir_out, char **vendor_srcdir_out); int install_app(transport_type transport, char* serial, int argc, char** argv); +int install_multiple_app(transport_type transport, char* serial, int argc, char** argv); int uninstall_app(transport_type transport, char* serial, int argc, char** argv); static const char *gProductOutPath = NULL; @@ -151,12 +152,15 @@ void help() " - remove a specific reversed socket connection\n" " adb reverse --remove-all - remove all reversed socket connections from device\n" " adb jdwp - list PIDs of processes hosting a JDWP transport\n" - " adb install [-l] [-r] [-s] [--algo <algorithm name> --key <hex-encoded key> --iv <hex-encoded iv>] <file>\n" + " adb install [-lrtsd] <file>\n" + " adb install-multiple [-lrtsdp] <file...>\n" " - push this package file to the device and install it\n" - " ('-l' means forward-lock the app)\n" - " ('-r' means reinstall the app, keeping its data)\n" - " ('-s' means install on SD card instead of internal storage)\n" - " ('--algo', '--key', and '--iv' mean the file is encrypted already)\n" + " (-l: forward lock application)\n" + " (-r: replace existing application)\n" + " (-t: allow test packages)\n" + " (-s: install application on sdcard)\n" + " (-d: allow version code downgrade)\n" + " (-p: partial application install)\n" " adb uninstall [-k] <package> - remove this app package from the device\n" " ('-k' means keep the data and cache directories)\n" " adb bugreport - return all information from the device\n" @@ -195,7 +199,7 @@ void help() " adb get-serialno - prints: <serial-number>\n" " adb get-devpath - prints: <device-path>\n" " adb status-window - continuously print device status for a specified device\n" - " adb remount - remounts the /system partition on the device read-write\n" + " adb remount - remounts the /system and /vendor (if present) partitions on the device read-write\n" " adb reboot [bootloader|recovery] - reboots the device, optionally into the bootloader or recovery program\n" " adb reboot-bootloader - reboots the device into the bootloader\n" " adb root - restarts the adbd daemon with root permissions\n" @@ -211,9 +215,9 @@ void help() "adb sync notes: adb sync [ <directory> ]\n" " <localdir> can be interpreted in several ways:\n" "\n" - " - If <directory> is not specified, both /system and /data partitions will be updated.\n" + " - If <directory> is not specified, /system, /vendor (if present), and /data partitions will be updated.\n" "\n" - " - If it is \"system\" or \"data\", only the corresponding partition\n" + " - If it is \"system\", \"vendor\" or \"data\", only the corresponding partition\n" " is updated.\n" "\n" "environmental variables:\n" @@ -279,6 +283,24 @@ static void read_and_dump(int fd) } } +static void read_status_line(int fd, char* buf, size_t count) +{ + count--; + while (count > 0) { + int len = adb_read(fd, buf, count); + if (len == 0) { + break; + } else if (len < 0) { + if (errno == EINTR) continue; + break; + } + + buf += len; + count -= len; + } + *buf = '\0'; +} + static void copy_to_file(int inFd, int outFd) { const size_t BUFSIZE = 32 * 1024; char* buf = (char*) malloc(BUFSIZE); @@ -500,6 +522,115 @@ int adb_download(const char *service, const char *fn, unsigned progress) return status; } +#define SIDELOAD_HOST_BLOCK_SIZE (CHUNK_SIZE) + +/* + * The sideload-host protocol serves the data in a file (given on the + * command line) to the client, using a simple protocol: + * + * - The connect message includes the total number of bytes in the + * file and a block size chosen by us. + * + * - The other side sends the desired block number as eight decimal + * digits (eg "00000023" for block 23). Blocks are numbered from + * zero. + * + * - We send back the data of the requested block. The last block is + * likely to be partial; when the last block is requested we only + * send the part of the block that exists, it's not padded up to the + * block size. + * + * - When the other side sends "DONEDONE" instead of a block number, + * we hang up. + */ +int adb_sideload_host(const char* fn) { + uint8_t* data; + unsigned sz; + size_t xfer = 0; + int status; + + printf("loading: '%s'", fn); + fflush(stdout); + data = load_file(fn, &sz); + if (data == 0) { + printf("\n"); + fprintf(stderr, "* cannot read '%s' *\n", fn); + return -1; + } + + char buf[100]; + sprintf(buf, "sideload-host:%d:%d", sz, SIDELOAD_HOST_BLOCK_SIZE); + int fd = adb_connect(buf); + if (fd < 0) { + // Try falling back to the older sideload method. Maybe this + // is an older device that doesn't support sideload-host. + printf("\n"); + status = adb_download_buffer("sideload", fn, data, sz, 1); + goto done; + } + + int opt = SIDELOAD_HOST_BLOCK_SIZE; + opt = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (const void *) &opt, sizeof(opt)); + + int last_percent = -1; + for (;;) { + if (readx(fd, buf, 8)) { + fprintf(stderr, "* failed to read command: %s\n", adb_error()); + status = -1; + goto done; + } + + if (strncmp("DONEDONE", buf, 8) == 0) { + status = 0; + break; + } + + buf[8] = '\0'; + int block = strtol(buf, NULL, 10); + + size_t offset = block * SIDELOAD_HOST_BLOCK_SIZE; + if (offset >= sz) { + fprintf(stderr, "* attempt to read past end: %s\n", adb_error()); + status = -1; + goto done; + } + uint8_t* start = data + offset; + size_t offset_end = offset + SIDELOAD_HOST_BLOCK_SIZE; + size_t to_write = SIDELOAD_HOST_BLOCK_SIZE; + if (offset_end > sz) { + to_write = sz - offset; + } + + if(writex(fd, start, to_write)) { + adb_status(fd); + fprintf(stderr,"* failed to write data '%s' *\n", adb_error()); + status = -1; + goto done; + } + xfer += to_write; + + // For normal OTA packages, we expect to transfer every byte + // twice, plus a bit of overhead (one read during + // verification, one read of each byte for installation, plus + // extra access to things like the zip central directory). + // This estimate of the completion becomes 100% when we've + // transferred ~2.13 (=100/47) times the package size. + int percent = (int)(xfer * 47LL / (sz ? sz : 1)); + if (percent != last_percent) { + printf("\rserving: '%s' (~%d%%) ", fn, percent); + fflush(stdout); + last_percent = percent; + } + } + + printf("\rTotal xfer: %.2fx%*s\n", (double)xfer / (sz ? sz : 1), (int)strlen(fn)+10, ""); + + done: + if (fd >= 0) adb_close(fd); + free(data); + return status; +} + static void status_window(transport_type ttype, const char* serial) { char command[4096]; @@ -544,39 +675,40 @@ static void status_window(transport_type ttype, const char* serial) } } -/** duplicate string and quote all \ " ( ) chars + space character. */ -static char * -dupAndQuote(const char *s) +/** Duplicate and escape given argument. */ +static char *escape_arg(const char *s) { const char *ts; size_t alloc_len; char *ret; char *dest; - ts = s; - alloc_len = 0; - - for( ;*ts != '\0'; ts++) { + for (ts = s; *ts != '\0'; ts++) { alloc_len++; if (*ts == ' ' || *ts == '"' || *ts == '\\' || *ts == '(' || *ts == ')') { alloc_len++; } } - ret = (char *)malloc(alloc_len + 1); + if (alloc_len == 0) { + // Preserve empty arguments + ret = (char *) malloc(3); + ret[0] = '\"'; + ret[1] = '\"'; + ret[2] = '\0'; + return ret; + } - ts = s; + ret = (char *) malloc(alloc_len + 1); dest = ret; - for ( ;*ts != '\0'; ts++) { + for (ts = s; *ts != '\0'; ts++) { if (*ts == ' ' || *ts == '"' || *ts == '\\' || *ts == '(' || *ts == ')') { *dest++ = '\\'; } - *dest++ = *ts; } - *dest++ = '\0'; return ret; @@ -683,30 +815,24 @@ static int logcat(transport_type transport, char* serial, int argc, char **argv) char buf[4096]; char *log_tags; - char *quoted_log_tags; + char *quoted; log_tags = getenv("ANDROID_LOG_TAGS"); - quoted_log_tags = dupAndQuote(log_tags == NULL ? "" : log_tags); - + quoted = escape_arg(log_tags == NULL ? "" : log_tags); snprintf(buf, sizeof(buf), - "shell:export ANDROID_LOG_TAGS=\"\%s\" ; exec logcat", - quoted_log_tags); - - free(quoted_log_tags); + "shell:export ANDROID_LOG_TAGS=\"%s\"; exec logcat", quoted); + free(quoted); - if (!strcmp(argv[0],"longcat")) { - strncat(buf, " -v long", sizeof(buf)-1); + if (!strcmp(argv[0], "longcat")) { + strncat(buf, " -v long", sizeof(buf) - 1); } argc -= 1; argv += 1; while(argc-- > 0) { - char *quoted; - - quoted = dupAndQuote (*argv++); - - strncat(buf, " ", sizeof(buf)-1); - strncat(buf, quoted, sizeof(buf)-1); + quoted = escape_arg(*argv++); + strncat(buf, " ", sizeof(buf) - 1); + strncat(buf, quoted, sizeof(buf) - 1); free(quoted); } @@ -1218,7 +1344,7 @@ top: argc -= 2; argv += 2; while (argc-- > 0) { - char *quoted = dupAndQuote(*argv++); + char *quoted = escape_arg(*argv++); strncat(buf, " ", sizeof(buf) - 1); strncat(buf, quoted, sizeof(buf) - 1); free(quoted); @@ -1261,7 +1387,7 @@ top: argc -= 2; argv += 2; while (argc-- > 0) { - char *quoted = dupAndQuote(*argv++); + char *quoted = escape_arg(*argv++); strncat(buf, " ", sizeof(buf) - 1); strncat(buf, quoted, sizeof(buf) - 1); free(quoted); @@ -1295,7 +1421,7 @@ top: if(!strcmp(argv[0], "sideload")) { if(argc != 2) return usage(); - if(adb_download("sideload", argv[1], 1)) { + if (adb_sideload_host(argv[1])) { return 1; } else { return 0; @@ -1471,7 +1597,7 @@ top: parse_push_pull_args(&argv[1], argc - 1, &lpath, &rpath, &show_progress, ©_attrs); if ((lpath != NULL) && (rpath != NULL)) { - return do_sync_push(lpath, rpath, 0 /* no verify APK */, show_progress); + return do_sync_push(lpath, rpath, show_progress); } return usage(); @@ -1491,18 +1617,23 @@ top: return usage(); } - if(!strcmp(argv[0], "install")) { + if (!strcmp(argv[0], "install")) { if (argc < 2) return usage(); return install_app(ttype, serial, argc, argv); } - if(!strcmp(argv[0], "uninstall")) { + if (!strcmp(argv[0], "install-multiple")) { + if (argc < 2) return usage(); + return install_multiple_app(ttype, serial, argc, argv); + } + + if (!strcmp(argv[0], "uninstall")) { if (argc < 2) return usage(); return uninstall_app(ttype, serial, argc, argv); } if(!strcmp(argv[0], "sync")) { - char *srcarg, *android_srcpath, *data_srcpath; + char *srcarg, *android_srcpath, *data_srcpath, *vendor_srcpath; int listonly = 0; int ret; @@ -1522,15 +1653,18 @@ top: } else { return usage(); } - ret = find_sync_dirs(srcarg, &android_srcpath, &data_srcpath); + ret = find_sync_dirs(srcarg, &android_srcpath, &data_srcpath, &vendor_srcpath); if(ret != 0) return usage(); if(android_srcpath != NULL) ret = do_sync_sync(android_srcpath, "/system", listonly); + if(ret == 0 && vendor_srcpath != NULL) + ret = do_sync_sync(vendor_srcpath, "/vendor", listonly); if(ret == 0 && data_srcpath != NULL) ret = do_sync_sync(data_srcpath, "/data", listonly); free(android_srcpath); + free(vendor_srcpath); free(data_srcpath); return ret; } @@ -1641,25 +1775,30 @@ static int do_cmd(transport_type ttype, char* serial, char *cmd, ...) } int find_sync_dirs(const char *srcarg, - char **android_srcdir_out, char **data_srcdir_out) + char **android_srcdir_out, char **data_srcdir_out, char **vendor_srcdir_out) { - char *android_srcdir, *data_srcdir; + char *android_srcdir = NULL, *data_srcdir = NULL, *vendor_srcdir = NULL; + struct stat st; if(srcarg == NULL) { android_srcdir = product_file("system"); data_srcdir = product_file("data"); + vendor_srcdir = product_file("vendor"); + /* Check if vendor partition exists */ + if (lstat(vendor_srcdir, &st) || !S_ISDIR(st.st_mode)) + vendor_srcdir = NULL; } else { /* srcarg may be "data", "system" or NULL. * if srcarg is NULL, then both data and system are synced */ if(strcmp(srcarg, "system") == 0) { android_srcdir = product_file("system"); - data_srcdir = NULL; } else if(strcmp(srcarg, "data") == 0) { - android_srcdir = NULL; data_srcdir = product_file("data"); + } else if(strcmp(srcarg, "vendor") == 0) { + vendor_srcdir = product_file("vendor"); } else { - /* It's not "system" or "data". + /* It's not "system", "vendor", or "data". */ return 1; } @@ -1670,11 +1809,15 @@ int find_sync_dirs(const char *srcarg, else free(android_srcdir); - if(data_srcdir_out != NULL) - *data_srcdir_out = data_srcdir; + if(vendor_srcdir_out != NULL) + *vendor_srcdir_out = vendor_srcdir; else - free(data_srcdir); + free(vendor_srcdir); + if(data_srcdir_out != NULL) + *data_srcdir_out = data_srcdir; + else + free(data_srcdir); return 0; } @@ -1686,12 +1829,9 @@ static int pm_command(transport_type transport, char* serial, snprintf(buf, sizeof(buf), "shell:pm"); while(argc-- > 0) { - char *quoted; - - quoted = dupAndQuote(*argv++); - - strncat(buf, " ", sizeof(buf)-1); - strncat(buf, quoted, sizeof(buf)-1); + char *quoted = escape_arg(*argv++); + strncat(buf, " ", sizeof(buf) - 1); + strncat(buf, quoted, sizeof(buf) - 1); free(quoted); } @@ -1723,7 +1863,7 @@ static int delete_file(transport_type transport, char* serial, char* filename) char* quoted; snprintf(buf, sizeof(buf), "shell:rm "); - quoted = dupAndQuote(filename); + quoted = escape_arg(filename); strncat(buf, quoted, sizeof(buf)-1); free(quoted); @@ -1742,117 +1882,186 @@ static const char* get_basename(const char* filename) } } -static int check_file(const char* filename) +int install_app(transport_type transport, char* serial, int argc, char** argv) { - struct stat st; + static const char *const DATA_DEST = "/data/local/tmp/%s"; + static const char *const SD_DEST = "/sdcard/tmp/%s"; + const char* where = DATA_DEST; + int i; + struct stat sb; - if (filename == NULL) { - return 0; + for (i = 1; i < argc; i++) { + if (!strcmp(argv[i], "-s")) { + where = SD_DEST; + } } - if (stat(filename, &st) != 0) { - fprintf(stderr, "can't find '%s' to install\n", filename); - return 1; + // Find last APK argument. + // All other arguments passed through verbatim. + int last_apk = -1; + for (i = argc - 1; i >= 0; i--) { + char* file = argv[i]; + char* dot = strrchr(file, '.'); + if (dot && !strcasecmp(dot, ".apk")) { + if (stat(file, &sb) == -1 || !S_ISREG(sb.st_mode)) { + fprintf(stderr, "Invalid APK file: %s\n", file); + return -1; + } + + last_apk = i; + break; + } } - if (!S_ISREG(st.st_mode)) { - fprintf(stderr, "can't install '%s' because it's not a file\n", filename); - return 1; + if (last_apk == -1) { + fprintf(stderr, "Missing APK file\n"); + return -1; } - return 0; + char* apk_file = argv[last_apk]; + char apk_dest[PATH_MAX]; + snprintf(apk_dest, sizeof apk_dest, where, get_basename(apk_file)); + int err = do_sync_push(apk_file, apk_dest, 0 /* no show progress */); + if (err) { + goto cleanup_apk; + } else { + argv[last_apk] = apk_dest; /* destination name, not source location */ + } + + pm_command(transport, serial, argc, argv); + +cleanup_apk: + delete_file(transport, serial, apk_dest); + return err; } -int install_app(transport_type transport, char* serial, int argc, char** argv) +int install_multiple_app(transport_type transport, char* serial, int argc, char** argv) { - static const char *const DATA_DEST = "/data/local/tmp/%s"; - static const char *const SD_DEST = "/sdcard/tmp/%s"; - const char* where = DATA_DEST; - char apk_dest[PATH_MAX]; - char verification_dest[PATH_MAX]; - char* apk_file; - char* verification_file = NULL; - int file_arg = -1; - int err; + char buf[1024]; int i; - int verify_apk = 1; + struct stat sb; + unsigned long long total_size = 0; + + // Find all APK arguments starting at end. + // All other arguments passed through verbatim. + int first_apk = -1; + for (i = argc - 1; i >= 0; i--) { + char* file = argv[i]; + char* dot = strrchr(file, '.'); + if (dot && !strcasecmp(dot, ".apk")) { + if (stat(file, &sb) == -1 || !S_ISREG(sb.st_mode)) { + fprintf(stderr, "Invalid APK file: %s\n", file); + return -1; + } - for (i = 1; i < argc; i++) { - if (*argv[i] != '-') { - file_arg = i; + total_size += sb.st_size; + first_apk = i; + } else { break; - } else if (!strcmp(argv[i], "-i")) { - // Skip the installer package name. - i++; - } else if (!strcmp(argv[i], "-s")) { - where = SD_DEST; - } else if (!strcmp(argv[i], "--algo")) { - verify_apk = 0; - i++; - } else if (!strcmp(argv[i], "--iv")) { - verify_apk = 0; - i++; - } else if (!strcmp(argv[i], "--key")) { - verify_apk = 0; - i++; - } else if (!strcmp(argv[i], "--abi")) { - i++; - } - } - - if (file_arg < 0) { - fprintf(stderr, "can't find filename in arguments\n"); - return 1; - } else if (file_arg + 2 < argc) { - fprintf(stderr, "too many files specified; only takes APK file and verifier file\n"); + } + } + + if (first_apk == -1) { + fprintf(stderr, "Missing APK file\n"); return 1; } - apk_file = argv[file_arg]; + snprintf(buf, sizeof(buf), "exec:pm install-create -S %lld", total_size); + for (i = 1; i < first_apk; i++) { + char *quoted = escape_arg(argv[i]); + strncat(buf, " ", sizeof(buf) - 1); + strncat(buf, quoted, sizeof(buf) - 1); + free(quoted); + } - if (file_arg != argc - 1) { - verification_file = argv[file_arg + 1]; + // Create install session + int fd = adb_connect(buf); + if (fd < 0) { + fprintf(stderr, "Connect error for create: %s\n", adb_error()); + return -1; } + read_status_line(fd, buf, sizeof(buf)); + adb_close(fd); - if (check_file(apk_file) || check_file(verification_file)) { - return 1; + int session_id = -1; + if (!strncmp("Success", buf, 7)) { + char* start = strrchr(buf, '['); + char* end = strrchr(buf, ']'); + if (start && end) { + *end = '\0'; + session_id = strtol(start + 1, NULL, 10); + } + } + if (session_id < 0) { + fprintf(stderr, "Failed to create session\n"); + fputs(buf, stderr); + return -1; } - snprintf(apk_dest, sizeof apk_dest, where, get_basename(apk_file)); - if (verification_file != NULL) { - snprintf(verification_dest, sizeof(verification_dest), where, get_basename(verification_file)); + // Valid session, now stream the APKs + int success = 1; + for (i = first_apk; i < argc; i++) { + char* file = argv[i]; + if (stat(file, &sb) == -1) { + fprintf(stderr, "Failed to stat %s\n", file); + success = 0; + goto finalize_session; + } - if (!strcmp(apk_dest, verification_dest)) { - fprintf(stderr, "APK and verification file can't have the same name\n"); - return 1; + snprintf(buf, sizeof(buf), "exec:pm install-write -S %lld %d %d_%s -", + (long long int) sb.st_size, session_id, i, get_basename(file)); + + int localFd = adb_open(file, O_RDONLY); + if (localFd < 0) { + fprintf(stderr, "Failed to open %s: %s\n", file, adb_error()); + success = 0; + goto finalize_session; } - } - err = do_sync_push(apk_file, apk_dest, verify_apk, 0 /* no show progress */); - if (err) { - goto cleanup_apk; - } else { - argv[file_arg] = apk_dest; /* destination name, not source location */ - } + int remoteFd = adb_connect(buf); + if (remoteFd < 0) { + fprintf(stderr, "Connect error for write: %s\n", adb_error()); + adb_close(localFd); + success = 0; + goto finalize_session; + } - if (verification_file != NULL) { - err = do_sync_push(verification_file, verification_dest, 0 /* no verify APK */, - 0 /* no show progress */); - if (err) { - goto cleanup_apk; - } else { - argv[file_arg + 1] = verification_dest; /* destination name, not source location */ + copy_to_file(localFd, remoteFd); + read_status_line(remoteFd, buf, sizeof(buf)); + + adb_close(localFd); + adb_close(remoteFd); + + if (strncmp("Success", buf, 7)) { + fprintf(stderr, "Failed to write %s\n", file); + fputs(buf, stderr); + success = 0; + goto finalize_session; } } - pm_command(transport, serial, argc, argv); - -cleanup_apk: - if (verification_file != NULL) { - delete_file(transport, serial, verification_dest); +finalize_session: + // Commit session if we streamed everything okay; otherwise abandon + if (success) { + snprintf(buf, sizeof(buf), "exec:pm install-commit %d", session_id); + } else { + snprintf(buf, sizeof(buf), "exec:pm install-abandon %d", session_id); } - delete_file(transport, serial, apk_dest); + fd = adb_connect(buf); + if (fd < 0) { + fprintf(stderr, "Connect error for finalize: %s\n", adb_error()); + return -1; + } + read_status_line(fd, buf, sizeof(buf)); + adb_close(fd); - return err; + if (!strncmp("Success", buf, 7)) { + fputs(buf, stderr); + return 0; + } else { + fprintf(stderr, "Failed to finalize session\n"); + fputs(buf, stderr); + return -1; + } } diff --git a/adb/file_sync_client.c b/adb/file_sync_client.c index d3cb113..ad59e81 100644 --- a/adb/file_sync_client.c +++ b/adb/file_sync_client.c @@ -335,7 +335,7 @@ static int write_data_link(int fd, const char *path, syncsendbuf *sbuf) #endif static int sync_send(int fd, const char *lpath, const char *rpath, - unsigned mtime, mode_t mode, int verifyApk, int show_progress) + unsigned mtime, mode_t mode, int show_progress) { syncmsg msg; int len, r; @@ -350,63 +350,6 @@ static int sync_send(int fd, const char *lpath, const char *rpath, snprintf(tmp, sizeof(tmp), ",%d", mode); r = strlen(tmp); - if (verifyApk) { - int lfd; - zipfile_t zip; - zipentry_t entry; - int amt; - - // if we are transferring an APK file, then sanity check to make sure - // we have a real zip file that contains an AndroidManifest.xml - // this requires that we read the entire file into memory. - lfd = adb_open(lpath, O_RDONLY); - if(lfd < 0) { - fprintf(stderr,"cannot open '%s': %s\n", lpath, strerror(errno)); - return -1; - } - - size = adb_lseek(lfd, 0, SEEK_END); - if (size == -1 || -1 == adb_lseek(lfd, 0, SEEK_SET)) { - fprintf(stderr, "error seeking in file '%s'\n", lpath); - adb_close(lfd); - return 1; - } - - file_buffer = (char *)malloc(size); - if (file_buffer == NULL) { - fprintf(stderr, "could not allocate buffer for '%s'\n", - lpath); - adb_close(lfd); - return 1; - } - amt = adb_read(lfd, file_buffer, size); - if (amt != size) { - fprintf(stderr, "error reading from file: '%s'\n", lpath); - adb_close(lfd); - free(file_buffer); - return 1; - } - - adb_close(lfd); - - zip = init_zipfile(file_buffer, size); - if (zip == NULL) { - fprintf(stderr, "file '%s' is not a valid zip file\n", - lpath); - free(file_buffer); - return 1; - } - - entry = lookup_zipentry(zip, "AndroidManifest.xml"); - release_zipfile(zip); - if (entry == NULL) { - fprintf(stderr, "file '%s' does not contain AndroidManifest.xml\n", - lpath); - free(file_buffer); - return 1; - } - } - msg.req.id = ID_SEND; msg.req.namelen = htoll(len + r); @@ -697,30 +640,33 @@ static int local_build_list(copyinfo **filelist, continue; strcpy(stat_path, lpath); strcat(stat_path, de->d_name); - stat(stat_path, &st); - if (S_ISDIR(st.st_mode)) { - ci = mkcopyinfo(lpath, rpath, name, 1); - ci->next = dirlist; - dirlist = ci; - } else { - ci = mkcopyinfo(lpath, rpath, name, 0); - if(lstat(ci->src, &st)) { - fprintf(stderr,"cannot stat '%s': %s\n", ci->src, strerror(errno)); - free(ci); - closedir(d); - return -1; - } - if(!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) { - fprintf(stderr, "skipping special file '%s'\n", ci->src); - free(ci); + if(!lstat(stat_path, &st)) { + if (S_ISDIR(st.st_mode)) { + ci = mkcopyinfo(lpath, rpath, name, 1); + ci->next = dirlist; + dirlist = ci; } else { - ci->time = st.st_mtime; - ci->mode = st.st_mode; - ci->size = st.st_size; - ci->next = *filelist; - *filelist = ci; + ci = mkcopyinfo(lpath, rpath, name, 0); + if(lstat(ci->src, &st)) { + fprintf(stderr,"cannot stat '%s': %s\n", ci->src, strerror(errno)); + free(ci); + closedir(d); + return -1; + } + if(!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) { + fprintf(stderr, "skipping special file '%s'\n", ci->src); + free(ci); + } else { + ci->time = st.st_mtime; + ci->mode = st.st_mode; + ci->size = st.st_size; + ci->next = *filelist; + *filelist = ci; + } } + } else { + fprintf(stderr, "cannot lstat '%s': %s\n",stat_path , strerror(errno)); } } @@ -786,7 +732,7 @@ static int copy_local_dir_remote(int fd, const char *lpath, const char *rpath, i if(ci->flag == 0) { fprintf(stderr,"%spush: %s -> %s\n", listonly ? "would " : "", ci->src, ci->dst); if(!listonly && - sync_send(fd, ci->src, ci->dst, ci->time, ci->mode, 0 /* no verify APK */, + sync_send(fd, ci->src, ci->dst, ci->time, ci->mode, 0 /* no show progress */)) { return 1; } @@ -805,7 +751,7 @@ static int copy_local_dir_remote(int fd, const char *lpath, const char *rpath, i } -int do_sync_push(const char *lpath, const char *rpath, int verifyApk, int show_progress) +int do_sync_push(const char *lpath, const char *rpath, int show_progress) { struct stat st; unsigned mode; @@ -852,7 +798,7 @@ int do_sync_push(const char *lpath, const char *rpath, int verifyApk, int show_p rpath = tmp; } BEGIN(); - if(sync_send(fd, lpath, rpath, st.st_mtime, st.st_mode, verifyApk, show_progress)) { + if(sync_send(fd, lpath, rpath, st.st_mtime, st.st_mode, show_progress)) { return 1; } else { END(); diff --git a/adb/file_sync_service.c b/adb/file_sync_service.c index b297552..7933858 100644 --- a/adb/file_sync_service.c +++ b/adb/file_sync_service.c @@ -39,6 +39,11 @@ static bool is_on_system(const char *name) { return (strncmp(SYSTEM, name, strlen(SYSTEM)) == 0); } +static bool is_on_vendor(const char *name) { + const char *VENDOR = "/vendor/"; + return (strncmp(VENDOR, name, strlen(VENDOR)) == 0); +} + static int mkdirs(char *name) { int ret; @@ -54,7 +59,7 @@ static int mkdirs(char *name) x = adb_dirstart(x); if(x == 0) return 0; *x = 0; - if (is_on_system(name)) { + if (is_on_system(name) || is_on_vendor(name)) { fs_config(name, 1, &uid, &gid, &mode, &cap); } ret = adb_mkdir(name, mode); @@ -369,7 +374,7 @@ static int do_send(int s, char *path, char *buffer) if(*tmp == '/') { tmp++; } - if (is_on_system(path)) { + if (is_on_system(path) || is_on_vendor(path)) { fs_config(tmp, 0, &uid, &gid, &mode, &cap); } ret = handle_send_file(s, path, uid, gid, mode, buffer, do_unlink); diff --git a/adb/file_sync_service.h b/adb/file_sync_service.h index 8ea239e..5dd2e80 100644 --- a/adb/file_sync_service.h +++ b/adb/file_sync_service.h @@ -78,7 +78,7 @@ typedef union { void file_sync_service(int fd, void *cookie); int do_sync_ls(const char *path); -int do_sync_push(const char *lpath, const char *rpath, int verifyApk, int show_progress); +int do_sync_push(const char *lpath, const char *rpath, int show_progress); int do_sync_sync(const char *lpath, const char *rpath, int listonly); int do_sync_pull(const char *rpath, const char *lpath, int show_progress, int pullTime); diff --git a/adb/remount_service.c b/adb/remount_service.c index 3a4535e..72d15a1 100644 --- a/adb/remount_service.c +++ b/adb/remount_service.c @@ -29,6 +29,7 @@ static int system_ro = 1; +static int vendor_ro = 1; /* Returns the device used to mount a directory in /proc/mounts */ static char *find_mount(const char *dir) @@ -67,18 +68,27 @@ static char *find_mount(const char *dir) return NULL; } +static int hasVendorPartition() +{ + struct stat info; + if (!lstat("/vendor", &info)) + if ((info.st_mode & S_IFMT) == S_IFDIR) + return true; + return false; +} + /* Init mounts /system as read only, remount to enable writes. */ -static int remount_system() +static int remount(const char* dir, int* dir_ro) { char *dev; int fd; int OFF = 0; - if (system_ro == 0) { + if (dir_ro == 0) { return 0; } - dev = find_mount("/system"); + dev = find_mount(dir); if (!dev) return -1; @@ -90,11 +100,11 @@ static int remount_system() ioctl(fd, BLKROSET, &OFF); adb_close(fd); - system_ro = mount(dev, "/system", "none", MS_REMOUNT, NULL); + *dir_ro = mount(dev, dir, "none", MS_REMOUNT, NULL); free(dev); - return system_ro; + return *dir_ro; } static void write_string(int fd, const char* str) @@ -104,14 +114,23 @@ static void write_string(int fd, const char* str) void remount_service(int fd, void *cookie) { - int ret = remount_system(); + char buffer[200]; + if (remount("/system", &system_ro)) { + snprintf(buffer, sizeof(buffer), "remount of system failed: %s\n",strerror(errno)); + write_string(fd, buffer); + } + + if (hasVendorPartition()) { + if (remount("/vendor", &vendor_ro)) { + snprintf(buffer, sizeof(buffer), "remount of vendor failed: %s\n",strerror(errno)); + write_string(fd, buffer); + } + } - if (!ret) - write_string(fd, "remount succeeded\n"); + if (!system_ro && (!vendor_ro || !hasVendorPartition())) + write_string(fd, "remount succeeded\n"); else { - char buffer[200]; - snprintf(buffer, sizeof(buffer), "remount failed: %s\n", strerror(errno)); - write_string(fd, buffer); + write_string(fd, "remount failed\n"); } adb_close(fd); diff --git a/adb/services.c b/adb/services.c index 2c20b81..e61371a 100644 --- a/adb/services.c +++ b/adb/services.c @@ -281,9 +281,10 @@ static int create_subproc_raw(const char *cmd, const char *arg0, const char *arg adb_close(sv[0]); init_subproc_child(); - // Only hook up stdin/stdout; drop stderr dup2(sv[1], STDIN_FILENO); dup2(sv[1], STDOUT_FILENO); + dup2(sv[1], STDERR_FILENO); + adb_close(sv[1]); execl(cmd, cmd, arg0, arg1, NULL); diff --git a/charger/Android.mk b/charger/Android.mk deleted file mode 100644 index 40c7d78..0000000 --- a/charger/Android.mk +++ /dev/null @@ -1,61 +0,0 @@ -# Copyright 2011 The Android Open Source Project - -ifneq ($(BUILD_TINY_ANDROID),true) - -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := \ - charger.c - -ifeq ($(strip $(BOARD_CHARGER_DISABLE_INIT_BLANK)),true) -LOCAL_CFLAGS := -DCHARGER_DISABLE_INIT_BLANK -endif - -ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true) -LOCAL_CFLAGS += -DCHARGER_ENABLE_SUSPEND -endif - -LOCAL_MODULE := charger -LOCAL_MODULE_TAGS := optional -LOCAL_FORCE_STATIC_EXECUTABLE := true -LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT) -LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED) - -LOCAL_C_INCLUDES := bootable/recovery - -LOCAL_STATIC_LIBRARIES := libminui libpng -ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true) -LOCAL_STATIC_LIBRARIES += libsuspend -endif -LOCAL_STATIC_LIBRARIES += libz libstdc++ libcutils liblog libm libc - -include $(BUILD_EXECUTABLE) - -define _add-charger-image -include $$(CLEAR_VARS) -LOCAL_MODULE := system_core_charger_$(notdir $(1)) -LOCAL_MODULE_STEM := $(notdir $(1)) -_img_modules += $$(LOCAL_MODULE) -LOCAL_SRC_FILES := $1 -LOCAL_MODULE_TAGS := optional -LOCAL_MODULE_CLASS := ETC -LOCAL_MODULE_PATH := $$(TARGET_ROOT_OUT)/res/images/charger -include $$(BUILD_PREBUILT) -endef - -_img_modules := -_images := -$(foreach _img, $(call find-subdir-subdir-files, "images", "*.png"), \ - $(eval $(call _add-charger-image,$(_img)))) - -include $(CLEAR_VARS) -LOCAL_MODULE := charger_res_images -LOCAL_MODULE_TAGS := optional -LOCAL_REQUIRED_MODULES := $(_img_modules) -include $(BUILD_PHONY_PACKAGE) - -_add-charger-image := -_img_modules := - -endif diff --git a/debuggerd/tombstone.cpp b/debuggerd/tombstone.cpp index f41166b..e447190 100755..100644 --- a/debuggerd/tombstone.cpp +++ b/debuggerd/tombstone.cpp @@ -674,7 +674,7 @@ static char* find_and_open_tombstone(int* fd) { if (errno != ENOENT) continue; - *fd = open(path, O_CREAT | O_EXCL | O_WRONLY, 0600); + *fd = open(path, O_CREAT | O_EXCL | O_WRONLY | O_NOFOLLOW | O_CLOEXEC, 0600); if (*fd < 0) continue; // raced ? @@ -689,7 +689,7 @@ static char* find_and_open_tombstone(int* fd) { // we didn't find an available file, so we clobber the oldest one snprintf(path, sizeof(path), TOMBSTONE_TEMPLATE, oldest); - *fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600); + *fd = open(path, O_CREAT | O_TRUNC | O_WRONLY | O_NOFOLLOW | O_CLOEXEC, 0600); if (*fd < 0) { ALOGE("failed to open tombstone file '%s': %s\n", path, strerror(errno)); return NULL; diff --git a/fastboot/Android.mk b/fastboot/Android.mk index 112bd02..d212b2c 100644 --- a/fastboot/Android.mk +++ b/fastboot/Android.mk @@ -17,7 +17,8 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_C_INCLUDES := $(LOCAL_PATH)/../mkbootimg \ - $(LOCAL_PATH)/../../extras/ext4_utils + $(LOCAL_PATH)/../../extras/ext4_utils \ + $(LOCAL_PATH)/../../extras/f2fs_utils LOCAL_SRC_FILES := protocol.c engine.c bootimg.c fastboot.c util.c fs.c LOCAL_MODULE := fastboot LOCAL_MODULE_TAGS := debug @@ -63,10 +64,24 @@ ifneq ($(HOST_OS),windows) LOCAL_STATIC_LIBRARIES += libselinux endif # HOST_OS != windows -include $(BUILD_HOST_EXECUTABLE) +ifeq ($(HOST_OS),linux) +# libf2fs_dlutils_host will dlopen("libf2fs_fmt_host_dyn") +LOCAL_CFLAGS += -DUSE_F2FS +LOCAL_LDFLAGS += -ldl -rdynamic -Wl,-rpath,. +LOCAL_REQUIRED_MODULES := libf2fs_fmt_host_dyn +# The following libf2fs_* are from system/extras/f2fs_utils, +# and do not use code in external/f2fs-tools. +LOCAL_STATIC_LIBRARIES += libf2fs_utils_host libf2fs_ioutils_host libf2fs_dlutils_host +endif +include $(BUILD_HOST_EXECUTABLE) -$(call dist-for-goals,dist_files sdk,$(LOCAL_BUILT_MODULE)) +my_dist_files := $(LOCAL_BUILT_MODULE) +ifeq ($(HOST_OS),linux) +my_dist_files += $(HOST_LIBRARY_PATH)/libf2fs_fmt_host_dyn$(HOST_SHLIB_SUFFIX) +endif +$(call dist-for-goals,dist_files sdk,$(my_dist_files)) +my_dist_files := ifeq ($(HOST_OS),linux) diff --git a/fastboot/fastboot.c b/fastboot/fastboot.c index 266d0b5..64a52b5 100644 --- a/fastboot/fastboot.c +++ b/fastboot/fastboot.c @@ -99,10 +99,11 @@ static struct { char sig_name[13]; char part_name[9]; bool is_optional; -} images[4] = { +} images[] = { {"boot.img", "boot.sig", "boot", false}, {"recovery.img", "recovery.sig", "recovery", true}, {"system.img", "system.sig", "system", false}, + {"vendor.img", "vendor.sig", "vendor", true}, {"tos.img", "tos.sig", "tos", true}, }; @@ -120,6 +121,8 @@ char *find_item(const char *item, const char *product) fn = "recovery.img"; } else if(!strcmp(item,"system")) { fn = "system.img"; + } else if(!strcmp(item,"vendor")) { + fn = "vendor.img"; } else if(!strcmp(item,"tos")) { fn = "tos.img"; } else if(!strcmp(item,"userdata")) { @@ -287,7 +290,7 @@ void usage(void) "\n" "commands:\n" " update <filename> reflash device from update.zip\n" - " flashall flash boot, system, and if found,\n" + " flashall flash boot, system, vendor and if found,\n" " recovery, tos\n" " flash <partition> [ <filename> ] write a file to a flash partition\n" " erase <partition> erase a flash partition\n" diff --git a/fastboot/fs.c b/fastboot/fs.c index cd4b880..8a15e6f 100644 --- a/fastboot/fs.c +++ b/fastboot/fs.c @@ -1,5 +1,6 @@ #include "fastboot.h" #include "make_ext4fs.h" +#include "make_f2fs.h" #include "fs.h" #include <errno.h> @@ -28,15 +29,23 @@ static int generate_ext4_image(int fd, long long partSize) return 0; } +#ifdef USE_F2FS +static int generate_f2fs_image(int fd, long long partSize) +{ + return make_f2fs_sparse_fd(fd, partSize, NULL, NULL); +} +#endif + static const struct fs_generator { char *fs_type; //must match what fastboot reports for partition type int (*generate)(int fd, long long partSize); //returns 0 or error value } generators[] = { - - { "ext4", generate_ext4_image} - + { "ext4", generate_ext4_image}, +#ifdef USE_F2FS + { "f2fs", generate_f2fs_image}, +#endif }; const struct fs_generator* fs_get_generator(const char *fs_type) diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c index dcda005..3f94af5 100644 --- a/fs_mgr/fs_mgr.c +++ b/fs_mgr/fs_mgr.c @@ -31,6 +31,7 @@ #include <linux/loop.h> #include <private/android_filesystem_config.h> +#include <cutils/android_reboot.h> #include <cutils/partition_utils.h> #include <cutils/properties.h> #include <logwrap/logwrap.h> @@ -46,6 +47,7 @@ #define KEY_IN_FOOTER "footer" #define E2FSCK_BIN "/system/bin/e2fsck" +#define F2FS_FSCK_BIN "/system/bin/fsck.f2fs" #define MKSWAP_BIN "/system/bin/mkswap" #define FSCK_LOG_FILE "/dev/fscklogs/log" @@ -112,6 +114,7 @@ static void check_fs(char *blk_device, char *fs_type, char *target) * fix the filesystem. */ ret = mount(blk_device, target, fs_type, tmpmnt_flags, tmpmnt_opts); + INFO("%s(): mount(%s,%s,%s)=%d\n", __func__, blk_device, target, fs_type, ret); if (!ret) { umount(target); } @@ -135,6 +138,20 @@ static void check_fs(char *blk_device, char *fs_type, char *target) ERROR("Failed trying to run %s\n", E2FSCK_BIN); } } + } else if (!strcmp(fs_type, "f2fs")) { + char *f2fs_fsck_argv[] = { + F2FS_FSCK_BIN, + blk_device + }; + INFO("Running %s on %s\n", F2FS_FSCK_BIN, blk_device); + + ret = android_fork_execvp_ext(ARRAY_SIZE(f2fs_fsck_argv), f2fs_fsck_argv, + &status, true, LOG_KLOG | LOG_FILE, + true, FSCK_LOG_FILE); + if (ret < 0) { + /* No need to check for error in fork, we can't really handle it now */ + ERROR("Failed trying to run %s\n", F2FS_FSCK_BIN); + } } return; @@ -175,16 +192,27 @@ static void fs_set_blk_ro(const char *blockdev) * sets the underlying block device to read-only if the mount is read-only. * See "man 2 mount" for return values. */ -static int __mount(const char *source, const char *target, - const char *filesystemtype, unsigned long mountflags, - const void *data) +static int __mount(const char *source, const char *target, const struct fstab_rec *rec) { - int ret = mount(source, target, filesystemtype, mountflags, data); + unsigned long mountflags = rec->flags; + int ret; + int save_errno; + /* We need this because sometimes we have legacy symlinks + * that are lingering around and need cleaning up. + */ + struct stat info; + if (!lstat(target, &info)) + if ((info.st_mode & S_IFMT) == S_IFLNK) + unlink(target); + mkdir(target, 0755); + ret = mount(source, target, rec->fs_type, mountflags, rec->fs_options); + save_errno = errno; + INFO("%s(source=%s,target=%s,type=%s)=%d\n", __func__, source, target, rec->fs_type, ret); if ((ret == 0) && (mountflags & MS_RDONLY) != 0) { fs_set_blk_ro(source); } - + errno = save_errno; return ret; } @@ -208,16 +236,101 @@ static int fs_match(char *in1, char *in2) return ret; } +static int device_is_debuggable() { + int ret = -1; + char value[PROP_VALUE_MAX]; + ret = __system_property_get("ro.debuggable", value); + if (ret < 0) + return ret; + return strcmp(value, "1") ? 0 : 1; +} + +/* + * Tries to mount any of the consecutive fstab entries that match + * the mountpoint of the one given by fstab->recs[start_idx]. + * + * end_idx: On return, will be the last rec that was looked at. + * attempted_idx: On return, will indicate which fstab rec + * succeeded. In case of failure, it will be the start_idx. + * Returns + * -1 on failure with errno set to match the 1st mount failure. + * 0 on success. + */ +static int mount_with_alternatives(struct fstab *fstab, int start_idx, int *end_idx, int *attempted_idx) +{ + int i; + int mount_errno = 0; + int mounted = 0; + + if (!end_idx || !attempted_idx || start_idx >= fstab->num_entries) { + errno = EINVAL; + if (end_idx) *end_idx = start_idx; + if (attempted_idx) *end_idx = start_idx; + return -1; + } + + /* Hunt down an fstab entry for the same mount point that might succeed */ + for (i = start_idx; + /* We required that fstab entries for the same mountpoint be consecutive */ + i < fstab->num_entries && !strcmp(fstab->recs[start_idx].mount_point, fstab->recs[i].mount_point); + i++) { + /* + * Don't try to mount/encrypt the same mount point again. + * Deal with alternate entries for the same point which are required to be all following + * each other. + */ + if (mounted) { + ERROR("%s(): skipping fstab dup mountpoint=%s rec[%d].fs_type=%s already mounted as %s.\n", __func__, + fstab->recs[i].mount_point, i, fstab->recs[i].fs_type, fstab->recs[*attempted_idx].fs_type); + continue; + } + + if (fstab->recs[i].fs_mgr_flags & MF_CHECK) { + check_fs(fstab->recs[i].blk_device, fstab->recs[i].fs_type, + fstab->recs[i].mount_point); + } + if (!__mount(fstab->recs[i].blk_device, fstab->recs[i].mount_point, &fstab->recs[i])) { + *attempted_idx = i; + mounted = 1; + if (i != start_idx) { + ERROR("%s(): Mounted %s on %s with fs_type=%s instead of %s\n", __func__, + fstab->recs[i].blk_device, fstab->recs[i].mount_point, fstab->recs[i].fs_type, + fstab->recs[start_idx].fs_type); + } + } else { + /* back up errno for crypto decisions */ + mount_errno = errno; + } + } + + /* Adjust i for the case where it was still withing the recs[] */ + if (i < fstab->num_entries) --i; + + *end_idx = i; + if (!mounted) { + *attempted_idx = start_idx; + errno = mount_errno; + return -1; + } + return 0; +} + +/* When multiple fstab records share the same mount_point, it will + * try to mount each one in turn, and ignore any duplicates after a + * first successful mount. + * Returns -1 on error, and FS_MGR_MNTALL_* otherwise. + */ int fs_mgr_mount_all(struct fstab *fstab) { int i = 0; - int encrypted = 0; - int ret = -1; - int mret; - int mount_errno; + int encryptable = FS_MGR_MNTALL_DEV_NOT_ENCRYPTED; + int error_count = 0; + int mret = -1; + int mount_errno = 0; + int attempted_idx = -1; if (!fstab) { - return ret; + return -1; } for (i = 0; i < fstab->num_entries; i++) { @@ -237,70 +350,92 @@ int fs_mgr_mount_all(struct fstab *fstab) wait_for_file(fstab->recs[i].blk_device, WAIT_TIMEOUT); } - if (fstab->recs[i].fs_mgr_flags & MF_CHECK) { - check_fs(fstab->recs[i].blk_device, fstab->recs[i].fs_type, - fstab->recs[i].mount_point); - } - - if (fstab->recs[i].fs_mgr_flags & MF_VERIFY) { + if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY) && + !device_is_debuggable()) { if (fs_mgr_setup_verity(&fstab->recs[i]) < 0) { - ERROR("Could not set up verified partition, skipping!"); + ERROR("Could not set up verified partition, skipping!\n"); continue; } } + int last_idx_inspected; + mret = mount_with_alternatives(fstab, i, &last_idx_inspected, &attempted_idx); + i = last_idx_inspected; + mount_errno = errno; - mret = __mount(fstab->recs[i].blk_device, fstab->recs[i].mount_point, - fstab->recs[i].fs_type, fstab->recs[i].flags, - fstab->recs[i].fs_options); - + /* Deal with encryptability. */ if (!mret) { + /* If this is encryptable, need to trigger encryption */ + if ((fstab->recs[attempted_idx].fs_mgr_flags & MF_FORCECRYPT)) { + if (umount(fstab->recs[attempted_idx].mount_point) == 0) { + if (encryptable == FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) { + ERROR("Will try to encrypt %s %s\n", fstab->recs[attempted_idx].mount_point, + fstab->recs[attempted_idx].fs_type); + encryptable = FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION; + } else { + ERROR("Only one encryptable/encrypted partition supported\n"); + encryptable = FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED; + } + } else { + INFO("Could not umount %s - allow continue unencrypted\n", + fstab->recs[attempted_idx].mount_point); + continue; + } + } /* Success! Go get the next one */ continue; } - /* back up errno as partition_wipe clobbers the value */ - mount_errno = errno; - - /* mount(2) returned an error, check if it's encrypted and deal with it */ - if ((fstab->recs[i].fs_mgr_flags & MF_CRYPT) && - !partition_wiped(fstab->recs[i].blk_device)) { - /* Need to mount a tmpfs at this mountpoint for now, and set - * properties that vold will query later for decrypting - */ - if (mount("tmpfs", fstab->recs[i].mount_point, "tmpfs", - MS_NOATIME | MS_NOSUID | MS_NODEV, CRYPTO_TMPFS_OPTIONS) < 0) { - ERROR("Cannot mount tmpfs filesystem for encrypted fs at %s error: %s\n", - fstab->recs[i].mount_point, strerror(errno)); - goto out; + /* mount(2) returned an error, check if it's encryptable and deal with it */ + if (mret && mount_errno != EBUSY && mount_errno != EACCES && + fs_mgr_is_encryptable(&fstab->recs[attempted_idx])) { + if(partition_wiped(fstab->recs[attempted_idx].blk_device)) { + ERROR("%s(): %s is wiped and %s %s is encryptable. Suggest recovery...\n", __func__, + fstab->recs[attempted_idx].blk_device, fstab->recs[attempted_idx].mount_point, + fstab->recs[attempted_idx].fs_type); + encryptable = FS_MGR_MNTALL_DEV_NEEDS_RECOVERY; + continue; + } else { + /* Need to mount a tmpfs at this mountpoint for now, and set + * properties that vold will query later for decrypting + */ + ERROR("%s(): possibly an encryptable blkdev %s for mount %s type %s )\n", __func__, + fstab->recs[attempted_idx].blk_device, fstab->recs[attempted_idx].mount_point, + fstab->recs[attempted_idx].fs_type); + if (fs_mgr_do_tmpfs_mount(fstab->recs[attempted_idx].mount_point) < 0) { + ++error_count; + continue; + } } - encrypted = 1; + encryptable = FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED; } else { ERROR("Failed to mount an un-encryptable or wiped partition on" - "%s at %s options: %s error: %s\n", - fstab->recs[i].blk_device, fstab->recs[i].mount_point, - fstab->recs[i].fs_options, strerror(mount_errno)); - goto out; + "%s at %s options: %s error: %s\n", + fstab->recs[attempted_idx].blk_device, fstab->recs[attempted_idx].mount_point, + fstab->recs[attempted_idx].fs_options, strerror(mount_errno)); + ++error_count; + continue; } } - if (encrypted) { - ret = 1; + if (error_count) { + return -1; } else { - ret = 0; + return encryptable; } - -out: - return ret; } /* If tmp_mount_point is non-null, mount the filesystem there. This is for the * tmp mount we do to check the user password + * If multiple fstab entries are to be mounted on "n_name", it will try to mount each one + * in turn, and stop on 1st success, or no more match. */ int fs_mgr_do_mount(struct fstab *fstab, char *n_name, char *n_blk_device, char *tmp_mount_point) { int i = 0; int ret = -1; + int mount_errors = 0; + int first_mount_errno = 0; char *m; if (!fstab) { @@ -332,9 +467,10 @@ int fs_mgr_do_mount(struct fstab *fstab, char *n_name, char *n_blk_device, fstab->recs[i].mount_point); } - if (fstab->recs[i].fs_mgr_flags & MF_VERIFY) { + if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY) && + !device_is_debuggable()) { if (fs_mgr_setup_verity(&fstab->recs[i]) < 0) { - ERROR("Could not set up verified partition, skipping!"); + ERROR("Could not set up verified partition, skipping!\n"); continue; } } @@ -345,19 +481,23 @@ int fs_mgr_do_mount(struct fstab *fstab, char *n_name, char *n_blk_device, } else { m = fstab->recs[i].mount_point; } - if (__mount(n_blk_device, m, fstab->recs[i].fs_type, - fstab->recs[i].flags, fstab->recs[i].fs_options)) { - ERROR("Cannot mount filesystem on %s at %s options: %s error: %s\n", - n_blk_device, m, fstab->recs[i].fs_options, strerror(errno)); - goto out; + if (__mount(n_blk_device, m, &fstab->recs[i])) { + if (!first_mount_errno) first_mount_errno = errno; + mount_errors++; + continue; } else { ret = 0; goto out; } } - - /* We didn't find a match, say so and return an error */ - ERROR("Cannot find mount point %s in fstab\n", fstab->recs[i].mount_point); + if (mount_errors) { + ERROR("Cannot mount filesystem on %s at %s. error: %s\n", + n_blk_device, m, strerror(first_mount_errno)); + ret = -1; + } else { + /* We didn't find a match, say so and return an error */ + ERROR("Cannot find mount point %s in fstab\n", fstab->recs[i].mount_point); + } out: return ret; @@ -437,7 +577,7 @@ int fs_mgr_swapon_all(struct fstab *fstab) zram_fp = fopen(ZRAM_CONF_DEV, "r+"); if (zram_fp == NULL) { - ERROR("Unable to open zram conf device " ZRAM_CONF_DEV); + ERROR("Unable to open zram conf device %s\n", ZRAM_CONF_DEV); ret = -1; continue; } @@ -504,7 +644,7 @@ int fs_mgr_get_crypt_info(struct fstab *fstab, char *key_loc, char *real_blk_dev if (fstab->recs[i].fs_mgr_flags & MF_VOLDMANAGED) { continue; } - if (!(fstab->recs[i].fs_mgr_flags & MF_CRYPT)) { + if (!(fstab->recs[i].fs_mgr_flags & (MF_CRYPT | MF_FORCECRYPT))) { continue; } diff --git a/fs_mgr/fs_mgr_fstab.c b/fs_mgr/fs_mgr_fstab.c index f86fc6a..3f84179 100644 --- a/fs_mgr/fs_mgr_fstab.c +++ b/fs_mgr/fs_mgr_fstab.c @@ -59,6 +59,7 @@ static struct flag_list fs_mgr_flags[] = { { "wait", MF_WAIT }, { "check", MF_CHECK }, { "encryptable=",MF_CRYPT }, + { "forceencrypt=",MF_FORCECRYPT }, { "nonremovable",MF_NONREMOVABLE }, { "voldmanaged=",MF_VOLDMANAGED}, { "length=", MF_LENGTH }, @@ -106,6 +107,11 @@ static int parse_flags(char *flags, struct flag_list *fl, * location of the keys. Get it and return it. */ flag_vals->key_loc = strdup(strchr(p, '=') + 1); + } else if ((fl[i].flag == MF_FORCECRYPT) && flag_vals) { + /* The forceencrypt flag is followed by an = and the + * location of the keys. Get it and return it. + */ + flag_vals->key_loc = strdup(strchr(p, '=') + 1); } else if ((fl[i].flag == MF_LENGTH) && flag_vals) { /* The length flag is followed by an = and the * size of the partition. Get it and return it. @@ -361,25 +367,47 @@ int fs_mgr_add_entry(struct fstab *fstab, return 0; } -struct fstab_rec *fs_mgr_get_entry_for_mount_point(struct fstab *fstab, const char *path) +/* + * Returns the 1st matching fstab_rec that follows the start_rec. + * start_rec is the result of a previous search or NULL. + */ +struct fstab_rec *fs_mgr_get_entry_for_mount_point_after(struct fstab_rec *start_rec, struct fstab *fstab, const char *path) { int i; - if (!fstab) { return NULL; } - for (i = 0; i < fstab->num_entries; i++) { + if (start_rec) { + for (i = 0; i < fstab->num_entries; i++) { + if (&fstab->recs[i] == start_rec) { + i++; + break; + } + } + } else { + i = 0; + } + for (; i < fstab->num_entries; i++) { int len = strlen(fstab->recs[i].mount_point); if (strncmp(path, fstab->recs[i].mount_point, len) == 0 && (path[len] == '\0' || path[len] == '/')) { return &fstab->recs[i]; } } - return NULL; } +/* + * Returns the 1st matching mount point. + * There might be more. To look for others, use fs_mgr_get_entry_for_mount_point_after() + * and give the fstab_rec from the previous search. + */ +struct fstab_rec *fs_mgr_get_entry_for_mount_point(struct fstab *fstab, const char *path) +{ + return fs_mgr_get_entry_for_mount_point_after(NULL, fstab, path); +} + int fs_mgr_is_voldmanaged(struct fstab_rec *fstab) { return fstab->fs_mgr_flags & MF_VOLDMANAGED; @@ -392,7 +420,7 @@ int fs_mgr_is_nonremovable(struct fstab_rec *fstab) int fs_mgr_is_encryptable(struct fstab_rec *fstab) { - return fstab->fs_mgr_flags & MF_CRYPT; + return fstab->fs_mgr_flags & (MF_CRYPT | MF_FORCECRYPT); } int fs_mgr_is_noemulatedsd(struct fstab_rec *fstab) diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h index 59ffd78..34938fa 100644 --- a/fs_mgr/fs_mgr_priv.h +++ b/fs_mgr/fs_mgr_priv.h @@ -23,7 +23,7 @@ #define INFO(x...) KLOG_INFO("fs_mgr", x) #define ERROR(x...) KLOG_ERROR("fs_mgr", x) -#define CRYPTO_TMPFS_OPTIONS "size=128m,mode=0771,uid=1000,gid=1000" +#define CRYPTO_TMPFS_OPTIONS "size=256m,mode=0771,uid=1000,gid=1000" #define WAIT_TIMEOUT 20 @@ -72,12 +72,9 @@ #define MF_SWAPPRIO 0x80 #define MF_ZRAMSIZE 0x100 #define MF_VERIFY 0x200 -/* - * There is no emulated sdcard daemon running on /data/media on this device, - * so treat the physical SD card as the only external storage device, - * a la the Nexus One. - */ -#define MF_NOEMULATEDSD 0x400 +#define MF_FORCECRYPT 0x400 +#define MF_NOEMULATEDSD 0x800 /* no emulated sdcard daemon, sd card is the only + external storage */ #define DM_BUF_SIZE 4096 diff --git a/fs_mgr/fs_mgr_verity.c b/fs_mgr/fs_mgr_verity.c index c9a2a9b..b79a4a8 100644 --- a/fs_mgr/fs_mgr_verity.c +++ b/fs_mgr/fs_mgr_verity.c @@ -30,6 +30,7 @@ #include <time.h> #include <private/android_filesystem_config.h> +#include <cutils/properties.h> #include <logwrap/logwrap.h> #include "mincrypt/rsa.h" @@ -334,6 +335,26 @@ static int test_access(char *device) { return -1; } +static int set_verified_property(char *name) { + int ret; + char *key; + ret = asprintf(&key, "partition.%s.verified", name); + if (ret < 0) { + ERROR("Error formatting verified property"); + return ret; + } + ret = PROP_NAME_MAX - strlen(key); + if (ret < 0) { + ERROR("Verified property name is too long"); + return -1; + } + ret = property_set(key, "1"); + if (ret < 0) + ERROR("Error setting verified property %s: %d", key, ret); + free(key); + return ret; +} + int fs_mgr_setup_verity(struct fstab_rec *fstab) { int retval = -1; @@ -350,6 +371,13 @@ int fs_mgr_setup_verity(struct fstab_rec *fstab) { io->flags |= 1; io->target_count = 1; + // check to ensure that the verity device is ext4 + // TODO: support non-ext4 filesystems + if (strcmp(fstab->fs_type, "ext4")) { + ERROR("Cannot verify non-ext4 device (%s)", fstab->fs_type); + return retval; + } + // get the device mapper fd int fd; if ((fd = open("/dev/device-mapper", O_RDWR)) < 0) { @@ -402,7 +430,8 @@ int fs_mgr_setup_verity(struct fstab_rec *fstab) { goto out; } - retval = 0; + // set the property indicating that the partition is verified + retval = set_verified_property(mount_point); out: close(fd); diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h index 835cf64..d9c58d4 100644 --- a/fs_mgr/include/fs_mgr.h +++ b/fs_mgr/include/fs_mgr.h @@ -24,6 +24,11 @@ extern "C" { #endif +/* + * The entries must be kept in the same order as they were seen in the fstab. + * Unless explicitly requested, a lookup on mount point should always + * return the 1st one. + */ struct fstab { int num_entries; struct fstab_rec *recs; @@ -48,6 +53,11 @@ struct fstab_rec { struct fstab *fs_mgr_read_fstab(const char *fstab_path); void fs_mgr_free_fstab(struct fstab *fstab); + +#define FS_MGR_MNTALL_DEV_NEEDS_RECOVERY 3 +#define FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION 2 +#define FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED 1 +#define FS_MGR_MNTALL_DEV_NOT_ENCRYPTED 0 int fs_mgr_mount_all(struct fstab *fstab); int fs_mgr_do_mount(struct fstab *fstab, char *n_name, char *n_blk_device, char *tmp_mount_point); diff --git a/healthd/Android.mk b/healthd/Android.mk index 473d375..1d238b1 100644 --- a/healthd/Android.mk +++ b/healthd/Android.mk @@ -7,12 +7,15 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES := healthd_board_default.cpp LOCAL_MODULE := libhealthd.default +LOCAL_CFLAGS := -Werror include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_SRC_FILES := \ healthd.cpp \ + healthd_mode_android.cpp \ + healthd_mode_charger.cpp \ BatteryMonitor.cpp \ BatteryPropertiesRegistrar.cpp @@ -22,9 +25,57 @@ LOCAL_FORCE_STATIC_EXECUTABLE := true LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN) LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_SBIN_UNSTRIPPED) -LOCAL_STATIC_LIBRARIES := libbatteryservice libbinder libz libutils libstdc++ libcutils liblog libm libc +LOCAL_CFLAGS := -D__STDC_LIMIT_MACROS -Werror + +ifeq ($(strip $(BOARD_CHARGER_DISABLE_INIT_BLANK)),true) +LOCAL_CFLAGS += -DCHARGER_DISABLE_INIT_BLANK +endif + +ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true) +LOCAL_CFLAGS += -DCHARGER_ENABLE_SUSPEND +endif + +LOCAL_C_INCLUDES := bootable/recovery + +LOCAL_STATIC_LIBRARIES := libbatteryservice libbinder libminui libpng libz libutils libstdc++ libcutils liblog libm libc + +ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true) +LOCAL_STATIC_LIBRARIES += libsuspend +endif + LOCAL_HAL_STATIC_LIBRARIES := libhealthd +# Symlink /charger to /sbin/healthd +LOCAL_POST_INSTALL_CMD := $(hide) mkdir -p $(TARGET_ROOT_OUT) \ + && ln -sf /sbin/healthd $(TARGET_ROOT_OUT)/charger + include $(BUILD_EXECUTABLE) + +define _add-charger-image +include $$(CLEAR_VARS) +LOCAL_MODULE := system_core_charger_$(notdir $(1)) +LOCAL_MODULE_STEM := $(notdir $(1)) +_img_modules += $$(LOCAL_MODULE) +LOCAL_SRC_FILES := $1 +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := ETC +LOCAL_MODULE_PATH := $$(TARGET_ROOT_OUT)/res/images/charger +include $$(BUILD_PREBUILT) +endef + +_img_modules := +_images := +$(foreach _img, $(call find-subdir-subdir-files, "images", "*.png"), \ + $(eval $(call _add-charger-image,$(_img)))) + +include $(CLEAR_VARS) +LOCAL_MODULE := charger_res_images +LOCAL_MODULE_TAGS := optional +LOCAL_REQUIRED_MODULES := $(_img_modules) +include $(BUILD_PHONY_PACKAGE) + +_add-charger-image := +_img_modules := + endif diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp index fa87274..4a6b702 100644 --- a/healthd/BatteryMonitor.cpp +++ b/healthd/BatteryMonitor.cpp @@ -18,7 +18,6 @@ #include "healthd.h" #include "BatteryMonitor.h" -#include "BatteryPropertiesRegistrar.h" #include <dirent.h> #include <errno.h> @@ -28,11 +27,16 @@ #include <unistd.h> #include <batteryservice/BatteryService.h> #include <cutils/klog.h> +#include <cutils/properties.h> +#include <sys/types.h> +#include <utils/Errors.h> #include <utils/String8.h> #include <utils/Vector.h> #define POWER_SUPPLY_SUBSYSTEM "power_supply" #define POWER_SUPPLY_SYSFS_PATH "/sys/class/" POWER_SUPPLY_SUBSYSTEM +#define FAKE_BATTERY_CAPACITY 42 +#define FAKE_BATTERY_TEMPERATURE 424 namespace android { @@ -170,7 +174,6 @@ int BatteryMonitor::getIntField(const String8& path) { } bool BatteryMonitor::update(void) { - struct BatteryProperties props; bool logthis; props.chargerAcOnline = false; @@ -178,24 +181,20 @@ bool BatteryMonitor::update(void) { props.chargerWirelessOnline = false; props.batteryStatus = BATTERY_STATUS_UNKNOWN; props.batteryHealth = BATTERY_HEALTH_UNKNOWN; - props.batteryCurrentNow = INT_MIN; - props.batteryChargeCounter = INT_MIN; if (!mHealthdConfig->batteryPresentPath.isEmpty()) props.batteryPresent = getBooleanField(mHealthdConfig->batteryPresentPath); else - props.batteryPresent = true; + props.batteryPresent = mBatteryDevicePresent; - props.batteryLevel = getIntField(mHealthdConfig->batteryCapacityPath); + props.batteryLevel = mBatteryFixedCapacity ? + mBatteryFixedCapacity : + getIntField(mHealthdConfig->batteryCapacityPath); props.batteryVoltage = getIntField(mHealthdConfig->batteryVoltagePath) / 1000; - if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) - props.batteryCurrentNow = getIntField(mHealthdConfig->batteryCurrentNowPath); - - if (!mHealthdConfig->batteryChargeCounterPath.isEmpty()) - props.batteryChargeCounter = getIntField(mHealthdConfig->batteryChargeCounterPath); - - props.batteryTemperature = getIntField(mHealthdConfig->batteryTemperaturePath); + props.batteryTemperature = mBatteryFixedTemperature ? + mBatteryFixedTemperature : + getIntField(mHealthdConfig->batteryTemperaturePath); const int SIZE = 128; char buf[SIZE]; @@ -244,7 +243,9 @@ bool BatteryMonitor::update(void) { if (logthis) { char dmesgline[256]; - snprintf(dmesgline, sizeof(dmesgline), + + if (props.batteryPresent) { + snprintf(dmesgline, sizeof(dmesgline), "battery l=%d v=%d t=%s%d.%d h=%d st=%d", props.batteryLevel, props.batteryVoltage, props.batteryTemperature < 0 ? "-" : "", @@ -252,11 +253,16 @@ bool BatteryMonitor::update(void) { abs(props.batteryTemperature % 10), props.batteryHealth, props.batteryStatus); - if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) { - char b[20]; + if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) { + int c = getIntField(mHealthdConfig->batteryCurrentNowPath); + char b[20]; - snprintf(b, sizeof(b), " c=%d", props.batteryCurrentNow / 1000); - strlcat(dmesgline, b, sizeof(dmesgline)); + snprintf(b, sizeof(b), " c=%d", c / 1000); + strlcat(dmesgline, b, sizeof(dmesgline)); + } + } else { + snprintf(dmesgline, sizeof(dmesgline), + "battery none"); } KLOG_INFO(LOG_TAG, "%s chg=%s%s%s\n", dmesgline, @@ -265,15 +271,110 @@ bool BatteryMonitor::update(void) { props.chargerWirelessOnline ? "w" : ""); } - if (mBatteryPropertiesRegistrar != NULL) - mBatteryPropertiesRegistrar->notifyListeners(props); - + healthd_mode_ops->battery_update(&props); return props.chargerAcOnline | props.chargerUsbOnline | props.chargerWirelessOnline; } -void BatteryMonitor::init(struct healthd_config *hc, bool nosvcmgr) { +status_t BatteryMonitor::getProperty(int id, struct BatteryProperty *val) { + status_t ret = BAD_VALUE; + + val->valueInt64 = LONG_MIN; + + switch(id) { + case BATTERY_PROP_CHARGE_COUNTER: + if (!mHealthdConfig->batteryChargeCounterPath.isEmpty()) { + val->valueInt64 = + getIntField(mHealthdConfig->batteryChargeCounterPath); + ret = NO_ERROR; + } else { + ret = NAME_NOT_FOUND; + } + break; + + case BATTERY_PROP_CURRENT_NOW: + if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) { + val->valueInt64 = + getIntField(mHealthdConfig->batteryCurrentNowPath); + ret = NO_ERROR; + } else { + ret = NAME_NOT_FOUND; + } + break; + + case BATTERY_PROP_CURRENT_AVG: + if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty()) { + val->valueInt64 = + getIntField(mHealthdConfig->batteryCurrentAvgPath); + ret = NO_ERROR; + } else { + ret = NAME_NOT_FOUND; + } + break; + + case BATTERY_PROP_CAPACITY: + if (!mHealthdConfig->batteryCapacityPath.isEmpty()) { + val->valueInt64 = + getIntField(mHealthdConfig->batteryCapacityPath); + ret = NO_ERROR; + } else { + ret = NAME_NOT_FOUND; + } + break; + + case BATTERY_PROP_ENERGY_COUNTER: + if (mHealthdConfig->energyCounter) { + ret = mHealthdConfig->energyCounter(&val->valueInt64); + } else { + ret = NAME_NOT_FOUND; + } + break; + + default: + break; + } + + return ret; +} + +void BatteryMonitor::dumpState(int fd) { + int v; + char vs[128]; + + snprintf(vs, sizeof(vs), "ac: %d usb: %d wireless: %d\n", + props.chargerAcOnline, props.chargerUsbOnline, + props.chargerWirelessOnline); + write(fd, vs, strlen(vs)); + snprintf(vs, sizeof(vs), "status: %d health: %d present: %d\n", + props.batteryStatus, props.batteryHealth, props.batteryPresent); + write(fd, vs, strlen(vs)); + snprintf(vs, sizeof(vs), "level: %d voltage: %d temp: %d\n", + props.batteryLevel, props.batteryVoltage, + props.batteryTemperature); + write(fd, vs, strlen(vs)); + + if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) { + v = getIntField(mHealthdConfig->batteryCurrentNowPath); + snprintf(vs, sizeof(vs), "current now: %d\n", v); + write(fd, vs, strlen(vs)); + } + + if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty()) { + v = getIntField(mHealthdConfig->batteryCurrentAvgPath); + snprintf(vs, sizeof(vs), "current avg: %d\n", v); + write(fd, vs, strlen(vs)); + } + + if (!mHealthdConfig->batteryChargeCounterPath.isEmpty()) { + v = getIntField(mHealthdConfig->batteryChargeCounterPath); + snprintf(vs, sizeof(vs), "charge counter: %d\n", v); + write(fd, vs, strlen(vs)); + } +} + +void BatteryMonitor::init(struct healthd_config *hc) { String8 path; + char pval[PROPERTY_VALUE_MAX]; mHealthdConfig = hc; DIR* dir = opendir(POWER_SUPPLY_SYSFS_PATH); @@ -303,6 +404,8 @@ void BatteryMonitor::init(struct healthd_config *hc, bool nosvcmgr) { break; case ANDROID_POWER_SUPPLY_TYPE_BATTERY: + mBatteryDevicePresent = true; + if (mHealthdConfig->batteryStatusPath.isEmpty()) { path.clear(); path.appendFormat("%s/%s/status", POWER_SUPPLY_SYSFS_PATH, @@ -358,6 +461,14 @@ void BatteryMonitor::init(struct healthd_config *hc, bool nosvcmgr) { mHealthdConfig->batteryCurrentNowPath = path; } + if (mHealthdConfig->batteryCurrentAvgPath.isEmpty()) { + path.clear(); + path.appendFormat("%s/%s/current_avg", + POWER_SUPPLY_SYSFS_PATH, name); + if (access(path, R_OK) == 0) + mHealthdConfig->batteryCurrentAvgPath = path; + } + if (mHealthdConfig->batteryChargeCounterPath.isEmpty()) { path.clear(); path.appendFormat("%s/%s/charge_counter", @@ -400,24 +511,31 @@ void BatteryMonitor::init(struct healthd_config *hc, bool nosvcmgr) { if (!mChargerNames.size()) KLOG_ERROR(LOG_TAG, "No charger supplies found\n"); - if (mHealthdConfig->batteryStatusPath.isEmpty()) - KLOG_WARNING(LOG_TAG, "BatteryStatusPath not found\n"); - if (mHealthdConfig->batteryHealthPath.isEmpty()) - KLOG_WARNING(LOG_TAG, "BatteryHealthPath not found\n"); - if (mHealthdConfig->batteryPresentPath.isEmpty()) - KLOG_WARNING(LOG_TAG, "BatteryPresentPath not found\n"); - if (mHealthdConfig->batteryCapacityPath.isEmpty()) - KLOG_WARNING(LOG_TAG, "BatteryCapacityPath not found\n"); - if (mHealthdConfig->batteryVoltagePath.isEmpty()) - KLOG_WARNING(LOG_TAG, "BatteryVoltagePath not found\n"); - if (mHealthdConfig->batteryTemperaturePath.isEmpty()) - KLOG_WARNING(LOG_TAG, "BatteryTemperaturePath not found\n"); - if (mHealthdConfig->batteryTechnologyPath.isEmpty()) - KLOG_WARNING(LOG_TAG, "BatteryTechnologyPath not found\n"); - - if (nosvcmgr == false) { - mBatteryPropertiesRegistrar = new BatteryPropertiesRegistrar(this); - mBatteryPropertiesRegistrar->publish(); + if (!mBatteryDevicePresent) { + KLOG_INFO(LOG_TAG, "No battery devices found\n"); + hc->periodic_chores_interval_fast = -1; + hc->periodic_chores_interval_slow = -1; + } else { + if (mHealthdConfig->batteryStatusPath.isEmpty()) + KLOG_WARNING(LOG_TAG, "BatteryStatusPath not found\n"); + if (mHealthdConfig->batteryHealthPath.isEmpty()) + KLOG_WARNING(LOG_TAG, "BatteryHealthPath not found\n"); + if (mHealthdConfig->batteryPresentPath.isEmpty()) + KLOG_WARNING(LOG_TAG, "BatteryPresentPath not found\n"); + if (mHealthdConfig->batteryCapacityPath.isEmpty()) + KLOG_WARNING(LOG_TAG, "BatteryCapacityPath not found\n"); + if (mHealthdConfig->batteryVoltagePath.isEmpty()) + KLOG_WARNING(LOG_TAG, "BatteryVoltagePath not found\n"); + if (mHealthdConfig->batteryTemperaturePath.isEmpty()) + KLOG_WARNING(LOG_TAG, "BatteryTemperaturePath not found\n"); + if (mHealthdConfig->batteryTechnologyPath.isEmpty()) + KLOG_WARNING(LOG_TAG, "BatteryTechnologyPath not found\n"); + } + + if (property_get("ro.boot.fake_battery", pval, NULL) > 0 + && strtol(pval, NULL, 10) != 0) { + mBatteryFixedCapacity = FAKE_BATTERY_CAPACITY; + mBatteryFixedTemperature = FAKE_BATTERY_TEMPERATURE; } } diff --git a/healthd/BatteryMonitor.h b/healthd/BatteryMonitor.h index ba291af..3425f27 100644 --- a/healthd/BatteryMonitor.h +++ b/healthd/BatteryMonitor.h @@ -17,17 +17,15 @@ #ifndef HEALTHD_BATTERYMONITOR_H #define HEALTHD_BATTERYMONITOR_H +#include <batteryservice/BatteryService.h> #include <binder/IInterface.h> #include <utils/String8.h> #include <utils/Vector.h> #include "healthd.h" -#include "BatteryPropertiesRegistrar.h" namespace android { -class BatteryPropertiesRegistrar; - class BatteryMonitor { public: @@ -39,14 +37,18 @@ class BatteryMonitor { ANDROID_POWER_SUPPLY_TYPE_BATTERY }; - void init(struct healthd_config *hc, bool nosvcmgr); + void init(struct healthd_config *hc); bool update(void); + status_t getProperty(int id, struct BatteryProperty *val); + void dumpState(int fd); private: struct healthd_config *mHealthdConfig; Vector<String8> mChargerNames; - - sp<BatteryPropertiesRegistrar> mBatteryPropertiesRegistrar; + bool mBatteryDevicePresent; + int mBatteryFixedCapacity; + int mBatteryFixedTemperature; + struct BatteryProperties props; int getBatteryStatus(const char* status); int getBatteryHealth(const char* status); diff --git a/healthd/BatteryPropertiesRegistrar.cpp b/healthd/BatteryPropertiesRegistrar.cpp index 6a33ad8..74bcbfd 100644 --- a/healthd/BatteryPropertiesRegistrar.cpp +++ b/healthd/BatteryPropertiesRegistrar.cpp @@ -18,19 +18,20 @@ #include <batteryservice/BatteryService.h> #include <batteryservice/IBatteryPropertiesListener.h> #include <batteryservice/IBatteryPropertiesRegistrar.h> +#include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> +#include <binder/PermissionCache.h> +#include <private/android_filesystem_config.h> #include <utils/Errors.h> #include <utils/Mutex.h> #include <utils/String16.h> -namespace android { +#include "healthd.h" -BatteryPropertiesRegistrar::BatteryPropertiesRegistrar(BatteryMonitor* monitor) { - mBatteryMonitor = monitor; -} +namespace android { void BatteryPropertiesRegistrar::publish() { - defaultServiceManager()->addService(String16("batterypropreg"), this); + defaultServiceManager()->addService(String16("batteryproperties"), this); } void BatteryPropertiesRegistrar::notifyListeners(struct BatteryProperties props) { @@ -42,6 +43,8 @@ void BatteryPropertiesRegistrar::notifyListeners(struct BatteryProperties props) void BatteryPropertiesRegistrar::registerListener(const sp<IBatteryPropertiesListener>& listener) { { + if (listener == NULL) + return; Mutex::Autolock _l(mRegistrationLock); // check whether this is a duplicate for (size_t i = 0; i < mListeners.size(); i++) { @@ -53,10 +56,12 @@ void BatteryPropertiesRegistrar::registerListener(const sp<IBatteryPropertiesLis mListeners.add(listener); listener->asBinder()->linkToDeath(this); } - mBatteryMonitor->update(); + healthd_battery_update(); } void BatteryPropertiesRegistrar::unregisterListener(const sp<IBatteryPropertiesListener>& listener) { + if (listener == NULL) + return; Mutex::Autolock _l(mRegistrationLock); for (size_t i = 0; i < mListeners.size(); i++) { if (mListeners[i]->asBinder() == listener->asBinder()) { @@ -67,6 +72,23 @@ void BatteryPropertiesRegistrar::unregisterListener(const sp<IBatteryPropertiesL } } +status_t BatteryPropertiesRegistrar::getProperty(int id, struct BatteryProperty *val) { + return healthd_get_property(id, val); +} + +status_t BatteryPropertiesRegistrar::dump(int fd, const Vector<String16>& /*args*/) { + IPCThreadState* self = IPCThreadState::self(); + const int pid = self->getCallingPid(); + const int uid = self->getCallingUid(); + if ((uid != AID_SHELL) && + !PermissionCache::checkPermission( + String16("android.permission.DUMP"), pid, uid)) + return PERMISSION_DENIED; + + healthd_dump_battery_state(fd); + return OK; +} + void BatteryPropertiesRegistrar::binderDied(const wp<IBinder>& who) { Mutex::Autolock _l(mRegistrationLock); diff --git a/healthd/BatteryPropertiesRegistrar.h b/healthd/BatteryPropertiesRegistrar.h index 793ddad..8853874 100644 --- a/healthd/BatteryPropertiesRegistrar.h +++ b/healthd/BatteryPropertiesRegistrar.h @@ -17,10 +17,9 @@ #ifndef HEALTHD_BATTERYPROPERTIES_REGISTRAR_H #define HEALTHD_BATTERYPROPERTIES_REGISTRAR_H -#include "BatteryMonitor.h" - #include <binder/IBinder.h> #include <utils/Mutex.h> +#include <utils/String16.h> #include <utils/Vector.h> #include <batteryservice/BatteryService.h> #include <batteryservice/IBatteryPropertiesListener.h> @@ -28,22 +27,20 @@ namespace android { -class BatteryMonitor; - class BatteryPropertiesRegistrar : public BnBatteryPropertiesRegistrar, public IBinder::DeathRecipient { public: - BatteryPropertiesRegistrar(BatteryMonitor* monitor); void publish(); void notifyListeners(struct BatteryProperties props); private: - BatteryMonitor* mBatteryMonitor; Mutex mRegistrationLock; Vector<sp<IBatteryPropertiesListener> > mListeners; void registerListener(const sp<IBatteryPropertiesListener>& listener); void unregisterListener(const sp<IBatteryPropertiesListener>& listener); + status_t getProperty(int id, struct BatteryProperty *val); + status_t dump(int fd, const Vector<String16>& args); void binderDied(const wp<IBinder>& who); }; diff --git a/healthd/healthd.cpp b/healthd/healthd.cpp index d30e771..30a4b42 100644 --- a/healthd/healthd.cpp +++ b/healthd/healthd.cpp @@ -21,16 +21,17 @@ #include "BatteryMonitor.h" #include <errno.h> +#include <libgen.h> #include <stdio.h> #include <stdlib.h> +#include <string.h> #include <unistd.h> #include <batteryservice/BatteryService.h> -#include <binder/IPCThreadState.h> -#include <binder/ProcessState.h> #include <cutils/klog.h> #include <cutils/uevent.h> #include <sys/epoll.h> #include <sys/timerfd.h> +#include <utils/Errors.h> using namespace android; @@ -49,13 +50,18 @@ static struct healthd_config healthd_config = { .batteryTemperaturePath = String8(String8::kEmptyString), .batteryTechnologyPath = String8(String8::kEmptyString), .batteryCurrentNowPath = String8(String8::kEmptyString), + .batteryCurrentAvgPath = String8(String8::kEmptyString), .batteryChargeCounterPath = String8(String8::kEmptyString), + .energyCounter = NULL, }; +static int eventct; +static int epollfd; + #define POWER_SUPPLY_SUBSYSTEM "power_supply" -// epoll events: uevent, wakealarm, binder -#define MAX_EPOLL_EVENTS 3 +// epoll_create() parameter is actually unused +#define MAX_EPOLL_EVENTS 40 static int uevent_fd; static int wakealarm_fd; static int binder_fd; @@ -67,7 +73,80 @@ static int wakealarm_wake_interval = DEFAULT_PERIODIC_CHORES_INTERVAL_FAST; static BatteryMonitor* gBatteryMonitor; -static bool nosvcmgr; +struct healthd_mode_ops *healthd_mode_ops; + +// Android mode + +extern void healthd_mode_android_init(struct healthd_config *config); +extern int healthd_mode_android_preparetowait(void); +extern void healthd_mode_android_battery_update( + struct android::BatteryProperties *props); + +// Charger mode + +extern void healthd_mode_charger_init(struct healthd_config *config); +extern int healthd_mode_charger_preparetowait(void); +extern void healthd_mode_charger_heartbeat(void); +extern void healthd_mode_charger_battery_update( + struct android::BatteryProperties *props); + +// NOPs for modes that need no special action + +static void healthd_mode_nop_init(struct healthd_config *config); +static int healthd_mode_nop_preparetowait(void); +static void healthd_mode_nop_heartbeat(void); +static void healthd_mode_nop_battery_update( + struct android::BatteryProperties *props); + +static struct healthd_mode_ops android_ops = { + .init = healthd_mode_android_init, + .preparetowait = healthd_mode_android_preparetowait, + .heartbeat = healthd_mode_nop_heartbeat, + .battery_update = healthd_mode_android_battery_update, +}; + +static struct healthd_mode_ops charger_ops = { + .init = healthd_mode_charger_init, + .preparetowait = healthd_mode_charger_preparetowait, + .heartbeat = healthd_mode_charger_heartbeat, + .battery_update = healthd_mode_charger_battery_update, +}; + +static struct healthd_mode_ops recovery_ops = { + .init = healthd_mode_nop_init, + .preparetowait = healthd_mode_nop_preparetowait, + .heartbeat = healthd_mode_nop_heartbeat, + .battery_update = healthd_mode_nop_battery_update, +}; + +static void healthd_mode_nop_init(struct healthd_config* /*config*/) { +} + +static int healthd_mode_nop_preparetowait(void) { + return -1; +} + +static void healthd_mode_nop_heartbeat(void) { +} + +static void healthd_mode_nop_battery_update( + struct android::BatteryProperties* /*props*/) { +} + +int healthd_register_event(int fd, void (*handler)(uint32_t)) { + struct epoll_event ev; + + ev.events = EPOLLIN | EPOLLWAKEUP; + ev.data.ptr = (void *)handler; + if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) == -1) { + KLOG_ERROR(LOG_TAG, + "epoll_ctl failed; errno=%d\n", errno); + return -1; + } + + eventct++; + return 0; +} static void wakealarm_set_interval(int interval) { struct itimerspec itval; @@ -89,7 +168,11 @@ static void wakealarm_set_interval(int interval) { KLOG_ERROR(LOG_TAG, "wakealarm_set_interval: timerfd_settime failed\n"); } -static void battery_update(void) { +status_t healthd_get_property(int id, struct BatteryProperty *val) { + return gBatteryMonitor->getProperty(id, val); +} + +void healthd_battery_update(void) { // Fast wake interval when on charger (watch for overheat); // slow wake interval when on battery (watch for drained battery). @@ -113,21 +196,17 @@ static void battery_update(void) { -1 : healthd_config.periodic_chores_interval_fast * 1000; } -static void periodic_chores() { - battery_update(); +void healthd_dump_battery_state(int fd) { + gBatteryMonitor->dumpState(fd); + fsync(fd); } -static void uevent_init(void) { - uevent_fd = uevent_open_socket(64*1024, true); - - if (uevent_fd >= 0) - fcntl(uevent_fd, F_SETFL, O_NONBLOCK); - else - KLOG_ERROR(LOG_TAG, "uevent_init: uevent_open_socket failed\n"); +static void periodic_chores() { + healthd_battery_update(); } #define UEVENT_MSG_LEN 2048 -static void uevent_event(void) { +static void uevent_event(uint32_t /*epevents*/) { char msg[UEVENT_MSG_LEN+2]; char *cp; int n; @@ -144,7 +223,7 @@ static void uevent_event(void) { while (*cp) { if (!strcmp(cp, "SUBSYSTEM=" POWER_SUPPLY_SUBSYSTEM)) { - battery_update(); + healthd_battery_update(); break; } @@ -154,89 +233,56 @@ static void uevent_event(void) { } } -static void wakealarm_init(void) { - wakealarm_fd = timerfd_create(CLOCK_BOOTTIME_ALARM, TFD_NONBLOCK); - if (wakealarm_fd == -1) { - KLOG_ERROR(LOG_TAG, "wakealarm_init: timerfd_create failed\n"); +static void uevent_init(void) { + uevent_fd = uevent_open_socket(64*1024, true); + + if (uevent_fd < 0) { + KLOG_ERROR(LOG_TAG, "uevent_init: uevent_open_socket failed\n"); return; } - wakealarm_set_interval(healthd_config.periodic_chores_interval_fast); + fcntl(uevent_fd, F_SETFL, O_NONBLOCK); + if (healthd_register_event(uevent_fd, uevent_event)) + KLOG_ERROR(LOG_TAG, + "register for uevent events failed\n"); } -static void wakealarm_event(void) { +static void wakealarm_event(uint32_t /*epevents*/) { unsigned long long wakeups; if (read(wakealarm_fd, &wakeups, sizeof(wakeups)) == -1) { - KLOG_ERROR(LOG_TAG, "wakealarm_event: read wakealarm_fd failed\n"); + KLOG_ERROR(LOG_TAG, "wakealarm_event: read wakealarm fd failed\n"); return; } periodic_chores(); } -static void binder_init(void) { - ProcessState::self()->setThreadPoolMaxThreadCount(0); - IPCThreadState::self()->disableBackgroundScheduling(true); - IPCThreadState::self()->setupPolling(&binder_fd); -} - -static void binder_event(void) { - IPCThreadState::self()->handlePolledCommands(); -} - -static void healthd_mainloop(void) { - struct epoll_event ev; - int epollfd; - int maxevents = 0; - - epollfd = epoll_create(MAX_EPOLL_EVENTS); - if (epollfd == -1) { - KLOG_ERROR(LOG_TAG, - "healthd_mainloop: epoll_create failed; errno=%d\n", - errno); +static void wakealarm_init(void) { + wakealarm_fd = timerfd_create(CLOCK_BOOTTIME_ALARM, TFD_NONBLOCK); + if (wakealarm_fd == -1) { + KLOG_ERROR(LOG_TAG, "wakealarm_init: timerfd_create failed\n"); return; } - if (uevent_fd >= 0) { - ev.events = EPOLLIN | EPOLLWAKEUP; - ev.data.ptr = (void *)uevent_event; - if (epoll_ctl(epollfd, EPOLL_CTL_ADD, uevent_fd, &ev) == -1) - KLOG_ERROR(LOG_TAG, - "healthd_mainloop: epoll_ctl for uevent_fd failed; errno=%d\n", - errno); - else - maxevents++; - } + if (healthd_register_event(wakealarm_fd, wakealarm_event)) + KLOG_ERROR(LOG_TAG, + "Registration of wakealarm event failed\n"); - if (wakealarm_fd >= 0) { - ev.events = EPOLLIN | EPOLLWAKEUP; - ev.data.ptr = (void *)wakealarm_event; - if (epoll_ctl(epollfd, EPOLL_CTL_ADD, wakealarm_fd, &ev) == -1) - KLOG_ERROR(LOG_TAG, - "healthd_mainloop: epoll_ctl for wakealarm_fd failed; errno=%d\n", - errno); - else - maxevents++; - } - - if (binder_fd >= 0) { - ev.events = EPOLLIN | EPOLLWAKEUP; - ev.data.ptr= (void *)binder_event; - if (epoll_ctl(epollfd, EPOLL_CTL_ADD, binder_fd, &ev) == -1) - KLOG_ERROR(LOG_TAG, - "healthd_mainloop: epoll_ctl for binder_fd failed; errno=%d\n", - errno); - else - maxevents++; - } + wakealarm_set_interval(healthd_config.periodic_chores_interval_fast); +} +static void healthd_mainloop(void) { while (1) { - struct epoll_event events[maxevents]; + struct epoll_event events[eventct]; int nevents; + int timeout = awake_poll_interval; + int mode_timeout; - IPCThreadState::self()->flushCommands(); - nevents = epoll_wait(epollfd, events, maxevents, awake_poll_interval); + mode_timeout = healthd_mode_ops->preparetowait(); + if (timeout < 0 || (mode_timeout > 0 && mode_timeout < timeout)) + timeout = mode_timeout; + nevents = epoll_wait(epollfd, events, eventct, timeout); if (nevents == -1) { if (errno == EINTR) @@ -247,39 +293,70 @@ static void healthd_mainloop(void) { for (int n = 0; n < nevents; ++n) { if (events[n].data.ptr) - (*(void (*)())events[n].data.ptr)(); + (*(void (*)(int))events[n].data.ptr)(events[n].events); } if (!nevents) periodic_chores(); + + healthd_mode_ops->heartbeat(); } return; } +static int healthd_init() { + epollfd = epoll_create(MAX_EPOLL_EVENTS); + if (epollfd == -1) { + KLOG_ERROR(LOG_TAG, + "epoll_create failed; errno=%d\n", + errno); + return -1; + } + + healthd_mode_ops->init(&healthd_config); + healthd_board_init(&healthd_config); + wakealarm_init(); + uevent_init(); + gBatteryMonitor = new BatteryMonitor(); + gBatteryMonitor->init(&healthd_config); + return 0; +} + int main(int argc, char **argv) { int ch; + int ret; klog_set_level(KLOG_LEVEL); - - while ((ch = getopt(argc, argv, "n")) != -1) { - switch (ch) { - case 'n': - nosvcmgr = true; - break; - case '?': - default: - KLOG_WARNING(LOG_TAG, "Unrecognized healthd option: %c\n", ch); + healthd_mode_ops = &android_ops; + + if (!strcmp(basename(argv[0]), "charger")) { + healthd_mode_ops = &charger_ops; + } else { + while ((ch = getopt(argc, argv, "cr")) != -1) { + switch (ch) { + case 'c': + healthd_mode_ops = &charger_ops; + break; + case 'r': + healthd_mode_ops = &recovery_ops; + break; + case '?': + default: + KLOG_ERROR(LOG_TAG, "Unrecognized healthd option: %c\n", + optopt); + exit(1); + } } } - healthd_board_init(&healthd_config); - wakealarm_init(); - uevent_init(); - binder_init(); - gBatteryMonitor = new BatteryMonitor(); - gBatteryMonitor->init(&healthd_config, nosvcmgr); + ret = healthd_init(); + if (ret) { + KLOG_ERROR("Initialization failed, exiting\n"); + exit(2); + } healthd_mainloop(); - return 0; + KLOG_ERROR("Main loop terminated, exiting\n"); + return 3; } diff --git a/healthd/healthd.h b/healthd/healthd.h index 5374fb1..972e728 100644 --- a/healthd/healthd.h +++ b/healthd/healthd.h @@ -18,6 +18,8 @@ #define _HEALTHD_H_ #include <batteryservice/BatteryService.h> +#include <sys/types.h> +#include <utils/Errors.h> #include <utils/String8.h> // periodic_chores_interval_fast, periodic_chores_interval_slow: intervals at @@ -61,9 +63,37 @@ struct healthd_config { android::String8 batteryTemperaturePath; android::String8 batteryTechnologyPath; android::String8 batteryCurrentNowPath; + android::String8 batteryCurrentAvgPath; android::String8 batteryChargeCounterPath; + + int (*energyCounter)(int64_t *); +}; + +// Global helper functions + +int healthd_register_event(int fd, void (*handler)(uint32_t)); +void healthd_battery_update(); +android::status_t healthd_get_property(int id, + struct android::BatteryProperty *val); +void healthd_dump_battery_state(int fd); + +struct healthd_mode_ops { + void (*init)(struct healthd_config *config); + int (*preparetowait)(void); + void (*heartbeat)(void); + void (*battery_update)(struct android::BatteryProperties *props); }; +extern struct healthd_mode_ops *healthd_mode_ops; + +// Charger mode + +void healthd_mode_charger_init(struct healthd_config *config); +int healthd_mode_charger_preparetowait(void); +void healthd_mode_charger_heartbeat(void); +void healthd_mode_charger_battery_update( + struct android::BatteryProperties *props); + // The following are implemented in libhealthd_board to handle board-specific // behavior. // diff --git a/healthd/healthd_mode_android.cpp b/healthd/healthd_mode_android.cpp new file mode 100644 index 0000000..fd153a2 --- /dev/null +++ b/healthd/healthd_mode_android.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "healthd-android" + +#include "healthd.h" +#include "BatteryPropertiesRegistrar.h" + +#include <binder/IPCThreadState.h> +#include <binder/ProcessState.h> +#include <cutils/klog.h> +#include <sys/epoll.h> + +using namespace android; + +static int gBinderFd; +static sp<BatteryPropertiesRegistrar> gBatteryPropertiesRegistrar; + +void healthd_mode_android_battery_update( + struct android::BatteryProperties *props) { + if (gBatteryPropertiesRegistrar != NULL) + gBatteryPropertiesRegistrar->notifyListeners(*props); + + return; +} + +int healthd_mode_android_preparetowait(void) { + IPCThreadState::self()->flushCommands(); + return -1; +} + +static void binder_event(uint32_t /*epevents*/) { + IPCThreadState::self()->handlePolledCommands(); +} + +void healthd_mode_android_init(struct healthd_config* /*config*/) { + ProcessState::self()->setThreadPoolMaxThreadCount(0); + IPCThreadState::self()->disableBackgroundScheduling(true); + IPCThreadState::self()->setupPolling(&gBinderFd); + + if (gBinderFd >= 0) { + if (healthd_register_event(gBinderFd, binder_event)) + KLOG_ERROR(LOG_TAG, + "Register for binder events failed\n"); + } + + gBatteryPropertiesRegistrar = new BatteryPropertiesRegistrar(); + gBatteryPropertiesRegistrar->publish(); +} diff --git a/charger/charger.c b/healthd/healthd_mode_charger.cpp index 15add87..67d4944 100644 --- a/charger/charger.c +++ b/healthd/healthd_mode_charger.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 The Android Open Source Project + * Copyright (C) 2011-2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,18 +14,16 @@ * limitations under the License. */ -//#define DEBUG_UEVENTS -#define CHARGER_KLOG_LEVEL 6 - #include <dirent.h> #include <errno.h> #include <fcntl.h> +#include <inttypes.h> #include <linux/input.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <sys/poll.h> +#include <sys/epoll.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/un.h> @@ -35,9 +33,9 @@ #include <sys/socket.h> #include <linux/netlink.h> +#include <batteryservice/BatteryService.h> #include <cutils/android_reboot.h> #include <cutils/klog.h> -#include <cutils/list.h> #include <cutils/misc.h> #include <cutils/uevent.h> #include <cutils/properties.h> @@ -48,6 +46,8 @@ #include "minui/minui.h" +#include "healthd.h" + char *locale; #ifndef max @@ -68,8 +68,10 @@ char *locale; #define UNPLUGGED_SHUTDOWN_TIME (10 * MSEC_PER_SEC) #define BATTERY_FULL_THRESH 95 +#define SCREEN_ON_BATTERY_THRESH 1 #define LAST_KMSG_PATH "/proc/last_kmsg" +#define LAST_KMSG_PSTORE_PATH "/sys/fs/pstore/console-ramoops" #define LAST_KMSG_MAX_SZ (32 * 1024) #define LOGE(x...) do { KLOG_ERROR("charger", x); } while (0) @@ -82,15 +84,6 @@ struct key_state { int64_t timestamp; }; -struct power_supply { - struct listnode list; - char name[256]; - char type[32]; - bool online; - bool valid; - char cap_path[PATH_MAX]; -}; - struct frame { int disp_time; int min_capacity; @@ -114,72 +107,73 @@ struct animation { }; struct charger { + bool have_battery_state; + bool charger_connected; + int capacity; int64_t next_screen_transition; int64_t next_key_check; int64_t next_pwr_check; struct key_state keys[KEY_MAX + 1]; - int uevent_fd; - - struct listnode supplies; - int num_supplies; - int num_supplies_online; struct animation *batt_anim; gr_surface surf_unknown; - - struct power_supply *battery; -}; - -struct uevent { - const char *action; - const char *path; - const char *subsystem; - const char *ps_name; - const char *ps_type; - const char *ps_online; }; static struct frame batt_anim_frames[] = { { .disp_time = 750, .min_capacity = 0, + .level_only = false, + .surface = NULL, }, { .disp_time = 750, .min_capacity = 20, + .level_only = false, + .surface = NULL, }, { .disp_time = 750, .min_capacity = 40, + .level_only = false, + .surface = NULL, }, { .disp_time = 750, .min_capacity = 60, + .level_only = false, + .surface = NULL, }, { .disp_time = 750, .min_capacity = 80, .level_only = true, + .surface = NULL, }, { .disp_time = 750, .min_capacity = BATTERY_FULL_THRESH, + .level_only = false, + .surface = NULL, }, }; static struct animation battery_animation = { + .run = false, .frames = batt_anim_frames, + .cur_frame = 0, .num_frames = ARRAY_SIZE(batt_anim_frames), + .cur_cycle = 0, .num_cycles = 3, + .capacity = 0, }; -static struct charger charger_state = { - .batt_anim = &battery_animation, -}; +static struct charger charger_state; static int char_width; static int char_height; +static bool minui_inited; /* current time in milliseconds */ static int64_t curr_time_ms(void) @@ -207,10 +201,14 @@ static void dump_last_kmsg(void) LOGI("\n"); LOGI("*************** LAST KMSG ***************\n"); LOGI("\n"); - buf = load_file(LAST_KMSG_PATH, &sz); + buf = (char *)load_file(LAST_KMSG_PSTORE_PATH, &sz); + if (!buf || !sz) { - LOGI("last_kmsg not found. Cold reset?\n"); - goto out; + buf = (char *)load_file(LAST_KMSG_PATH, &sz); + if (!buf || !sz) { + LOGI("last_kmsg not found. Cold reset?\n"); + goto out; + } } len = min(sz, LAST_KMSG_MAX_SZ); @@ -221,7 +219,7 @@ static void dump_last_kmsg(void) char yoink; char *nl; - nl = memrchr(ptr, '\n', cnt - 1); + nl = (char *)memrchr(ptr, '\n', cnt - 1); if (nl) cnt = nl - ptr + 1; @@ -242,114 +240,9 @@ out: LOGI("\n"); } -static int read_file(const char *path, char *buf, size_t sz) -{ - int fd; - size_t cnt; - - fd = open(path, O_RDONLY, 0); - if (fd < 0) - goto err; - - cnt = read(fd, buf, sz - 1); - if (cnt <= 0) - goto err; - buf[cnt] = '\0'; - if (buf[cnt - 1] == '\n') { - cnt--; - buf[cnt] = '\0'; - } - - close(fd); - return cnt; - -err: - if (fd >= 0) - close(fd); - return -1; -} - -static int read_file_int(const char *path, int *val) -{ - char buf[32]; - int ret; - int tmp; - char *end; - - ret = read_file(path, buf, sizeof(buf)); - if (ret < 0) - return -1; - - tmp = strtol(buf, &end, 0); - if (end == buf || - ((end < buf+sizeof(buf)) && (*end != '\n' && *end != '\0'))) - goto err; - - *val = tmp; - return 0; - -err: - return -1; -} - -static int get_battery_capacity(struct charger *charger) +static int get_battery_capacity() { - int ret; - int batt_cap = -1; - - if (!charger->battery) - return -1; - - ret = read_file_int(charger->battery->cap_path, &batt_cap); - if (ret < 0 || batt_cap > 100) { - batt_cap = -1; - } - - return batt_cap; -} - -static struct power_supply *find_supply(struct charger *charger, - const char *name) -{ - struct listnode *node; - struct power_supply *supply; - - list_for_each(node, &charger->supplies) { - supply = node_to_item(node, struct power_supply, list); - if (!strncmp(name, supply->name, sizeof(supply->name))) - return supply; - } - return NULL; -} - -static struct power_supply *add_supply(struct charger *charger, - const char *name, const char *type, - const char *path, bool online) -{ - struct power_supply *supply; - - supply = calloc(1, sizeof(struct power_supply)); - if (!supply) - return NULL; - - strlcpy(supply->name, name, sizeof(supply->name)); - strlcpy(supply->type, type, sizeof(supply->type)); - snprintf(supply->cap_path, sizeof(supply->cap_path), - "/sys/%s/capacity", path); - supply->online = online; - list_add_tail(&charger->supplies, &supply->list); - charger->num_supplies++; - LOGV("... added %s %s %d\n", supply->name, supply->type, online); - return supply; -} - -static void remove_supply(struct charger *charger, struct power_supply *supply) -{ - if (!supply) - return; - list_remove(&supply->list); - charger->num_supplies--; - free(supply); + return charger_state.capacity; } #ifdef CHARGER_ENABLE_SUSPEND @@ -361,243 +254,12 @@ static int request_suspend(bool enable) return autosuspend_disable(); } #else -static int request_suspend(bool enable) +static int request_suspend(bool /*enable*/) { return 0; } #endif -static void parse_uevent(const char *msg, struct uevent *uevent) -{ - uevent->action = ""; - uevent->path = ""; - uevent->subsystem = ""; - uevent->ps_name = ""; - uevent->ps_online = ""; - uevent->ps_type = ""; - - /* currently ignoring SEQNUM */ - while (*msg) { -#ifdef DEBUG_UEVENTS - LOGV("uevent str: %s\n", msg); -#endif - if (!strncmp(msg, "ACTION=", 7)) { - msg += 7; - uevent->action = msg; - } else if (!strncmp(msg, "DEVPATH=", 8)) { - msg += 8; - uevent->path = msg; - } else if (!strncmp(msg, "SUBSYSTEM=", 10)) { - msg += 10; - uevent->subsystem = msg; - } else if (!strncmp(msg, "POWER_SUPPLY_NAME=", 18)) { - msg += 18; - uevent->ps_name = msg; - } else if (!strncmp(msg, "POWER_SUPPLY_ONLINE=", 20)) { - msg += 20; - uevent->ps_online = msg; - } else if (!strncmp(msg, "POWER_SUPPLY_TYPE=", 18)) { - msg += 18; - uevent->ps_type = msg; - } - - /* advance to after the next \0 */ - while (*msg++) - ; - } - - LOGV("event { '%s', '%s', '%s', '%s', '%s', '%s' }\n", - uevent->action, uevent->path, uevent->subsystem, - uevent->ps_name, uevent->ps_type, uevent->ps_online); -} - -static void process_ps_uevent(struct charger *charger, struct uevent *uevent) -{ - int online; - char ps_type[32]; - struct power_supply *supply = NULL; - int i; - bool was_online = false; - bool battery = false; - - if (uevent->ps_type[0] == '\0') { - char *path; - int ret; - - if (uevent->path[0] == '\0') - return; - ret = asprintf(&path, "/sys/%s/type", uevent->path); - if (ret <= 0) - return; - ret = read_file(path, ps_type, sizeof(ps_type)); - free(path); - if (ret < 0) - return; - } else { - strlcpy(ps_type, uevent->ps_type, sizeof(ps_type)); - } - - if (!strncmp(ps_type, "Battery", 7)) - battery = true; - - online = atoi(uevent->ps_online); - supply = find_supply(charger, uevent->ps_name); - if (supply) { - was_online = supply->online; - supply->online = online; - } - - if (!strcmp(uevent->action, "add")) { - if (!supply) { - supply = add_supply(charger, uevent->ps_name, ps_type, uevent->path, - online); - if (!supply) { - LOGE("cannot add supply '%s' (%s %d)\n", uevent->ps_name, - uevent->ps_type, online); - return; - } - /* only pick up the first battery for now */ - if (battery && !charger->battery) - charger->battery = supply; - } else { - LOGE("supply '%s' already exists..\n", uevent->ps_name); - } - } else if (!strcmp(uevent->action, "remove")) { - if (supply) { - if (charger->battery == supply) - charger->battery = NULL; - remove_supply(charger, supply); - supply = NULL; - } - } else if (!strcmp(uevent->action, "change")) { - if (!supply) { - LOGE("power supply '%s' not found ('%s' %d)\n", - uevent->ps_name, ps_type, online); - return; - } - } else { - return; - } - - /* allow battery to be managed in the supply list but make it not - * contribute to online power supplies. */ - if (!battery) { - if (was_online && !online) - charger->num_supplies_online--; - else if (supply && !was_online && online) - charger->num_supplies_online++; - } - - LOGI("power supply %s (%s) %s (action=%s num_online=%d num_supplies=%d)\n", - uevent->ps_name, ps_type, battery ? "" : online ? "online" : "offline", - uevent->action, charger->num_supplies_online, charger->num_supplies); -} - -static void process_uevent(struct charger *charger, struct uevent *uevent) -{ - if (!strcmp(uevent->subsystem, "power_supply")) - process_ps_uevent(charger, uevent); -} - -#define UEVENT_MSG_LEN 1024 -static int handle_uevent_fd(struct charger *charger, int fd) -{ - char msg[UEVENT_MSG_LEN+2]; - int n; - - if (fd < 0) - return -1; - - while (true) { - struct uevent uevent; - - n = uevent_kernel_multicast_recv(fd, msg, UEVENT_MSG_LEN); - if (n <= 0) - break; - if (n >= UEVENT_MSG_LEN) /* overflow -- discard */ - continue; - - msg[n] = '\0'; - msg[n+1] = '\0'; - - parse_uevent(msg, &uevent); - process_uevent(charger, &uevent); - } - - return 0; -} - -static int uevent_callback(int fd, short revents, void *data) -{ - struct charger *charger = data; - - if (!(revents & POLLIN)) - return -1; - return handle_uevent_fd(charger, fd); -} - -/* force the kernel to regenerate the change events for the existing - * devices, if valid */ -static void do_coldboot(struct charger *charger, DIR *d, const char *event, - bool follow_links, int max_depth) -{ - struct dirent *de; - int dfd, fd; - - dfd = dirfd(d); - - fd = openat(dfd, "uevent", O_WRONLY); - if (fd >= 0) { - write(fd, event, strlen(event)); - close(fd); - handle_uevent_fd(charger, charger->uevent_fd); - } - - while ((de = readdir(d)) && max_depth > 0) { - DIR *d2; - - LOGV("looking at '%s'\n", de->d_name); - - if ((de->d_type != DT_DIR && !(de->d_type == DT_LNK && follow_links)) || - de->d_name[0] == '.') { - LOGV("skipping '%s' type %d (depth=%d follow=%d)\n", - de->d_name, de->d_type, max_depth, follow_links); - continue; - } - LOGV("can descend into '%s'\n", de->d_name); - - fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY); - if (fd < 0) { - LOGE("cannot openat %d '%s' (%d: %s)\n", dfd, de->d_name, - errno, strerror(errno)); - continue; - } - - d2 = fdopendir(fd); - if (d2 == 0) - close(fd); - else { - LOGV("opened '%s'\n", de->d_name); - do_coldboot(charger, d2, event, follow_links, max_depth - 1); - closedir(d2); - } - } -} - -static void coldboot(struct charger *charger, const char *path, - const char *event) -{ - char str[256]; - - LOGV("doing coldboot '%s' in '%s'\n", event, path); - DIR *d = opendir(path); - if (d) { - snprintf(str, sizeof(str), "%s\n", event); - do_coldboot(charger, d, str, true, 1); - closedir(d); - } -} - static int draw_text(const char *str, int x, int y) { int str_len_px = gr_measure(str); @@ -617,7 +279,7 @@ static void android_green(void) } /* returns the last y-offset of where the surface ends */ -static int draw_surface_centered(struct charger *charger, gr_surface surface) +static int draw_surface_centered(struct charger* /*charger*/, gr_surface surface) { int w; int h; @@ -694,13 +356,34 @@ static void update_screen_state(struct charger *charger, int64_t now) if (!batt_anim->run || now < charger->next_screen_transition) return; + if (!minui_inited) { + int batt_cap = get_battery_capacity(); + + if (batt_cap < SCREEN_ON_BATTERY_THRESH) { + LOGV("[%" PRId64 "] level %d, leave screen off\n", now, batt_cap); + batt_anim->run = false; + charger->next_screen_transition = -1; + if (charger->charger_connected) + request_suspend(true); + return; + } + + gr_init(); + gr_font_size(&char_width, &char_height); + +#ifndef CHARGER_DISABLE_INIT_BLANK + gr_fb_blank(true); +#endif + minui_inited = true; + } + /* animation is over, blank screen and leave */ if (batt_anim->cur_cycle == batt_anim->num_cycles) { reset_animation(batt_anim); charger->next_screen_transition = -1; gr_fb_blank(true); - LOGV("[%lld] animation done\n", now); - if (charger->num_supplies_online > 0) + LOGV("[%" PRId64 "] animation done\n", now); + if (!charger->charger_connected) request_suspend(true); return; } @@ -712,8 +395,8 @@ static void update_screen_state(struct charger *charger, int64_t now) int batt_cap; int ret; - LOGV("[%lld] animation starting\n", now); - batt_cap = get_battery_capacity(charger); + LOGV("[%" PRId64 "] animation starting\n", now); + batt_cap = get_battery_capacity(); if (batt_cap >= 0 && batt_anim->num_frames != 0) { int i; @@ -742,7 +425,7 @@ static void update_screen_state(struct charger *charger, int64_t now) * the cycle counter and exit */ if (batt_anim->num_frames == 0 || batt_anim->capacity < 0) { - LOGV("[%lld] animation missing or unknown battery status\n", now); + LOGV("[%" PRId64 "] animation missing or unknown battery status\n", now); charger->next_screen_transition = now + BATTERY_UNKNOWN_TIME; batt_anim->cur_cycle++; return; @@ -751,30 +434,39 @@ static void update_screen_state(struct charger *charger, int64_t now) /* schedule next screen transition */ charger->next_screen_transition = now + disp_time; - /* advance frame cntr to the next valid frame + /* advance frame cntr to the next valid frame only if we are charging * if necessary, advance cycle cntr, and reset frame cntr */ - batt_anim->cur_frame++; - - /* if the frame is used for level-only, that is only show it when it's - * the current level, skip it during the animation. - */ - while (batt_anim->cur_frame < batt_anim->num_frames && - batt_anim->frames[batt_anim->cur_frame].level_only) + if (charger->charger_connected) { batt_anim->cur_frame++; - if (batt_anim->cur_frame >= batt_anim->num_frames) { - batt_anim->cur_cycle++; - batt_anim->cur_frame = 0; - /* don't reset the cycle counter, since we use that as a signal - * in a test above to check if animation is over + /* if the frame is used for level-only, that is only show it when it's + * the current level, skip it during the animation. */ + while (batt_anim->cur_frame < batt_anim->num_frames && + batt_anim->frames[batt_anim->cur_frame].level_only) + batt_anim->cur_frame++; + if (batt_anim->cur_frame >= batt_anim->num_frames) { + batt_anim->cur_cycle++; + batt_anim->cur_frame = 0; + + /* don't reset the cycle counter, since we use that as a signal + * in a test above to check if animation is over + */ + } + } else { + /* Stop animating if we're not charging. + * If we stop it immediately instead of going through this loop, then + * the animation would stop somewhere in the middle. + */ + batt_anim->cur_frame = 0; + batt_anim->cur_cycle++; } } static int set_key_callback(int code, int value, void *data) { - struct charger *charger = data; + struct charger *charger = (struct charger *)data; int64_t now = curr_time_ms(); int down = !!value; @@ -792,13 +484,13 @@ static int set_key_callback(int code, int value, void *data) charger->keys[code].down = down; charger->keys[code].pending = true; if (down) { - LOGV("[%lld] key[%d] down\n", now, code); + LOGV("[%" PRId64 "] key[%d] down\n", now, code); } else { int64_t duration = now - charger->keys[code].timestamp; int64_t secs = duration / 1000; int64_t msecs = duration - secs * 1000; - LOGV("[%lld] key[%d] up (was down for %lld.%lldsec)\n", now, - code, secs, msecs); + LOGV("[%" PRId64 "] key[%d] up (was down for %" PRId64 ".%" PRId64 "sec)\n", + now, code, secs, msecs); } return 0; @@ -835,10 +527,10 @@ static void process_key(struct charger *charger, int code, int64_t now) all devices. Check the property and continue booting or reboot accordingly. */ if (property_get_bool("ro.enable_boot_charger_mode", false)) { - LOGI("[%lld] booting from charger mode\n", now); + LOGI("[%" PRId64 "] booting from charger mode\n", now); property_set("sys.boot_from_charger_mode", "1"); } else { - LOGI("[%lld] rebooting\n", now); + LOGI("[%" PRId64 "] rebooting\n", now); android_reboot(ANDROID_RB_RESTART, 0, 0); } } else { @@ -869,14 +561,17 @@ static void handle_input_state(struct charger *charger, int64_t now) static void handle_power_supply_state(struct charger *charger, int64_t now) { - if (charger->num_supplies_online == 0) { + if (!charger->have_battery_state) + return; + + if (!charger->charger_connected) { request_suspend(false); if (charger->next_pwr_check == -1) { charger->next_pwr_check = now + UNPLUGGED_SHUTDOWN_TIME; - LOGI("[%lld] device unplugged: shutting down in %lld (@ %lld)\n", - now, UNPLUGGED_SHUTDOWN_TIME, charger->next_pwr_check); + LOGI("[%" PRId64 "] device unplugged: shutting down in %" PRId64 " (@ %" PRId64 ")\n", + now, (int64_t)UNPLUGGED_SHUTDOWN_TIME, charger->next_pwr_check); } else if (now >= charger->next_pwr_check) { - LOGI("[%lld] shutting down\n", now); + LOGI("[%" PRId64 "] shutting down\n", now); android_reboot(ANDROID_RB_POWEROFF, 0, 0); } else { /* otherwise we already have a shutdown timer scheduled */ @@ -884,21 +579,56 @@ static void handle_power_supply_state(struct charger *charger, int64_t now) } else { /* online supply present, reset shutdown timer if set */ if (charger->next_pwr_check != -1) { - LOGI("[%lld] device plugged in: shutdown cancelled\n", now); + LOGI("[%" PRId64 "] device plugged in: shutdown cancelled\n", now); kick_animation(charger->batt_anim); } charger->next_pwr_check = -1; } } -static void wait_next_event(struct charger *charger, int64_t now) +void healthd_mode_charger_heartbeat() +{ + struct charger *charger = &charger_state; + int64_t now = curr_time_ms(); + int ret; + + handle_input_state(charger, now); + handle_power_supply_state(charger, now); + + /* do screen update last in case any of the above want to start + * screen transitions (animations, etc) + */ + update_screen_state(charger, now); +} + +void healthd_mode_charger_battery_update( + struct android::BatteryProperties *props) +{ + struct charger *charger = &charger_state; + + charger->charger_connected = + props->chargerAcOnline || props->chargerUsbOnline || + props->chargerWirelessOnline; + charger->capacity = props->batteryLevel; + + if (!charger->have_battery_state) { + charger->have_battery_state = true; + charger->next_screen_transition = curr_time_ms() - 1; + reset_animation(charger->batt_anim); + kick_animation(charger->batt_anim); + } +} + +int healthd_mode_charger_preparetowait(void) { + struct charger *charger = &charger_state; + int64_t now = curr_time_ms(); int64_t next_event = INT64_MAX; int64_t timeout; struct input_event ev; int ret; - LOGV("[%lld] next screen: %lld next key: %lld next pwr: %lld\n", now, + LOGV("[%" PRId64 "] next screen: %" PRId64 " next key: %" PRId64 " next pwr: %" PRId64 "\n", now, charger->next_screen_transition, charger->next_key_check, charger->next_pwr_check); @@ -913,74 +643,48 @@ static void wait_next_event(struct charger *charger, int64_t now) timeout = max(0, next_event - now); else timeout = -1; - LOGV("[%lld] blocking (%lld)\n", now, timeout); - ret = ev_wait((int)timeout); - if (!ret) - ev_dispatch(); + + return (int)timeout; } -static int input_callback(int fd, short revents, void *data) +static int input_callback(int fd, unsigned int epevents, void *data) { - struct charger *charger = data; + struct charger *charger = (struct charger *)data; struct input_event ev; int ret; - ret = ev_get_input(fd, revents, &ev); + ret = ev_get_input(fd, epevents, &ev); if (ret) return -1; update_input_state(charger, &ev); return 0; } -static void event_loop(struct charger *charger) +static void charger_event_handler(uint32_t /*epevents*/) { int ret; - while (true) { - int64_t now = curr_time_ms(); - - LOGV("[%lld] event_loop()\n", now); - handle_input_state(charger, now); - handle_power_supply_state(charger, now); - - /* do screen update last in case any of the above want to start - * screen transitions (animations, etc) - */ - update_screen_state(charger, now); - - wait_next_event(charger, now); - } + ret = ev_wait(-1); + if (!ret) + ev_dispatch(); } -int main(int argc, char **argv) +void healthd_mode_charger_init(struct healthd_config* /*config*/) { int ret; struct charger *charger = &charger_state; - int64_t now = curr_time_ms() - 1; - int fd; int i; - - list_init(&charger->supplies); - - klog_init(); - klog_set_level(CHARGER_KLOG_LEVEL); + int epollfd; dump_last_kmsg(); LOGI("--------------- STARTING CHARGER MODE ---------------\n"); - gr_init(); - gr_font_size(&char_width, &char_height); - - ev_init(input_callback, charger); - - fd = uevent_open_socket(64*1024, true); - if (fd >= 0) { - fcntl(fd, F_SETFL, O_NONBLOCK); - ev_add_fd(fd, uevent_callback, charger); + ret = ev_init(input_callback, charger); + if (!ret) { + epollfd = ev_get_epollfd(); + healthd_register_event(epollfd, charger_event_handler); } - charger->uevent_fd = fd; - coldboot(charger, "/sys/class/power_supply", "add"); ret = res_create_display_surface("charger/battery_fail", &charger->surf_unknown); if (ret < 0) { @@ -1010,17 +714,7 @@ int main(int argc, char **argv) ev_sync_key_state(set_key_callback, charger); -#ifndef CHARGER_DISABLE_INIT_BLANK - gr_fb_blank(true); -#endif - - charger->next_screen_transition = now - 1; + charger->next_screen_transition = -1; charger->next_key_check = -1; charger->next_pwr_check = -1; - reset_animation(charger->batt_anim); - kick_animation(charger->batt_anim); - - event_loop(charger); - - return 0; } diff --git a/charger/images/battery_fail.png b/healthd/images/battery_fail.png Binary files differindex aded88a..aded88a 100644 --- a/charger/images/battery_fail.png +++ b/healthd/images/battery_fail.png diff --git a/charger/images/battery_scale.png b/healthd/images/battery_scale.png Binary files differindex 2ae8f0f..2ae8f0f 100644 --- a/charger/images/battery_scale.png +++ b/healthd/images/battery_scale.png diff --git a/include/cutils/klog.h b/include/cutils/klog.h index 3635e89..d5ae6d7 100644 --- a/include/cutils/klog.h +++ b/include/cutils/klog.h @@ -25,7 +25,7 @@ __BEGIN_DECLS void klog_init(void); int klog_get_level(void); void klog_set_level(int level); -void klog_close(void); +/* TODO: void klog_close(void); - and make klog_fd users thread safe. */ void klog_write(int level, const char *fmt, ...) __attribute__ ((format(printf, 2, 3))); void klog_vwrite(int level, const char *fmt, va_list ap); diff --git a/include/cutils/str_parms.h b/include/cutils/str_parms.h index 247c996..66f3637 100644 --- a/include/cutils/str_parms.h +++ b/include/cutils/str_parms.h @@ -34,6 +34,12 @@ int str_parms_add_int(struct str_parms *str_parms, const char *key, int value); int str_parms_add_float(struct str_parms *str_parms, const char *key, float value); +// Returns non-zero if the str_parms contains the specified key. +int str_parms_has_key(struct str_parms *str_parms, const char *key); + +// Gets value associated with the specified key (if present), placing it in the buffer +// pointed to by the out_val parameter. Returns the length of the returned string value. +// If 'key' isn't in the parms, then return -ENOENT (-2) and leave 'out_val' untouched. int str_parms_get_str(struct str_parms *str_parms, const char *key, char *out_val, int len); int str_parms_get_int(struct str_parms *str_parms, const char *key, diff --git a/include/netutils/ifc.h b/include/netutils/ifc.h index 1f5421d..9a8b282 100644 --- a/include/netutils/ifc.h +++ b/include/netutils/ifc.h @@ -49,19 +49,8 @@ extern int ifc_set_prefixLength(const char *name, int prefixLength); extern int ifc_set_hwaddr(const char *name, const void *ptr); extern int ifc_clear_addresses(const char *name); -/* This function is deprecated. Use ifc_add_route instead. */ -extern int ifc_add_host_route(const char *name, in_addr_t addr); -extern int ifc_remove_host_routes(const char *name); -extern int ifc_get_default_route(const char *ifname); -/* This function is deprecated. Use ifc_add_route instead */ -extern int ifc_set_default_route(const char *ifname, in_addr_t gateway); -/* This function is deprecated. Use ifc_add_route instead */ extern int ifc_create_default_route(const char *name, in_addr_t addr); extern int ifc_remove_default_route(const char *ifname); -extern int ifc_add_route(const char *name, const char *addr, int prefix_length, - const char *gw); -extern int ifc_remove_route(const char *ifname, const char *dst, - int prefix_length, const char *gw); extern int ifc_get_info(const char *name, in_addr_t *addr, int *prefixLength, unsigned *flags); diff --git a/include/private/android_filesystem_config.h b/include/private/android_filesystem_config.h index d8e938e..eea9e2b 100644 --- a/include/private/android_filesystem_config.h +++ b/include/private/android_filesystem_config.h @@ -254,6 +254,8 @@ static const struct fs_path_config android_files[] = { /* the following files have enhanced capabilities and ARE included in user builds. */ { 00750, AID_ROOT, AID_SHELL, (1 << CAP_SETUID) | (1 << CAP_SETGID), "system/bin/run-as" }, + { 00750, AID_ROOT, AID_ROOT, 0, "system/bin/uncrypt" }, + { 00750, AID_ROOT, AID_ROOT, 0, "system/bin/install-recovery.sh" }, { 00755, AID_ROOT, AID_SHELL, 0, "system/bin/*" }, { 00755, AID_ROOT, AID_ROOT, 0, "system/lib/valgrind/*" }, { 00755, AID_ROOT, AID_ROOT, 0, "system/lib64/valgrind/*" }, @@ -263,7 +265,6 @@ static const struct fs_path_config android_files[] = { { 00750, AID_ROOT, AID_SHELL, 0, "sbin/*" }, { 00755, AID_ROOT, AID_ROOT, 0, "bin/*" }, { 00750, AID_ROOT, AID_SHELL, 0, "init*" }, - { 00750, AID_ROOT, AID_SHELL, 0, "charger*" }, { 00750, AID_ROOT, AID_SHELL, 0, "sbin/fs_mgr" }, { 00640, AID_ROOT, AID_SHELL, 0, "fstab.*" }, { 00644, AID_ROOT, AID_ROOT, 0, 0 }, diff --git a/include/system/audio.h b/include/system/audio.h index 8838e71..719a7c4 100644 --- a/include/system/audio.h +++ b/include/system/audio.h @@ -20,6 +20,7 @@ #include <stdbool.h> #include <stdint.h> +#include <stdio.h> #include <sys/cdefs.h> #include <sys/types.h> @@ -34,11 +35,17 @@ __BEGIN_DECLS /* device address used to refer to the standard remote submix */ #define AUDIO_REMOTE_SUBMIX_DEVICE_ADDRESS "0" +/* AudioFlinger and AudioPolicy services use I/O handles to identify audio sources and sinks */ typedef int audio_io_handle_t; +#define AUDIO_IO_HANDLE_NONE 0 /* Audio stream types */ typedef enum { + /* These values must kept in sync with + * frameworks/base/media/java/android/media/AudioSystem.java + */ AUDIO_STREAM_DEFAULT = -1, + AUDIO_STREAM_MIN = 0, AUDIO_STREAM_VOICE_CALL = 0, AUDIO_STREAM_SYSTEM = 1, AUDIO_STREAM_RING = 2, @@ -46,7 +53,9 @@ typedef enum { AUDIO_STREAM_ALARM = 4, AUDIO_STREAM_NOTIFICATION = 5, AUDIO_STREAM_BLUETOOTH_SCO = 6, - AUDIO_STREAM_ENFORCED_AUDIBLE = 7, /* Sounds that cannot be muted by user and must be routed to speaker */ + AUDIO_STREAM_ENFORCED_AUDIBLE = 7, /* Sounds that cannot be muted by user + * and must be routed to speaker + */ AUDIO_STREAM_DTMF = 8, AUDIO_STREAM_TTS = 9, @@ -55,7 +64,59 @@ typedef enum { } audio_stream_type_t; /* Do not change these values without updating their counterparts - * in media/java/android/media/MediaRecorder.java! + * in frameworks/base/media/java/android/media/AudioAttributes.java + */ +typedef enum { + AUDIO_CONTENT_TYPE_UNKNOWN = 0, + AUDIO_CONTENT_TYPE_SPEECH = 1, + AUDIO_CONTENT_TYPE_MUSIC = 2, + AUDIO_CONTENT_TYPE_MOVIE = 3, + AUDIO_CONTENT_TYPE_SONIFICATION = 4, + + AUDIO_CONTENT_TYPE_CNT, + AUDIO_CONTENT_TYPE_MAX = AUDIO_CONTENT_TYPE_CNT - 1, +} audio_content_type_t; + +/* Do not change these values without updating their counterparts + * in frameworks/base/media/java/android/media/AudioAttributes.java + */ +typedef enum { + AUDIO_USAGE_UNKNOWN = 0, + AUDIO_USAGE_MEDIA = 1, + AUDIO_USAGE_VOICE_COMMUNICATION = 2, + AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING = 3, + AUDIO_USAGE_ALARM = 4, + AUDIO_USAGE_NOTIFICATION = 5, + AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE = 6, + AUDIO_USAGE_NOTIFICATION_COMMUNICATION_REQUEST = 7, + AUDIO_USAGE_NOTIFICATION_COMMUNICATION_INSTANT = 8, + AUDIO_USAGE_NOTIFICATION_COMMUNICATION_DELAYED = 9, + AUDIO_USAGE_NOTIFICATION_EVENT = 10, + AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY = 11, + AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE = 12, + AUDIO_USAGE_ASSISTANCE_SONIFICATION = 13, + AUDIO_USAGE_GAME = 14, + + AUDIO_USAGE_CNT, + AUDIO_USAGE_MAX = AUDIO_USAGE_CNT - 1, +} audio_usage_t; + +typedef uint32_t audio_flags_mask_t; + +/* Do not change these values without updating their counterparts + * in frameworks/base/media/java/android/media/AudioAttributes.java + */ +enum { + AUDIO_FLAG_AUDIBILITY_ENFORCED = 0x1, + AUDIO_FLAG_SECURE = 0x2, + AUDIO_FLAG_SCO = 0x4, + AUDIO_FLAG_BEACON = 0x8 +}; + +/* Do not change these values without updating their counterparts + * in frameworks/base/media/java/android/media/MediaRecorder.java, + * frameworks/av/services/audiopolicy/AudioPolicyService.cpp, + * and system/media/audio_effects/include/audio_effects/audio_effects_conf.h! */ typedef enum { AUDIO_SOURCE_DEFAULT = 0, @@ -79,6 +140,16 @@ typedef enum { at the audio HAL. */ } audio_source_t; +/* Audio attributes */ +#define AUDIO_ATTRIBUTES_TAGS_MAX_SIZE 256 +typedef struct { + audio_content_type_t content_type; + audio_usage_t usage; + audio_source_t source; + audio_flags_mask_t flags; + char tags[AUDIO_ATTRIBUTES_TAGS_MAX_SIZE]; /* UTF8 */ +} audio_attributes_t; + /* special audio session values * (XXX: should this be living in the audio effects land?) */ @@ -93,18 +164,35 @@ typedef enum { * (value must be 0) */ AUDIO_SESSION_OUTPUT_MIX = 0, + + /* application does not specify an explicit session ID to be used, + * and requests a new session ID to be allocated + * TODO use unique values for AUDIO_SESSION_OUTPUT_MIX and AUDIO_SESSION_ALLOCATE, + * after all uses have been updated from 0 to the appropriate symbol, and have been tested. + */ + AUDIO_SESSION_ALLOCATE = 0, } audio_session_t; +/* a unique ID allocated by AudioFlinger for use as a audio_io_handle_t or audio_session_t */ +typedef int audio_unique_id_t; + +#define AUDIO_UNIQUE_ID_ALLOCATE AUDIO_SESSION_ALLOCATE + /* Audio sub formats (see enum audio_format). */ /* PCM sub formats */ typedef enum { + /* All of these are in native byte order */ AUDIO_FORMAT_PCM_SUB_16_BIT = 0x1, /* DO NOT CHANGE - PCM signed 16 bits */ AUDIO_FORMAT_PCM_SUB_8_BIT = 0x2, /* DO NOT CHANGE - PCM unsigned 8 bits */ AUDIO_FORMAT_PCM_SUB_32_BIT = 0x3, /* PCM signed .31 fixed point */ AUDIO_FORMAT_PCM_SUB_8_24_BIT = 0x4, /* PCM signed 7.24 fixed point */ + AUDIO_FORMAT_PCM_SUB_FLOAT = 0x5, /* PCM single-precision floating point */ + AUDIO_FORMAT_PCM_SUB_24_BIT_PACKED = 0x6, /* PCM signed .23 fixed point packed in 3 bytes */ } audio_format_pcm_sub_fmt_t; +/* The audio_format_*_sub_fmt_t declarations are not currently used */ + /* MP3 sub format field definition : can use 11 LSBs in the same way as MP3 * frame header to specify bit rate, stereo mode, version... */ @@ -121,7 +209,16 @@ typedef enum { /* AAC sub format field definition: specify profile or bitrate for recording... */ typedef enum { - AUDIO_FORMAT_AAC_SUB_NONE = 0x0, + AUDIO_FORMAT_AAC_SUB_MAIN = 0x1, + AUDIO_FORMAT_AAC_SUB_LC = 0x2, + AUDIO_FORMAT_AAC_SUB_SSR = 0x4, + AUDIO_FORMAT_AAC_SUB_LTP = 0x8, + AUDIO_FORMAT_AAC_SUB_HE_V1 = 0x10, + AUDIO_FORMAT_AAC_SUB_SCALABLE = 0x20, + AUDIO_FORMAT_AAC_SUB_ERLC = 0x40, + AUDIO_FORMAT_AAC_SUB_LD = 0x80, + AUDIO_FORMAT_AAC_SUB_HE_V2 = 0x100, + AUDIO_FORMAT_AAC_SUB_ELD = 0x200, } audio_format_aac_sub_fmt_t; /* VORBIS sub format field definition: specify quality for recording... */ @@ -129,7 +226,7 @@ typedef enum { AUDIO_FORMAT_VORBIS_SUB_NONE = 0x0, } audio_format_vorbis_sub_fmt_t; -/* Audio format consists in a main format field (upper 8 bits) and a sub format +/* Audio format consists of a main format field (upper 8 bits) and a sub format * field (lower 24 bits). * * The main format indicates the main codec type. The sub format field @@ -146,24 +243,65 @@ typedef enum { AUDIO_FORMAT_AMR_NB = 0x02000000UL, AUDIO_FORMAT_AMR_WB = 0x03000000UL, AUDIO_FORMAT_AAC = 0x04000000UL, - AUDIO_FORMAT_HE_AAC_V1 = 0x05000000UL, - AUDIO_FORMAT_HE_AAC_V2 = 0x06000000UL, + AUDIO_FORMAT_HE_AAC_V1 = 0x05000000UL, /* Deprecated, Use AUDIO_FORMAT_AAC_HE_V1*/ + AUDIO_FORMAT_HE_AAC_V2 = 0x06000000UL, /* Deprecated, Use AUDIO_FORMAT_AAC_HE_V2*/ AUDIO_FORMAT_VORBIS = 0x07000000UL, + AUDIO_FORMAT_OPUS = 0x08000000UL, + AUDIO_FORMAT_AC3 = 0x09000000UL, + AUDIO_FORMAT_E_AC3 = 0x0A000000UL, AUDIO_FORMAT_MAIN_MASK = 0xFF000000UL, AUDIO_FORMAT_SUB_MASK = 0x00FFFFFFUL, /* Aliases */ + /* note != AudioFormat.ENCODING_PCM_16BIT */ AUDIO_FORMAT_PCM_16_BIT = (AUDIO_FORMAT_PCM | AUDIO_FORMAT_PCM_SUB_16_BIT), + /* note != AudioFormat.ENCODING_PCM_8BIT */ AUDIO_FORMAT_PCM_8_BIT = (AUDIO_FORMAT_PCM | AUDIO_FORMAT_PCM_SUB_8_BIT), AUDIO_FORMAT_PCM_32_BIT = (AUDIO_FORMAT_PCM | AUDIO_FORMAT_PCM_SUB_32_BIT), AUDIO_FORMAT_PCM_8_24_BIT = (AUDIO_FORMAT_PCM | AUDIO_FORMAT_PCM_SUB_8_24_BIT), + AUDIO_FORMAT_PCM_FLOAT = (AUDIO_FORMAT_PCM | + AUDIO_FORMAT_PCM_SUB_FLOAT), + AUDIO_FORMAT_PCM_24_BIT_PACKED = (AUDIO_FORMAT_PCM | + AUDIO_FORMAT_PCM_SUB_24_BIT_PACKED), + AUDIO_FORMAT_AAC_MAIN = (AUDIO_FORMAT_AAC | + AUDIO_FORMAT_AAC_SUB_MAIN), + AUDIO_FORMAT_AAC_LC = (AUDIO_FORMAT_AAC | + AUDIO_FORMAT_AAC_SUB_LC), + AUDIO_FORMAT_AAC_SSR = (AUDIO_FORMAT_AAC | + AUDIO_FORMAT_AAC_SUB_SSR), + AUDIO_FORMAT_AAC_LTP = (AUDIO_FORMAT_AAC | + AUDIO_FORMAT_AAC_SUB_LTP), + AUDIO_FORMAT_AAC_HE_V1 = (AUDIO_FORMAT_AAC | + AUDIO_FORMAT_AAC_SUB_HE_V1), + AUDIO_FORMAT_AAC_SCALABLE = (AUDIO_FORMAT_AAC | + AUDIO_FORMAT_AAC_SUB_SCALABLE), + AUDIO_FORMAT_AAC_ERLC = (AUDIO_FORMAT_AAC | + AUDIO_FORMAT_AAC_SUB_ERLC), + AUDIO_FORMAT_AAC_LD = (AUDIO_FORMAT_AAC | + AUDIO_FORMAT_AAC_SUB_LD), + AUDIO_FORMAT_AAC_HE_V2 = (AUDIO_FORMAT_AAC | + AUDIO_FORMAT_AAC_SUB_HE_V2), + AUDIO_FORMAT_AAC_ELD = (AUDIO_FORMAT_AAC | + AUDIO_FORMAT_AAC_SUB_ELD), } audio_format_t; +/* For the channel mask for position assignment representation */ enum { + +/* These can be a complete audio_channel_mask_t. */ + + AUDIO_CHANNEL_NONE = 0x0, + AUDIO_CHANNEL_INVALID = 0xC0000000, + +/* These can be the bits portion of an audio_channel_mask_t + * with representation AUDIO_CHANNEL_REPRESENTATION_POSITION. + * Using these bits as a complete audio_channel_mask_t is deprecated. + */ + /* output channels */ AUDIO_CHANNEL_OUT_FRONT_LEFT = 0x1, AUDIO_CHANNEL_OUT_FRONT_RIGHT = 0x2, @@ -184,6 +322,8 @@ enum { AUDIO_CHANNEL_OUT_TOP_BACK_CENTER = 0x10000, AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT = 0x20000, +/* TODO: should these be considered complete channel masks, or only bits? */ + AUDIO_CHANNEL_OUT_MONO = AUDIO_CHANNEL_OUT_FRONT_LEFT, AUDIO_CHANNEL_OUT_STEREO = (AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT), @@ -191,16 +331,26 @@ enum { AUDIO_CHANNEL_OUT_FRONT_RIGHT | AUDIO_CHANNEL_OUT_BACK_LEFT | AUDIO_CHANNEL_OUT_BACK_RIGHT), - AUDIO_CHANNEL_OUT_SURROUND = (AUDIO_CHANNEL_OUT_FRONT_LEFT | + AUDIO_CHANNEL_OUT_QUAD_BACK = AUDIO_CHANNEL_OUT_QUAD, + /* like AUDIO_CHANNEL_OUT_QUAD_BACK with *_SIDE_* instead of *_BACK_* */ + AUDIO_CHANNEL_OUT_QUAD_SIDE = (AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT | - AUDIO_CHANNEL_OUT_FRONT_CENTER | - AUDIO_CHANNEL_OUT_BACK_CENTER), + AUDIO_CHANNEL_OUT_SIDE_LEFT | + AUDIO_CHANNEL_OUT_SIDE_RIGHT), AUDIO_CHANNEL_OUT_5POINT1 = (AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT | AUDIO_CHANNEL_OUT_FRONT_CENTER | AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_BACK_LEFT | AUDIO_CHANNEL_OUT_BACK_RIGHT), + AUDIO_CHANNEL_OUT_5POINT1_BACK = AUDIO_CHANNEL_OUT_5POINT1, + /* like AUDIO_CHANNEL_OUT_5POINT1_BACK with *_SIDE_* instead of *_BACK_* */ + AUDIO_CHANNEL_OUT_5POINT1_SIDE = (AUDIO_CHANNEL_OUT_FRONT_LEFT | + AUDIO_CHANNEL_OUT_FRONT_RIGHT | + AUDIO_CHANNEL_OUT_FRONT_CENTER | + AUDIO_CHANNEL_OUT_LOW_FREQUENCY | + AUDIO_CHANNEL_OUT_SIDE_LEFT | + AUDIO_CHANNEL_OUT_SIDE_RIGHT), // matches the correct AudioFormat.CHANNEL_OUT_7POINT1_SURROUND definition for 7.1 AUDIO_CHANNEL_OUT_7POINT1 = (AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT | @@ -229,6 +379,8 @@ enum { AUDIO_CHANNEL_OUT_TOP_BACK_CENTER| AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT), +/* These are bits only, not complete values */ + /* input channels */ AUDIO_CHANNEL_IN_LEFT = 0x4, AUDIO_CHANNEL_IN_RIGHT = 0x8, @@ -245,6 +397,8 @@ enum { AUDIO_CHANNEL_IN_VOICE_UPLINK = 0x4000, AUDIO_CHANNEL_IN_VOICE_DNLINK = 0x8000, +/* TODO: should these be considered complete channel masks, or only bits, or deprecated? */ + AUDIO_CHANNEL_IN_MONO = AUDIO_CHANNEL_IN_FRONT, AUDIO_CHANNEL_IN_STEREO = (AUDIO_CHANNEL_IN_LEFT | AUDIO_CHANNEL_IN_RIGHT), AUDIO_CHANNEL_IN_FRONT_BACK = (AUDIO_CHANNEL_IN_FRONT | AUDIO_CHANNEL_IN_BACK), @@ -264,8 +418,111 @@ enum { AUDIO_CHANNEL_IN_VOICE_DNLINK), }; +/* A channel mask per se only defines the presence or absence of a channel, not the order. + * But see AUDIO_INTERLEAVE_* below for the platform convention of order. + * + * audio_channel_mask_t is an opaque type and its internal layout should not + * be assumed as it may change in the future. + * Instead, always use the functions declared in this header to examine. + * + * These are the current representations: + * + * AUDIO_CHANNEL_REPRESENTATION_POSITION + * is a channel mask representation for position assignment. + * Each low-order bit corresponds to the spatial position of a transducer (output), + * or interpretation of channel (input). + * The user of a channel mask needs to know the context of whether it is for output or input. + * The constants AUDIO_CHANNEL_OUT_* or AUDIO_CHANNEL_IN_* apply to the bits portion. + * It is not permitted for no bits to be set. + * + * AUDIO_CHANNEL_REPRESENTATION_INDEX + * is a channel mask representation for index assignment. + * Each low-order bit corresponds to a selected channel. + * There is no platform interpretation of the various bits. + * There is no concept of output or input. + * It is not permitted for no bits to be set. + * + * All other representations are reserved for future use. + * + * Warning: current representation distinguishes between input and output, but this will not the be + * case in future revisions of the platform. Wherever there is an ambiguity between input and output + * that is currently resolved by checking the channel mask, the implementer should look for ways to + * fix it with additional information outside of the mask. + */ typedef uint32_t audio_channel_mask_t; +/* Maximum number of channels for all representations */ +#define AUDIO_CHANNEL_COUNT_MAX 30 + +/* log(2) of maximum number of representations, not part of public API */ +#define AUDIO_CHANNEL_REPRESENTATION_LOG2 2 + +/* Representations */ +typedef enum { + AUDIO_CHANNEL_REPRESENTATION_POSITION = 0, // must be zero for compatibility + // 1 is reserved for future use + AUDIO_CHANNEL_REPRESENTATION_INDEX = 2, + // 3 is reserved for future use +} audio_channel_representation_t; + +/* The return value is undefined if the channel mask is invalid. */ +static inline uint32_t audio_channel_mask_get_bits(audio_channel_mask_t channel) +{ + return channel & ((1 << AUDIO_CHANNEL_COUNT_MAX) - 1); +} + +/* The return value is undefined if the channel mask is invalid. */ +static inline audio_channel_representation_t audio_channel_mask_get_representation( + audio_channel_mask_t channel) +{ + // The right shift should be sufficient, but also "and" for safety in case mask is not 32 bits + return (audio_channel_representation_t) + ((channel >> AUDIO_CHANNEL_COUNT_MAX) & ((1 << AUDIO_CHANNEL_REPRESENTATION_LOG2) - 1)); +} + +/* Returns true if the channel mask is valid, + * or returns false for AUDIO_CHANNEL_NONE, AUDIO_CHANNEL_INVALID, and other invalid values. + * This function is unable to determine whether a channel mask for position assignment + * is invalid because an output mask has an invalid output bit set, + * or because an input mask has an invalid input bit set. + * All other APIs that take a channel mask assume that it is valid. + */ +static inline bool audio_channel_mask_is_valid(audio_channel_mask_t channel) +{ + uint32_t bits = audio_channel_mask_get_bits(channel); + audio_channel_representation_t representation = audio_channel_mask_get_representation(channel); + switch (representation) { + case AUDIO_CHANNEL_REPRESENTATION_POSITION: + case AUDIO_CHANNEL_REPRESENTATION_INDEX: + break; + default: + bits = 0; + break; + } + return bits != 0; +} + +/* Not part of public API */ +static inline audio_channel_mask_t audio_channel_mask_from_representation_and_bits( + audio_channel_representation_t representation, uint32_t bits) +{ + return (audio_channel_mask_t) ((representation << AUDIO_CHANNEL_COUNT_MAX) | bits); +} + +/* Expresses the convention when stereo audio samples are stored interleaved + * in an array. This should improve readability by allowing code to use + * symbolic indices instead of hard-coded [0] and [1]. + * + * For multi-channel beyond stereo, the platform convention is that channels + * are interleaved in order from least significant channel mask bit + * to most significant channel mask bit, with unused bits skipped. + * Any exceptions to this convention will be noted at the appropriate API. + */ +enum { + AUDIO_INTERLEAVE_LEFT = 0, + AUDIO_INTERLEAVE_RIGHT = 1, +}; + typedef enum { AUDIO_MODE_INVALID = -2, AUDIO_MODE_CURRENT = -1, @@ -278,7 +535,9 @@ typedef enum { AUDIO_MODE_MAX = AUDIO_MODE_CNT - 1, } audio_mode_t; +/* This enum is deprecated */ typedef enum { + AUDIO_IN_ACOUSTICS_NONE = 0, AUDIO_IN_ACOUSTICS_AGC_ENABLE = 0x0001, AUDIO_IN_ACOUSTICS_AGC_DISABLE = 0, AUDIO_IN_ACOUSTICS_NS_ENABLE = 0x0002, @@ -304,11 +563,27 @@ enum { AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES = 0x100, AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER = 0x200, AUDIO_DEVICE_OUT_AUX_DIGITAL = 0x400, + AUDIO_DEVICE_OUT_HDMI = AUDIO_DEVICE_OUT_AUX_DIGITAL, + /* uses an analog connection (multiplexed over the USB connector pins for instance) */ AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET = 0x800, AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET = 0x1000, + /* USB accessory mode: your Android device is a USB device and the dock is a USB host */ AUDIO_DEVICE_OUT_USB_ACCESSORY = 0x2000, + /* USB host mode: your Android device is a USB host and the dock is a USB device */ AUDIO_DEVICE_OUT_USB_DEVICE = 0x4000, AUDIO_DEVICE_OUT_REMOTE_SUBMIX = 0x8000, + /* Telephony voice TX path */ + AUDIO_DEVICE_OUT_TELEPHONY_TX = 0x10000, + /* Analog jack with line impedance detected */ + AUDIO_DEVICE_OUT_LINE = 0x20000, + /* HDMI Audio Return Channel */ + AUDIO_DEVICE_OUT_HDMI_ARC = 0x40000, + /* S/PDIF out */ + AUDIO_DEVICE_OUT_SPDIF = 0x80000, + /* FM transmitter out */ + AUDIO_DEVICE_OUT_FM = 0x100000, + /* Line out for av devices */ + AUDIO_DEVICE_OUT_AUX_LINE = 0x200000, AUDIO_DEVICE_OUT_DEFAULT = AUDIO_DEVICE_BIT_DEFAULT, AUDIO_DEVICE_OUT_ALL = (AUDIO_DEVICE_OUT_EARPIECE | AUDIO_DEVICE_OUT_SPEAKER | @@ -320,12 +595,18 @@ enum { AUDIO_DEVICE_OUT_BLUETOOTH_A2DP | AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES | AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER | - AUDIO_DEVICE_OUT_AUX_DIGITAL | + AUDIO_DEVICE_OUT_HDMI | AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET | AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET | AUDIO_DEVICE_OUT_USB_ACCESSORY | AUDIO_DEVICE_OUT_USB_DEVICE | AUDIO_DEVICE_OUT_REMOTE_SUBMIX | + AUDIO_DEVICE_OUT_TELEPHONY_TX | + AUDIO_DEVICE_OUT_LINE | + AUDIO_DEVICE_OUT_HDMI_ARC | + AUDIO_DEVICE_OUT_SPDIF | + AUDIO_DEVICE_OUT_FM | + AUDIO_DEVICE_OUT_AUX_LINE | AUDIO_DEVICE_OUT_DEFAULT), AUDIO_DEVICE_OUT_ALL_A2DP = (AUDIO_DEVICE_OUT_BLUETOOTH_A2DP | AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES | @@ -343,13 +624,26 @@ enum { AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET = AUDIO_DEVICE_BIT_IN | 0x8, AUDIO_DEVICE_IN_WIRED_HEADSET = AUDIO_DEVICE_BIT_IN | 0x10, AUDIO_DEVICE_IN_AUX_DIGITAL = AUDIO_DEVICE_BIT_IN | 0x20, + AUDIO_DEVICE_IN_HDMI = AUDIO_DEVICE_IN_AUX_DIGITAL, + /* Telephony voice RX path */ AUDIO_DEVICE_IN_VOICE_CALL = AUDIO_DEVICE_BIT_IN | 0x40, + AUDIO_DEVICE_IN_TELEPHONY_RX = AUDIO_DEVICE_IN_VOICE_CALL, AUDIO_DEVICE_IN_BACK_MIC = AUDIO_DEVICE_BIT_IN | 0x80, AUDIO_DEVICE_IN_REMOTE_SUBMIX = AUDIO_DEVICE_BIT_IN | 0x100, AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET = AUDIO_DEVICE_BIT_IN | 0x200, AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET = AUDIO_DEVICE_BIT_IN | 0x400, AUDIO_DEVICE_IN_USB_ACCESSORY = AUDIO_DEVICE_BIT_IN | 0x800, AUDIO_DEVICE_IN_USB_DEVICE = AUDIO_DEVICE_BIT_IN | 0x1000, + /* FM tuner input */ + AUDIO_DEVICE_IN_FM_TUNER = AUDIO_DEVICE_BIT_IN | 0x2000, + /* TV tuner input */ + AUDIO_DEVICE_IN_TV_TUNER = AUDIO_DEVICE_BIT_IN | 0x4000, + /* Analog jack with line impedance detected */ + AUDIO_DEVICE_IN_LINE = AUDIO_DEVICE_BIT_IN | 0x8000, + /* S/PDIF in */ + AUDIO_DEVICE_IN_SPDIF = AUDIO_DEVICE_BIT_IN | 0x10000, + AUDIO_DEVICE_IN_BLUETOOTH_A2DP = AUDIO_DEVICE_BIT_IN | 0x20000, + AUDIO_DEVICE_IN_LOOPBACK = AUDIO_DEVICE_BIT_IN | 0x40000, AUDIO_DEVICE_IN_DEFAULT = AUDIO_DEVICE_BIT_IN | AUDIO_DEVICE_BIT_DEFAULT, AUDIO_DEVICE_IN_ALL = (AUDIO_DEVICE_IN_COMMUNICATION | @@ -357,16 +651,24 @@ enum { AUDIO_DEVICE_IN_BUILTIN_MIC | AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET | AUDIO_DEVICE_IN_WIRED_HEADSET | - AUDIO_DEVICE_IN_AUX_DIGITAL | - AUDIO_DEVICE_IN_VOICE_CALL | + AUDIO_DEVICE_IN_HDMI | + AUDIO_DEVICE_IN_TELEPHONY_RX | AUDIO_DEVICE_IN_BACK_MIC | AUDIO_DEVICE_IN_REMOTE_SUBMIX | AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET | AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET | AUDIO_DEVICE_IN_USB_ACCESSORY | AUDIO_DEVICE_IN_USB_DEVICE | + AUDIO_DEVICE_IN_FM_TUNER | + AUDIO_DEVICE_IN_TV_TUNER | + AUDIO_DEVICE_IN_LINE | + AUDIO_DEVICE_IN_SPDIF | + AUDIO_DEVICE_IN_BLUETOOTH_A2DP | + AUDIO_DEVICE_IN_LOOPBACK | AUDIO_DEVICE_IN_DEFAULT), AUDIO_DEVICE_IN_ALL_SCO = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET, + AUDIO_DEVICE_IN_ALL_USB = (AUDIO_DEVICE_IN_USB_ACCESSORY | + AUDIO_DEVICE_IN_USB_DEVICE), }; typedef uint32_t audio_devices_t; @@ -444,6 +746,257 @@ static const audio_offload_info_t AUDIO_INFO_INITIALIZER = { is_streaming: false }; +/* common audio stream configuration parameters + * You should memset() the entire structure to zero before use to + * ensure forward compatibility + */ +struct audio_config { + uint32_t sample_rate; + audio_channel_mask_t channel_mask; + audio_format_t format; + audio_offload_info_t offload_info; + size_t frame_count; +}; +typedef struct audio_config audio_config_t; + +static const audio_config_t AUDIO_CONFIG_INITIALIZER = { + sample_rate: 0, + channel_mask: AUDIO_CHANNEL_NONE, + format: AUDIO_FORMAT_DEFAULT, + offload_info: { + version: AUDIO_OFFLOAD_INFO_VERSION_CURRENT, + size: sizeof(audio_offload_info_t), + sample_rate: 0, + channel_mask: 0, + format: AUDIO_FORMAT_DEFAULT, + stream_type: AUDIO_STREAM_VOICE_CALL, + bit_rate: 0, + duration_us: 0, + has_video: false, + is_streaming: false + }, + frame_count: 0, +}; + + +/* audio hw module handle functions or structures referencing a module */ +typedef int audio_module_handle_t; + +/****************************** + * Volume control + *****************************/ + +/* If the audio hardware supports gain control on some audio paths, + * the platform can expose them in the audio_policy.conf file. The audio HAL + * will then implement gain control functions that will use the following data + * structures. */ + +/* Type of gain control exposed by an audio port */ +#define AUDIO_GAIN_MODE_JOINT 0x1 /* supports joint channel gain control */ +#define AUDIO_GAIN_MODE_CHANNELS 0x2 /* supports separate channel gain control */ +#define AUDIO_GAIN_MODE_RAMP 0x4 /* supports gain ramps */ + +typedef uint32_t audio_gain_mode_t; + + +/* An audio_gain struct is a representation of a gain stage. + * A gain stage is always attached to an audio port. */ +struct audio_gain { + audio_gain_mode_t mode; /* e.g. AUDIO_GAIN_MODE_JOINT */ + audio_channel_mask_t channel_mask; /* channels which gain an be controlled. + N/A if AUDIO_GAIN_MODE_CHANNELS is not supported */ + int min_value; /* minimum gain value in millibels */ + int max_value; /* maximum gain value in millibels */ + int default_value; /* default gain value in millibels */ + unsigned int step_value; /* gain step in millibels */ + unsigned int min_ramp_ms; /* minimum ramp duration in ms */ + unsigned int max_ramp_ms; /* maximum ramp duration in ms */ +}; + +/* The gain configuration structure is used to get or set the gain values of a + * given port */ +struct audio_gain_config { + int index; /* index of the corresponding audio_gain in the + audio_port gains[] table */ + audio_gain_mode_t mode; /* mode requested for this command */ + audio_channel_mask_t channel_mask; /* channels which gain value follows. + N/A in joint mode */ + int values[sizeof(audio_channel_mask_t) * 8]; /* gain values in millibels + for each channel ordered from LSb to MSb in + channel mask. The number of values is 1 in joint + mode or popcount(channel_mask) */ + unsigned int ramp_duration_ms; /* ramp duration in ms */ +}; + +/****************************** + * Routing control + *****************************/ + +/* Types defined here are used to describe an audio source or sink at internal + * framework interfaces (audio policy, patch panel) or at the audio HAL. + * Sink and sources are grouped in a concept of “audio port” representing an + * audio end point at the edge of the system managed by the module exposing + * the interface. */ + +/* Audio port role: either source or sink */ +typedef enum { + AUDIO_PORT_ROLE_NONE, + AUDIO_PORT_ROLE_SOURCE, + AUDIO_PORT_ROLE_SINK, +} audio_port_role_t; + +/* Audio port type indicates if it is a session (e.g AudioTrack), + * a mix (e.g PlaybackThread output) or a physical device + * (e.g AUDIO_DEVICE_OUT_SPEAKER) */ +typedef enum { + AUDIO_PORT_TYPE_NONE, + AUDIO_PORT_TYPE_DEVICE, + AUDIO_PORT_TYPE_MIX, + AUDIO_PORT_TYPE_SESSION, +} audio_port_type_t; + +/* Each port has a unique ID or handle allocated by policy manager */ +typedef int audio_port_handle_t; +#define AUDIO_PORT_HANDLE_NONE 0 + + +/* maximum audio device address length */ +#define AUDIO_DEVICE_MAX_ADDRESS_LEN 32 + +/* extension for audio port configuration structure when the audio port is a + * hardware device */ +struct audio_port_config_device_ext { + audio_module_handle_t hw_module; /* module the device is attached to */ + audio_devices_t type; /* device type (e.g AUDIO_DEVICE_OUT_SPEAKER) */ + char address[AUDIO_DEVICE_MAX_ADDRESS_LEN]; /* device address. "" if N/A */ +}; + +/* extension for audio port configuration structure when the audio port is a + * sub mix */ +struct audio_port_config_mix_ext { + audio_module_handle_t hw_module; /* module the stream is attached to */ + audio_io_handle_t handle; /* I/O handle of the input/output stream */ + union { + //TODO: change use case for output streams: use strategy and mixer attributes + audio_stream_type_t stream; + audio_source_t source; + } usecase; +}; + +/* extension for audio port configuration structure when the audio port is an + * audio session */ +struct audio_port_config_session_ext { + audio_session_t session; /* audio session */ +}; + +/* Flags indicating which fields are to be considered in struct audio_port_config */ +#define AUDIO_PORT_CONFIG_SAMPLE_RATE 0x1 +#define AUDIO_PORT_CONFIG_CHANNEL_MASK 0x2 +#define AUDIO_PORT_CONFIG_FORMAT 0x4 +#define AUDIO_PORT_CONFIG_GAIN 0x8 +#define AUDIO_PORT_CONFIG_ALL (AUDIO_PORT_CONFIG_SAMPLE_RATE | \ + AUDIO_PORT_CONFIG_CHANNEL_MASK | \ + AUDIO_PORT_CONFIG_FORMAT | \ + AUDIO_PORT_CONFIG_GAIN) + +/* audio port configuration structure used to specify a particular configuration of + * an audio port */ +struct audio_port_config { + audio_port_handle_t id; /* port unique ID */ + audio_port_role_t role; /* sink or source */ + audio_port_type_t type; /* device, mix ... */ + unsigned int config_mask; /* e.g AUDIO_PORT_CONFIG_ALL */ + unsigned int sample_rate; /* sampling rate in Hz */ + audio_channel_mask_t channel_mask; /* channel mask if applicable */ + audio_format_t format; /* format if applicable */ + struct audio_gain_config gain; /* gain to apply if applicable */ + union { + struct audio_port_config_device_ext device; /* device specific info */ + struct audio_port_config_mix_ext mix; /* mix specific info */ + struct audio_port_config_session_ext session; /* session specific info */ + } ext; +}; + + +/* max number of sampling rates in audio port */ +#define AUDIO_PORT_MAX_SAMPLING_RATES 16 +/* max number of channel masks in audio port */ +#define AUDIO_PORT_MAX_CHANNEL_MASKS 16 +/* max number of audio formats in audio port */ +#define AUDIO_PORT_MAX_FORMATS 16 +/* max number of gain controls in audio port */ +#define AUDIO_PORT_MAX_GAINS 16 + +/* extension for audio port structure when the audio port is a hardware device */ +struct audio_port_device_ext { + audio_module_handle_t hw_module; /* module the device is attached to */ + audio_devices_t type; /* device type (e.g AUDIO_DEVICE_OUT_SPEAKER) */ + char address[AUDIO_DEVICE_MAX_ADDRESS_LEN]; +}; + +/* Latency class of the audio mix */ +typedef enum { + AUDIO_LATENCY_LOW, + AUDIO_LATENCY_NORMAL, +} audio_mix_latency_class_t; + +/* extension for audio port structure when the audio port is a sub mix */ +struct audio_port_mix_ext { + audio_module_handle_t hw_module; /* module the stream is attached to */ + audio_io_handle_t handle; /* I/O handle of the input.output stream */ + audio_mix_latency_class_t latency_class; /* latency class */ + // other attributes: routing strategies +}; + +/* extension for audio port structure when the audio port is an audio session */ +struct audio_port_session_ext { + audio_session_t session; /* audio session */ +}; + + +struct audio_port { + audio_port_handle_t id; /* port unique ID */ + audio_port_role_t role; /* sink or source */ + audio_port_type_t type; /* device, mix ... */ + unsigned int num_sample_rates; /* number of sampling rates in following array */ + unsigned int sample_rates[AUDIO_PORT_MAX_SAMPLING_RATES]; + unsigned int num_channel_masks; /* number of channel masks in following array */ + audio_channel_mask_t channel_masks[AUDIO_PORT_MAX_CHANNEL_MASKS]; + unsigned int num_formats; /* number of formats in following array */ + audio_format_t formats[AUDIO_PORT_MAX_FORMATS]; + unsigned int num_gains; /* number of gains in following array */ + struct audio_gain gains[AUDIO_PORT_MAX_GAINS]; + struct audio_port_config active_config; /* current audio port configuration */ + union { + struct audio_port_device_ext device; + struct audio_port_mix_ext mix; + struct audio_port_session_ext session; + } ext; +}; + +/* An audio patch represents a connection between one or more source ports and + * one or more sink ports. Patches are connected and disconnected by audio policy manager or by + * applications via framework APIs. + * Each patch is identified by a handle at the interface used to create that patch. For instance, + * when a patch is created by the audio HAL, the HAL allocates and returns a handle. + * This handle is unique to a given audio HAL hardware module. + * But the same patch receives another system wide unique handle allocated by the framework. + * This unique handle is used for all transactions inside the framework. + */ +typedef int audio_patch_handle_t; +#define AUDIO_PATCH_HANDLE_NONE 0 + +#define AUDIO_PATCH_PORTS_MAX 16 + +struct audio_patch { + audio_patch_handle_t id; /* patch unique ID */ + unsigned int num_sources; /* number of sources in following array */ + struct audio_port_config sources[AUDIO_PATCH_PORTS_MAX]; + unsigned int num_sinks; /* number of sinks in following array */ + struct audio_port_config sinks[AUDIO_PATCH_PORTS_MAX]; +}; + + static inline bool audio_is_output_device(audio_devices_t device) { if (((device & AUDIO_DEVICE_BIT_IN) == 0) && @@ -468,8 +1021,17 @@ static inline bool audio_is_output_devices(audio_devices_t device) return (device & AUDIO_DEVICE_BIT_IN) == 0; } +static inline bool audio_is_a2dp_in_device(audio_devices_t device) +{ + if ((device & AUDIO_DEVICE_BIT_IN) != 0) { + device &= ~AUDIO_DEVICE_BIT_IN; + if ((popcount(device) == 1) && (device & AUDIO_DEVICE_IN_BLUETOOTH_A2DP)) + return true; + } + return false; +} -static inline bool audio_is_a2dp_device(audio_devices_t device) +static inline bool audio_is_a2dp_out_device(audio_devices_t device) { if ((popcount(device) == 1) && (device & AUDIO_DEVICE_OUT_ALL_A2DP)) return true; @@ -477,6 +1039,12 @@ static inline bool audio_is_a2dp_device(audio_devices_t device) return false; } +// Deprecated - use audio_is_a2dp_out_device() instead +static inline bool audio_is_a2dp_device(audio_devices_t device) +{ + return audio_is_a2dp_out_device(device); +} + static inline bool audio_is_bluetooth_sco_device(audio_devices_t device) { if ((device & AUDIO_DEVICE_BIT_IN) == 0) { @@ -491,12 +1059,25 @@ static inline bool audio_is_bluetooth_sco_device(audio_devices_t device) return false; } +static inline bool audio_is_usb_out_device(audio_devices_t device) +{ + return ((popcount(device) == 1) && (device & AUDIO_DEVICE_OUT_ALL_USB)); +} + +static inline bool audio_is_usb_in_device(audio_devices_t device) +{ + if ((device & AUDIO_DEVICE_BIT_IN) != 0) { + device &= ~AUDIO_DEVICE_BIT_IN; + if (popcount(device) == 1 && (device & AUDIO_DEVICE_IN_ALL_USB) != 0) + return true; + } + return false; +} + +/* OBSOLETE - use audio_is_usb_out_device() instead. */ static inline bool audio_is_usb_device(audio_devices_t device) { - if ((popcount(device) == 1) && (device & AUDIO_DEVICE_OUT_ALL_USB)) - return true; - else - return false; + return audio_is_usb_out_device(device); } static inline bool audio_is_remote_submix_device(audio_devices_t device) @@ -508,75 +1089,200 @@ static inline bool audio_is_remote_submix_device(audio_devices_t device) return false; } +/* Returns true if: + * representation is valid, and + * there is at least one channel bit set which _could_ correspond to an input channel, and + * there are no channel bits set which could _not_ correspond to an input channel. + * Otherwise returns false. + */ static inline bool audio_is_input_channel(audio_channel_mask_t channel) { - if ((channel & ~AUDIO_CHANNEL_IN_ALL) == 0) - return channel != 0; - else + uint32_t bits = audio_channel_mask_get_bits(channel); + switch (audio_channel_mask_get_representation(channel)) { + case AUDIO_CHANNEL_REPRESENTATION_POSITION: + if (bits & ~AUDIO_CHANNEL_IN_ALL) { + bits = 0; + } + // fall through + case AUDIO_CHANNEL_REPRESENTATION_INDEX: + return bits != 0; + default: return false; + } } +/* Returns true if: + * representation is valid, and + * there is at least one channel bit set which _could_ correspond to an output channel, and + * there are no channel bits set which could _not_ correspond to an output channel. + * Otherwise returns false. + */ static inline bool audio_is_output_channel(audio_channel_mask_t channel) { - if ((channel & ~AUDIO_CHANNEL_OUT_ALL) == 0) - return channel != 0; - else + uint32_t bits = audio_channel_mask_get_bits(channel); + switch (audio_channel_mask_get_representation(channel)) { + case AUDIO_CHANNEL_REPRESENTATION_POSITION: + if (bits & ~AUDIO_CHANNEL_OUT_ALL) { + bits = 0; + } + // fall through + case AUDIO_CHANNEL_REPRESENTATION_INDEX: + return bits != 0; + default: return false; + } +} + +/* Returns the number of channels from an input channel mask, + * used in the context of audio input or recording. + * If a channel bit is set which could _not_ correspond to an input channel, + * it is excluded from the count. + * Returns zero if the representation is invalid. + */ +static inline uint32_t audio_channel_count_from_in_mask(audio_channel_mask_t channel) +{ + uint32_t bits = audio_channel_mask_get_bits(channel); + switch (audio_channel_mask_get_representation(channel)) { + case AUDIO_CHANNEL_REPRESENTATION_POSITION: + // TODO: We can now merge with from_out_mask and remove anding + bits &= AUDIO_CHANNEL_IN_ALL; + // fall through + case AUDIO_CHANNEL_REPRESENTATION_INDEX: + return popcount(bits); + default: + return 0; + } } -/* Derive an output channel mask from a channel count. +/* Returns the number of channels from an output channel mask, + * used in the context of audio output or playback. + * If a channel bit is set which could _not_ correspond to an output channel, + * it is excluded from the count. + * Returns zero if the representation is invalid. + */ +static inline uint32_t audio_channel_count_from_out_mask(audio_channel_mask_t channel) +{ + uint32_t bits = audio_channel_mask_get_bits(channel); + switch (audio_channel_mask_get_representation(channel)) { + case AUDIO_CHANNEL_REPRESENTATION_POSITION: + // TODO: We can now merge with from_in_mask and remove anding + bits &= AUDIO_CHANNEL_OUT_ALL; + // fall through + case AUDIO_CHANNEL_REPRESENTATION_INDEX: + return popcount(bits); + default: + return 0; + } +} + +/* Derive an output channel mask for position assignment from a channel count. * This is to be used when the content channel mask is unknown. The 1, 2, 4, 5, 6, 7 and 8 channel * cases are mapped to the standard game/home-theater layouts, but note that 4 is mapped to quad, * and not stereo + FC + mono surround. A channel count of 3 is arbitrarily mapped to stereo + FC * for continuity with stereo. - * Returns the matching channel mask, or 0 if the number of channels exceeds that of the - * configurations for which a default channel mask is defined. + * Returns the matching channel mask, + * or AUDIO_CHANNEL_NONE if the channel count is zero, + * or AUDIO_CHANNEL_INVALID if the channel count exceeds that of the + * configurations for which a default output channel mask is defined. */ static inline audio_channel_mask_t audio_channel_out_mask_from_count(uint32_t channel_count) { - switch(channel_count) { + uint32_t bits; + switch (channel_count) { + case 0: + return AUDIO_CHANNEL_NONE; case 1: - return AUDIO_CHANNEL_OUT_MONO; + bits = AUDIO_CHANNEL_OUT_MONO; + break; case 2: - return AUDIO_CHANNEL_OUT_STEREO; + bits = AUDIO_CHANNEL_OUT_STEREO; + break; case 3: - return (AUDIO_CHANNEL_OUT_STEREO | AUDIO_CHANNEL_OUT_FRONT_CENTER); + bits = AUDIO_CHANNEL_OUT_STEREO | AUDIO_CHANNEL_OUT_FRONT_CENTER; + break; case 4: // 4.0 - return AUDIO_CHANNEL_OUT_QUAD; + bits = AUDIO_CHANNEL_OUT_QUAD; + break; case 5: // 5.0 - return (AUDIO_CHANNEL_OUT_QUAD | AUDIO_CHANNEL_OUT_FRONT_CENTER); + bits = AUDIO_CHANNEL_OUT_QUAD | AUDIO_CHANNEL_OUT_FRONT_CENTER; + break; case 6: // 5.1 - return AUDIO_CHANNEL_OUT_5POINT1; + bits = AUDIO_CHANNEL_OUT_5POINT1; + break; case 7: // 6.1 - return (AUDIO_CHANNEL_OUT_5POINT1 | AUDIO_CHANNEL_OUT_BACK_CENTER); + bits = AUDIO_CHANNEL_OUT_5POINT1 | AUDIO_CHANNEL_OUT_BACK_CENTER; + break; case 8: - return AUDIO_CHANNEL_OUT_7POINT1; + bits = AUDIO_CHANNEL_OUT_7POINT1; + break; default: - return 0; + return AUDIO_CHANNEL_INVALID; } + return audio_channel_mask_from_representation_and_bits( + AUDIO_CHANNEL_REPRESENTATION_POSITION, bits); } -/* Similar to above, but for input. Currently handles only mono and stereo. */ +/* Derive an input channel mask for position assignment from a channel count. + * Currently handles only mono and stereo. + * Returns the matching channel mask, + * or AUDIO_CHANNEL_NONE if the channel count is zero, + * or AUDIO_CHANNEL_INVALID if the channel count exceeds that of the + * configurations for which a default input channel mask is defined. + */ static inline audio_channel_mask_t audio_channel_in_mask_from_count(uint32_t channel_count) { + uint32_t bits; switch (channel_count) { + case 0: + return AUDIO_CHANNEL_NONE; case 1: - return AUDIO_CHANNEL_IN_MONO; + bits = AUDIO_CHANNEL_IN_MONO; + break; case 2: - return AUDIO_CHANNEL_IN_STEREO; + bits = AUDIO_CHANNEL_IN_STEREO; + break; default: - return 0; + return AUDIO_CHANNEL_INVALID; + } + return audio_channel_mask_from_representation_and_bits( + AUDIO_CHANNEL_REPRESENTATION_POSITION, bits); +} + +/* Derive a channel mask for index assignment from a channel count. + * Returns the matching channel mask, + * or AUDIO_CHANNEL_NONE if the channel count is zero, + * or AUDIO_CHANNEL_INVALID if the channel count exceeds AUDIO_CHANNEL_COUNT_MAX. + */ +static inline audio_channel_mask_t audio_channel_mask_for_index_assignment_from_count( + uint32_t channel_count) +{ + if (channel_count == 0) { + return AUDIO_CHANNEL_NONE; } + if (channel_count > AUDIO_CHANNEL_COUNT_MAX) { + return AUDIO_CHANNEL_INVALID; + } + uint32_t bits = (1 << channel_count) - 1; + return audio_channel_mask_from_representation_and_bits( + AUDIO_CHANNEL_REPRESENTATION_INDEX, bits); } static inline bool audio_is_valid_format(audio_format_t format) { switch (format & AUDIO_FORMAT_MAIN_MASK) { case AUDIO_FORMAT_PCM: - if (format != AUDIO_FORMAT_PCM_16_BIT && - format != AUDIO_FORMAT_PCM_8_BIT) { + switch (format) { + case AUDIO_FORMAT_PCM_16_BIT: + case AUDIO_FORMAT_PCM_8_BIT: + case AUDIO_FORMAT_PCM_32_BIT: + case AUDIO_FORMAT_PCM_8_24_BIT: + case AUDIO_FORMAT_PCM_FLOAT: + case AUDIO_FORMAT_PCM_24_BIT_PACKED: + return true; + default: return false; } + /* not reached */ case AUDIO_FORMAT_MP3: case AUDIO_FORMAT_AMR_NB: case AUDIO_FORMAT_AMR_WB: @@ -584,6 +1290,9 @@ static inline bool audio_is_valid_format(audio_format_t format) case AUDIO_FORMAT_HE_AAC_V1: case AUDIO_FORMAT_HE_AAC_V2: case AUDIO_FORMAT_VORBIS: + case AUDIO_FORMAT_OPUS: + case AUDIO_FORMAT_AC3: + case AUDIO_FORMAT_E_AC3: return true; default: return false; @@ -604,18 +1313,41 @@ static inline size_t audio_bytes_per_sample(audio_format_t format) case AUDIO_FORMAT_PCM_8_24_BIT: size = sizeof(int32_t); break; + case AUDIO_FORMAT_PCM_24_BIT_PACKED: + size = sizeof(uint8_t) * 3; + break; case AUDIO_FORMAT_PCM_16_BIT: size = sizeof(int16_t); break; case AUDIO_FORMAT_PCM_8_BIT: size = sizeof(uint8_t); break; + case AUDIO_FORMAT_PCM_FLOAT: + size = sizeof(float); + break; default: break; } return size; } +/* converts device address to string sent to audio HAL via set_parameters */ +static char *audio_device_address_to_parameter(audio_devices_t device, const char *address) +{ + const size_t kSize = AUDIO_DEVICE_MAX_ADDRESS_LEN + sizeof("a2dp_sink_address="); + char param[kSize]; + + if (device & AUDIO_DEVICE_OUT_ALL_A2DP) + snprintf(param, kSize, "%s=%s", "a2dp_sink_address", address); + else if (device & AUDIO_DEVICE_OUT_REMOTE_SUBMIX) + snprintf(param, kSize, "%s=%s", "mix", address); + else + snprintf(param, kSize, "%s", address); + + return strdup(param); +} + + __END_DECLS #endif // ANDROID_AUDIO_CORE_H diff --git a/include/system/audio_policy.h b/include/system/audio_policy.h index a6554de..2881104 100644 --- a/include/system/audio_policy.h +++ b/include/system/audio_policy.h @@ -44,6 +44,7 @@ typedef enum { AUDIO_POLICY_FORCE_DIGITAL_DOCK, AUDIO_POLICY_FORCE_NO_BT_A2DP, /* A2DP sink is not preferred to speaker or wired HS */ AUDIO_POLICY_FORCE_SYSTEM_ENFORCED, + AUDIO_POLICY_FORCE_HDMI_SYSTEM_AUDIO_ENFORCED, AUDIO_POLICY_FORCE_CFG_CNT, AUDIO_POLICY_FORCE_CFG_MAX = AUDIO_POLICY_FORCE_CFG_CNT - 1, @@ -58,6 +59,7 @@ typedef enum { AUDIO_POLICY_FORCE_FOR_RECORD, AUDIO_POLICY_FORCE_FOR_DOCK, AUDIO_POLICY_FORCE_FOR_SYSTEM, + AUDIO_POLICY_FORCE_FOR_HDMI_SYSTEM_AUDIO, AUDIO_POLICY_FORCE_USE_CNT, AUDIO_POLICY_FORCE_USE_MAX = AUDIO_POLICY_FORCE_USE_CNT - 1, diff --git a/include/system/graphics.h b/include/system/graphics.h index be86ae4..c3fca97 100644 --- a/include/system/graphics.h +++ b/include/system/graphics.h @@ -165,24 +165,104 @@ enum { /* * Android RAW sensor format: * - * This format is exposed outside of the HAL to applications. + * This format is exposed outside of the camera HAL to applications. * - * RAW_SENSOR is a single-channel 16-bit format, typically representing raw - * Bayer-pattern images from an image sensor, with minimal processing. + * RAW_SENSOR is a single-channel, 16-bit, little endian format, typically + * representing raw Bayer-pattern images from an image sensor, with minimal + * processing. * * The exact pixel layout of the data in the buffer is sensor-dependent, and * needs to be queried from the camera device. * * Generally, not all 16 bits are used; more common values are 10 or 12 - * bits. All parameters to interpret the raw data (black and white points, + * bits. If not all bits are used, the lower-order bits are filled first. + * All parameters to interpret the raw data (black and white points, * color space, etc) must be queried from the camera device. * * This format assumes * - an even width * - an even height - * - a horizontal stride multiple of 16 pixels (32 bytes). + * - a horizontal stride multiple of 16 pixels + * - a vertical stride equal to the height + * - strides are specified in pixels, not in bytes + * + * size = stride * height * 2 + * + * This format must be accepted by the gralloc module when used with the + * following usage flags: + * - GRALLOC_USAGE_HW_CAMERA_* + * - GRALLOC_USAGE_SW_* + * - GRALLOC_USAGE_RENDERSCRIPT + */ + HAL_PIXEL_FORMAT_RAW16 = 0x20, + HAL_PIXEL_FORMAT_RAW_SENSOR = 0x20, // TODO(rubenbrunk): Remove RAW_SENSOR. + + /* + * Android RAW10 format: + * + * This format is exposed outside of the camera HAL to applications. + * + * RAW10 is a single-channel, 10-bit per pixel, densely packed in each row, + * unprocessed format, usually representing raw Bayer-pattern images coming from + * an image sensor. + * + * In an image buffer with this format, starting from the first pixel of each + * row, each 4 consecutive pixels are packed into 5 bytes (40 bits). Each one + * of the first 4 bytes contains the top 8 bits of each pixel, The fifth byte + * contains the 2 least significant bits of the 4 pixels, the exact layout data + * for each 4 consecutive pixels is illustrated below (Pi[j] stands for the jth + * bit of the ith pixel): + * + * bit 7 bit 0 + * =====|=====|=====|=====|=====|=====|=====|=====| + * Byte 0: |P0[9]|P0[8]|P0[7]|P0[6]|P0[5]|P0[4]|P0[3]|P0[2]| + * |-----|-----|-----|-----|-----|-----|-----|-----| + * Byte 1: |P1[9]|P1[8]|P1[7]|P1[6]|P1[5]|P1[4]|P1[3]|P1[2]| + * |-----|-----|-----|-----|-----|-----|-----|-----| + * Byte 2: |P2[9]|P2[8]|P2[7]|P2[6]|P2[5]|P2[4]|P2[3]|P2[2]| + * |-----|-----|-----|-----|-----|-----|-----|-----| + * Byte 3: |P3[9]|P3[8]|P3[7]|P3[6]|P3[5]|P3[4]|P3[3]|P3[2]| + * |-----|-----|-----|-----|-----|-----|-----|-----| + * Byte 4: |P3[1]|P3[0]|P2[1]|P2[0]|P1[1]|P1[0]|P0[1]|P0[0]| + * =============================================== + * + * This format assumes + * - a width multiple of 4 pixels + * - an even height + * - a vertical stride equal to the height + * - strides are specified in bytes, not in pixels + * + * size = stride * height + * + * When stride is equal to width * (10 / 8), there will be no padding bytes at + * the end of each row, the entire image data is densely packed. When stride is + * larger than width * (10 / 8), padding bytes will be present at the end of each + * row (including the last row). + * + * This format must be accepted by the gralloc module when used with the + * following usage flags: + * - GRALLOC_USAGE_HW_CAMERA_* + * - GRALLOC_USAGE_SW_* + * - GRALLOC_USAGE_RENDERSCRIPT */ - HAL_PIXEL_FORMAT_RAW_SENSOR = 0x20, + HAL_PIXEL_FORMAT_RAW10 = 0x25, + + /* + * Android opaque RAW format: + * + * This format is exposed outside of the camera HAL to applications. + * + * RAW_OPAQUE is a format for unprocessed raw image buffers coming from an + * image sensor. The actual structure of buffers of this format is + * implementation-dependent. + * + * This format must be accepted by the gralloc module when used with the + * following usage flags: + * - GRALLOC_USAGE_HW_CAMERA_* + * - GRALLOC_USAGE_SW_* + * - GRALLOC_USAGE_RENDERSCRIPT + */ + HAL_PIXEL_FORMAT_RAW_OPAQUE = 0x24, /* * Android binary blob graphics buffer format: @@ -297,6 +377,136 @@ enum { HAL_TRANSFORM_RESERVED = 0x08, }; +/** + * Colorspace Definitions + * ====================== + * + * Colorspace is the definition of how pixel values should be interpreted. + * It includes primaries (including white point) and the transfer + * characteristic function, which describes both gamma curve and numeric + * range (within the bit depth). + */ + +enum { + /* + * Arbitrary colorspace with manually defined characteristics. + * Colorspace definition must be communicated separately. + * + * This is used when specifying primaries, transfer characteristics, + * etc. separately. + * + * A typical use case is in video encoding parameters (e.g. for H.264), + * where a colorspace can have separately defined primaries, transfer + * characteristics, etc. + */ + HAL_COLORSPACE_ARBITRARY = 0x1, + + /* + * YCbCr Colorspaces + * ----------------- + * + * Primaries are given using (x,y) coordinates in the CIE 1931 definition + * of x and y specified by ISO 11664-1. + * + * Transfer characteristics are the opto-electronic transfer characteristic + * at the source as a function of linear optical intensity (luminance). + */ + + /* + * JPEG File Interchange Format (JFIF) + * + * Same model as BT.601-625, but all values (Y, Cb, Cr) range from 0 to 255 + * + * Transfer characteristic curve: + * E = 1.099 * L ^ 0.45 - 0.099, 1.00 >= L >= 0.018 + * E = 4.500 L, 0.018 > L >= 0 + * L - luminance of image 0 <= L <= 1 for conventional colorimetry + * E - corresponding electrical signal + * + * Primaries: x y + * green 0.290 0.600 + * blue 0.150 0.060 + * red 0.640 0.330 + * white (D65) 0.3127 0.3290 + */ + HAL_COLORSPACE_JFIF = 0x101, + + /* + * ITU-R Recommendation 601 (BT.601) - 625-line + * + * Standard-definition television, 625 Lines (PAL) + * + * For 8-bit-depth formats: + * Luma (Y) samples should range from 16 to 235, inclusive + * Chroma (Cb, Cr) samples should range from 16 to 240, inclusive + * + * For 10-bit-depth formats: + * Luma (Y) samples should range from 64 to 940, inclusive + * Chroma (Cb, Cr) samples should range from 64 to 960, inclusive + * + * Transfer characteristic curve: + * E = 1.099 * L ^ 0.45 - 0.099, 1.00 >= L >= 0.018 + * E = 4.500 L, 0.018 > L >= 0 + * L - luminance of image 0 <= L <= 1 for conventional colorimetry + * E - corresponding electrical signal + * + * Primaries: x y + * green 0.290 0.600 + * blue 0.150 0.060 + * red 0.640 0.330 + * white (D65) 0.3127 0.3290 + */ + HAL_COLORSPACE_BT601_625 = 0x102, + + /* + * ITU-R Recommendation 601 (BT.601) - 525-line + * + * Standard-definition television, 525 Lines (NTSC) + * + * For 8-bit-depth formats: + * Luma (Y) samples should range from 16 to 235, inclusive + * Chroma (Cb, Cr) samples should range from 16 to 240, inclusive + * + * For 10-bit-depth formats: + * Luma (Y) samples should range from 64 to 940, inclusive + * Chroma (Cb, Cr) samples should range from 64 to 960, inclusive + * + * Transfer characteristic curve: + * E = 1.099 * L ^ 0.45 - 0.099, 1.00 >= L >= 0.018 + * E = 4.500 L, 0.018 > L >= 0 + * L - luminance of image 0 <= L <= 1 for conventional colorimetry + * E - corresponding electrical signal + * + * Primaries: x y + * green 0.310 0.595 + * blue 0.155 0.070 + * red 0.630 0.340 + * white (D65) 0.3127 0.3290 + */ + HAL_COLORSPACE_BT601_525 = 0x103, + + /* + * ITU-R Recommendation 709 (BT.709) + * + * High-definition television + * + * For 8-bit-depth formats: + * Luma (Y) samples should range from 16 to 235, inclusive + * Chroma (Cb, Cr) samples should range from 16 to 240, inclusive + * + * For 10-bit-depth formats: + * Luma (Y) samples should range from 64 to 940, inclusive + * Chroma (Cb, Cr) samples should range from 64 to 960, inclusive + * + * Primaries: x y + * green 0.300 0.600 + * blue 0.150 0.060 + * red 0.640 0.330 + * white (D65) 0.3127 0.3290 + */ + HAL_COLORSPACE_BT709 = 0x104, +}; + #ifdef __cplusplus } #endif diff --git a/include/system/sound_trigger.h b/include/system/sound_trigger.h new file mode 100644 index 0000000..dcca34d --- /dev/null +++ b/include/system/sound_trigger.h @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_SOUND_TRIGGER_H +#define ANDROID_SOUND_TRIGGER_H + +#include <stdbool.h> +#include <system/audio.h> + +#define SOUND_TRIGGER_MAX_STRING_LEN 64 /* max length of strings in properties or + descriptor structs */ +#define SOUND_TRIGGER_MAX_LOCALE_LEN 6 /* max length of locale string. e.g en_US */ +#define SOUND_TRIGGER_MAX_USERS 10 /* max number of concurrent users */ +#define SOUND_TRIGGER_MAX_PHRASES 10 /* max number of concurrent phrases */ + +#define RECOGNITION_MODE_VOICE_TRIGGER 0x1 /* simple voice trigger */ +#define RECOGNITION_MODE_USER_IDENTIFICATION 0x2 /* trigger only if one user in model identified */ +#define RECOGNITION_MODE_USER_AUTHENTICATION 0x4 /* trigger only if one user in mode + authenticated */ + +#define RECOGNITION_STATUS_SUCCESS 0 +#define RECOGNITION_STATUS_ABORT 1 +#define RECOGNITION_STATUS_FAILURE 2 + +#define SOUND_MODEL_STATUS_UPDATED 0 + +typedef enum { + SOUND_MODEL_TYPE_UNKNOWN = -1, /* use for unspecified sound model type */ + SOUND_MODEL_TYPE_KEYPHRASE = 0 /* use for key phrase sound models */ +} sound_trigger_sound_model_type_t; + + +typedef struct sound_trigger_uuid_s { + unsigned int timeLow; + unsigned short timeMid; + unsigned short timeHiAndVersion; + unsigned short clockSeq; + unsigned char node[6]; +} sound_trigger_uuid_t; + +/* + * sound trigger implementation descriptor read by the framework via get_properties(). + * Used by SoundTrigger service to report to applications and manage concurrency and policy. + */ +struct sound_trigger_properties { + char implementor[SOUND_TRIGGER_MAX_STRING_LEN]; /* implementor name */ + char description[SOUND_TRIGGER_MAX_STRING_LEN]; /* implementation description */ + unsigned int version; /* implementation version */ + sound_trigger_uuid_t uuid; /* unique implementation ID. + Must change with version each version */ + unsigned int max_sound_models; /* maximum number of concurrent sound models + loaded */ + unsigned int max_key_phrases; /* maximum number of key phrases */ + unsigned int max_users; /* maximum number of concurrent users detected */ + unsigned int recognition_modes; /* all supported modes. + e.g RECOGNITION_MODE_VOICE_TRIGGER */ + bool capture_transition; /* supports seamless transition from detection + to capture */ + unsigned int max_buffer_ms; /* maximum buffering capacity in ms if + capture_transition is true*/ + bool concurrent_capture; /* supports capture by other use cases while + detection is active */ + unsigned int power_consumption_mw; /* Rated power consumption when detection is active + with TDB silence/sound/speech ratio */ +}; + +typedef int sound_trigger_module_handle_t; + +struct sound_trigger_module_descriptor { + sound_trigger_module_handle_t handle; + struct sound_trigger_properties properties; +}; + +typedef int sound_model_handle_t; + +/* + * Generic sound model descriptor. This struct is the header of a larger block passed to + * load_sound_model() and containing the binary data of the sound model. + * Proprietary representation of users in binary data must match information indicated + * by users field + */ +struct sound_trigger_sound_model { + sound_trigger_sound_model_type_t type; /* model type. e.g. SOUND_MODEL_TYPE_KEYPHRASE */ + sound_trigger_uuid_t uuid; /* unique sound model ID. */ + sound_trigger_uuid_t vendor_uuid; /* unique vendor ID. Identifies the engine the + sound model was build for */ + unsigned int data_size; /* size of opaque model data */ + unsigned int data_offset; /* offset of opaque data start from head of struct + (e.g sizeof struct sound_trigger_sound_model) */ +}; + +/* key phrase descriptor */ +struct sound_trigger_phrase { + unsigned int id; /* keyphrase ID */ + unsigned int recognition_mode; /* recognition modes supported by this key phrase */ + unsigned int num_users; /* number of users in the key phrase */ + unsigned int users[SOUND_TRIGGER_MAX_USERS]; /* users ids: (not uid_t but sound trigger + specific IDs */ + char locale[SOUND_TRIGGER_MAX_LOCALE_LEN]; /* locale - JAVA Locale style (e.g. en_US) */ + char text[SOUND_TRIGGER_MAX_STRING_LEN]; /* phrase text in UTF-8 format. */ +}; + +/* + * Specialized sound model for key phrase detection. + * Proprietary representation of key phrases in binary data must match information indicated + * by phrases field + */ +struct sound_trigger_phrase_sound_model { + struct sound_trigger_sound_model common; + unsigned int num_phrases; /* number of key phrases in model */ + struct sound_trigger_phrase phrases[SOUND_TRIGGER_MAX_PHRASES]; +}; + + +/* + * Generic recognition event sent via recognition callback + */ +struct sound_trigger_recognition_event { + int status; /* recognition status e.g. + RECOGNITION_STATUS_SUCCESS */ + sound_trigger_sound_model_type_t type; /* event type, same as sound model type. + e.g. SOUND_MODEL_TYPE_KEYPHRASE */ + sound_model_handle_t model; /* loaded sound model that triggered the + event */ + bool capture_available; /* it is possible to capture audio from this + utterance buffered by the + implementation */ + int capture_session; /* audio session ID. framework use */ + int capture_delay_ms; /* delay in ms between end of model + detection and start of audio available + for capture. A negative value is possible + (e.g. if key phrase is also available for + capture */ + int capture_preamble_ms; /* duration in ms of audio captured + before the start of the trigger. + 0 if none. */ + unsigned int data_size; /* size of opaque event data */ + unsigned int data_offset; /* offset of opaque data start from start of + this struct (e.g sizeof struct + sound_trigger_phrase_recognition_event) */ +}; + +/* + * Confidence level for each user in struct sound_trigger_phrase_recognition_extra + */ +struct sound_trigger_confidence_level { + unsigned int user_id; /* user ID */ + unsigned int level; /* confidence level in percent (0 - 100). + - min level for recognition configuration + - detected level for recognition event */ +}; + +/* + * Specialized recognition event for key phrase detection + */ +struct sound_trigger_phrase_recognition_extra { + unsigned int id; /* keyphrase ID */ + unsigned int recognition_modes; /* recognition modes used for this keyphrase */ + unsigned int num_levels; /* number of user confidence levels */ + struct sound_trigger_confidence_level levels[SOUND_TRIGGER_MAX_USERS]; +}; + +struct sound_trigger_phrase_recognition_event { + struct sound_trigger_recognition_event common; + bool key_phrase_in_capture; /* true if the key phrase is + present in audio data available + for capture after recognition + event is fired */ + unsigned int num_phrases; + struct sound_trigger_phrase_recognition_extra phrase_extras[SOUND_TRIGGER_MAX_PHRASES]; +}; + +/* + * configuration for sound trigger capture session passed to start_recognition() + */ +struct sound_trigger_recognition_config { + audio_io_handle_t capture_handle; /* IO handle that will be used for capture. + N/A if capture_requested is false */ + audio_devices_t capture_device; /* input device requested for detection capture */ + bool capture_requested; /* capture and buffer audio for this recognition + instance */ + unsigned int num_phrases; /* number of key phrases recognition extras */ + struct sound_trigger_phrase_recognition_extra phrases[SOUND_TRIGGER_MAX_PHRASES]; + /* configuration for each key phrase */ + unsigned int data_size; /* size of opaque capture configuration data */ + unsigned int data_offset; /* offset of opaque data start from start of this struct + (e.g sizeof struct sound_trigger_recognition_config) */ +}; + +/* + * Event sent via load sound model callback + */ +struct sound_trigger_model_event { + int status; /* sound model status e.g. SOUND_MODEL_STATUS_UPDATED */ + unsigned int data_size; /* size of event data if any. Size of updated sound model if + status is SOUND_MODEL_STATUS_UPDATED */ + unsigned int data_offset; /* offset of data start from start of this struct + (e.g sizeof struct sound_trigger_model_event) */ +}; + + +#endif // ANDROID_SOUND_TRIGGER_H diff --git a/include/system/window.h b/include/system/window.h index 31f202f..60ed165 100644 --- a/include/system/window.h +++ b/include/system/window.h @@ -242,7 +242,26 @@ enum { * The consumer gralloc usage bits currently set by the consumer. * The values are defined in hardware/libhardware/include/gralloc.h. */ - NATIVE_WINDOW_CONSUMER_USAGE_BITS = 10 + NATIVE_WINDOW_CONSUMER_USAGE_BITS = 10, + + /** + * Transformation that will by applied to buffers by the hwcomposer. + * This must not be set or checked by producer endpoints, and will + * disable the transform hint set in SurfaceFlinger (see + * NATIVE_WINDOW_TRANSFORM_HINT). + * + * INTENDED USE: + * Temporary - Please do not use this. This is intended only to be used + * by the camera's LEGACY mode. + * + * In situations where a SurfaceFlinger client wishes to set a transform + * that is not visible to the producer, and will always be applied in the + * hardware composer, the client can set this flag with + * native_window_set_buffers_sticky_transform. This can be used to rotate + * and flip buffers consumed by hardware composer without actually changing + * the aspect ratio of the buffers produced. + */ + NATIVE_WINDOW_STICKY_TRANSFORM = 11, }; /* Valid operations for the (*perform)() hook. @@ -273,6 +292,7 @@ enum { NATIVE_WINDOW_API_DISCONNECT = 14, /* private */ NATIVE_WINDOW_SET_BUFFERS_USER_DIMENSIONS = 15, /* private */ NATIVE_WINDOW_SET_POST_TRANSFORM_CROP = 16, /* private */ + NATIVE_WINDOW_SET_BUFFERS_STICKY_TRANSFORM = 17,/* private */ }; /* parameter for NATIVE_WINDOW_[API_][DIS]CONNECT */ @@ -791,6 +811,23 @@ static inline int native_window_set_buffers_transform( } /* + * native_window_set_buffers_sticky_transform(..., int transform) + * All buffers queued after this call will be displayed transformed according + * to the transform parameter specified applied on top of the regular buffer + * transform. Setting this transform will disable the transform hint. + * + * Temporary - This is only intended to be used by the LEGACY camera mode, do + * not use this for anything else. + */ +static inline int native_window_set_buffers_sticky_transform( + struct ANativeWindow* window, + int transform) +{ + return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_STICKY_TRANSFORM, + transform); +} + +/* * native_window_set_buffers_timestamp(..., int64_t timestamp) * All buffers queued after this call will be associated with the timestamp * parameter specified. If the timestamp is set to NATIVE_WINDOW_TIMESTAMP_AUTO diff --git a/include/sysutils/NetlinkEvent.h b/include/sysutils/NetlinkEvent.h index c0a9418..c345cdb 100644 --- a/include/sysutils/NetlinkEvent.h +++ b/include/sysutils/NetlinkEvent.h @@ -37,6 +37,8 @@ public: const static int NlActionAddressUpdated; const static int NlActionAddressRemoved; const static int NlActionRdnss; + const static int NlActionRouteUpdated; + const static int NlActionRouteRemoved; NetlinkEvent(); virtual ~NetlinkEvent(); @@ -52,8 +54,11 @@ public: protected: bool parseBinaryNetlinkMessage(char *buffer, int size); bool parseAsciiNetlinkMessage(char *buffer, int size); - bool parseIfAddrMessage(int type, struct ifaddrmsg *ifaddr, int rtasize); - bool parseNdUserOptMessage(struct nduseroptmsg *msg, int optsize); + bool parseIfInfoMessage(const struct nlmsghdr *nh); + bool parseIfAddrMessage(const struct nlmsghdr *nh); + bool parseUlogPacketMessage(const struct nlmsghdr *nh); + bool parseRtMessage(const struct nlmsghdr *nh); + bool parseNdUserOptMessage(const struct nlmsghdr *nh); }; #endif diff --git a/include/usbhost/usbhost.h b/include/usbhost/usbhost.h index 1d67c12..d26e931 100644 --- a/include/usbhost/usbhost.h +++ b/include/usbhost/usbhost.h @@ -189,6 +189,13 @@ int usb_device_release_interface(struct usb_device *device, unsigned int interfa int usb_device_connect_kernel_driver(struct usb_device *device, unsigned int interface, int connect); +/* Sets the current configuration for the device to the specified configuration */ +int usb_device_set_configuration(struct usb_device *device, int configuration); + +/* Sets the specified interface of a USB device */ +int usb_device_set_interface(struct usb_device *device, unsigned int interface, + unsigned int alt_setting); + /* Sends a control message to the specified device on endpoint zero */ int usb_device_control_transfer(struct usb_device *device, int requestType, diff --git a/include/utils/BitSet.h b/include/utils/BitSet.h index 19c03d1..8c61293 100644 --- a/include/utils/BitSet.h +++ b/include/utils/BitSet.h @@ -30,73 +30,103 @@ namespace android { struct BitSet32 { uint32_t value; - inline BitSet32() : value(0) { } + inline BitSet32() : value(0UL) { } explicit inline BitSet32(uint32_t value) : value(value) { } // Gets the value associated with a particular bit index. - static inline uint32_t valueForBit(uint32_t n) { return 0x80000000 >> n; } + static inline uint32_t valueForBit(uint32_t n) { return 0x80000000UL >> n; } // Clears the bit set. - inline void clear() { value = 0; } + inline void clear() { clear(value); } + + static inline void clear(uint32_t& value) { value = 0UL; } // Returns the number of marked bits in the set. - inline uint32_t count() const { return __builtin_popcount(value); } + inline uint32_t count() const { return count(value); } + + static inline uint32_t count(uint32_t value) { return __builtin_popcountl(value); } // Returns true if the bit set does not contain any marked bits. - inline bool isEmpty() const { return ! value; } + inline bool isEmpty() const { return isEmpty(value); } + + static inline bool isEmpty(uint32_t value) { return ! value; } // Returns true if the bit set does not contain any unmarked bits. - inline bool isFull() const { return value == 0xffffffff; } + inline bool isFull() const { return isFull(value); } + + static inline bool isFull(uint32_t value) { return value == 0xffffffffUL; } // Returns true if the specified bit is marked. - inline bool hasBit(uint32_t n) const { return value & valueForBit(n); } + inline bool hasBit(uint32_t n) const { return hasBit(value, n); } + + static inline bool hasBit(uint32_t value, uint32_t n) { return value & valueForBit(n); } // Marks the specified bit. - inline void markBit(uint32_t n) { value |= valueForBit(n); } + inline void markBit(uint32_t n) { markBit(value, n); } + + static inline void markBit (uint32_t& value, uint32_t n) { value |= valueForBit(n); } // Clears the specified bit. - inline void clearBit(uint32_t n) { value &= ~ valueForBit(n); } + inline void clearBit(uint32_t n) { clearBit(value, n); } + + static inline void clearBit(uint32_t& value, uint32_t n) { value &= ~ valueForBit(n); } // Finds the first marked bit in the set. // Result is undefined if all bits are unmarked. - inline uint32_t firstMarkedBit() const { return __builtin_clz(value); } + inline uint32_t firstMarkedBit() const { return firstMarkedBit(value); } + + static uint32_t firstMarkedBit(uint32_t value) { return clz_checked(value); } // Finds the first unmarked bit in the set. // Result is undefined if all bits are marked. - inline uint32_t firstUnmarkedBit() const { return __builtin_clz(~ value); } + inline uint32_t firstUnmarkedBit() const { return firstUnmarkedBit(value); } + + static inline uint32_t firstUnmarkedBit(uint32_t value) { return clz_checked(~ value); } // Finds the last marked bit in the set. // Result is undefined if all bits are unmarked. - inline uint32_t lastMarkedBit() const { return 31 - __builtin_ctz(value); } + inline uint32_t lastMarkedBit() const { return lastMarkedBit(value); } + + static inline uint32_t lastMarkedBit(uint32_t value) { return 31 - ctz_checked(value); } // Finds the first marked bit in the set and clears it. Returns the bit index. // Result is undefined if all bits are unmarked. - inline uint32_t clearFirstMarkedBit() { - uint32_t n = firstMarkedBit(); - clearBit(n); + inline uint32_t clearFirstMarkedBit() { return clearFirstMarkedBit(value); } + + static inline uint32_t clearFirstMarkedBit(uint32_t& value) { + uint32_t n = firstMarkedBit(value); + clearBit(value, n); return n; } // Finds the first unmarked bit in the set and marks it. Returns the bit index. // Result is undefined if all bits are marked. - inline uint32_t markFirstUnmarkedBit() { - uint32_t n = firstUnmarkedBit(); - markBit(n); + inline uint32_t markFirstUnmarkedBit() { return markFirstUnmarkedBit(value); } + + static inline uint32_t markFirstUnmarkedBit(uint32_t& value) { + uint32_t n = firstUnmarkedBit(value); + markBit(value, n); return n; } // Finds the last marked bit in the set and clears it. Returns the bit index. // Result is undefined if all bits are unmarked. - inline uint32_t clearLastMarkedBit() { - uint32_t n = lastMarkedBit(); - clearBit(n); + inline uint32_t clearLastMarkedBit() { return clearLastMarkedBit(value); } + + static inline uint32_t clearLastMarkedBit(uint32_t& value) { + uint32_t n = lastMarkedBit(value); + clearBit(value, n); return n; } // Gets the index of the specified bit in the set, which is the number of // marked bits that appear before the specified bit. inline uint32_t getIndexOfBit(uint32_t n) const { - return __builtin_popcount(value & ~(0xffffffffUL >> n)); + return getIndexOfBit(value, n); + } + + static inline uint32_t getIndexOfBit(uint32_t value, uint32_t n) { + return __builtin_popcountl(value & ~(0xffffffffUL >> n)); } inline bool operator== (const BitSet32& other) const { return value == other.value; } @@ -115,10 +145,150 @@ struct BitSet32 { value |= other.value; return *this; } + +private: + // We use these helpers as the signature of __builtin_c{l,t}z has "unsigned int" for the + // input, which is only guaranteed to be 16b, not 32. The compiler should optimize this away. + static inline uint32_t clz_checked(uint32_t value) { + if (sizeof(unsigned int) == sizeof(uint32_t)) { + return __builtin_clz(value); + } else { + return __builtin_clzl(value); + } + } + + static inline uint32_t ctz_checked(uint32_t value) { + if (sizeof(unsigned int) == sizeof(uint32_t)) { + return __builtin_ctz(value); + } else { + return __builtin_ctzl(value); + } + } }; ANDROID_BASIC_TYPES_TRAITS(BitSet32) +// A simple set of 64 bits that can be individually marked or cleared. +struct BitSet64 { + uint64_t value; + + inline BitSet64() : value(0ULL) { } + explicit inline BitSet64(uint64_t value) : value(value) { } + + // Gets the value associated with a particular bit index. + static inline uint64_t valueForBit(uint32_t n) { return 0x8000000000000000ULL >> n; } + + // Clears the bit set. + inline void clear() { clear(value); } + + static inline void clear(uint64_t& value) { value = 0ULL; } + + // Returns the number of marked bits in the set. + inline uint32_t count() const { return count(value); } + + static inline uint32_t count(uint64_t value) { return __builtin_popcountll(value); } + + // Returns true if the bit set does not contain any marked bits. + inline bool isEmpty() const { return isEmpty(value); } + + static inline bool isEmpty(uint64_t value) { return ! value; } + + // Returns true if the bit set does not contain any unmarked bits. + inline bool isFull() const { return isFull(value); } + + static inline bool isFull(uint64_t value) { return value == 0xffffffffffffffffULL; } + + // Returns true if the specified bit is marked. + inline bool hasBit(uint32_t n) const { return hasBit(value, n); } + + static inline bool hasBit(uint64_t value, uint32_t n) { return value & valueForBit(n); } + + // Marks the specified bit. + inline void markBit(uint32_t n) { markBit(value, n); } + + static inline void markBit(uint64_t& value, uint32_t n) { value |= valueForBit(n); } + + // Clears the specified bit. + inline void clearBit(uint32_t n) { clearBit(value, n); } + + static inline void clearBit(uint64_t& value, uint32_t n) { value &= ~ valueForBit(n); } + + // Finds the first marked bit in the set. + // Result is undefined if all bits are unmarked. + inline uint32_t firstMarkedBit() const { return firstMarkedBit(value); } + + static inline uint32_t firstMarkedBit(uint64_t value) { return __builtin_clzll(value); } + + // Finds the first unmarked bit in the set. + // Result is undefined if all bits are marked. + inline uint32_t firstUnmarkedBit() const { return firstUnmarkedBit(value); } + + static inline uint32_t firstUnmarkedBit(uint64_t value) { return __builtin_clzll(~ value); } + + // Finds the last marked bit in the set. + // Result is undefined if all bits are unmarked. + inline uint32_t lastMarkedBit() const { return lastMarkedBit(value); } + + static inline uint32_t lastMarkedBit(uint64_t value) { return 63 - __builtin_ctzll(value); } + + // Finds the first marked bit in the set and clears it. Returns the bit index. + // Result is undefined if all bits are unmarked. + inline uint32_t clearFirstMarkedBit() { return clearFirstMarkedBit(value); } + + static inline uint32_t clearFirstMarkedBit(uint64_t& value) { + uint64_t n = firstMarkedBit(value); + clearBit(value, n); + return n; + } + + // Finds the first unmarked bit in the set and marks it. Returns the bit index. + // Result is undefined if all bits are marked. + inline uint32_t markFirstUnmarkedBit() { return markFirstUnmarkedBit(value); } + + static inline uint32_t markFirstUnmarkedBit(uint64_t& value) { + uint64_t n = firstUnmarkedBit(value); + markBit(value, n); + return n; + } + + // Finds the last marked bit in the set and clears it. Returns the bit index. + // Result is undefined if all bits are unmarked. + inline uint32_t clearLastMarkedBit() { return clearLastMarkedBit(value); } + + static inline uint32_t clearLastMarkedBit(uint64_t& value) { + uint64_t n = lastMarkedBit(value); + clearBit(value, n); + return n; + } + + // Gets the index of the specified bit in the set, which is the number of + // marked bits that appear before the specified bit. + inline uint32_t getIndexOfBit(uint32_t n) const { return getIndexOfBit(value, n); } + + static inline uint32_t getIndexOfBit(uint64_t value, uint32_t n) { + return __builtin_popcountll(value & ~(0xffffffffffffffffULL >> n)); + } + + inline bool operator== (const BitSet64& other) const { return value == other.value; } + inline bool operator!= (const BitSet64& other) const { return value != other.value; } + inline BitSet64 operator& (const BitSet64& other) const { + return BitSet64(value & other.value); + } + inline BitSet64& operator&= (const BitSet64& other) { + value &= other.value; + return *this; + } + inline BitSet64 operator| (const BitSet64& other) const { + return BitSet64(value | other.value); + } + inline BitSet64& operator|= (const BitSet64& other) { + value |= other.value; + return *this; + } +}; + +ANDROID_BASIC_TYPES_TRAITS(BitSet64) + } // namespace android #endif // UTILS_BITSET_H diff --git a/include/utils/Condition.h b/include/utils/Condition.h index e63ba7e..1c99d1a 100644 --- a/include/utils/Condition.h +++ b/include/utils/Condition.h @@ -60,7 +60,7 @@ public: status_t wait(Mutex& mutex); // same with relative timeout status_t waitRelative(Mutex& mutex, nsecs_t reltime); - // Signal the condition variable, allowing one thread to continue. + // Signal the condition variable, allowing exactly one thread to continue. void signal(); // Signal the condition variable, allowing one or all threads to continue. void signal(WakeUpType type) { @@ -132,6 +132,17 @@ inline status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime) { #endif // HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE } inline void Condition::signal() { + /* + * POSIX says pthread_cond_signal wakes up "one or more" waiting threads. + * However bionic follows the glibc guarantee which wakes up "exactly one" + * waiting thread. + * + * man 3 pthread_cond_signal + * pthread_cond_signal restarts one of the threads that are waiting on + * the condition variable cond. If no threads are waiting on cond, + * nothing happens. If several threads are waiting on cond, exactly one + * is restarted, but it is not specified which. + */ pthread_cond_signal(&mCond); } inline void Condition::broadcast() { diff --git a/include/utils/LruCache.h b/include/utils/LruCache.h index 9248ac9..cd9d7f9 100644 --- a/include/utils/LruCache.h +++ b/include/utils/LruCache.h @@ -17,8 +17,8 @@ #ifndef ANDROID_UTILS_LRU_CACHE_H #define ANDROID_UTILS_LRU_CACHE_H +#include <UniquePtr.h> #include <utils/BasicHashtable.h> -#include <utils/UniquePtr.h> namespace android { @@ -48,6 +48,7 @@ public: bool remove(const TKey& key); bool removeOldest(); void clear(); + const TValue& peekOldestValue(); class Iterator { public: @@ -184,6 +185,14 @@ bool LruCache<TKey, TValue>::removeOldest() { } template <typename TKey, typename TValue> +const TValue& LruCache<TKey, TValue>::peekOldestValue() { + if (mOldest) { + return mOldest->value; + } + return mNullValue; +} + +template <typename TKey, typename TValue> void LruCache<TKey, TValue>::clear() { if (mListener) { for (Entry* p = mOldest; p != NULL; p = p->child) { diff --git a/include/utils/NativeHandle.h b/include/utils/NativeHandle.h new file mode 100644 index 0000000..b825168 --- /dev/null +++ b/include/utils/NativeHandle.h @@ -0,0 +1,56 @@ +/* + * Copyright 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_NATIVE_HANDLE_H +#define ANDROID_NATIVE_HANDLE_H + +#include <utils/RefBase.h> +#include <utils/StrongPointer.h> + +typedef struct native_handle native_handle_t; + +namespace android { + +class NativeHandle: public LightRefBase<NativeHandle> { +public: + // Create a refcounted wrapper around a native_handle_t, and declare + // whether the wrapper owns the handle (so that it should clean up the + // handle upon destruction) or not. + // If handle is NULL, no NativeHandle will be created. + static sp<NativeHandle> create(native_handle_t* handle, bool ownsHandle); + + const native_handle_t* handle() const { + return mHandle; + } + +private: + // for access to the destructor + friend class LightRefBase<NativeHandle>; + + NativeHandle(native_handle_t* handle, bool ownsHandle); + virtual ~NativeHandle(); + + native_handle_t* mHandle; + bool mOwnsHandle; + + // non-copyable + NativeHandle(const NativeHandle&); + NativeHandle& operator=(const NativeHandle&); +}; + +} // namespace android + +#endif // ANDROID_NATIVE_HANDLE_H diff --git a/include/utils/RefBase.h b/include/utils/RefBase.h index cbfe13a..8e15c19 100644 --- a/include/utils/RefBase.h +++ b/include/utils/RefBase.h @@ -203,6 +203,13 @@ private: mutable volatile int32_t mCount; }; +// This is a wrapper around LightRefBase that simply enforces a virtual +// destructor to eliminate the template requirement of LightRefBase +class VirtualLightRefBase : public LightRefBase<VirtualLightRefBase> { +public: + virtual ~VirtualLightRefBase() {} +}; + // --------------------------------------------------------------------------- template <typename T> diff --git a/include/utils/String8.h b/include/utils/String8.h index ef59470..ecfcf10 100644 --- a/include/utils/String8.h +++ b/include/utils/String8.h @@ -130,11 +130,19 @@ public: // start, or -1 if not found ssize_t find(const char* other, size_t start = 0) const; + // return true if this string contains the specified substring + inline bool contains(const char* other) const; + + // removes all occurrence of the specified substring + // returns true if any were found and removed + bool removeAll(const char* other); + void toLower(); void toLower(size_t start, size_t numChars); void toUpper(); void toUpper(size_t start, size_t numChars); + /* * These methods operate on the string as if it were a path name. */ @@ -280,6 +288,11 @@ inline const SharedBuffer* String8::sharedBuffer() const return SharedBuffer::bufferFromData(mString); } +inline bool String8::contains(const char* other) const +{ + return find(other) >= 0; +} + inline String8& String8::operator=(const String8& other) { setTo(other); diff --git a/include/utils/UniquePtr.h b/include/utils/UniquePtr.h deleted file mode 100644 index bc62fe6..0000000 --- a/include/utils/UniquePtr.h +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* === NOTE === NOTE === NOTE === NOTE === NOTE === NOTE === NOTE === NOTE === - * - * THIS IS A COPY OF libcore/include/UniquePtr.h AND AS SUCH THAT IS THE - * CANONICAL SOURCE OF THIS FILE. PLEASE KEEP THEM IN SYNC. - * - * === NOTE === NOTE === NOTE === NOTE === NOTE === NOTE === NOTE === NOTE === - */ - -#ifndef UNIQUE_PTR_H_included -#define UNIQUE_PTR_H_included - -#include <cstdlib> // For NULL. - -// Default deleter for pointer types. -template <typename T> -struct DefaultDelete { - enum { type_must_be_complete = sizeof(T) }; - DefaultDelete() {} - void operator()(T* p) const { - delete p; - } -}; - -// Default deleter for array types. -template <typename T> -struct DefaultDelete<T[]> { - enum { type_must_be_complete = sizeof(T) }; - void operator()(T* p) const { - delete[] p; - } -}; - -// A smart pointer that deletes the given pointer on destruction. -// Equivalent to C++0x's std::unique_ptr (a combination of boost::scoped_ptr -// and boost::scoped_array). -// Named to be in keeping with Android style but also to avoid -// collision with any other implementation, until we can switch over -// to unique_ptr. -// Use thus: -// UniquePtr<C> c(new C); -template <typename T, typename D = DefaultDelete<T> > -class UniquePtr { -public: - // Construct a new UniquePtr, taking ownership of the given raw pointer. - explicit UniquePtr(T* ptr = NULL) : mPtr(ptr) { - } - - ~UniquePtr() { - reset(); - } - - // Accessors. - T& operator*() const { return *mPtr; } - T* operator->() const { return mPtr; } - T* get() const { return mPtr; } - - // Returns the raw pointer and hands over ownership to the caller. - // The pointer will not be deleted by UniquePtr. - T* release() __attribute__((warn_unused_result)) { - T* result = mPtr; - mPtr = NULL; - return result; - } - - // Takes ownership of the given raw pointer. - // If this smart pointer previously owned a different raw pointer, that - // raw pointer will be freed. - void reset(T* ptr = NULL) { - if (ptr != mPtr) { - D()(mPtr); - mPtr = ptr; - } - } - -private: - // The raw pointer. - T* mPtr; - - // Comparing unique pointers is probably a mistake, since they're unique. - template <typename T2> bool operator==(const UniquePtr<T2>& p) const; - template <typename T2> bool operator!=(const UniquePtr<T2>& p) const; - - // Disallow copy and assignment. - UniquePtr(const UniquePtr&); - void operator=(const UniquePtr&); -}; - -// Partial specialization for array types. Like std::unique_ptr, this removes -// operator* and operator-> but adds operator[]. -template <typename T, typename D> -class UniquePtr<T[], D> { -public: - explicit UniquePtr(T* ptr = NULL) : mPtr(ptr) { - } - - ~UniquePtr() { - reset(); - } - - T& operator[](size_t i) const { - return mPtr[i]; - } - T* get() const { return mPtr; } - - T* release() __attribute__((warn_unused_result)) { - T* result = mPtr; - mPtr = NULL; - return result; - } - - void reset(T* ptr = NULL) { - if (ptr != mPtr) { - D()(mPtr); - mPtr = ptr; - } - } - -private: - T* mPtr; - - // Disallow copy and assignment. - UniquePtr(const UniquePtr&); - void operator=(const UniquePtr&); -}; - -#if UNIQUE_PTR_TESTS - -// Run these tests with: -// g++ -g -DUNIQUE_PTR_TESTS -x c++ UniquePtr.h && ./a.out - -#include <stdio.h> - -static void assert(bool b) { - if (!b) { - fprintf(stderr, "FAIL\n"); - abort(); - } - fprintf(stderr, "OK\n"); -} -static int cCount = 0; -struct C { - C() { ++cCount; } - ~C() { --cCount; } -}; -static bool freed = false; -struct Freer { - void operator()(int* p) { - assert(*p == 123); - free(p); - freed = true; - } -}; - -int main(int argc, char* argv[]) { - // - // UniquePtr<T> tests... - // - - // Can we free a single object? - { - UniquePtr<C> c(new C); - assert(cCount == 1); - } - assert(cCount == 0); - // Does release work? - C* rawC; - { - UniquePtr<C> c(new C); - assert(cCount == 1); - rawC = c.release(); - } - assert(cCount == 1); - delete rawC; - // Does reset work? - { - UniquePtr<C> c(new C); - assert(cCount == 1); - c.reset(new C); - assert(cCount == 1); - } - assert(cCount == 0); - - // - // UniquePtr<T[]> tests... - // - - // Can we free an array? - { - UniquePtr<C[]> cs(new C[4]); - assert(cCount == 4); - } - assert(cCount == 0); - // Does release work? - { - UniquePtr<C[]> c(new C[4]); - assert(cCount == 4); - rawC = c.release(); - } - assert(cCount == 4); - delete[] rawC; - // Does reset work? - { - UniquePtr<C[]> c(new C[4]); - assert(cCount == 4); - c.reset(new C[2]); - assert(cCount == 2); - } - assert(cCount == 0); - - // - // Custom deleter tests... - // - assert(!freed); - { - UniquePtr<int, Freer> i(reinterpret_cast<int*>(malloc(sizeof(int)))); - *i = 123; - } - assert(freed); - return 0; -} -#endif - -#endif // UNIQUE_PTR_H_included diff --git a/init/builtins.c b/init/builtins.c index c474198..6032184 100644 --- a/init/builtins.c +++ b/init/builtins.c @@ -473,6 +473,26 @@ exit_success: } +static int wipe_data_via_recovery() +{ + mkdir("/cache/recovery", 0700); + int fd = open("/cache/recovery/command", O_RDWR|O_CREAT|O_TRUNC, 0600); + if (fd >= 0) { + write(fd, "--wipe_data", strlen("--wipe_data") + 1); + close(fd); + } else { + ERROR("could not open /cache/recovery/command\n"); + return -1; + } + android_reboot(ANDROID_RB_RESTART2, 0, "recovery"); + while (1) { pause(); } // never reached +} + + +/* + * This function might request a reboot, in which case it will + * not return. + */ int do_mount_all(int nargs, char **args) { pid_t pid; @@ -510,23 +530,33 @@ int do_mount_all(int nargs, char **args) if (child_ret == -1) { ERROR("fs_mgr_mount_all returned an error\n"); } - exit(child_ret); + _exit(child_ret); } else { /* fork failed, return an error */ return -1; } - /* ret is 1 if the device is encrypted, 0 if not, and -1 on error */ - if (ret == 1) { + if (ret == FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION) { + property_set("ro.crypto.state", "unencrypted"); + property_set("vold.decrypt", "trigger_encryption"); + } else if (ret == FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED) { property_set("ro.crypto.state", "encrypted"); - property_set("vold.decrypt", "1"); - } else if (ret == 0) { + property_set("vold.decrypt", "trigger_default_encryption"); + } else if (ret == FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) { property_set("ro.crypto.state", "unencrypted"); /* If fs_mgr determined this is an unencrypted device, then trigger * that action. */ action_for_each_trigger("nonencrypted", action_add_queue_tail); - } + } else if (ret == FS_MGR_MNTALL_DEV_NEEDS_RECOVERY) { + /* Setup a wipe via recovery, and reboot into recovery */ + ERROR("fs_mgr_mount_all suggested recovery, so wiping data via recovery.\n"); + ret = wipe_data_via_recovery(); + /* If reboot worked, there is no return. */ + } else if (ret > 0) { + ERROR("fs_mgr_mount_all returned unexpected error %d\n", ret); + } + /* else ... < 0: error */ return ret; } diff --git a/init/devices.c b/init/devices.c index e27c311..1012fee 100644 --- a/init/devices.c +++ b/init/devices.c @@ -942,7 +942,7 @@ static void handle_firmware_event(struct uevent *uevent) } } -#define UEVENT_MSG_LEN 1024 +#define UEVENT_MSG_LEN 2048 void handle_device_fd() { char msg[UEVENT_MSG_LEN+2]; diff --git a/init/property_service.c b/init/property_service.c index d112699..1902b77 100644 --- a/init/property_service.c +++ b/init/property_service.c @@ -24,6 +24,7 @@ #include <dirent.h> #include <limits.h> #include <errno.h> +#include <sys/poll.h> #include <cutils/misc.h> #include <cutils/sockets.h> @@ -181,7 +182,6 @@ static void write_persistent_property(const char *name, const char *value) static bool is_legal_property_name(const char* name, size_t namelen) { size_t i; - bool previous_was_dot = false; if (namelen >= PROP_NAME_MAX) return false; if (namelen < 1) return false; if (name[0] == '.') return false; @@ -191,11 +191,10 @@ static bool is_legal_property_name(const char* name, size_t namelen) /* Don't allow ".." to appear in a property name */ for (i = 0; i < namelen; i++) { if (name[i] == '.') { - if (previous_was_dot == true) return false; - previous_was_dot = true; + // i=0 is guaranteed to never have a dot. See above. + if (name[i-1] == '.') return false; continue; } - previous_was_dot = false; if (name[i] == '_' || name[i] == '-') continue; if (name[i] >= 'a' && name[i] <= 'z') continue; if (name[i] >= 'A' && name[i] <= 'Z') continue; @@ -268,6 +267,9 @@ void handle_property_set_fd() socklen_t addr_size = sizeof(addr); socklen_t cr_size = sizeof(cr); char * source_ctx = NULL; + struct pollfd ufds[1]; + const int timeout_ms = 2 * 1000; /* Default 2 sec timeout for caller to send property. */ + int nr; if ((s = accept(property_set_fd, (struct sockaddr *) &addr, &addr_size)) < 0) { return; @@ -280,7 +282,21 @@ void handle_property_set_fd() return; } - r = TEMP_FAILURE_RETRY(recv(s, &msg, sizeof(msg), 0)); + ufds[0].fd = s; + ufds[0].events = POLLIN; + ufds[0].revents = 0; + nr = TEMP_FAILURE_RETRY(poll(ufds, 1, timeout_ms)); + if (nr == 0) { + ERROR("sys_prop: timeout waiting for uid=%d to send property message.\n", cr.uid); + close(s); + return; + } else if (nr < 0) { + ERROR("sys_prop: error waiting for uid=%d to send property message. err=%d %s\n", cr.uid, errno, strerror(errno)); + close(s); + return; + } + + r = TEMP_FAILURE_RETRY(recv(s, &msg, sizeof(msg), MSG_DONTWAIT)); if(r != sizeof(prop_msg)) { ERROR("sys_prop: mis-match msg size received: %d expected: %zu errno: %d\n", r, sizeof(prop_msg), errno); diff --git a/libbacktrace/map_info.c b/libbacktrace/map_info.c new file mode 100644 index 0000000..073b24a --- /dev/null +++ b/libbacktrace/map_info.c @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <limits.h> +#include <pthread.h> +#include <unistd.h> +#include <log/log.h> +#include <sys/time.h> + +#include <backtrace/backtrace.h> + +#if defined(__APPLE__) + +// Mac OS vmmap(1) output: +// __TEXT 0009f000-000a1000 [ 8K 8K] r-x/rwx SM=COW /Volumes/android/dalvik-dev/out/host/darwin-x86/bin/libcorkscrew_test\n +// 012345678901234567890123456789012345678901234567890123456789 +// 0 1 2 3 4 5 +static backtrace_map_info_t* parse_vmmap_line(const char* line) { + unsigned long int start; + unsigned long int end; + char permissions[4]; + int name_pos; + if (sscanf(line, "%*21c %lx-%lx [%*13c] %3c/%*3c SM=%*3c %n", + &start, &end, permissions, &name_pos) != 3) { + return NULL; + } + + const char* name = line + name_pos; + size_t name_len = strlen(name); + + backtrace_map_info_t* mi = calloc(1, sizeof(backtrace_map_info_t) + name_len); + if (mi != NULL) { + mi->start = start; + mi->end = end; + mi->is_readable = permissions[0] == 'r'; + mi->is_writable = permissions[1] == 'w'; + mi->is_executable = permissions[2] == 'x'; + memcpy(mi->name, name, name_len); + mi->name[name_len - 1] = '\0'; + ALOGV("Parsed map: start=0x%08x, end=0x%08x, " + "is_readable=%d, is_writable=%d is_executable=%d, name=%s", + mi->start, mi->end, + mi->is_readable, mi->is_writable, mi->is_executable, mi->name); + } + return mi; +} + +backtrace_map_info_t* backtrace_create_map_info_list(pid_t pid) { + char cmd[1024]; + if (pid < 0) { + pid = getpid(); + } + snprintf(cmd, sizeof(cmd), "vmmap -w -resident -submap -allSplitLibs -interleaved %d", pid); + FILE* fp = popen(cmd, "r"); + if (fp == NULL) { + return NULL; + } + + char line[1024]; + backtrace_map_info_t* milist = NULL; + while (fgets(line, sizeof(line), fp) != NULL) { + backtrace_map_info_t* mi = parse_vmmap_line(line); + if (mi != NULL) { + mi->next = milist; + milist = mi; + } + } + pclose(fp); + return milist; +} + +#else + +// Linux /proc/<pid>/maps lines: +// 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so\n +// 012345678901234567890123456789012345678901234567890123456789 +// 0 1 2 3 4 5 +static backtrace_map_info_t* parse_maps_line(const char* line) +{ + unsigned long int start; + unsigned long int end; + char permissions[5]; + int name_pos; + if (sscanf(line, "%lx-%lx %4s %*x %*x:%*x %*d%n", &start, &end, + permissions, &name_pos) != 3) { + return NULL; + } + + while (isspace(line[name_pos])) { + name_pos += 1; + } + const char* name = line + name_pos; + size_t name_len = strlen(name); + if (name_len && name[name_len - 1] == '\n') { + name_len -= 1; + } + + backtrace_map_info_t* mi = calloc(1, sizeof(backtrace_map_info_t) + name_len + 1); + if (mi) { + mi->start = start; + mi->end = end; + mi->is_readable = strlen(permissions) == 4 && permissions[0] == 'r'; + mi->is_writable = strlen(permissions) == 4 && permissions[1] == 'w'; + mi->is_executable = strlen(permissions) == 4 && permissions[2] == 'x'; + memcpy(mi->name, name, name_len); + mi->name[name_len] = '\0'; + ALOGV("Parsed map: start=0x%08x, end=0x%08x, " + "is_readable=%d, is_writable=%d, is_executable=%d, name=%s", + mi->start, mi->end, + mi->is_readable, mi->is_writable, mi->is_executable, mi->name); + } + return mi; +} + +backtrace_map_info_t* backtrace_create_map_info_list(pid_t tid) { + char path[PATH_MAX]; + char line[1024]; + FILE* fp; + backtrace_map_info_t* milist = NULL; + + if (tid < 0) { + tid = getpid(); + } + snprintf(path, PATH_MAX, "/proc/%d/maps", tid); + fp = fopen(path, "r"); + if (fp) { + while(fgets(line, sizeof(line), fp)) { + backtrace_map_info_t* mi = parse_maps_line(line); + if (mi) { + mi->next = milist; + milist = mi; + } + } + fclose(fp); + } + return milist; +} + +#endif + +void backtrace_destroy_map_info_list(backtrace_map_info_t* milist) { + while (milist) { + backtrace_map_info_t* next = milist->next; + free(milist); + milist = next; + } +} + +const backtrace_map_info_t* backtrace_find_map_info( + const backtrace_map_info_t* milist, uintptr_t addr) { + const backtrace_map_info_t* mi = milist; + while (mi && !(addr >= mi->start && addr < mi->end)) { + mi = mi->next; + } + return mi; +} diff --git a/libcutils/fs.c b/libcutils/fs.c index 286a8eb..45c7add 100644 --- a/libcutils/fs.c +++ b/libcutils/fs.c @@ -212,7 +212,7 @@ int fs_mkdirs(const char* path, mode_t mode) { /* Yay, segment is ready for us to step into */ int next_fd; - if ((next_fd = openat(fd, segment, 0)) == -1) { + if ((next_fd = openat(fd, segment, O_NOFOLLOW | O_CLOEXEC)) == -1) { ALOGE("Failed to openat(%s): %s", buf, strerror(errno)); res = -errno; goto done_close; diff --git a/libcutils/sched_policy.c b/libcutils/sched_policy.c index 9f092d6..2acc3c3 100644 --- a/libcutils/sched_policy.c +++ b/libcutils/sched_policy.c @@ -39,21 +39,20 @@ static inline SchedPolicy _policy(SchedPolicy p) #if defined(HAVE_ANDROID_OS) && defined(HAVE_SCHED_H) && defined(HAVE_PTHREADS) -#include <sched.h> #include <pthread.h> - -#ifndef SCHED_NORMAL - #define SCHED_NORMAL 0 -#endif - -#ifndef SCHED_BATCH - #define SCHED_BATCH 3 -#endif +#include <sched.h> +#include <sys/prctl.h> #define POLICY_DEBUG 0 #define CAN_SET_SP_SYSTEM 0 // non-zero means to implement set_sched_policy(tid, SP_SYSTEM) +// This prctl is only available in Android kernels. +#define PR_SET_TIMERSLACK_PID 41 + +// timer slack value in nS enforced when the thread moves to background +#define TIMER_SLACK_BG 40000000 + static pthread_once_t the_once = PTHREAD_ONCE_INIT; static int __sys_supports_schedgroups = -1; @@ -325,6 +324,8 @@ int set_sched_policy(int tid, SchedPolicy policy) ¶m); } + prctl(PR_SET_TIMERSLACK_PID, policy == SP_BACKGROUND ? TIMER_SLACK_BG : 0, tid); + return 0; } diff --git a/libcutils/str_parms.c b/libcutils/str_parms.c index 2e3ce9f..dfe8c4b 100644 --- a/libcutils/str_parms.c +++ b/libcutils/str_parms.c @@ -262,6 +262,10 @@ int str_parms_add_float(struct str_parms *str_parms, const char *key, return ret; } +int str_parms_has_key(struct str_parms *str_parms, const char *key) { + return hashmapGet(str_parms->map, (void *)key) != NULL; +} + int str_parms_get_str(struct str_parms *str_parms, const char *key, char *val, int len) { diff --git a/libdiskconfig/config_mbr.c b/libdiskconfig/config_mbr.c index 7641b29..7b6ca1c 100644 --- a/libdiskconfig/config_mbr.c +++ b/libdiskconfig/config_mbr.c @@ -208,6 +208,26 @@ fail: } +static struct write_list * +mk_mbr_sig() +{ + struct write_list *item; + if (!(item = alloc_wl(sizeof(uint16_t)))) { + ALOGE("Unable to allocate memory for MBR signature."); + return NULL; + } + + { + /* DO NOT DEREFERENCE */ + struct pc_boot_record *mbr = (void *)PC_MBR_DISK_OFFSET; + /* grab the offset in mbr where to write mbr signature. */ + item->offset = (loff_t)((uintptr_t)((uint8_t *)(&mbr->mbr_sig))); + } + + *((uint16_t*)item->data) = PC_BIOS_BOOT_SIG; + return item; +} + struct write_list * config_mbr(struct disk_info *dinfo) { @@ -276,6 +296,13 @@ config_mbr(struct disk_info *dinfo) wlist_add(&wr_list, temp_wr); } + if ((temp_wr = mk_mbr_sig())) + wlist_add(&wr_list, temp_wr); + else { + ALOGE("Cannot set MBR signature"); + goto fail; + } + return wr_list; nospace: diff --git a/libnetutils/ifc_utils.c b/libnetutils/ifc_utils.c index 4d004f6..3f6c6b5 100644 --- a/libnetutils/ifc_utils.c +++ b/libnetutils/ifc_utils.c @@ -563,17 +563,7 @@ int ifc_create_default_route(const char *name, in_addr_t gw) return ret; } -/* deprecated v4-only */ -int ifc_add_host_route(const char *name, in_addr_t dst) -{ - struct in_addr in_dst, in_gw; - - in_dst.s_addr = dst; - in_gw.s_addr = 0; - - return ifc_act_on_ipv4_route(SIOCADDRT, name, in_dst, 32, in_gw); -} - +// Needed by code in hidden partner repositories / branches, so don't delete. int ifc_enable(const char *ifname) { int result; @@ -584,6 +574,7 @@ int ifc_enable(const char *ifname) return result; } +// Needed by code in hidden partner repositories / branches, so don't delete. int ifc_disable(const char *ifname) { unsigned addr, count; @@ -648,118 +639,6 @@ int ifc_reset_connections(const char *ifname, const int reset_mask) } /* - * Remove the routes associated with the named interface. - */ -int ifc_remove_host_routes(const char *name) -{ - char ifname[64]; - in_addr_t dest, gway, mask; - int flags, refcnt, use, metric, mtu, win, irtt; - struct rtentry rt; - FILE *fp; - struct in_addr addr; - - fp = fopen("/proc/net/route", "r"); - if (fp == NULL) - return -1; - /* Skip the header line */ - if (fscanf(fp, "%*[^\n]\n") < 0) { - fclose(fp); - return -1; - } - ifc_init(); - for (;;) { - int nread = fscanf(fp, "%63s%X%X%X%d%d%d%X%d%d%d\n", - ifname, &dest, &gway, &flags, &refcnt, &use, &metric, &mask, - &mtu, &win, &irtt); - if (nread != 11) { - break; - } - if ((flags & (RTF_UP|RTF_HOST)) != (RTF_UP|RTF_HOST) - || strcmp(ifname, name) != 0) { - continue; - } - memset(&rt, 0, sizeof(rt)); - rt.rt_dev = (void *)name; - init_sockaddr_in(&rt.rt_dst, dest); - init_sockaddr_in(&rt.rt_gateway, gway); - init_sockaddr_in(&rt.rt_genmask, mask); - addr.s_addr = dest; - if (ioctl(ifc_ctl_sock, SIOCDELRT, &rt) < 0) { - ALOGD("failed to remove route for %s to %s: %s", - ifname, inet_ntoa(addr), strerror(errno)); - } - } - fclose(fp); - ifc_close(); - return 0; -} - -/* - * Return the address of the default gateway - * - * TODO: factor out common code from this and remove_host_routes() - * so that we only scan /proc/net/route in one place. - * - * DEPRECATED - */ -int ifc_get_default_route(const char *ifname) -{ - char name[64]; - in_addr_t dest, gway, mask; - int flags, refcnt, use, metric, mtu, win, irtt; - int result; - FILE *fp; - - fp = fopen("/proc/net/route", "r"); - if (fp == NULL) - return 0; - /* Skip the header line */ - if (fscanf(fp, "%*[^\n]\n") < 0) { - fclose(fp); - return 0; - } - ifc_init(); - result = 0; - for (;;) { - int nread = fscanf(fp, "%63s%X%X%X%d%d%d%X%d%d%d\n", - name, &dest, &gway, &flags, &refcnt, &use, &metric, &mask, - &mtu, &win, &irtt); - if (nread != 11) { - break; - } - if ((flags & (RTF_UP|RTF_GATEWAY)) == (RTF_UP|RTF_GATEWAY) - && dest == 0 - && strcmp(ifname, name) == 0) { - result = gway; - break; - } - } - fclose(fp); - ifc_close(); - return result; -} - -/* - * Sets the specified gateway as the default route for the named interface. - * DEPRECATED - */ -int ifc_set_default_route(const char *ifname, in_addr_t gateway) -{ - struct in_addr addr; - int result; - - ifc_init(); - addr.s_addr = gateway; - if ((result = ifc_create_default_route(ifname, gateway)) < 0) { - ALOGD("failed to add %s as default route for %s: %s", - inet_ntoa(addr), ifname, strerror(errno)); - } - ifc_close(); - return result; -} - -/* * Removes the default route for the named interface. */ int ifc_remove_default_route(const char *ifname) @@ -821,151 +700,3 @@ ifc_configure(const char *ifname, return 0; } - -int ifc_act_on_ipv6_route(int action, const char *ifname, struct in6_addr dst, int prefix_length, - struct in6_addr gw) -{ - struct in6_rtmsg rtmsg; - int result; - int ifindex; - - memset(&rtmsg, 0, sizeof(rtmsg)); - - ifindex = if_nametoindex(ifname); - if (ifindex == 0) { - printerr("if_nametoindex() failed: interface %s\n", ifname); - return -ENXIO; - } - - rtmsg.rtmsg_ifindex = ifindex; - rtmsg.rtmsg_dst = dst; - rtmsg.rtmsg_dst_len = prefix_length; - rtmsg.rtmsg_flags = RTF_UP; - - if (prefix_length == 128) { - rtmsg.rtmsg_flags |= RTF_HOST; - } - - if (memcmp(&gw, &in6addr_any, sizeof(in6addr_any))) { - rtmsg.rtmsg_flags |= RTF_GATEWAY; - rtmsg.rtmsg_gateway = gw; - } - - ifc_init6(); - - if (ifc_ctl_sock6 < 0) { - return -errno; - } - - result = ioctl(ifc_ctl_sock6, action, &rtmsg); - if (result < 0) { - if (errno == EEXIST) { - result = 0; - } else { - result = -errno; - } - } - ifc_close6(); - return result; -} - -int ifc_act_on_route(int action, const char *ifname, const char *dst, int prefix_length, - const char *gw) -{ - int ret = 0; - struct sockaddr_in ipv4_dst, ipv4_gw; - struct sockaddr_in6 ipv6_dst, ipv6_gw; - struct addrinfo hints, *addr_ai, *gw_ai; - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ - hints.ai_flags = AI_NUMERICHOST; - - ret = getaddrinfo(dst, NULL, &hints, &addr_ai); - - if (ret != 0) { - printerr("getaddrinfo failed: invalid address %s\n", dst); - return -EINVAL; - } - - if (gw == NULL || (strlen(gw) == 0)) { - if (addr_ai->ai_family == AF_INET6) { - gw = "::"; - } else if (addr_ai->ai_family == AF_INET) { - gw = "0.0.0.0"; - } - } - - if (((addr_ai->ai_family == AF_INET6) && (prefix_length < 0 || prefix_length > 128)) || - ((addr_ai->ai_family == AF_INET) && (prefix_length < 0 || prefix_length > 32))) { - printerr("ifc_add_route: invalid prefix length"); - freeaddrinfo(addr_ai); - return -EINVAL; - } - - ret = getaddrinfo(gw, NULL, &hints, &gw_ai); - if (ret != 0) { - printerr("getaddrinfo failed: invalid gateway %s\n", gw); - freeaddrinfo(addr_ai); - return -EINVAL; - } - - if (addr_ai->ai_family != gw_ai->ai_family) { - printerr("ifc_add_route: different address families: %s and %s\n", dst, gw); - freeaddrinfo(addr_ai); - freeaddrinfo(gw_ai); - return -EINVAL; - } - - if (addr_ai->ai_family == AF_INET6) { - memcpy(&ipv6_dst, addr_ai->ai_addr, sizeof(struct sockaddr_in6)); - memcpy(&ipv6_gw, gw_ai->ai_addr, sizeof(struct sockaddr_in6)); - ret = ifc_act_on_ipv6_route(action, ifname, ipv6_dst.sin6_addr, - prefix_length, ipv6_gw.sin6_addr); - } else if (addr_ai->ai_family == AF_INET) { - memcpy(&ipv4_dst, addr_ai->ai_addr, sizeof(struct sockaddr_in)); - memcpy(&ipv4_gw, gw_ai->ai_addr, sizeof(struct sockaddr_in)); - ret = ifc_act_on_ipv4_route(action, ifname, ipv4_dst.sin_addr, - prefix_length, ipv4_gw.sin_addr); - } else { - printerr("ifc_add_route: getaddrinfo returned un supported address family %d\n", - addr_ai->ai_family); - ret = -EAFNOSUPPORT; - } - - freeaddrinfo(addr_ai); - freeaddrinfo(gw_ai); - return ret; -} - -/* - * DEPRECATED - */ -int ifc_add_ipv4_route(const char *ifname, struct in_addr dst, int prefix_length, - struct in_addr gw) -{ - int i =ifc_act_on_ipv4_route(SIOCADDRT, ifname, dst, prefix_length, gw); - if (DBG) printerr("ifc_add_ipv4_route(%s, xx, %d, xx) = %d", ifname, prefix_length, i); - return i; -} - -/* - * DEPRECATED - */ -int ifc_add_ipv6_route(const char *ifname, struct in6_addr dst, int prefix_length, - struct in6_addr gw) -{ - return ifc_act_on_ipv6_route(SIOCADDRT, ifname, dst, prefix_length, gw); -} - -int ifc_add_route(const char *ifname, const char *dst, int prefix_length, const char *gw) -{ - int i = ifc_act_on_route(SIOCADDRT, ifname, dst, prefix_length, gw); - if (DBG) printerr("ifc_add_route(%s, %s, %d, %s) = %d", ifname, dst, prefix_length, gw, i); - return i; -} - -int ifc_remove_route(const char *ifname, const char*dst, int prefix_length, const char *gw) -{ - return ifc_act_on_route(SIOCDELRT, ifname, dst, prefix_length, gw); -} diff --git a/libnetutils/packet.c b/libnetutils/packet.c index be4e0db..3cdefb0 100644 --- a/libnetutils/packet.c +++ b/libnetutils/packet.c @@ -230,6 +230,8 @@ int receive_packet(int s, struct dhcp_msg *msg) packet.udp.check = 0; sum = finish_sum(checksum(&packet, nread, 0)); packet.udp.check = temp; + if (!sum) + sum = finish_sum(sum); if (temp != sum) { ALOGW("UDP header checksum failure (0x%x should be 0x%x)", sum, temp); return -1; diff --git a/libprocessgroup/Android.mk b/libprocessgroup/Android.mk new file mode 100644 index 0000000..501321f --- /dev/null +++ b/libprocessgroup/Android.mk @@ -0,0 +1,21 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := processgroup.cpp +LOCAL_MODULE := libprocessgroup +LOCAL_SHARED_LIBRARIES := liblog +LOCAL_C_INCLUDES := $(LOCAL_PATH)/include +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include +LOCAL_CFLAGS := -Wall -Werror +LOCAL_REQUIRED_MODULE := processgroup_cleanup +include external/libcxx/libcxx.mk +include $(BUILD_SHARED_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := cleanup.cpp +LOCAL_MODULE := processgroup_cleanup +LOCAL_C_INCLUDES := $(LOCAL_PATH)/include +LOCAL_CFLAGS := -Wall -Werror +LOCAL_FORCE_STATIC_EXECUTABLE := true +LOCAL_STATIC_LIBRARIES := libc libcutils +include $(BUILD_EXECUTABLE) diff --git a/libprocessgroup/cleanup.cpp b/libprocessgroup/cleanup.cpp new file mode 100644 index 0000000..cca8dc4 --- /dev/null +++ b/libprocessgroup/cleanup.cpp @@ -0,0 +1,31 @@ +/* + * Copyright 2014 Google, Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include <string.h> +#include <unistd.h> +#include <sys/syslimits.h> + +#include "processgroup_priv.h" + +int main(int argc, char **argv) +{ + char buf[PATH_MAX]; + if (argc != 2) + return -1; + + memcpy(buf, PROCESSGROUP_CGROUP_PATH, sizeof(PROCESSGROUP_CGROUP_PATH)); + strlcat(buf, argv[1], sizeof(buf)); + return rmdir(buf); +} diff --git a/libprocessgroup/include/processgroup/processgroup.h b/libprocessgroup/include/processgroup/processgroup.h new file mode 100644 index 0000000..11bd8cc --- /dev/null +++ b/libprocessgroup/include/processgroup/processgroup.h @@ -0,0 +1,33 @@ +/* + * Copyright 2014 Google, Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _PROCESSGROUP_H_ +#define _PROCESSGROUP_H_ + +#include <sys/cdefs.h> +#include <sys/types.h> + +__BEGIN_DECLS + +int killProcessGroup(uid_t uid, int initialPid, int signal); + +int createProcessGroup(uid_t uid, int initialPid); + +void removeAllProcessGroups(void); + +__END_DECLS + +#endif diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp new file mode 100644 index 0000000..c32e741 --- /dev/null +++ b/libprocessgroup/processgroup.cpp @@ -0,0 +1,316 @@ +/* + * Copyright 2014 Google, Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "libprocessgroup" + +#include <assert.h> +#include <dirent.h> +#include <fcntl.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <log/log.h> +#include <private/android_filesystem_config.h> + +#include <processgroup/processgroup.h> +#include "processgroup_priv.h" + +struct ctx { + bool initialized; + int fd; + char buf[128]; + char *buf_ptr; + size_t buf_len; +}; + +static int convertUidToPath(char *path, size_t size, uid_t uid) +{ + return snprintf(path, size, "%s/%s%d", + PROCESSGROUP_CGROUP_PATH, + PROCESSGROUP_UID_PREFIX, + uid); +} + +static int convertUidPidToPath(char *path, size_t size, uid_t uid, int pid) +{ + return snprintf(path, size, "%s/%s%d/%s%d", + PROCESSGROUP_CGROUP_PATH, + PROCESSGROUP_UID_PREFIX, + uid, + PROCESSGROUP_PID_PREFIX, + pid); +} + +static int initCtx(uid_t uid, int pid, struct ctx *ctx) +{ + int ret; + char path[PROCESSGROUP_MAX_PATH_LEN] = {0}; + convertUidPidToPath(path, sizeof(path), uid, pid); + strlcat(path, PROCESSGROUP_CGROUP_PROCS_FILE, sizeof(path)); + + int fd = open(path, O_RDONLY); + if (fd < 0) { + ret = -errno; + SLOGV("failed to open %s: %s", path, strerror(errno)); + return ret; + } + + ctx->fd = fd; + ctx->buf_ptr = ctx->buf; + ctx->buf_len = 0; + ctx->initialized = true; + + return 0; +} + +static int refillBuffer(struct ctx *ctx) +{ + memmove(ctx->buf, ctx->buf_ptr, ctx->buf_len); + ctx->buf_ptr = ctx->buf; + + ssize_t ret = read(ctx->fd, ctx->buf_ptr + ctx->buf_len, + sizeof(ctx->buf) - ctx->buf_len); + if (ret < 0) { + return -errno; + } else if (ret == 0) { + return 0; + } + + ctx->buf_len += ret; + assert(ctx->buf_len <= sizeof(ctx->buf)); + + return ret; +} + +static pid_t getOneAppProcess(uid_t uid, int appProcessPid, struct ctx *ctx) +{ + if (!ctx->initialized) { + int ret = initCtx(uid, appProcessPid, ctx); + if (ret < 0) { + return ret; + } + } + + char *eptr; + while ((eptr = (char *)memchr(ctx->buf_ptr, '\n', ctx->buf_len)) == NULL) { + int ret = refillBuffer(ctx); + if (ret == 0) { + return -ERANGE; + } + if (ret < 0) { + return ret; + } + } + + *eptr = '\0'; + char *pid_eptr = NULL; + errno = 0; + long pid = strtol(ctx->buf_ptr, &pid_eptr, 10); + if (errno != 0) { + return -errno; + } + if (pid_eptr != eptr) { + return -EINVAL; + } + + ctx->buf_ptr = eptr + 1; + + return (pid_t)pid; +} + +static int removeProcessGroup(uid_t uid, int pid) +{ + int ret; + char path[PROCESSGROUP_MAX_PATH_LEN] = {0}; + + convertUidPidToPath(path, sizeof(path), uid, pid); + ret = rmdir(path); + + convertUidToPath(path, sizeof(path), uid); + rmdir(path); + + return ret; +} + +static void removeUidProcessGroups(const char *uid_path) +{ + DIR *uid = opendir(uid_path); + if (uid != NULL) { + struct dirent cur; + struct dirent *dir; + while ((readdir_r(uid, &cur, &dir) == 0) && dir) { + char path[PROCESSGROUP_MAX_PATH_LEN]; + + if (dir->d_type != DT_DIR) { + continue; + } + + if (strncmp(dir->d_name, PROCESSGROUP_PID_PREFIX, strlen(PROCESSGROUP_PID_PREFIX))) { + continue; + } + + snprintf(path, sizeof(path), "%s/%s", uid_path, dir->d_name); + SLOGV("removing %s\n", path); + rmdir(path); + } + } +} + +void removeAllProcessGroups() +{ + SLOGV("removeAllProcessGroups()"); + DIR *root = opendir(PROCESSGROUP_CGROUP_PATH); + if (root == NULL) { + SLOGE("failed to open %s: %s", PROCESSGROUP_CGROUP_PATH, strerror(errno)); + } + if (root != NULL) { + struct dirent cur; + struct dirent *dir; + while ((readdir_r(root, &cur, &dir) == 0) && dir) { + char path[PROCESSGROUP_MAX_PATH_LEN]; + + if (dir->d_type != DT_DIR) { + continue; + } + if (strncmp(dir->d_name, PROCESSGROUP_UID_PREFIX, strlen(PROCESSGROUP_UID_PREFIX))) { + continue; + } + + snprintf(path, sizeof(path), "%s/%s", PROCESSGROUP_CGROUP_PATH, dir->d_name); + removeUidProcessGroups(path); + SLOGV("removing %s\n", path); + rmdir(path); + } + } +} + +static int killProcessGroupOnce(uid_t uid, int initialPid, int signal) +{ + int processes = 0; + struct ctx ctx; + pid_t pid; + + ctx.initialized = false; + + while ((pid = getOneAppProcess(uid, initialPid, &ctx)) >= 0) { + processes++; + SLOGV("sending processgroup kill to pid %d\n", pid); + int ret = kill(pid, signal); + if (ret == -1) { + SLOGV("failed to kill pid %d: %s", pid, strerror(errno)); + } + } + + if (ctx.initialized) { + close(ctx.fd); + } + + return processes; +} + +int killProcessGroup(uid_t uid, int initialPid, int signal) +{ + int processes; + int sleep_us = 100; + + while ((processes = killProcessGroupOnce(uid, initialPid, signal)) > 0) { + SLOGV("killed %d processes for processgroup %d\n", processes, initialPid); + if (sleep_us < 128000) { + usleep(sleep_us); + sleep_us *= 2; + } else { + SLOGE("failed to kill %d processes for processgroup %d\n", + processes, initialPid); + break; + } + } + + if (processes == 0) { + return removeProcessGroup(uid, initialPid); + } else { + return -1; + } +} + +static int mkdirAndChown(const char *path, mode_t mode, uid_t uid, gid_t gid) +{ + int ret; + + ret = mkdir(path, 0750); + if (ret < 0 && errno != EEXIST) { + return -errno; + } + + ret = chown(path, AID_SYSTEM, AID_SYSTEM); + if (ret < 0) { + ret = -errno; + rmdir(path); + return ret; + } + + return 0; +} + +int createProcessGroup(uid_t uid, int initialPid) +{ + char path[PROCESSGROUP_MAX_PATH_LEN] = {0}; + int ret; + + convertUidToPath(path, sizeof(path), uid); + + ret = mkdirAndChown(path, 0750, AID_SYSTEM, AID_SYSTEM); + if (ret < 0) { + SLOGE("failed to make and chown %s: %s", path, strerror(-ret)); + return ret; + } + + convertUidPidToPath(path, sizeof(path), uid, initialPid); + + ret = mkdirAndChown(path, 0750, AID_SYSTEM, AID_SYSTEM); + if (ret < 0) { + SLOGE("failed to make and chown %s: %s", path, strerror(-ret)); + return ret; + } + + strlcat(path, PROCESSGROUP_CGROUP_PROCS_FILE, sizeof(path)); + + int fd = open(path, O_WRONLY); + if (fd < 0) { + ret = -errno; + SLOGE("failed to open %s: %s", path, strerror(errno)); + return ret; + } + + char pid[PROCESSGROUP_MAX_PID_LEN + 1] = {0}; + int len = snprintf(pid, sizeof(pid), "%d", initialPid); + + ret = write(fd, pid, len); + if (ret < 0) { + ret = -errno; + SLOGE("failed to write '%s' to %s: %s", pid, path, strerror(errno)); + } else { + ret = 0; + } + + close(fd); + return ret; +} + diff --git a/libprocessgroup/processgroup_priv.h b/libprocessgroup/processgroup_priv.h new file mode 100644 index 0000000..1895bf9 --- /dev/null +++ b/libprocessgroup/processgroup_priv.h @@ -0,0 +1,35 @@ +/* + * Copyright 2014 Google, Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _PROCESSGROUP_PRIV_H_ +#define _PROCESSGROUP_PRIV_H_ + +#define PROCESSGROUP_CGROUP_PATH "/acct" +#define PROCESSGROUP_UID_PREFIX "uid_" +#define PROCESSGROUP_PID_PREFIX "pid_" +#define PROCESSGROUP_CGROUP_PROCS_FILE "/cgroup.procs" +#define PROCESSGROUP_MAX_UID_LEN 11 +#define PROCESSGROUP_MAX_PID_LEN 11 +#define PROCESSGROUP_MAX_PATH_LEN \ + (sizeof(PROCESSGROUP_CGROUP_PATH) + \ + sizeof(PROCESSGROUP_UID_PREFIX) + 1 + \ + PROCESSGROUP_MAX_UID_LEN + \ + sizeof(PROCESSGROUP_PID_PREFIX) + 1 + \ + PROCESSGROUP_MAX_PID_LEN + \ + sizeof(PROCESSGROUP_CGROUP_PROCS_FILE) + \ + 1) + +#endif diff --git a/libsparse/Android.mk b/libsparse/Android.mk index 02ab412..0abe33d 100644 --- a/libsparse/Android.mk +++ b/libsparse/Android.mk @@ -88,15 +88,18 @@ LOCAL_CFLAGS := -Werror include $(BUILD_EXECUTABLE) +ifneq ($(HOST_OS),windows) + include $(CLEAR_VARS) -LOCAL_SRC_FILES := simg2simg.c -LOCAL_MODULE := simg2simg +LOCAL_SRC_FILES := append2simg.c +LOCAL_MODULE := append2simg LOCAL_STATIC_LIBRARIES := \ libsparse_host \ libz LOCAL_CFLAGS := -Werror include $(BUILD_HOST_EXECUTABLE) +endif include $(CLEAR_VARS) LOCAL_MODULE := simg_dump.py diff --git a/libsparse/append2simg.c b/libsparse/append2simg.c new file mode 100644 index 0000000..65e6cc2 --- /dev/null +++ b/libsparse/append2simg.c @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define _FILE_OFFSET_BITS 64 +#define _LARGEFILE64_SOURCE 1 +#define _GNU_SOURCE + +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <sparse/sparse.h> +#include "sparse_file.h" +#include "backed_block.h" + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +#if defined(__APPLE__) && defined(__MACH__) +#define lseek64 lseek +#endif +#if defined(__APPLE__) && defined(__MACH__) +#define lseek64 lseek +#define off64_t off_t +#endif + +void usage() +{ + fprintf(stderr, "Usage: append2simg <output> <input>\n"); +} + +int main(int argc, char *argv[]) +{ + int output; + int output_block; + char *output_path; + struct sparse_file *sparse_output; + + int input; + char *input_path; + off64_t input_len; + + int tmp_fd; + char *tmp_path; + + int ret; + + if (argc == 3) { + output_path = argv[1]; + input_path = argv[2]; + } else { + usage(); + exit(-1); + } + + ret = asprintf(&tmp_path, "%s.append2simg", output_path); + if (ret < 0) { + fprintf(stderr, "Couldn't allocate filename\n"); + exit(-1); + } + + output = open(output_path, O_RDWR | O_BINARY); + if (output < 0) { + fprintf(stderr, "Couldn't open output file (%s)\n", strerror(errno)); + exit(-1); + } + + sparse_output = sparse_file_import_auto(output, true); + if (!sparse_output) { + fprintf(stderr, "Couldn't import output file\n"); + exit(-1); + } + + input = open(input_path, O_RDONLY | O_BINARY); + if (input < 0) { + fprintf(stderr, "Couldn't open input file (%s)\n", strerror(errno)); + exit(-1); + } + + input_len = lseek64(input, 0, SEEK_END); + if (input_len < 0) { + fprintf(stderr, "Couldn't get input file length (%s)\n", strerror(errno)); + exit(-1); + } else if (input_len % sparse_output->block_size) { + fprintf(stderr, "Input file is not a multiple of the output file's block size"); + exit(-1); + } + lseek64(input, 0, SEEK_SET); + + output_block = sparse_output->len / sparse_output->block_size; + if (sparse_file_add_fd(sparse_output, input, 0, input_len, output_block) < 0) { + fprintf(stderr, "Couldn't add input file\n"); + exit(-1); + } + sparse_output->len += input_len; + + tmp_fd = open(tmp_path, O_WRONLY | O_CREAT | O_BINARY, 0664); + if (tmp_fd < 0) { + fprintf(stderr, "Couldn't open temporary file (%s)\n", strerror(errno)); + exit(-1); + } + + lseek64(output, 0, SEEK_SET); + if (sparse_file_write(sparse_output, tmp_fd, false, true, false) < 0) { + fprintf(stderr, "Failed to write sparse file\n"); + exit(-1); + } + + sparse_file_destroy(sparse_output); + close(tmp_fd); + close(output); + close(input); + + ret = rename(tmp_path, output_path); + if (ret < 0) { + fprintf(stderr, "Failed to rename temporary file (%s)\n", strerror(errno)); + exit(-1); + } + + free(tmp_path); + + exit(0); +} diff --git a/libsuspend/autosuspend.c b/libsuspend/autosuspend.c index eb1f66e..edd1007 100644 --- a/libsuspend/autosuspend.c +++ b/libsuspend/autosuspend.c @@ -38,10 +38,13 @@ static int autosuspend_init(void) goto out; } +/* Remove autosleep so userspace can manager suspend/resume and keep stats */ +#if 0 autosuspend_ops = autosuspend_autosleep_init(); if (autosuspend_ops) { goto out; } +#endif autosuspend_ops = autosuspend_wakeup_count_init(); if (autosuspend_ops) { diff --git a/libsuspend/autosuspend_wakeup_count.c b/libsuspend/autosuspend_wakeup_count.c index a88e677..7483a8f 100644 --- a/libsuspend/autosuspend_wakeup_count.c +++ b/libsuspend/autosuspend_wakeup_count.c @@ -25,6 +25,7 @@ #include <unistd.h> #define LOG_TAG "libsuspend" +//#define LOG_NDEBUG 0 #include <cutils/log.h> #include "autosuspend_ops.h" @@ -37,6 +38,7 @@ static int wakeup_count_fd; static pthread_t suspend_thread; static sem_t suspend_lockout; static const char *sleep_state = "mem"; +static void (*wakeup_func)(void) = NULL; static void *suspend_thread_func(void *arg __attribute__((unused))) { @@ -80,6 +82,11 @@ static void *suspend_thread_func(void *arg __attribute__((unused))) if (ret < 0) { strerror_r(errno, buf, sizeof(buf)); ALOGE("Error writing to %s: %s\n", SYS_POWER_STATE, buf); + } else { + void (*func)(void) = wakeup_func; + if (func != NULL) { + (*func)(); + } } } @@ -131,6 +138,15 @@ static int autosuspend_wakeup_count_disable(void) return ret; } +void set_wakeup_callback(void (*func)(void)) +{ + if (wakeup_func != NULL) { + ALOGE("Duplicate wakeup callback applied, keeping original"); + return; + } + wakeup_func = func; +} + struct autosuspend_ops autosuspend_wakeup_count_ops = { .enable = autosuspend_wakeup_count_enable, .disable = autosuspend_wakeup_count_disable, diff --git a/libsuspend/include/suspend/autosuspend.h b/libsuspend/include/suspend/autosuspend.h index f56fc6a..10e3d27 100644 --- a/libsuspend/include/suspend/autosuspend.h +++ b/libsuspend/include/suspend/autosuspend.h @@ -43,6 +43,13 @@ int autosuspend_enable(void); */ int autosuspend_disable(void); +/* + * set_wakeup_callback + * + * Set a function to be called each time the device wakes up from suspend. + */ +void set_wakeup_callback(void (*func)(void)); + __END_DECLS #endif diff --git a/libsync/tests/Android.mk b/libsync/tests/Android.mk new file mode 100644 index 0000000..ad20e50 --- /dev/null +++ b/libsync/tests/Android.mk @@ -0,0 +1,31 @@ +# +# Copyright 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) +include external/libcxx/libcxx.mk +LOCAL_CLANG := true +LOCAL_MODULE := sync-unit-tests +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk +LOCAL_CFLAGS += -g -Wall -Werror -std=gnu++11 -Wno-missing-field-initializers -Wno-sign-compare +LOCAL_SHARED_LIBRARIES += libsync +LOCAL_STATIC_LIBRARIES += libgtest_main +LOCAL_C_INCLUDES += $(LOCAL_PATH)/../include +LOCAL_C_INCLUDES += $(LOCAL_PATH)/.. +LOCAL_SRC_FILES := \ + sync_test.cpp +include $(BUILD_NATIVE_TEST) diff --git a/libsync/tests/sync_test.cpp b/libsync/tests/sync_test.cpp new file mode 100644 index 0000000..55cd687 --- /dev/null +++ b/libsync/tests/sync_test.cpp @@ -0,0 +1,615 @@ +#include <gtest/gtest.h> +#include <sync/sync.h> +#include <sw_sync.h> +#include <fcntl.h> +#include <vector> +#include <string> +#include <cassert> +#include <iostream> +#include <unistd.h> +#include <thread> +#include <poll.h> +#include <mutex> +#include <algorithm> +#include <tuple> +#include <random> +#include <unordered_map> + +// TODO: better stress tests? +// Handle more than 64 fd's simultaneously, i.e. fix sync_fence_info's 4k limit. +// Handle wraparound in timelines like nvidia. + +using namespace std; + +namespace { + +// C++ wrapper class for sync timeline. +class SyncTimeline { + int m_fd = -1; + bool m_fdInitialized = false; +public: + SyncTimeline(const SyncTimeline &) = delete; + SyncTimeline& operator=(SyncTimeline&) = delete; + SyncTimeline() noexcept { + int fd = sw_sync_timeline_create(); + if (fd == -1) + return; + m_fdInitialized = true; + m_fd = fd; + } + void destroy() { + if (m_fdInitialized) { + close(m_fd); + m_fd = -1; + m_fdInitialized = false; + } + } + ~SyncTimeline() { + destroy(); + } + bool isValid() const { + if (m_fdInitialized) { + int status = fcntl(m_fd, F_GETFD, 0); + if (status == 0) + return true; + else + return false; + } + else { + return false; + } + } + int getFd() const { + return m_fd; + } + int inc(int val = 1) { + return sw_sync_timeline_inc(m_fd, val); + } +}; + +struct SyncPointInfo { + std::string driverName; + std::string objectName; + uint64_t timeStampNs; + int status; // 1 sig, 0 active, neg is err +}; + +// Wrapper class for sync fence. +class SyncFence { + int m_fd = -1; + bool m_fdInitialized = false; + static int s_fenceCount; + + void setFd(int fd) { + m_fd = fd; + m_fdInitialized = true; + } + void clearFd() { + m_fd = -1; + m_fdInitialized = false; + } +public: + bool isValid() const { + if (m_fdInitialized) { + int status = fcntl(m_fd, F_GETFD, 0); + if (status == 0) + return true; + else + return false; + } + else { + return false; + } + } + SyncFence& operator=(SyncFence &&rhs) noexcept { + destroy(); + if (rhs.isValid()) { + setFd(rhs.getFd()); + rhs.clearFd(); + } + return *this; + } + SyncFence(SyncFence &&fence) noexcept { + if (fence.isValid()) { + setFd(fence.getFd()); + fence.clearFd(); + } + } + SyncFence(const SyncFence &fence) noexcept { + // This is ok, as sync fences are immutable after construction, so a dup + // is basically the same thing as a copy. + if (fence.isValid()) { + int fd = dup(fence.getFd()); + if (fd == -1) + return; + setFd(fd); + } + } + SyncFence(const SyncTimeline &timeline, + int value, + const char *name = nullptr) noexcept { + std::string autoName = "allocFence"; + autoName += s_fenceCount; + s_fenceCount++; + int fd = sw_sync_fence_create(timeline.getFd(), name ? name : autoName.c_str(), value); + if (fd == -1) + return; + setFd(fd); + } + SyncFence(const SyncFence &a, const SyncFence &b, const char *name = nullptr) noexcept { + std::string autoName = "mergeFence"; + autoName += s_fenceCount; + s_fenceCount++; + int fd = sync_merge(name ? name : autoName.c_str(), a.getFd(), b.getFd()); + if (fd == -1) + return; + setFd(fd); + } + SyncFence(const vector<SyncFence> &sources) noexcept { + assert(sources.size()); + SyncFence temp(*begin(sources)); + for (auto itr = ++begin(sources); itr != end(sources); ++itr) { + temp = SyncFence(*itr, temp); + } + if (temp.isValid()) { + setFd(temp.getFd()); + temp.clearFd(); + } + } + void destroy() { + if (isValid()) { + close(m_fd); + clearFd(); + } + } + ~SyncFence() { + destroy(); + } + int getFd() const { + return m_fd; + } + int wait(int timeout = -1) { + return sync_wait(m_fd, timeout); + } + vector<SyncPointInfo> getInfo() const { + struct sync_pt_info *pointInfo = nullptr; + vector<SyncPointInfo> fenceInfo; + sync_fence_info_data *info = sync_fence_info(getFd()); + if (!info) { + return fenceInfo; + } + while ((pointInfo = sync_pt_info(info, pointInfo))) { + fenceInfo.push_back(SyncPointInfo{ + pointInfo->driver_name, + pointInfo->obj_name, + pointInfo->timestamp_ns, + pointInfo->status}); + } + sync_fence_info_free(info); + return fenceInfo; + } + int getSize() const { + return getInfo().size(); + } + int getSignaledCount() const { + return countWithStatus(1); + } + int getActiveCount() const { + return countWithStatus(0); + } + int getErrorCount() const { + return countWithStatus(-1); + } +private: + int countWithStatus(int status) const { + int count = 0; + for (auto &info : getInfo()) { + if (info.status == status) { + count++; + } + } + return count; + } +}; + +int SyncFence::s_fenceCount = 0; + +TEST(AllocTest, Timeline) { + SyncTimeline timeline; + ASSERT_TRUE(timeline.isValid()); +} + +TEST(AllocTest, Fence) { + SyncTimeline timeline; + ASSERT_TRUE(timeline.isValid()); + + SyncFence fence(timeline, 1); + ASSERT_TRUE(fence.isValid()); +} + +TEST(AllocTest, FenceNegative) { + int timeline = sw_sync_timeline_create(); + ASSERT_GT(timeline, 0); + + // bad fd. + ASSERT_LT(sw_sync_fence_create(-1, "fence", 1), 0); + + // No name - segfaults in user space. + // Maybe we should be friendlier here? + /* + ASSERT_LT(sw_sync_fence_create(timeline, nullptr, 1), 0); + */ + close(timeline); +} + +TEST(FenceTest, OneTimelineWait) { + SyncTimeline timeline; + ASSERT_TRUE(timeline.isValid()); + + SyncFence fence(timeline, 5); + ASSERT_TRUE(fence.isValid()); + + // Wait on fence until timeout. + ASSERT_EQ(fence.wait(0), -1); + ASSERT_EQ(errno, ETIME); + + // Advance timeline from 0 -> 1 + ASSERT_EQ(timeline.inc(1), 0); + + // Wait on fence until timeout. + ASSERT_EQ(fence.wait(0), -1); + ASSERT_EQ(errno, ETIME); + + // Signal the fence. + ASSERT_EQ(timeline.inc(4), 0); + + // Wait successfully. + ASSERT_EQ(fence.wait(0), 0); + + // Go even futher, and confirm wait still succeeds. + ASSERT_EQ(timeline.inc(10), 0); + ASSERT_EQ(fence.wait(0), 0); +} + +TEST(FenceTest, OneTimelinePoll) { + SyncTimeline timeline; + ASSERT_TRUE(timeline.isValid()); + + SyncFence fence(timeline, 100); + ASSERT_TRUE(fence.isValid()); + + fd_set set; + FD_ZERO(&set); + FD_SET(fence.getFd(), &set); + + // Poll the fence, and wait till timeout. + timeval time = {0}; + ASSERT_EQ(select(fence.getFd() + 1, &set, nullptr, nullptr, &time), 0); + + // Advance the timeline. + timeline.inc(100); + timeline.inc(100); + + // Select should return that the fd is read for reading. + FD_ZERO(&set); + FD_SET(fence.getFd(), &set); + + ASSERT_EQ(select(fence.getFd() + 1, &set, nullptr, nullptr, &time), 1); + ASSERT_TRUE(FD_ISSET(fence.getFd(), &set)); +} + +TEST(FenceTest, OneTimelineMerge) { + SyncTimeline timeline; + ASSERT_TRUE(timeline.isValid()); + + // create fence a,b,c and then merge them all into fence d. + SyncFence a(timeline, 1), b(timeline, 2), c(timeline, 3); + ASSERT_TRUE(a.isValid()); + ASSERT_TRUE(b.isValid()); + ASSERT_TRUE(c.isValid()); + + SyncFence d({a,b,c}); + ASSERT_TRUE(d.isValid()); + + // confirm all fences have one active point (even d). + ASSERT_EQ(a.getActiveCount(), 1); + ASSERT_EQ(b.getActiveCount(), 1); + ASSERT_EQ(c.getActiveCount(), 1); + ASSERT_EQ(d.getActiveCount(), 1); + + // confirm that d is not signaled until the max of a,b,c + timeline.inc(1); + ASSERT_EQ(a.getSignaledCount(), 1); + ASSERT_EQ(d.getActiveCount(), 1); + + timeline.inc(1); + ASSERT_EQ(b.getSignaledCount(), 1); + ASSERT_EQ(d.getActiveCount(), 1); + + timeline.inc(1); + ASSERT_EQ(c.getSignaledCount(), 1); + ASSERT_EQ(d.getActiveCount(), 0); + ASSERT_EQ(d.getSignaledCount(), 1); +} + +TEST(FenceTest, MergeSameFence) { + SyncTimeline timeline; + ASSERT_TRUE(timeline.isValid()); + + SyncFence fence(timeline, 5); + ASSERT_TRUE(fence.isValid()); + + SyncFence selfMergeFence(fence, fence); + ASSERT_TRUE(selfMergeFence.isValid()); + + ASSERT_EQ(selfMergeFence.getSignaledCount(), 0); + + timeline.inc(5); + ASSERT_EQ(selfMergeFence.getSignaledCount(), 1); +} + +TEST(FenceTest, WaitOnDestroyedTimeline) { + SyncTimeline timeline; + ASSERT_TRUE(timeline.isValid()); + + SyncFence fenceSig(timeline, 100); + SyncFence fenceKill(timeline, 200); + + // Spawn a thread to wait on a fence when the timeline is killed. + thread waitThread{ + [&]() { + ASSERT_EQ(timeline.inc(100), 0); + + ASSERT_EQ(fenceKill.wait(-1), -1); + ASSERT_EQ(errno, ENOENT); + } + }; + + // Wait for the thread to spool up. + fenceSig.wait(); + + // Kill the timeline. + timeline.destroy(); + + // wait for the thread to clean up. + waitThread.join(); +} + +TEST(FenceTest, PollOnDestroyedTimeline) { + SyncTimeline timeline; + ASSERT_TRUE(timeline.isValid()); + + SyncFence fenceSig(timeline, 100); + SyncFence fenceKill(timeline, 200); + + // Spawn a thread to wait on a fence when the timeline is killed. + thread waitThread{ + [&]() { + ASSERT_EQ(timeline.inc(100), 0); + + // Wait on the fd. + struct pollfd fds; + fds.fd = fenceKill.getFd(); + fds.events = POLLIN | POLLERR; + ASSERT_EQ(poll(&fds, 1, -1), 1); + ASSERT_TRUE(fds.revents & POLLERR); + } + }; + + // Wait for the thread to spool up. + fenceSig.wait(); + + // Kill the timeline. + timeline.destroy(); + + // wait for the thread to clean up. + waitThread.join(); +} + +TEST(FenceTest, MultiTimelineWait) { + SyncTimeline timelineA, timelineB, timelineC; + + SyncFence fenceA(timelineA, 5); + SyncFence fenceB(timelineB, 5); + SyncFence fenceC(timelineC, 5); + + // Make a larger fence using 3 other fences from different timelines. + SyncFence mergedFence({fenceA, fenceB, fenceC}); + ASSERT_TRUE(mergedFence.isValid()); + + // Confirm fence isn't signaled + ASSERT_EQ(mergedFence.getActiveCount(), 3); + ASSERT_EQ(mergedFence.wait(0), -1); + ASSERT_EQ(errno, ETIME); + + timelineA.inc(5); + ASSERT_EQ(mergedFence.getActiveCount(), 2); + ASSERT_EQ(mergedFence.getSignaledCount(), 1); + + timelineB.inc(5); + ASSERT_EQ(mergedFence.getActiveCount(), 1); + ASSERT_EQ(mergedFence.getSignaledCount(), 2); + + timelineC.inc(5); + ASSERT_EQ(mergedFence.getActiveCount(), 0); + ASSERT_EQ(mergedFence.getSignaledCount(), 3); + + // confirm you can successfully wait. + ASSERT_EQ(mergedFence.wait(100), 0); +} + +TEST(StressTest, TwoThreadsSharedTimeline) { + const int iterations = 1 << 16; + int counter = 0; + SyncTimeline timeline; + ASSERT_TRUE(timeline.isValid()); + + // Use a single timeline to synchronize two threads + // hammmering on the same counter. + auto threadMain = [&](int threadId) { + for (int i = 0; i < iterations; i++) { + SyncFence fence(timeline, i * 2 + threadId); + ASSERT_TRUE(fence.isValid()); + + // Wait on the prior thread to complete. + ASSERT_EQ(fence.wait(), 0); + + // Confirm the previous thread's writes are visible and then inc. + ASSERT_EQ(counter, i * 2 + threadId); + counter++; + + // Kick off the other thread. + ASSERT_EQ(timeline.inc(), 0); + } + }; + + thread a{threadMain, 0}; + thread b{threadMain, 1}; + a.join(); + b.join(); + + // make sure the threads did not trample on one another. + ASSERT_EQ(counter, iterations * 2); +} + +class ConsumerStressTest : public ::testing::TestWithParam<int> {}; + +TEST_P(ConsumerStressTest, MultiProducerSingleConsumer) { + mutex lock; + int counter = 0; + int iterations = 1 << 12; + + vector<SyncTimeline> producerTimelines(GetParam()); + vector<thread> threads; + SyncTimeline consumerTimeline; + + // Producer threads run this lambda. + auto threadMain = [&](int threadId) { + for (int i = 0; i < iterations; i++) { + SyncFence fence(consumerTimeline, i); + ASSERT_TRUE(fence.isValid()); + + // Wait for the consumer to finish. Use alternate + // means of waiting on the fence. + if ((iterations + threadId) % 8 != 0) { + ASSERT_EQ(fence.wait(), 0); + } + else { + while (fence.getSignaledCount() != 1) { + ASSERT_EQ(fence.getErrorCount(), 0); + } + } + + // Every producer increments the counter, the consumer checks + erases it. + lock.lock(); + counter++; + lock.unlock(); + + ASSERT_EQ(producerTimelines[threadId].inc(), 0); + } + }; + + for (int i = 0; i < GetParam(); i++) { + threads.push_back(thread{threadMain, i}); + } + + // Consumer thread runs this loop. + for (int i = 1; i <= iterations; i++) { + // Create a fence representing all producers final timelines. + vector<SyncFence> fences; + for (auto& timeline : producerTimelines) { + fences.push_back(SyncFence(timeline, i)); + } + SyncFence mergeFence(fences); + ASSERT_TRUE(mergeFence.isValid()); + + // Make sure we see an increment from every producer thread. Vary + // the means by which we wait. + if (iterations % 8 != 0) { + ASSERT_EQ(mergeFence.wait(), 0); + } + else { + while (mergeFence.getSignaledCount() != mergeFence.getSize()) { + ASSERT_EQ(mergeFence.getErrorCount(), 0); + } + } + ASSERT_EQ(counter, GetParam()*i); + + // Release the producer threads. + ASSERT_EQ(consumerTimeline.inc(), 0); + } + + for_each(begin(threads), end(threads), [](thread& thread) { thread.join(); }); +} +INSTANTIATE_TEST_CASE_P( + ParameterizedStressTest, + ConsumerStressTest, + ::testing::Values(2,4,16)); + +class MergeStressTest : public ::testing::TestWithParam<tuple<int, int>> {}; + +template <typename K, typename V> using dict = unordered_map<K,V>; + +TEST_P(MergeStressTest, RandomMerge) { + int timelineCount = get<0>(GetParam()); + int mergeCount = get<1>(GetParam()); + + vector<SyncTimeline> timelines(timelineCount); + + default_random_engine generator; + uniform_int_distribution<int> timelineDist(0, timelines.size()-1); + uniform_int_distribution<int> syncPointDist(0, numeric_limits<int>::max()); + + SyncFence fence(timelines[0], 0); + ASSERT_TRUE(fence.isValid()); + + unordered_map<int, int> fenceMap; + fenceMap.insert(make_tuple(0, 0)); + + // Randomly create syncpoints out of a fixed set of timelines, and merge them together. + for (int i = 0; i < mergeCount; i++) { + + // Generate syncpoint. + int timelineOffset = timelineDist(generator); + const SyncTimeline& timeline = timelines[timelineOffset]; + int syncPoint = syncPointDist(generator); + + // Keep track of the latest syncpoint in each timeline. + auto itr = fenceMap.find(timelineOffset); + if (itr == end(fenceMap)) { + fenceMap.insert(tie(timelineOffset, syncPoint)); + } + else { + int oldSyncPoint = itr->second; + fenceMap.erase(itr); + fenceMap.insert(tie(timelineOffset, max(syncPoint, oldSyncPoint))); + } + + // Merge. + fence = SyncFence(fence, SyncFence(timeline, syncPoint)); + ASSERT_TRUE(fence.isValid()); + } + + // Confirm our map matches the fence. + ASSERT_EQ(fence.getSize(), fenceMap.size()); + + // Trigger the merged fence. + for (auto& item: fenceMap) { + ASSERT_EQ(fence.wait(0), -1); + ASSERT_EQ(errno, ETIME); + + // Increment the timeline to the last syncpoint. + timelines[item.first].inc(item.second); + } + + // Check that the fence is triggered. + ASSERT_EQ(fence.wait(0), 0); +} + +INSTANTIATE_TEST_CASE_P( + ParameterizedMergeStressTest, + MergeStressTest, + ::testing::Combine(::testing::Values(16,32), ::testing::Values(32, 1024, 1024*32))); + +} + diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp index 1c9c70a..9d596ef 100644 --- a/libsysutils/src/NetlinkEvent.cpp +++ b/libsysutils/src/NetlinkEvent.cpp @@ -29,6 +29,8 @@ #include <net/if.h> #include <linux/if.h> +#include <linux/if_addr.h> +#include <linux/if_link.h> #include <linux/netfilter/nfnetlink.h> #include <linux/netfilter_ipv4/ipt_ULOG.h> /* From kernel's net/netfilter/xt_quota2.c */ @@ -46,6 +48,8 @@ const int NetlinkEvent::NlActionLinkDown = 5; const int NetlinkEvent::NlActionAddressUpdated = 6; const int NetlinkEvent::NlActionAddressRemoved = 7; const int NetlinkEvent::NlActionRdnss = 8; +const int NetlinkEvent::NlActionRouteUpdated = 9; +const int NetlinkEvent::NlActionRouteRemoved = 10; NetlinkEvent::NetlinkEvent() { mAction = NlActionUnknown; @@ -78,32 +82,109 @@ void NetlinkEvent::dump() { } /* - * Parse a RTM_NEWADDR or RTM_DELADDR message. + * Returns the message name for a message in the NETLINK_ROUTE family, or NULL + * if parsing that message is not supported. + */ +static const char *rtMessageName(int type) { +#define NL_EVENT_RTM_NAME(rtm) case rtm: return #rtm; + switch (type) { + NL_EVENT_RTM_NAME(RTM_NEWLINK); + NL_EVENT_RTM_NAME(RTM_DELLINK); + NL_EVENT_RTM_NAME(RTM_NEWADDR); + NL_EVENT_RTM_NAME(RTM_DELADDR); + NL_EVENT_RTM_NAME(RTM_NEWROUTE); + NL_EVENT_RTM_NAME(RTM_DELROUTE); + NL_EVENT_RTM_NAME(RTM_NEWNDUSEROPT); + NL_EVENT_RTM_NAME(QLOG_NL_EVENT); + default: + return NULL; + } +#undef NL_EVENT_RTM_NAME +} + +/* + * Checks that a binary NETLINK_ROUTE message is long enough for a payload of + * size bytes. */ -bool NetlinkEvent::parseIfAddrMessage(int type, struct ifaddrmsg *ifaddr, - int rtasize) { +static bool checkRtNetlinkLength(const struct nlmsghdr *nh, size_t size) { + if (nh->nlmsg_len < NLMSG_LENGTH(size)) { + SLOGE("Got a short %s message\n", rtMessageName(nh->nlmsg_type)); + return false; + } + return true; +} + +/* + * Utility function to log errors. + */ +static bool maybeLogDuplicateAttribute(bool isDup, + const char *attributeName, + const char *messageName) { + if (isDup) { + SLOGE("Multiple %s attributes in %s, ignoring\n", attributeName, messageName); + return true; + } + return false; +} + +/* + * Parse a RTM_NEWLINK message. + */ +bool NetlinkEvent::parseIfInfoMessage(const struct nlmsghdr *nh) { + struct ifinfomsg *ifi = (struct ifinfomsg *) NLMSG_DATA(nh); + if (!checkRtNetlinkLength(nh, sizeof(*ifi))) + return false; + + if ((ifi->ifi_flags & IFF_LOOPBACK) != 0) { + return false; + } + + int len = IFLA_PAYLOAD(nh); struct rtattr *rta; + for (rta = IFLA_RTA(ifi); RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) { + switch(rta->rta_type) { + case IFLA_IFNAME: + asprintf(&mParams[0], "INTERFACE=%s", (char *) RTA_DATA(rta)); + mAction = (ifi->ifi_flags & IFF_LOWER_UP) ? NlActionLinkUp : + NlActionLinkDown; + mSubsystem = strdup("net"); + return true; + } + } + + return false; +} + +/* + * Parse a RTM_NEWADDR or RTM_DELADDR message. + */ +bool NetlinkEvent::parseIfAddrMessage(const struct nlmsghdr *nh) { + struct ifaddrmsg *ifaddr = (struct ifaddrmsg *) NLMSG_DATA(nh); struct ifa_cacheinfo *cacheinfo = NULL; char addrstr[INET6_ADDRSTRLEN] = ""; + char ifname[IFNAMSIZ]; + + if (!checkRtNetlinkLength(nh, sizeof(*ifaddr))) + return false; // Sanity check. + int type = nh->nlmsg_type; if (type != RTM_NEWADDR && type != RTM_DELADDR) { SLOGE("parseIfAddrMessage on incorrect message type 0x%x\n", type); return false; } // For log messages. - const char *msgtype = (type == RTM_NEWADDR) ? "RTM_NEWADDR" : "RTM_DELADDR"; + const char *msgtype = rtMessageName(type); - for (rta = IFA_RTA(ifaddr); RTA_OK(rta, rtasize); - rta = RTA_NEXT(rta, rtasize)) { + struct rtattr *rta; + int len = IFA_PAYLOAD(nh); + for (rta = IFA_RTA(ifaddr); RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) { if (rta->rta_type == IFA_ADDRESS) { // Only look at the first address, because we only support notifying // one change at a time. - if (*addrstr != '\0') { - SLOGE("Multiple IFA_ADDRESSes in %s, ignoring\n", msgtype); + if (maybeLogDuplicateAttribute(*addrstr != '\0', "IFA_ADDRESS", msgtype)) continue; - } // Convert the IP address to a string. if (ifaddr->ifa_family == AF_INET) { @@ -128,28 +209,15 @@ bool NetlinkEvent::parseIfAddrMessage(int type, struct ifaddrmsg *ifaddr, } // Find the interface name. - char ifname[IFNAMSIZ + 1]; if (!if_indextoname(ifaddr->ifa_index, ifname)) { SLOGE("Unknown ifindex %d in %s", ifaddr->ifa_index, msgtype); return false; } - // Fill in interface information. - mAction = (type == RTM_NEWADDR) ? NlActionAddressUpdated : - NlActionAddressRemoved; - mSubsystem = strdup("net"); - asprintf(&mParams[0], "ADDRESS=%s/%d", addrstr, - ifaddr->ifa_prefixlen); - asprintf(&mParams[1], "INTERFACE=%s", ifname); - asprintf(&mParams[2], "FLAGS=%u", ifaddr->ifa_flags); - asprintf(&mParams[3], "SCOPE=%u", ifaddr->ifa_scope); } else if (rta->rta_type == IFA_CACHEINFO) { // Address lifetime information. - if (cacheinfo) { - // We only support one address. - SLOGE("Multiple IFA_CACHEINFOs in %s, ignoring\n", msgtype); + if (maybeLogDuplicateAttribute(cacheinfo, "IFA_CACHEINFO", msgtype)) continue; - } if (RTA_PAYLOAD(rta) < sizeof(*cacheinfo)) { SLOGE("Short IFA_CACHEINFO (%zu vs. %zu bytes) in %s", @@ -158,10 +226,6 @@ bool NetlinkEvent::parseIfAddrMessage(int type, struct ifaddrmsg *ifaddr, } cacheinfo = (struct ifa_cacheinfo *) RTA_DATA(rta); - asprintf(&mParams[4], "PREFERRED=%u", cacheinfo->ifa_prefered); - asprintf(&mParams[5], "VALID=%u", cacheinfo->ifa_valid); - asprintf(&mParams[6], "CSTAMP=%u", cacheinfo->cstamp); - asprintf(&mParams[7], "TSTAMP=%u", cacheinfo->tstamp); } } @@ -170,14 +234,145 @@ bool NetlinkEvent::parseIfAddrMessage(int type, struct ifaddrmsg *ifaddr, return false; } + // Fill in netlink event information. + mAction = (type == RTM_NEWADDR) ? NlActionAddressUpdated : + NlActionAddressRemoved; + mSubsystem = strdup("net"); + asprintf(&mParams[0], "ADDRESS=%s/%d", addrstr, + ifaddr->ifa_prefixlen); + asprintf(&mParams[1], "INTERFACE=%s", ifname); + asprintf(&mParams[2], "FLAGS=%u", ifaddr->ifa_flags); + asprintf(&mParams[3], "SCOPE=%u", ifaddr->ifa_scope); + + if (cacheinfo) { + asprintf(&mParams[4], "PREFERRED=%u", cacheinfo->ifa_prefered); + asprintf(&mParams[5], "VALID=%u", cacheinfo->ifa_valid); + asprintf(&mParams[6], "CSTAMP=%u", cacheinfo->cstamp); + asprintf(&mParams[7], "TSTAMP=%u", cacheinfo->tstamp); + } + + return true; +} + +/* + * Parse a QLOG_NL_EVENT message. + */ +bool NetlinkEvent::parseUlogPacketMessage(const struct nlmsghdr *nh) { + const char *devname; + ulog_packet_msg_t *pm = (ulog_packet_msg_t *) NLMSG_DATA(nh); + if (!checkRtNetlinkLength(nh, sizeof(*pm))) + return false; + + devname = pm->indev_name[0] ? pm->indev_name : pm->outdev_name; + asprintf(&mParams[0], "ALERT_NAME=%s", pm->prefix); + asprintf(&mParams[1], "INTERFACE=%s", devname); + mSubsystem = strdup("qlog"); + mAction = NlActionChange; + return true; +} + +/* + * Parse a RTM_NEWROUTE or RTM_DELROUTE message. + */ +bool NetlinkEvent::parseRtMessage(const struct nlmsghdr *nh) { + uint8_t type = nh->nlmsg_type; + const char *msgname = rtMessageName(type); + + // Sanity check. + if (type != RTM_NEWROUTE && type != RTM_DELROUTE) { + SLOGE("%s: incorrect message type %d (%s)\n", __func__, type, msgname); + return false; + } + + struct rtmsg *rtm = (struct rtmsg *) NLMSG_DATA(nh); + if (!checkRtNetlinkLength(nh, sizeof(*rtm))) + return false; + + if (// Ignore static routes we've set up ourselves. + (rtm->rtm_protocol != RTPROT_KERNEL && + rtm->rtm_protocol != RTPROT_RA) || + // We're only interested in global unicast routes. + (rtm->rtm_scope != RT_SCOPE_UNIVERSE) || + (rtm->rtm_type != RTN_UNICAST) || + // We don't support source routing. + (rtm->rtm_src_len != 0) || + // Cloned routes aren't real routes. + (rtm->rtm_flags & RTM_F_CLONED)) { + return false; + } + + int family = rtm->rtm_family; + int prefixLength = rtm->rtm_dst_len; + + // Currently we only support: destination, (one) next hop, ifindex. + char dst[INET6_ADDRSTRLEN] = ""; + char gw[INET6_ADDRSTRLEN] = ""; + char dev[IFNAMSIZ] = ""; + + size_t len = RTM_PAYLOAD(nh); + struct rtattr *rta; + for (rta = RTM_RTA(rtm); RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) { + switch (rta->rta_type) { + case RTA_DST: + if (maybeLogDuplicateAttribute(*dst, "RTA_DST", msgname)) + continue; + if (!inet_ntop(family, RTA_DATA(rta), dst, sizeof(dst))) + return false; + continue; + case RTA_GATEWAY: + if (maybeLogDuplicateAttribute(*gw, "RTA_GATEWAY", msgname)) + continue; + if (!inet_ntop(family, RTA_DATA(rta), gw, sizeof(gw))) + return false; + continue; + case RTA_OIF: + if (maybeLogDuplicateAttribute(*dev, "RTA_OIF", msgname)) + continue; + if (!if_indextoname(* (int *) RTA_DATA(rta), dev)) + return false; + default: + continue; + } + } + + // If there's no RTA_DST attribute, then: + // - If the prefix length is zero, it's the default route. + // - If the prefix length is nonzero, there's something we don't understand. + // Ignore the event. + if (!*dst && !prefixLength) { + if (family == AF_INET) { + strncpy(dst, "0.0.0.0", sizeof(dst)); + } else if (family == AF_INET6) { + strncpy(dst, "::", sizeof(dst)); + } + } + + // A useful route must have a destination and at least either a gateway or + // an interface. + if (!*dst || (!*gw && !*dev)) + return false; + + // Fill in netlink event information. + mAction = (type == RTM_NEWROUTE) ? NlActionRouteUpdated : + NlActionRouteRemoved; + mSubsystem = strdup("net"); + asprintf(&mParams[0], "ROUTE=%s/%d", dst, prefixLength); + asprintf(&mParams[1], "GATEWAY=%s", (*gw) ? gw : ""); + asprintf(&mParams[2], "INTERFACE=%s", (*dev) ? dev : ""); + return true; } /* * Parse a RTM_NEWNDUSEROPT message. */ -bool NetlinkEvent::parseNdUserOptMessage(struct nduseroptmsg *msg, int len) { +bool NetlinkEvent::parseNdUserOptMessage(const struct nlmsghdr *nh) { + struct nduseroptmsg *msg = (struct nduseroptmsg *) NLMSG_DATA(nh); + if (!checkRtNetlinkLength(nh, sizeof(*msg))) + return false; + // Check the length is valid. + int len = NLMSG_PAYLOAD(nh, sizeof(*msg)); if (msg->nduseropt_opts_len > len) { SLOGE("RTM_NEWNDUSEROPT invalid length %d > %d\n", msg->nduseropt_opts_len, len); @@ -200,7 +395,7 @@ bool NetlinkEvent::parseNdUserOptMessage(struct nduseroptmsg *msg, int len) { } // Find the interface name. - char ifname[IFNAMSIZ + 1]; + char ifname[IFNAMSIZ]; if (!if_indextoname(msg->nduseropt_ifindex, ifname)) { SLOGE("RTM_NEWNDUSEROPT on unknown ifindex %d\n", msg->nduseropt_ifindex); @@ -273,6 +468,14 @@ bool NetlinkEvent::parseNdUserOptMessage(struct nduseroptmsg *msg, int len) { /* * Parse a binary message from a NETLINK_ROUTE netlink socket. + * + * Note that this function can only parse one message, because the message's + * content has to be stored in the class's member variables (mAction, + * mSubsystem, etc.). Invalid or unrecognized messages are skipped, but if + * there are multiple valid messages in the buffer, only the first one will be + * returned. + * + * TODO: consider only ever looking at the first message. */ bool NetlinkEvent::parseBinaryNetlinkMessage(char *buffer, int size) { const struct nlmsghdr *nh; @@ -281,93 +484,37 @@ bool NetlinkEvent::parseBinaryNetlinkMessage(char *buffer, int size) { NLMSG_OK(nh, (unsigned) size) && (nh->nlmsg_type != NLMSG_DONE); nh = NLMSG_NEXT(nh, size)) { - if (nh->nlmsg_type == RTM_NEWLINK) { - int len = nh->nlmsg_len - sizeof(*nh); - struct ifinfomsg *ifi; - - if (sizeof(*ifi) > (size_t) len) { - SLOGE("Got a short RTM_NEWLINK message\n"); - continue; - } - - ifi = (ifinfomsg *)NLMSG_DATA(nh); - if ((ifi->ifi_flags & IFF_LOOPBACK) != 0) { - continue; - } - - struct rtattr *rta = (struct rtattr *) - ((char *) ifi + NLMSG_ALIGN(sizeof(*ifi))); - len = NLMSG_PAYLOAD(nh, sizeof(*ifi)); - - while(RTA_OK(rta, len)) { - switch(rta->rta_type) { - case IFLA_IFNAME: - char buffer[16 + IFNAMSIZ]; - snprintf(buffer, sizeof(buffer), "INTERFACE=%s", - (char *) RTA_DATA(rta)); - mParams[0] = strdup(buffer); - mAction = (ifi->ifi_flags & IFF_LOWER_UP) ? - NlActionLinkUp : NlActionLinkDown; - mSubsystem = strdup("net"); - break; - } + if (!rtMessageName(nh->nlmsg_type)) { + SLOGD("Unexpected netlink message type %d\n", nh->nlmsg_type); + continue; + } - rta = RTA_NEXT(rta, len); - } + if (nh->nlmsg_type == RTM_NEWLINK) { + if (parseIfInfoMessage(nh)) + return true; } else if (nh->nlmsg_type == QLOG_NL_EVENT) { - char *devname; - ulog_packet_msg_t *pm; - size_t len = nh->nlmsg_len - sizeof(*nh); - if (sizeof(*pm) > len) { - SLOGE("Got a short QLOG message\n"); - continue; - } - pm = (ulog_packet_msg_t *)NLMSG_DATA(nh); - devname = pm->indev_name[0] ? pm->indev_name : pm->outdev_name; - asprintf(&mParams[0], "ALERT_NAME=%s", pm->prefix); - asprintf(&mParams[1], "INTERFACE=%s", devname); - mSubsystem = strdup("qlog"); - mAction = NlActionChange; + if (parseUlogPacketMessage(nh)) + return true; } else if (nh->nlmsg_type == RTM_NEWADDR || nh->nlmsg_type == RTM_DELADDR) { - int len = nh->nlmsg_len - sizeof(*nh); - struct ifaddrmsg *ifa; + if (parseIfAddrMessage(nh)) + return true; - if (sizeof(*ifa) > (size_t) len) { - SLOGE("Got a short RTM_xxxADDR message\n"); - continue; - } - - ifa = (ifaddrmsg *)NLMSG_DATA(nh); - size_t rtasize = IFA_PAYLOAD(nh); - if (!parseIfAddrMessage(nh->nlmsg_type, ifa, rtasize)) { - continue; - } + } else if (nh->nlmsg_type == RTM_NEWROUTE || + nh->nlmsg_type == RTM_DELROUTE) { + if (parseRtMessage(nh)) + return true; } else if (nh->nlmsg_type == RTM_NEWNDUSEROPT) { - int len = nh->nlmsg_len - sizeof(*nh); - struct nduseroptmsg *ndmsg = (struct nduseroptmsg *) NLMSG_DATA(nh); - - if (sizeof(*ndmsg) > (size_t) len) { - SLOGE("Got a short RTM_NEWNDUSEROPT message\n"); - continue; - } - - size_t optsize = NLMSG_PAYLOAD(nh, sizeof(*ndmsg)); - if (!parseNdUserOptMessage(ndmsg, optsize)) { - continue; - } + if (parseNdUserOptMessage(nh)) + return true; - - } else { - SLOGD("Unexpected netlink message. type=0x%x\n", - nh->nlmsg_type); } } - return true; + return false; } /* If the string between 'str' and 'end' begins with 'prefixlen' characters diff --git a/libsysutils/src/NetlinkListener.cpp b/libsysutils/src/NetlinkListener.cpp index 9c447ca..81c5cc2 100644 --- a/libsysutils/src/NetlinkListener.cpp +++ b/libsysutils/src/NetlinkListener.cpp @@ -57,10 +57,12 @@ bool NetlinkListener::onDataAvailable(SocketClient *cli) } NetlinkEvent *evt = new NetlinkEvent(); - if (!evt->decode(mBuffer, count, mFormat)) { - SLOGE("Error decoding NetlinkEvent"); - } else { + if (evt->decode(mBuffer, count, mFormat)) { onEvent(evt); + } else if (mFormat != NETLINK_FORMAT_BINARY) { + // Don't complain if parseBinaryNetlinkMessage returns false. That can + // just mean that the buffer contained no messages we're interested in. + SLOGE("Error decoding NetlinkEvent"); } delete evt; diff --git a/libusbhost/usbhost.c b/libusbhost/usbhost.c index cd8000a..488dd0e 100644 --- a/libusbhost/usbhost.c +++ b/libusbhost/usbhost.c @@ -454,6 +454,8 @@ char* usb_device_get_string(struct usb_device *device, int id) int i, result; int languageCount = 0; + if (id == 0) return NULL; + string[0] = 0; memset(languages, 0, sizeof(languages)); @@ -487,31 +489,19 @@ char* usb_device_get_string(struct usb_device *device, int id) char* usb_device_get_manufacturer_name(struct usb_device *device) { struct usb_device_descriptor *desc = (struct usb_device_descriptor *)device->desc; - - if (desc->iManufacturer) - return usb_device_get_string(device, desc->iManufacturer); - else - return NULL; + return usb_device_get_string(device, desc->iManufacturer); } char* usb_device_get_product_name(struct usb_device *device) { struct usb_device_descriptor *desc = (struct usb_device_descriptor *)device->desc; - - if (desc->iProduct) - return usb_device_get_string(device, desc->iProduct); - else - return NULL; + return usb_device_get_string(device, desc->iProduct); } char* usb_device_get_serial(struct usb_device *device) { struct usb_device_descriptor *desc = (struct usb_device_descriptor *)device->desc; - - if (desc->iSerialNumber) - return usb_device_get_string(device, desc->iSerialNumber); - else - return NULL; + return usb_device_get_string(device, desc->iSerialNumber); } int usb_device_is_writeable(struct usb_device *device) @@ -557,6 +547,21 @@ int usb_device_connect_kernel_driver(struct usb_device *device, return ioctl(device->fd, USBDEVFS_IOCTL, &ctl); } +int usb_device_set_configuration(struct usb_device *device, int configuration) +{ + return ioctl(device->fd, USBDEVFS_SETCONFIGURATION, &configuration); +} + +int usb_device_set_interface(struct usb_device *device, unsigned int interface, + unsigned int alt_setting) +{ + struct usbdevfs_setinterface ctl; + + ctl.interface = interface; + ctl.altsetting = alt_setting; + return ioctl(device->fd, USBDEVFS_SETINTERFACE, &ctl); +} + int usb_device_control_transfer(struct usb_device *device, int requestType, int request, diff --git a/libutils/Android.mk b/libutils/Android.mk index 2d77ef2..dc3668b 100644 --- a/libutils/Android.mk +++ b/libutils/Android.mk @@ -26,6 +26,7 @@ commonSources:= \ LinearAllocator.cpp \ LinearTransform.cpp \ Log.cpp \ + NativeHandle.cpp \ Printer.cpp \ ProcessCallStack.cpp \ PropertyMap.cpp \ diff --git a/libutils/NativeHandle.cpp b/libutils/NativeHandle.cpp new file mode 100644 index 0000000..e4daca7 --- /dev/null +++ b/libutils/NativeHandle.cpp @@ -0,0 +1,38 @@ +/* + * Copyright 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <utils/NativeHandle.h> +#include <cutils/native_handle.h> + +namespace android { + +sp<NativeHandle> NativeHandle::create( + native_handle_t* handle, bool ownsHandle) { + return handle ? new NativeHandle(handle, ownsHandle) : NULL; +} + +NativeHandle::NativeHandle(native_handle_t* handle, bool ownsHandle) +: mHandle(handle), mOwnsHandle(ownsHandle) +{} + +NativeHandle::~NativeHandle() { + if (mOwnsHandle) { + native_handle_close(mHandle); + native_handle_delete(mHandle); + } +} + +} // namespace android diff --git a/libutils/String8.cpp b/libutils/String8.cpp index 49340bb..9092cbc 100644 --- a/libutils/String8.cpp +++ b/libutils/String8.cpp @@ -408,6 +408,30 @@ ssize_t String8::find(const char* other, size_t start) const return p ? p-mString : -1; } +bool String8::removeAll(const char* other) { + ssize_t index = find(other); + if (index < 0) return false; + + char* buf = lockBuffer(size()); + if (!buf) return false; // out of memory + + size_t skip = strlen(other); + size_t len = size(); + size_t tail = index; + while (size_t(index) < len) { + ssize_t next = find(other, index + skip); + if (next < 0) { + next = len; + } + + memcpy(buf + tail, buf + index + skip, next - index - skip); + tail += next - index - skip; + index = next; + } + unlockBuffer(tail); + return true; +} + void String8::toLower() { toLower(0, size()); diff --git a/libutils/Unicode.cpp b/libutils/Unicode.cpp index fe8887d..378d2a7 100644 --- a/libutils/Unicode.cpp +++ b/libutils/Unicode.cpp @@ -342,7 +342,8 @@ void utf16_to_utf8(const char16_t* src, size_t src_len, char* dst) while (cur_utf16 < end_utf16) { char32_t utf32; // surrogate pairs - if ((*cur_utf16 & 0xFC00) == 0xD800) { + if((*cur_utf16 & 0xFC00) == 0xD800 && (cur_utf16 + 1) < end_utf16 + && (*(cur_utf16 + 1) & 0xFC00) == 0xDC00) { utf32 = (*cur_utf16++ - 0xD800) << 10; utf32 |= *cur_utf16++ - 0xDC00; utf32 += 0x10000; diff --git a/libutils/tests/BitSet_test.cpp b/libutils/tests/BitSet_test.cpp index 752e56d..38b668a 100644 --- a/libutils/tests/BitSet_test.cpp +++ b/libutils/tests/BitSet_test.cpp @@ -23,7 +23,7 @@ namespace android { -class BitSetTest : public testing::Test { +class BitSet32Test : public testing::Test { protected: BitSet32 b1; BitSet32 b2; @@ -34,7 +34,7 @@ protected: }; -TEST_F(BitSetTest, BitWiseOr) { +TEST_F(BitSet32Test, BitWiseOr) { b1.markBit(2); b2.markBit(4); @@ -49,7 +49,7 @@ TEST_F(BitSetTest, BitWiseOr) { EXPECT_TRUE(b1.hasBit(2) && b1.hasBit(4)); EXPECT_TRUE(b2.hasBit(4) && b2.count() == 1u); } -TEST_F(BitSetTest, BitWiseAnd_Disjoint) { +TEST_F(BitSet32Test, BitWiseAnd_Disjoint) { b1.markBit(2); b1.markBit(4); b1.markBit(6); @@ -65,7 +65,7 @@ TEST_F(BitSetTest, BitWiseAnd_Disjoint) { EXPECT_TRUE(b1.hasBit(2) && b1.hasBit(4) && b1.hasBit(6)); } -TEST_F(BitSetTest, BitWiseAnd_NonDisjoint) { +TEST_F(BitSet32Test, BitWiseAnd_NonDisjoint) { b1.markBit(2); b1.markBit(4); b1.markBit(6); @@ -84,4 +84,187 @@ TEST_F(BitSetTest, BitWiseAnd_NonDisjoint) { EXPECT_EQ(b2.count(), 3u); EXPECT_TRUE(b2.hasBit(3) && b2.hasBit(6) && b2.hasBit(9)); } + +TEST_F(BitSet32Test, MarkFirstUnmarkedBit) { + b1.markBit(1); + + b1.markFirstUnmarkedBit(); + EXPECT_EQ(b1.count(), 2u); + EXPECT_TRUE(b1.hasBit(0) && b1.hasBit(1)); + + b1.markFirstUnmarkedBit(); + EXPECT_EQ(b1.count(), 3u); + EXPECT_TRUE(b1.hasBit(0) && b1.hasBit(1) && b1.hasBit(2)); +} + +TEST_F(BitSet32Test, ClearFirstMarkedBit) { + b1.markBit(0); + b1.markBit(10); + + b1.clearFirstMarkedBit(); + EXPECT_EQ(b1.count(), 1u); + EXPECT_TRUE(b1.hasBit(10)); + + b1.markBit(30); + b1.clearFirstMarkedBit(); + EXPECT_EQ(b1.count(), 1u); + EXPECT_TRUE(b1.hasBit(30)); +} + +TEST_F(BitSet32Test, ClearLastMarkedBit) { + b1.markBit(10); + b1.markBit(31); + + b1.clearLastMarkedBit(); + EXPECT_EQ(b1.count(), 1u); + EXPECT_TRUE(b1.hasBit(10)); + + b1.markBit(5); + b1.clearLastMarkedBit(); + EXPECT_EQ(b1.count(), 1u); + EXPECT_TRUE(b1.hasBit(5)); +} + +TEST_F(BitSet32Test, FillAndClear) { + EXPECT_TRUE(b1.isEmpty()); + for (size_t i = 0; i < 32; i++) { + b1.markFirstUnmarkedBit(); + } + EXPECT_TRUE(b1.isFull()); + b1.clear(); + EXPECT_TRUE(b1.isEmpty()); +} + +TEST_F(BitSet32Test, GetIndexOfBit) { + b1.markBit(1); + b1.markBit(4); + EXPECT_EQ(b1.getIndexOfBit(1), 0); + EXPECT_EQ(b1.getIndexOfBit(4), 1); + b1.markFirstUnmarkedBit(); + EXPECT_EQ(b1.getIndexOfBit(1), 1); + EXPECT_EQ(b1.getIndexOfBit(4), 2); +} + +class BitSet64Test : public testing::Test { +protected: + BitSet64 b1; + BitSet64 b2; + virtual void TearDown() { + b1.clear(); + b2.clear(); + } +}; + + +TEST_F(BitSet64Test, BitWiseOr) { + b1.markBit(20); + b2.markBit(40); + + BitSet64 tmp = b1 | b2; + EXPECT_EQ(tmp.count(), 2u); + EXPECT_TRUE(tmp.hasBit(20) && tmp.hasBit(40)); + // Check that the operator is symmetric + EXPECT_TRUE((b2 | b1) == (b1 | b2)); + + b1 |= b2; + EXPECT_EQ(b1.count(), 2u); + EXPECT_TRUE(b1.hasBit(20) && b1.hasBit(40)); + EXPECT_TRUE(b2.hasBit(40) && b2.count() == 1u); +} +TEST_F(BitSet64Test, BitWiseAnd_Disjoint) { + b1.markBit(20); + b1.markBit(40); + b1.markBit(60); + + BitSet64 tmp = b1 & b2; + EXPECT_TRUE(tmp.isEmpty()); + // Check that the operator is symmetric + EXPECT_TRUE((b2 & b1) == (b1 & b2)); + + b2 &= b1; + EXPECT_TRUE(b2.isEmpty()); + EXPECT_EQ(b1.count(), 3u); + EXPECT_TRUE(b1.hasBit(20) && b1.hasBit(40) && b1.hasBit(60)); +} + +TEST_F(BitSet64Test, BitWiseAnd_NonDisjoint) { + b1.markBit(20); + b1.markBit(40); + b1.markBit(60); + b2.markBit(30); + b2.markBit(60); + b2.markBit(63); + + BitSet64 tmp = b1 & b2; + EXPECT_EQ(tmp.count(), 1u); + EXPECT_TRUE(tmp.hasBit(60)); + // Check that the operator is symmetric + EXPECT_TRUE((b2 & b1) == (b1 & b2)); + + b1 &= b2; + EXPECT_EQ(b1.count(), 1u); + EXPECT_EQ(b2.count(), 3u); + EXPECT_TRUE(b2.hasBit(30) && b2.hasBit(60) && b2.hasBit(63)); +} + +TEST_F(BitSet64Test, MarkFirstUnmarkedBit) { + b1.markBit(1); + + b1.markFirstUnmarkedBit(); + EXPECT_EQ(b1.count(), 2u); + EXPECT_TRUE(b1.hasBit(0) && b1.hasBit(1)); + + b1.markFirstUnmarkedBit(); + EXPECT_EQ(b1.count(), 3u); + EXPECT_TRUE(b1.hasBit(0) && b1.hasBit(1) && b1.hasBit(2)); +} + +TEST_F(BitSet64Test, ClearFirstMarkedBit) { + b1.markBit(0); + b1.markBit(10); + + b1.clearFirstMarkedBit(); + EXPECT_EQ(b1.count(), 1u); + EXPECT_TRUE(b1.hasBit(10)); + + b1.markBit(50); + b1.clearFirstMarkedBit(); + EXPECT_EQ(b1.count(), 1u); + EXPECT_TRUE(b1.hasBit(50)); +} + +TEST_F(BitSet64Test, ClearLastMarkedBit) { + b1.markBit(10); + b1.markBit(63); + + b1.clearLastMarkedBit(); + EXPECT_EQ(b1.count(), 1u); + EXPECT_TRUE(b1.hasBit(10)); + + b1.markBit(5); + b1.clearLastMarkedBit(); + EXPECT_EQ(b1.count(), 1u); + EXPECT_TRUE(b1.hasBit(5)); +} + +TEST_F(BitSet64Test, FillAndClear) { + EXPECT_TRUE(b1.isEmpty()); + for (size_t i = 0; i < 64; i++) { + b1.markFirstUnmarkedBit(); + } + EXPECT_TRUE(b1.isFull()); + b1.clear(); + EXPECT_TRUE(b1.isEmpty()); +} + +TEST_F(BitSet64Test, GetIndexOfBit) { + b1.markBit(10); + b1.markBit(40); + EXPECT_EQ(b1.getIndexOfBit(10), 0); + EXPECT_EQ(b1.getIndexOfBit(40), 1); + b1.markFirstUnmarkedBit(); + EXPECT_EQ(b1.getIndexOfBit(10), 1); + EXPECT_EQ(b1.getIndexOfBit(40), 2); +} + } // namespace android diff --git a/lmkd/Android.mk b/lmkd/Android.mk new file mode 100644 index 0000000..39081d6 --- /dev/null +++ b/lmkd/Android.mk @@ -0,0 +1,10 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := lmkd.c +LOCAL_SHARED_LIBRARIES := liblog libm libc libprocessgroup +LOCAL_CFLAGS := -Werror + +LOCAL_MODULE := lmkd + +include $(BUILD_EXECUTABLE) diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c new file mode 100644 index 0000000..a534a24 --- /dev/null +++ b/lmkd/lmkd.c @@ -0,0 +1,833 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "lowmemorykiller" + +#include <arpa/inet.h> +#include <errno.h> +#include <signal.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <sys/cdefs.h> +#include <sys/epoll.h> +#include <sys/eventfd.h> +#include <sys/mman.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <unistd.h> + +#include <cutils/sockets.h> +#include <log/log.h> +#include <processgroup/processgroup.h> + +#ifndef __unused +#define __unused __attribute__((__unused__)) +#endif + +#define MEMCG_SYSFS_PATH "/dev/memcg/" +#define MEMPRESSURE_WATCH_LEVEL "medium" +#define ZONEINFO_PATH "/proc/zoneinfo" +#define LINE_MAX 128 + +#define INKERNEL_MINFREE_PATH "/sys/module/lowmemorykiller/parameters/minfree" +#define INKERNEL_ADJ_PATH "/sys/module/lowmemorykiller/parameters/adj" + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x))) + +enum lmk_cmd { + LMK_TARGET, + LMK_PROCPRIO, + LMK_PROCREMOVE, +}; + +#define MAX_TARGETS 6 +/* + * longest is LMK_TARGET followed by MAX_TARGETS each minfree and minkillprio + * values + */ +#define CTRL_PACKET_MAX (sizeof(int) * (MAX_TARGETS * 2 + 1)) + +/* default to old in-kernel interface if no memory pressure events */ +static int use_inkernel_interface = 1; + +/* memory pressure level medium event */ +static int mpevfd; + +/* control socket listen and data */ +static int ctrl_lfd; +static int ctrl_dfd = -1; +static int ctrl_dfd_reopened; /* did we reopen ctrl conn on this loop? */ + +/* 1 memory pressure level, 1 ctrl listen socket, 1 ctrl data socket */ +#define MAX_EPOLL_EVENTS 3 +static int epollfd; +static int maxevents; + +#define OOM_DISABLE (-17) +/* inclusive */ +#define OOM_ADJUST_MIN (-16) +#define OOM_ADJUST_MAX 15 + +/* kernel OOM score values */ +#define OOM_SCORE_ADJ_MIN (-1000) +#define OOM_SCORE_ADJ_MAX 1000 + +static int lowmem_adj[MAX_TARGETS]; +static int lowmem_minfree[MAX_TARGETS]; +static int lowmem_targets_size; + +struct sysmeminfo { + int nr_free_pages; + int nr_file_pages; + int nr_shmem; + int totalreserve_pages; +}; + +struct adjslot_list { + struct adjslot_list *next; + struct adjslot_list *prev; +}; + +struct proc { + struct adjslot_list asl; + int pid; + uid_t uid; + int oomadj; + struct proc *pidhash_next; +}; + +#define PIDHASH_SZ 1024 +static struct proc *pidhash[PIDHASH_SZ]; +#define pid_hashfn(x) ((((x) >> 8) ^ (x)) & (PIDHASH_SZ - 1)) + +#define ADJTOSLOT(adj) (adj + -OOM_ADJUST_MIN) +static struct adjslot_list procadjslot_list[ADJTOSLOT(OOM_ADJUST_MAX) + 1]; + +/* + * Wait 1-2 seconds for the death report of a killed process prior to + * considering killing more processes. + */ +#define KILL_TIMEOUT 2 +/* Time of last process kill we initiated, stop me before I kill again */ +static time_t kill_lasttime; + +/* PAGE_SIZE / 1024 */ +static long page_k; + +static ssize_t read_all(int fd, char *buf, size_t max_len) +{ + ssize_t ret = 0; + + while (max_len > 0) { + ssize_t r = read(fd, buf, max_len); + if (r == 0) { + break; + } + if (r == -1) { + return -1; + } + ret += r; + buf += r; + max_len -= r; + } + + return ret; +} + +static int lowmem_oom_adj_to_oom_score_adj(int oom_adj) +{ + if (oom_adj == OOM_ADJUST_MAX) + return OOM_SCORE_ADJ_MAX; + else + return (oom_adj * OOM_SCORE_ADJ_MAX) / -OOM_DISABLE; +} + +static struct proc *pid_lookup(int pid) { + struct proc *procp; + + for (procp = pidhash[pid_hashfn(pid)]; procp && procp->pid != pid; + procp = procp->pidhash_next) + ; + + return procp; +} + +static void adjslot_insert(struct adjslot_list *head, struct adjslot_list *new) +{ + struct adjslot_list *next = head->next; + new->prev = head; + new->next = next; + next->prev = new; + head->next = new; +} + +static void adjslot_remove(struct adjslot_list *old) +{ + struct adjslot_list *prev = old->prev; + struct adjslot_list *next = old->next; + next->prev = prev; + prev->next = next; +} + +static struct adjslot_list *adjslot_tail(struct adjslot_list *head) { + struct adjslot_list *asl = head->prev; + + return asl == head ? NULL : asl; +} + +static void proc_slot(struct proc *procp) { + int adjslot = ADJTOSLOT(procp->oomadj); + + adjslot_insert(&procadjslot_list[adjslot], &procp->asl); +} + +static void proc_unslot(struct proc *procp) { + adjslot_remove(&procp->asl); +} + +static void proc_insert(struct proc *procp) { + int hval = pid_hashfn(procp->pid); + + procp->pidhash_next = pidhash[hval]; + pidhash[hval] = procp; + proc_slot(procp); +} + +static int pid_remove(int pid) { + int hval = pid_hashfn(pid); + struct proc *procp; + struct proc *prevp; + + for (procp = pidhash[hval], prevp = NULL; procp && procp->pid != pid; + procp = procp->pidhash_next) + prevp = procp; + + if (!procp) + return -1; + + if (!prevp) + pidhash[hval] = procp->pidhash_next; + else + prevp->pidhash_next = procp->pidhash_next; + + proc_unslot(procp); + free(procp); + return 0; +} + +static void writefilestring(char *path, char *s) { + int fd = open(path, O_WRONLY); + int len = strlen(s); + int ret; + + if (fd < 0) { + ALOGE("Error opening %s; errno=%d", path, errno); + return; + } + + ret = write(fd, s, len); + if (ret < 0) { + ALOGE("Error writing %s; errno=%d", path, errno); + } else if (ret < len) { + ALOGE("Short write on %s; length=%d", path, ret); + } + + close(fd); +} + +static void cmd_procprio(int pid, int uid, int oomadj) { + struct proc *procp; + char path[80]; + char val[20]; + + if (oomadj < OOM_DISABLE || oomadj > OOM_ADJUST_MAX) { + ALOGE("Invalid PROCPRIO oomadj argument %d", oomadj); + return; + } + + snprintf(path, sizeof(path), "/proc/%d/oom_score_adj", pid); + snprintf(val, sizeof(val), "%d", lowmem_oom_adj_to_oom_score_adj(oomadj)); + writefilestring(path, val); + + if (use_inkernel_interface) + return; + + procp = pid_lookup(pid); + if (!procp) { + procp = malloc(sizeof(struct proc)); + if (!procp) { + // Oh, the irony. May need to rebuild our state. + return; + } + + procp->pid = pid; + procp->uid = uid; + procp->oomadj = oomadj; + proc_insert(procp); + } else { + proc_unslot(procp); + procp->oomadj = oomadj; + proc_slot(procp); + } +} + +static void cmd_procremove(int pid) { + if (use_inkernel_interface) + return; + + pid_remove(pid); + kill_lasttime = 0; +} + +static void cmd_target(int ntargets, int *params) { + int i; + + if (ntargets > (int)ARRAY_SIZE(lowmem_adj)) + return; + + for (i = 0; i < ntargets; i++) { + lowmem_minfree[i] = ntohl(*params++); + lowmem_adj[i] = ntohl(*params++); + } + + lowmem_targets_size = ntargets; + + if (use_inkernel_interface) { + char minfreestr[128]; + char killpriostr[128]; + + minfreestr[0] = '\0'; + killpriostr[0] = '\0'; + + for (i = 0; i < lowmem_targets_size; i++) { + char val[40]; + + if (i) { + strlcat(minfreestr, ",", sizeof(minfreestr)); + strlcat(killpriostr, ",", sizeof(killpriostr)); + } + + snprintf(val, sizeof(val), "%d", lowmem_minfree[i]); + strlcat(minfreestr, val, sizeof(minfreestr)); + snprintf(val, sizeof(val), "%d", lowmem_adj[i]); + strlcat(killpriostr, val, sizeof(killpriostr)); + } + + writefilestring(INKERNEL_MINFREE_PATH, minfreestr); + writefilestring(INKERNEL_ADJ_PATH, killpriostr); + } +} + +static void ctrl_data_close(void) { + ALOGI("Closing Activity Manager data connection"); + close(ctrl_dfd); + ctrl_dfd = -1; + maxevents--; +} + +static int ctrl_data_read(char *buf, size_t bufsz) { + int ret = 0; + + ret = read(ctrl_dfd, buf, bufsz); + + if (ret == -1) { + ALOGE("control data socket read failed; errno=%d", errno); + } else if (ret == 0) { + ALOGE("Got EOF on control data socket"); + ret = -1; + } + + return ret; +} + +static void ctrl_command_handler(void) { + int ibuf[CTRL_PACKET_MAX / sizeof(int)]; + int len; + int cmd = -1; + int nargs; + int targets; + + len = ctrl_data_read((char *)ibuf, CTRL_PACKET_MAX); + if (len <= 0) + return; + + nargs = len / sizeof(int) - 1; + if (nargs < 0) + goto wronglen; + + cmd = ntohl(ibuf[0]); + + switch(cmd) { + case LMK_TARGET: + targets = nargs / 2; + if (nargs & 0x1 || targets > (int)ARRAY_SIZE(lowmem_adj)) + goto wronglen; + cmd_target(targets, &ibuf[1]); + break; + case LMK_PROCPRIO: + if (nargs != 3) + goto wronglen; + cmd_procprio(ntohl(ibuf[1]), ntohl(ibuf[2]), ntohl(ibuf[3])); + break; + case LMK_PROCREMOVE: + if (nargs != 1) + goto wronglen; + cmd_procremove(ntohl(ibuf[1])); + break; + default: + ALOGE("Received unknown command code %d", cmd); + return; + } + + return; + +wronglen: + ALOGE("Wrong control socket read length cmd=%d len=%d", cmd, len); +} + +static void ctrl_data_handler(uint32_t events) { + if (events & EPOLLHUP) { + ALOGI("ActivityManager disconnected"); + if (!ctrl_dfd_reopened) + ctrl_data_close(); + } else if (events & EPOLLIN) { + ctrl_command_handler(); + } +} + +static void ctrl_connect_handler(uint32_t events __unused) { + struct sockaddr addr; + socklen_t alen; + struct epoll_event epev; + + if (ctrl_dfd >= 0) { + ctrl_data_close(); + ctrl_dfd_reopened = 1; + } + + alen = sizeof(addr); + ctrl_dfd = accept(ctrl_lfd, &addr, &alen); + + if (ctrl_dfd < 0) { + ALOGE("lmkd control socket accept failed; errno=%d", errno); + return; + } + + ALOGI("ActivityManager connected"); + maxevents++; + epev.events = EPOLLIN; + epev.data.ptr = (void *)ctrl_data_handler; + if (epoll_ctl(epollfd, EPOLL_CTL_ADD, ctrl_dfd, &epev) == -1) { + ALOGE("epoll_ctl for data connection socket failed; errno=%d", errno); + ctrl_data_close(); + return; + } +} + +static int zoneinfo_parse_protection(char *cp) { + int max = 0; + int zoneval; + char *save_ptr; + + for (cp = strtok_r(cp, "(), ", &save_ptr); cp; cp = strtok_r(NULL, "), ", &save_ptr)) { + zoneval = strtol(cp, &cp, 0); + if (zoneval > max) + max = zoneval; + } + + return max; +} + +static void zoneinfo_parse_line(char *line, struct sysmeminfo *mip) { + char *cp = line; + char *ap; + char *save_ptr; + + cp = strtok_r(line, " ", &save_ptr); + if (!cp) + return; + + ap = strtok_r(NULL, " ", &save_ptr); + if (!ap) + return; + + if (!strcmp(cp, "nr_free_pages")) + mip->nr_free_pages += strtol(ap, NULL, 0); + else if (!strcmp(cp, "nr_file_pages")) + mip->nr_file_pages += strtol(ap, NULL, 0); + else if (!strcmp(cp, "nr_shmem")) + mip->nr_shmem += strtol(ap, NULL, 0); + else if (!strcmp(cp, "high")) + mip->totalreserve_pages += strtol(ap, NULL, 0); + else if (!strcmp(cp, "protection:")) + mip->totalreserve_pages += zoneinfo_parse_protection(ap); +} + +static int zoneinfo_parse(struct sysmeminfo *mip) { + int fd; + ssize_t size; + char buf[PAGE_SIZE]; + char *save_ptr; + char *line; + + memset(mip, 0, sizeof(struct sysmeminfo)); + + fd = open(ZONEINFO_PATH, O_RDONLY); + if (fd == -1) { + ALOGE("%s open: errno=%d", ZONEINFO_PATH, errno); + return -1; + } + + size = read_all(fd, buf, sizeof(buf) - 1); + if (size < 0) { + ALOGE("%s read: errno=%d", ZONEINFO_PATH, errno); + close(fd); + return -1; + } + ALOG_ASSERT((size_t)size < sizeof(buf) - 1, "/proc/zoneinfo too large"); + buf[size] = 0; + + for (line = strtok_r(buf, "\n", &save_ptr); line; line = strtok_r(NULL, "\n", &save_ptr)) + zoneinfo_parse_line(line, mip); + + close(fd); + return 0; +} + +static int proc_get_size(int pid) { + char path[PATH_MAX]; + char line[LINE_MAX]; + int fd; + int rss = 0; + int total; + ssize_t ret; + + snprintf(path, PATH_MAX, "/proc/%d/statm", pid); + fd = open(path, O_RDONLY); + if (fd == -1) + return -1; + + ret = read_all(fd, line, sizeof(line) - 1); + if (ret < 0) { + close(fd); + return -1; + } + + sscanf(line, "%d %d ", &total, &rss); + close(fd); + return rss; +} + +static char *proc_get_name(int pid) { + char path[PATH_MAX]; + static char line[LINE_MAX]; + int fd; + char *cp; + ssize_t ret; + + snprintf(path, PATH_MAX, "/proc/%d/cmdline", pid); + fd = open(path, O_RDONLY); + if (fd == -1) + return NULL; + ret = read_all(fd, line, sizeof(line) - 1); + close(fd); + if (ret < 0) { + return NULL; + } + + cp = strchr(line, ' '); + if (cp) + *cp = '\0'; + + return line; +} + +static struct proc *proc_adj_lru(int oomadj) { + return (struct proc *)adjslot_tail(&procadjslot_list[ADJTOSLOT(oomadj)]); +} + +/* Kill one process specified by procp. Returns the size of the process killed */ +static int kill_one_process(struct proc *procp, int other_free, int other_file, + int minfree, int min_score_adj, bool first) +{ + int pid = procp->pid; + uid_t uid = procp->uid; + char *taskname; + int tasksize; + int r; + + taskname = proc_get_name(pid); + if (!taskname) { + pid_remove(pid); + return -1; + } + + tasksize = proc_get_size(pid); + if (tasksize <= 0) { + pid_remove(pid); + return -1; + } + + ALOGI("Killing '%s' (%d), uid %d, adj %d\n" + " to free %ldkB because cache %s%ldkB is below limit %ldkB for oom_adj %d\n" + " Free memory is %s%ldkB %s reserved", + taskname, pid, uid, procp->oomadj, tasksize * page_k, + first ? "" : "~", other_file * page_k, minfree * page_k, min_score_adj, + first ? "" : "~", other_free * page_k, other_free >= 0 ? "above" : "below"); + r = kill(pid, SIGKILL); + killProcessGroup(uid, pid, SIGKILL); + pid_remove(pid); + + if (r) { + ALOGE("kill(%d): errno=%d", procp->pid, errno); + return -1; + } else { + return tasksize; + } +} + +/* + * Find a process to kill based on the current (possibly estimated) free memory + * and cached memory sizes. Returns the size of the killed processes. + */ +static int find_and_kill_process(int other_free, int other_file, bool first) +{ + int i; + int r; + int min_score_adj = OOM_ADJUST_MAX + 1; + int minfree = 0; + int killed_size = 0; + + for (i = 0; i < lowmem_targets_size; i++) { + minfree = lowmem_minfree[i]; + if (other_free < minfree && other_file < minfree) { + min_score_adj = lowmem_adj[i]; + break; + } + } + + if (min_score_adj == OOM_ADJUST_MAX + 1) + return 0; + + for (i = OOM_ADJUST_MAX; i >= min_score_adj; i--) { + struct proc *procp; + +retry: + procp = proc_adj_lru(i); + + if (procp) { + killed_size = kill_one_process(procp, other_free, other_file, minfree, min_score_adj, first); + if (killed_size < 0) { + goto retry; + } else { + return killed_size; + } + } + } + + return 0; +} + +static void mp_event(uint32_t events __unused) { + int i; + int ret; + unsigned long long evcount; + struct sysmeminfo mi; + int other_free; + int other_file; + int killed_size; + bool first = true; + + ret = read(mpevfd, &evcount, sizeof(evcount)); + if (ret < 0) + ALOGE("Error reading memory pressure event fd; errno=%d", + errno); + + if (time(NULL) - kill_lasttime < KILL_TIMEOUT) + return; + + while (zoneinfo_parse(&mi) < 0) { + // Failed to read /proc/zoneinfo, assume ENOMEM and kill something + find_and_kill_process(0, 0, true); + } + + other_free = mi.nr_free_pages - mi.totalreserve_pages; + other_file = mi.nr_file_pages - mi.nr_shmem; + + do { + killed_size = find_and_kill_process(other_free, other_file, first); + if (killed_size > 0) { + first = false; + other_free += killed_size; + other_file += killed_size; + } + } while (killed_size > 0); +} + +static int init_mp(char *levelstr, void *event_handler) +{ + int mpfd; + int evfd; + int evctlfd; + char buf[256]; + struct epoll_event epev; + int ret; + + mpfd = open(MEMCG_SYSFS_PATH "memory.pressure_level", O_RDONLY); + if (mpfd < 0) { + ALOGI("No kernel memory.pressure_level support (errno=%d)", errno); + goto err_open_mpfd; + } + + evctlfd = open(MEMCG_SYSFS_PATH "cgroup.event_control", O_WRONLY); + if (evctlfd < 0) { + ALOGI("No kernel memory cgroup event control (errno=%d)", errno); + goto err_open_evctlfd; + } + + evfd = eventfd(0, EFD_NONBLOCK); + if (evfd < 0) { + ALOGE("eventfd failed for level %s; errno=%d", levelstr, errno); + goto err_eventfd; + } + + ret = snprintf(buf, sizeof(buf), "%d %d %s", evfd, mpfd, levelstr); + if (ret >= (ssize_t)sizeof(buf)) { + ALOGE("cgroup.event_control line overflow for level %s", levelstr); + goto err; + } + + ret = write(evctlfd, buf, strlen(buf) + 1); + if (ret == -1) { + ALOGE("cgroup.event_control write failed for level %s; errno=%d", + levelstr, errno); + goto err; + } + + epev.events = EPOLLIN; + epev.data.ptr = event_handler; + ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, evfd, &epev); + if (ret == -1) { + ALOGE("epoll_ctl for level %s failed; errno=%d", levelstr, errno); + goto err; + } + maxevents++; + mpevfd = evfd; + return 0; + +err: + close(evfd); +err_eventfd: + close(evctlfd); +err_open_evctlfd: + close(mpfd); +err_open_mpfd: + return -1; +} + +static int init(void) { + struct epoll_event epev; + int i; + int ret; + + page_k = sysconf(_SC_PAGESIZE); + if (page_k == -1) + page_k = PAGE_SIZE; + page_k /= 1024; + + epollfd = epoll_create(MAX_EPOLL_EVENTS); + if (epollfd == -1) { + ALOGE("epoll_create failed (errno=%d)", errno); + return -1; + } + + ctrl_lfd = android_get_control_socket("lmkd"); + if (ctrl_lfd < 0) { + ALOGE("get lmkd control socket failed"); + return -1; + } + + ret = listen(ctrl_lfd, 1); + if (ret < 0) { + ALOGE("lmkd control socket listen failed (errno=%d)", errno); + return -1; + } + + epev.events = EPOLLIN; + epev.data.ptr = (void *)ctrl_connect_handler; + if (epoll_ctl(epollfd, EPOLL_CTL_ADD, ctrl_lfd, &epev) == -1) { + ALOGE("epoll_ctl for lmkd control socket failed (errno=%d)", errno); + return -1; + } + maxevents++; + + use_inkernel_interface = !access(INKERNEL_MINFREE_PATH, W_OK); + + if (use_inkernel_interface) { + ALOGI("Using in-kernel low memory killer interface"); + } else { + ret = init_mp(MEMPRESSURE_WATCH_LEVEL, (void *)&mp_event); + if (ret) + ALOGE("Kernel does not support memory pressure events or in-kernel low memory killer"); + } + + for (i = 0; i <= ADJTOSLOT(OOM_ADJUST_MAX); i++) { + procadjslot_list[i].next = &procadjslot_list[i]; + procadjslot_list[i].prev = &procadjslot_list[i]; + } + + return 0; +} + +static void mainloop(void) { + while (1) { + struct epoll_event events[maxevents]; + int nevents; + int i; + + ctrl_dfd_reopened = 0; + nevents = epoll_wait(epollfd, events, maxevents, -1); + + if (nevents == -1) { + if (errno == EINTR) + continue; + ALOGE("epoll_wait failed (errno=%d)", errno); + continue; + } + + for (i = 0; i < nevents; ++i) { + if (events[i].events & EPOLLERR) + ALOGD("EPOLLERR on event #%d", i); + if (events[i].data.ptr) + (*(void (*)(uint32_t))events[i].data.ptr)(events[i].events); + } + } +} + +int main(int argc __unused, char **argv __unused) { + struct sched_param param = { + .sched_priority = 1, + }; + + mlockall(MCL_FUTURE); + sched_setscheduler(0, SCHED_FIFO, ¶m); + if (!init()) + mainloop(); + + ALOGI("exiting"); + return 0; +} diff --git a/logcat/event.logtags b/logcat/event.logtags index a325692..1b5c6f4 100644 --- a/logcat/event.logtags +++ b/logcat/event.logtags @@ -134,5 +134,7 @@ # libcore failure logging 90100 exp_det_cert_pin_failure (certs|4) +1397638484 snet_event_log (subtag|3) (uid|1) (message|3) + # NOTE - the range 1000000-2000000 is reserved for partners and others who # want to define their own log tags without conflicting with the core platform. diff --git a/netcfg/netcfg.c b/netcfg/netcfg.c index 2308f37..4e83ba4 100644 --- a/netcfg/netcfg.c +++ b/netcfg/netcfg.c @@ -102,7 +102,6 @@ struct { "dhcp", 1, do_dhcp }, { "up", 1, ifc_up }, { "down", 1, ifc_down }, - { "flhosts", 1, ifc_remove_host_routes }, { "deldefault", 1, ifc_remove_default_route }, { "hwaddr", 2, set_hwaddr }, { 0, 0, 0 }, diff --git a/rootdir/init.rc b/rootdir/init.rc index 3644219..8eef76b 100644 --- a/rootdir/init.rc +++ b/rootdir/init.rc @@ -12,7 +12,7 @@ import /init.trace.rc on early-init # Set init and its forked children's oom_adj. - write /proc/1/oom_adj -16 + write /proc/1/oom_score_adj -1000 # Apply strict SELinux checking of PROT_EXEC on mmap/mprotect calls. write /sys/fs/selinux/checkreqprot 0 @@ -88,6 +88,10 @@ on init mkdir /mnt/obb 0700 root system mount tmpfs tmpfs /mnt/obb mode=0755,gid=1000 + # memory control cgroup + mkdir /dev/memcg 0700 root system + mount cgroup none /dev/memcg memory + write /proc/sys/kernel/panic_on_oops 1 write /proc/sys/kernel/hung_task_timeout_secs 0 write /proc/cpu/alignment 4 @@ -267,6 +271,7 @@ on post-fs-data mkdir /data/misc/wifi 0770 wifi wifi mkdir /data/misc/wifi/sockets 0770 wifi wifi mkdir /data/misc/wifi/wpa_supplicant 0770 wifi wifi + mkdir /data/misc/ethernet 0770 system system mkdir /data/misc/dhcp 0770 dhcp dhcp mkdir /data/misc/user 0771 root root # give system access to wpa_supplicant.conf for backup and restore @@ -337,9 +342,9 @@ on boot write /proc/sys/vm/overcommit_memory 1 write /proc/sys/vm/min_free_order_shift 4 chown root system /sys/module/lowmemorykiller/parameters/adj - chmod 0664 /sys/module/lowmemorykiller/parameters/adj + chmod 0220 /sys/module/lowmemorykiller/parameters/adj chown root system /sys/module/lowmemorykiller/parameters/minfree - chmod 0664 /sys/module/lowmemorykiller/parameters/minfree + chmod 0220 /sys/module/lowmemorykiller/parameters/minfree # Tweak background writeout write /proc/sys/vm/dirty_expire_centisecs 200 @@ -428,11 +433,19 @@ on boot setprop net.tcp.default_init_rwnd 60 class_start core - class_start main on nonencrypted + class_start main class_start late_start +on property:vold.decrypt=trigger_default_encryption + start defaultcrypto + +on property:vold.decrypt=trigger_encryption + start surfaceflinger + start encrypt + class_start main + on property:sys.init_log_level=* loglevel ${sys.init_log_level} @@ -492,11 +505,6 @@ service healthd /sbin/healthd critical seclabel u:r:healthd:s0 -service healthd-charger /sbin/healthd -n - class charger - critical - seclabel u:r:healthd:s0 - service console /system/bin/sh class core console @@ -519,6 +527,11 @@ service adbd /sbin/adbd --root_seclabel=u:r:su:s0 on property:ro.kernel.qemu=1 start adbd +service lmkd /system/bin/lmkd + class core + critical + socket lmkd seqpacket 0660 system system + service servicemanager /system/bin/servicemanager class core user system @@ -528,6 +541,7 @@ service servicemanager /system/bin/servicemanager onrestart restart zygote onrestart restart media onrestart restart surfaceflinger + onrestart restart inputflinger onrestart restart drm service vold /system/bin/vold @@ -561,6 +575,12 @@ service surfaceflinger /system/bin/surfaceflinger group graphics drmrpc onrestart restart zygote +service inputflinger /system/bin/inputflinger + class main + user system + group input + onrestart restart zygote + service drm /system/bin/drmserver class main user drm @@ -572,6 +592,20 @@ service media /system/bin/mediaserver group audio camera inet net_bt net_bt_admin net_bw_acct drmrpc mediadrm ioprio rt 4 +# One shot invocation to deal with encrypted volume. +service defaultcrypto /system/bin/vdc --wait cryptfs mountdefaultencrypted + disabled + oneshot + # vold will set vold.decrypt to trigger_restart_framework (default + # encryption) or trigger_restart_min_framework (other encryption) + +# One shot invocation to encrypt unencrypted volumes +service encrypt /system/bin/vdc --wait cryptfs enablecrypto inplace default + disabled + oneshot + # vold will set vold.decrypt to trigger_restart_framework (default + # encryption) + service bootanim /system/bin/bootanimation class main user graphics @@ -583,8 +617,9 @@ service installd /system/bin/installd class main socket installd stream 600 system system -service flash_recovery /system/etc/install-recovery.sh +service flash_recovery /system/bin/install-recovery.sh class main + seclabel u:r:install_recovery:s0 oneshot service racoon /system/bin/racoon @@ -621,3 +656,8 @@ service mdnsd /system/bin/mdnsd socket mdnsd stream 0660 mdnsr inet disabled oneshot + +service pre-recovery /system/bin/uncrypt + class main + disabled + oneshot diff --git a/sdcard/sdcard.c b/sdcard/sdcard.c index e413596..587a4cd 100644 --- a/sdcard/sdcard.c +++ b/sdcard/sdcard.c @@ -143,6 +143,8 @@ typedef enum { PERM_ANDROID_DATA, /* This node is "/Android/obb" */ PERM_ANDROID_OBB, + /* This node is "/Android/media" */ + PERM_ANDROID_MEDIA, /* This node is "/Android/user" */ PERM_ANDROID_USER, } perm_t; @@ -479,6 +481,10 @@ static void derive_permissions_locked(struct fuse* fuse, struct node *parent, /* Single OBB directory is always shared */ node->graft_path = fuse->obbpath; node->graft_pathlen = strlen(fuse->obbpath); + } else if (!strcasecmp(node->name, "media")) { + /* App-specific directories inside; let anyone traverse */ + node->perm = PERM_ANDROID_MEDIA; + node->mode = 0771; } else if (!strcasecmp(node->name, "user")) { /* User directories must only be accessible to system, protected * by sdcard_all. Zygote will bind mount the appropriate user- @@ -490,6 +496,7 @@ static void derive_permissions_locked(struct fuse* fuse, struct node *parent, break; case PERM_ANDROID_DATA: case PERM_ANDROID_OBB: + case PERM_ANDROID_MEDIA: appid = (appid_t) (uintptr_t) hashmapGet(fuse->package_to_appid, node->name); if (appid != 0) { node->uid = multiuser_get_uid(parent->userid, appid); diff --git a/toolbox/ioctl.c b/toolbox/ioctl.c index 17fabff..fd90812 100644 --- a/toolbox/ioctl.c +++ b/toolbox/ioctl.c @@ -63,10 +63,14 @@ int ioctl_main(int argc, char *argv[]) exit(1); } - fd = open(argv[optind], O_RDWR | O_SYNC); - if (fd < 0) { - fprintf(stderr, "cannot open %s\n", argv[optind]); - return 1; + if (!strcmp(argv[optind], "-")) { + fd = STDIN_FILENO; + } else { + fd = open(argv[optind], read_only ? O_RDONLY : (O_RDWR | O_SYNC)); + if (fd < 0) { + fprintf(stderr, "cannot open %s\n", argv[optind]); + return 1; + } } optind++; diff --git a/toolbox/newfs_msdos.c b/toolbox/newfs_msdos.c index 30b9f77..f69987c 100644 --- a/toolbox/newfs_msdos.c +++ b/toolbox/newfs_msdos.c @@ -27,20 +27,20 @@ #ifndef lint static const char rcsid[] = - "$FreeBSD: src/sbin/newfs_msdos/newfs_msdos.c,v 1.33 2009/04/11 14:56:29 ed Exp $"; + "$FreeBSD: src/sbin/newfs_msdos/newfs_msdos.c,v 1.33 2009/04/11 14:56:29 ed Exp $"; #endif /* not lint */ #include <sys/param.h> #ifndef ANDROID - #include <sys/fdcio.h> - #include <sys/disk.h> - #include <sys/disklabel.h> - #include <sys/mount.h> +#include <sys/fdcio.h> +#include <sys/disk.h> +#include <sys/disklabel.h> +#include <sys/mount.h> #else - #include <stdarg.h> - #include <linux/fs.h> - #include <linux/hdreg.h> +#include <stdarg.h> +#include <linux/fs.h> +#include <linux/hdreg.h> #endif #include <sys/stat.h> @@ -58,44 +58,44 @@ static const char rcsid[] = #include <time.h> #include <unistd.h> -#define MAXU16 0xffff /* maximum unsigned 16-bit quantity */ -#define BPN 4 /* bits per nibble */ -#define NPB 2 /* nibbles per byte */ - -#define DOSMAGIC 0xaa55 /* DOS magic number */ -#define MINBPS 512 /* minimum bytes per sector */ -#define MAXSPC 128 /* maximum sectors per cluster */ -#define MAXNFT 16 /* maximum number of FATs */ -#define DEFBLK 4096 /* default block size */ -#define DEFBLK16 2048 /* default block size FAT16 */ -#define DEFRDE 512 /* default root directory entries */ -#define RESFTE 2 /* reserved FAT entries */ -#define MINCLS12 1 /* minimum FAT12 clusters */ -#define MINCLS16 0x1000 /* minimum FAT16 clusters */ -#define MINCLS32 2 /* minimum FAT32 clusters */ -#define MAXCLS12 0xfed /* maximum FAT12 clusters */ -#define MAXCLS16 0xfff5 /* maximum FAT16 clusters */ -#define MAXCLS32 0xffffff5 /* maximum FAT32 clusters */ - -#define mincls(fat) ((fat) == 12 ? MINCLS12 : \ - (fat) == 16 ? MINCLS16 : \ - MINCLS32) - -#define maxcls(fat) ((fat) == 12 ? MAXCLS12 : \ - (fat) == 16 ? MAXCLS16 : \ - MAXCLS32) - -#define mk1(p, x) \ +#define MAXU16 0xffff /* maximum unsigned 16-bit quantity */ +#define BPN 4 /* bits per nibble */ +#define NPB 2 /* nibbles per byte */ + +#define DOSMAGIC 0xaa55 /* DOS magic number */ +#define MINBPS 512 /* minimum bytes per sector */ +#define MAXSPC 128 /* maximum sectors per cluster */ +#define MAXNFT 16 /* maximum number of FATs */ +#define DEFBLK 4096 /* default block size */ +#define DEFBLK16 2048 /* default block size FAT16 */ +#define DEFRDE 512 /* default root directory entries */ +#define RESFTE 2 /* reserved FAT entries */ +#define MINCLS12 1 /* minimum FAT12 clusters */ +#define MINCLS16 0x1000 /* minimum FAT16 clusters */ +#define MINCLS32 2 /* minimum FAT32 clusters */ +#define MAXCLS12 0xfed /* maximum FAT12 clusters */ +#define MAXCLS16 0xfff5 /* maximum FAT16 clusters */ +#define MAXCLS32 0xffffff5 /* maximum FAT32 clusters */ + +#define mincls(fat) ((fat) == 12 ? MINCLS12 : \ + (fat) == 16 ? MINCLS16 : \ + MINCLS32) + +#define maxcls(fat) ((fat) == 12 ? MAXCLS12 : \ + (fat) == 16 ? MAXCLS16 : \ + MAXCLS32) + +#define mk1(p, x) \ (p) = (u_int8_t)(x) -#define mk2(p, x) \ - (p)[0] = (u_int8_t)(x), \ +#define mk2(p, x) \ + (p)[0] = (u_int8_t)(x), \ (p)[1] = (u_int8_t)((x) >> 010) -#define mk4(p, x) \ - (p)[0] = (u_int8_t)(x), \ - (p)[1] = (u_int8_t)((x) >> 010), \ - (p)[2] = (u_int8_t)((x) >> 020), \ +#define mk4(p, x) \ + (p)[0] = (u_int8_t)(x), \ + (p)[1] = (u_int8_t)((x) >> 010), \ + (p)[2] = (u_int8_t)((x) >> 020), \ (p)[3] = (u_int8_t)((x) >> 030) #define argto1(arg, lo, msg) argtou(arg, lo, 0xff, msg) @@ -104,71 +104,71 @@ static const char rcsid[] = #define argtox(arg, lo, msg) argtou(arg, lo, UINT_MAX, msg) struct bs { - u_int8_t jmp[3]; /* bootstrap entry point */ - u_int8_t oem[8]; /* OEM name and version */ + u_int8_t jmp[3]; /* bootstrap entry point */ + u_int8_t oem[8]; /* OEM name and version */ }; struct bsbpb { - u_int8_t bps[2]; /* bytes per sector */ - u_int8_t spc; /* sectors per cluster */ - u_int8_t res[2]; /* reserved sectors */ - u_int8_t nft; /* number of FATs */ - u_int8_t rde[2]; /* root directory entries */ - u_int8_t sec[2]; /* total sectors */ - u_int8_t mid; /* media descriptor */ - u_int8_t spf[2]; /* sectors per FAT */ - u_int8_t spt[2]; /* sectors per track */ - u_int8_t hds[2]; /* drive heads */ - u_int8_t hid[4]; /* hidden sectors */ - u_int8_t bsec[4]; /* big total sectors */ + u_int8_t bps[2]; /* bytes per sector */ + u_int8_t spc; /* sectors per cluster */ + u_int8_t res[2]; /* reserved sectors */ + u_int8_t nft; /* number of FATs */ + u_int8_t rde[2]; /* root directory entries */ + u_int8_t sec[2]; /* total sectors */ + u_int8_t mid; /* media descriptor */ + u_int8_t spf[2]; /* sectors per FAT */ + u_int8_t spt[2]; /* sectors per track */ + u_int8_t hds[2]; /* drive heads */ + u_int8_t hid[4]; /* hidden sectors */ + u_int8_t bsec[4]; /* big total sectors */ }; struct bsxbpb { - u_int8_t bspf[4]; /* big sectors per FAT */ - u_int8_t xflg[2]; /* FAT control flags */ - u_int8_t vers[2]; /* file system version */ - u_int8_t rdcl[4]; /* root directory start cluster */ - u_int8_t infs[2]; /* file system info sector */ - u_int8_t bkbs[2]; /* backup boot sector */ - u_int8_t rsvd[12]; /* reserved */ + u_int8_t bspf[4]; /* big sectors per FAT */ + u_int8_t xflg[2]; /* FAT control flags */ + u_int8_t vers[2]; /* file system version */ + u_int8_t rdcl[4]; /* root directory start cluster */ + u_int8_t infs[2]; /* file system info sector */ + u_int8_t bkbs[2]; /* backup boot sector */ + u_int8_t rsvd[12]; /* reserved */ }; struct bsx { - u_int8_t drv; /* drive number */ - u_int8_t rsvd; /* reserved */ - u_int8_t sig; /* extended boot signature */ - u_int8_t volid[4]; /* volume ID number */ - u_int8_t label[11]; /* volume label */ - u_int8_t type[8]; /* file system type */ + u_int8_t drv; /* drive number */ + u_int8_t rsvd; /* reserved */ + u_int8_t sig; /* extended boot signature */ + u_int8_t volid[4]; /* volume ID number */ + u_int8_t label[11]; /* volume label */ + u_int8_t type[8]; /* file system type */ }; struct de { - u_int8_t namext[11]; /* name and extension */ - u_int8_t attr; /* attributes */ - u_int8_t rsvd[10]; /* reserved */ - u_int8_t time[2]; /* creation time */ - u_int8_t date[2]; /* creation date */ - u_int8_t clus[2]; /* starting cluster */ - u_int8_t size[4]; /* size */ + u_int8_t namext[11]; /* name and extension */ + u_int8_t attr; /* attributes */ + u_int8_t rsvd[10]; /* reserved */ + u_int8_t time[2]; /* creation time */ + u_int8_t date[2]; /* creation date */ + u_int8_t clus[2]; /* starting cluster */ + u_int8_t size[4]; /* size */ }; struct bpb { - u_int bps; /* bytes per sector */ - u_int spc; /* sectors per cluster */ - u_int res; /* reserved sectors */ - u_int nft; /* number of FATs */ - u_int rde; /* root directory entries */ - u_int sec; /* total sectors */ - u_int mid; /* media descriptor */ - u_int spf; /* sectors per FAT */ - u_int spt; /* sectors per track */ - u_int hds; /* drive heads */ - u_int hid; /* hidden sectors */ - u_int bsec; /* big total sectors */ - u_int bspf; /* big sectors per FAT */ - u_int rdcl; /* root directory start cluster */ - u_int infs; /* file system info sector */ - u_int bkbs; /* backup boot sector */ + u_int bps; /* bytes per sector */ + u_int spc; /* sectors per cluster */ + u_int res; /* reserved sectors */ + u_int nft; /* number of FATs */ + u_int rde; /* root directory entries */ + u_int sec; /* total sectors */ + u_int mid; /* media descriptor */ + u_int spf; /* sectors per FAT */ + u_int spt; /* sectors per track */ + u_int hds; /* drive heads */ + u_int hid; /* hidden sectors */ + u_int bsec; /* big total sectors */ + u_int bspf; /* big sectors per FAT */ + u_int rdcl; /* root directory start cluster */ + u_int infs; /* file system info sector */ + u_int bkbs; /* backup boot sector */ }; #define BPBGAP 0, 0, 0, 0, 0, 0 @@ -181,35 +181,35 @@ static struct { {"180", {512, 1, 1, 2, 64, 360, 0xfc, 2, 9, 1, BPBGAP}}, {"320", {512, 2, 1, 2, 112, 640, 0xff, 1, 8, 2, BPBGAP}}, {"360", {512, 2, 1, 2, 112, 720, 0xfd, 2, 9, 2, BPBGAP}}, - {"640", {512, 2, 1, 2, 112, 1280, 0xfb, 2, 8, 2, BPBGAP}}, + {"640", {512, 2, 1, 2, 112, 1280, 0xfb, 2, 8, 2, BPBGAP}}, {"720", {512, 2, 1, 2, 112, 1440, 0xf9, 3, 9, 2, BPBGAP}}, {"1200", {512, 1, 1, 2, 224, 2400, 0xf9, 7, 15, 2, BPBGAP}}, - {"1232", {1024,1, 1, 2, 192, 1232, 0xfe, 2, 8, 2, BPBGAP}}, + {"1232", {1024,1, 1, 2, 192, 1232, 0xfe, 2, 8, 2, BPBGAP}}, {"1440", {512, 1, 1, 2, 224, 2880, 0xf0, 9, 18, 2, BPBGAP}}, {"2880", {512, 2, 1, 2, 240, 5760, 0xf0, 9, 36, 2, BPBGAP}} }; static const u_int8_t bootcode[] = { - 0xfa, /* cli */ - 0x31, 0xc0, /* xor ax,ax */ - 0x8e, 0xd0, /* mov ss,ax */ - 0xbc, 0x00, 0x7c, /* mov sp,7c00h */ - 0xfb, /* sti */ - 0x8e, 0xd8, /* mov ds,ax */ - 0xe8, 0x00, 0x00, /* call $ + 3 */ - 0x5e, /* pop si */ - 0x83, 0xc6, 0x19, /* add si,+19h */ - 0xbb, 0x07, 0x00, /* mov bx,0007h */ - 0xfc, /* cld */ - 0xac, /* lodsb */ - 0x84, 0xc0, /* test al,al */ - 0x74, 0x06, /* jz $ + 8 */ - 0xb4, 0x0e, /* mov ah,0eh */ - 0xcd, 0x10, /* int 10h */ - 0xeb, 0xf5, /* jmp $ - 9 */ - 0x30, 0xe4, /* xor ah,ah */ - 0xcd, 0x16, /* int 16h */ - 0xcd, 0x19, /* int 19h */ + 0xfa, /* cli */ + 0x31, 0xc0, /* xor ax,ax */ + 0x8e, 0xd0, /* mov ss,ax */ + 0xbc, 0x00, 0x7c, /* mov sp,7c00h */ + 0xfb, /* sti */ + 0x8e, 0xd8, /* mov ds,ax */ + 0xe8, 0x00, 0x00, /* call $ + 3 */ + 0x5e, /* pop si */ + 0x83, 0xc6, 0x19, /* add si,+19h */ + 0xbb, 0x07, 0x00, /* mov bx,0007h */ + 0xfc, /* cld */ + 0xac, /* lodsb */ + 0x84, 0xc0, /* test al,al */ + 0x74, 0x06, /* jz $ + 8 */ + 0xb4, 0x0e, /* mov ah,0eh */ + 0xcd, 0x10, /* int 10h */ + 0xeb, 0xf5, /* jmp $ - 9 */ + 0x30, 0xe4, /* xor ah,ah */ + 0xcd, 0x16, /* int 16h */ + 0xcd, 0x19, /* int 19h */ 0x0d, 0x0a, 'N', 'o', 'n', '-', 's', 'y', 's', 't', 'e', 'm', ' ', 'd', 'i', 's', 'k', @@ -223,8 +223,7 @@ static const u_int8_t bootcode[] = { static void check_mounted(const char *, mode_t); static void getstdfmt(const char *, struct bpb *); -static void getdiskinfo(int, const char *, const char *, int, - struct bpb *); +static void getdiskinfo(int, const char *, const char *, int, struct bpb *); static void print_bpb(struct bpb *); static u_int ckgeom(const char *, u_int, const char *); static u_int argtou(const char *, u_int, u_int, const char *); @@ -237,14 +236,14 @@ static void usage(void); /* * Construct a FAT12, FAT16, or FAT32 file system. */ -int -newfs_msdos_main(int argc, char *argv[]) +int newfs_msdos_main(int argc, char *argv[]) { - static const char opts[] = "@:NB:C:F:I:L:O:S:a:b:c:e:f:h:i:k:m:n:o:r:s:u:"; + static const char opts[] = "@:NAB:C:F:I:L:O:S:a:b:c:e:f:h:i:k:m:n:o:r:s:u:"; const char *opt_B = NULL, *opt_L = NULL, *opt_O = NULL, *opt_f = NULL; u_int opt_F = 0, opt_I = 0, opt_S = 0, opt_a = 0, opt_b = 0, opt_c = 0; u_int opt_e = 0, opt_h = 0, opt_i = 0, opt_k = 0, opt_m = 0, opt_n = 0; u_int opt_o = 0, opt_r = 0, opt_s = 0, opt_u = 0; + u_int opt_A = 0; int opt_N = 0; int Iflag = 0, mflag = 0, oflag = 0; char buf[MAXPATHLEN]; @@ -262,462 +261,484 @@ newfs_msdos_main(int argc, char *argv[]) ssize_t n; time_t now; u_int fat, bss, rds, cls, dir, lsn, x, x1, x2; + u_int extra_res, alignment=0, set_res, set_spf, set_spc, tempx, attempts=0; int ch, fd, fd1; off_t opt_create = 0, opt_ofs = 0; while ((ch = getopt(argc, argv, opts)) != -1) - switch (ch) { - case '@': - opt_ofs = argtooff(optarg, "offset"); - break; - case 'N': - opt_N = 1; - break; - case 'B': - opt_B = optarg; - break; - case 'C': - opt_create = argtooff(optarg, "create size"); - break; - case 'F': - if (strcmp(optarg, "12") && - strcmp(optarg, "16") && - strcmp(optarg, "32")) - errx(1, "%s: bad FAT type", optarg); - opt_F = atoi(optarg); - break; - case 'I': - opt_I = argto4(optarg, 0, "volume ID"); - Iflag = 1; - break; - case 'L': - if (!oklabel(optarg)) - errx(1, "%s: bad volume label", optarg); - opt_L = optarg; - break; - case 'O': - if (strlen(optarg) > 8) - errx(1, "%s: bad OEM string", optarg); - opt_O = optarg; - break; - case 'S': - opt_S = argto2(optarg, 1, "bytes/sector"); - break; - case 'a': - opt_a = argto4(optarg, 1, "sectors/FAT"); - break; - case 'b': - opt_b = argtox(optarg, 1, "block size"); - opt_c = 0; - break; - case 'c': - opt_c = argto1(optarg, 1, "sectors/cluster"); - opt_b = 0; - break; - case 'e': - opt_e = argto2(optarg, 1, "directory entries"); - break; - case 'f': - opt_f = optarg; - break; - case 'h': - opt_h = argto2(optarg, 1, "drive heads"); - break; - case 'i': - opt_i = argto2(optarg, 1, "info sector"); - break; - case 'k': - opt_k = argto2(optarg, 1, "backup sector"); - break; - case 'm': - opt_m = argto1(optarg, 0, "media descriptor"); - mflag = 1; - break; - case 'n': - opt_n = argto1(optarg, 1, "number of FATs"); - break; - case 'o': - opt_o = argto4(optarg, 0, "hidden sectors"); - oflag = 1; - break; - case 'r': - opt_r = argto2(optarg, 1, "reserved sectors"); - break; - case 's': - opt_s = argto4(optarg, 1, "file system size"); - break; - case 'u': - opt_u = argto2(optarg, 1, "sectors/track"); - break; - default: - usage(); - } + switch (ch) { + case '@': + opt_ofs = argtooff(optarg, "offset"); + break; + case 'N': + opt_N = 1; + break; + case 'A': + opt_A = 1; + break; + case 'B': + opt_B = optarg; + break; + case 'C': + opt_create = argtooff(optarg, "create size"); + break; + case 'F': + if (strcmp(optarg, "12") && strcmp(optarg, "16") && strcmp(optarg, "32")) + errx(1, "%s: bad FAT type", optarg); + opt_F = atoi(optarg); + break; + case 'I': + opt_I = argto4(optarg, 0, "volume ID"); + Iflag = 1; + break; + case 'L': + if (!oklabel(optarg)) + errx(1, "%s: bad volume label", optarg); + opt_L = optarg; + break; + case 'O': + if (strlen(optarg) > 8) + errx(1, "%s: bad OEM string", optarg); + opt_O = optarg; + break; + case 'S': + opt_S = argto2(optarg, 1, "bytes/sector"); + break; + case 'a': + opt_a = argto4(optarg, 1, "sectors/FAT"); + break; + case 'b': + opt_b = argtox(optarg, 1, "block size"); + opt_c = 0; + break; + case 'c': + opt_c = argto1(optarg, 1, "sectors/cluster"); + opt_b = 0; + break; + case 'e': + opt_e = argto2(optarg, 1, "directory entries"); + break; + case 'f': + opt_f = optarg; + break; + case 'h': + opt_h = argto2(optarg, 1, "drive heads"); + break; + case 'i': + opt_i = argto2(optarg, 1, "info sector"); + break; + case 'k': + opt_k = argto2(optarg, 1, "backup sector"); + break; + case 'm': + opt_m = argto1(optarg, 0, "media descriptor"); + mflag = 1; + break; + case 'n': + opt_n = argto1(optarg, 1, "number of FATs"); + break; + case 'o': + opt_o = argto4(optarg, 0, "hidden sectors"); + oflag = 1; + break; + case 'r': + opt_r = argto2(optarg, 1, "reserved sectors"); + break; + case 's': + opt_s = argto4(optarg, 1, "file system size"); + break; + case 'u': + opt_u = argto2(optarg, 1, "sectors/track"); + break; + default: + usage(); + } argc -= optind; argv += optind; if (argc < 1 || argc > 2) - usage(); + usage(); fname = *argv++; if (!opt_create && !strchr(fname, '/')) { - snprintf(buf, sizeof(buf), "%s%s", _PATH_DEV, fname); - if (!(fname = strdup(buf))) - err(1, "%s", buf); + snprintf(buf, sizeof(buf), "%s%s", _PATH_DEV, fname); + if (!(fname = strdup(buf))) + err(1, "%s", buf); } dtype = *argv; + if (opt_A) { + if (opt_r) + errx(1, "align (-A) is incompatible with -r"); + if (opt_N) + errx(1, "align (-A) is incompatible with -N"); + } if (opt_create) { - if (opt_N) - errx(1, "create (-C) is incompatible with -N"); - fd = open(fname, O_RDWR | O_CREAT | O_TRUNC, 0644); - if (fd == -1) - errx(1, "failed to create %s", fname); - if (ftruncate(fd, opt_create)) - errx(1, "failed to initialize %jd bytes", (intmax_t)opt_create); + if (opt_N) + errx(1, "create (-C) is incompatible with -N"); + fd = open(fname, O_RDWR | O_CREAT | O_TRUNC, 0644); + if (fd == -1) + errx(1, "failed to create %s", fname); + if (ftruncate(fd, opt_create)) + errx(1, "failed to initialize %jd bytes", (intmax_t)opt_create); } else if ((fd = open(fname, opt_N ? O_RDONLY : O_RDWR)) == -1) - err(1, "%s", fname); + err(1, "%s", fname); if (fstat(fd, &sb)) - err(1, "%s", fname); + err(1, "%s", fname); if (opt_create) { - if (!S_ISREG(sb.st_mode)) - warnx("warning, %s is not a regular file", fname); + if (!S_ISREG(sb.st_mode)) + warnx("warning, %s is not a regular file", fname); } else { - if (!S_ISCHR(sb.st_mode)) - warnx("warning, %s is not a character device", fname); + if (!S_ISCHR(sb.st_mode)) + warnx("warning, %s is not a character device", fname); } if (!opt_N) - check_mounted(fname, sb.st_mode); + check_mounted(fname, sb.st_mode); if (opt_ofs && opt_ofs != lseek(fd, opt_ofs, SEEK_SET)) - errx(1, "cannot seek to %jd", (intmax_t)opt_ofs); + errx(1, "cannot seek to %jd", (intmax_t)opt_ofs); memset(&bpb, 0, sizeof(bpb)); if (opt_f) { - getstdfmt(opt_f, &bpb); - bpb.bsec = bpb.sec; - bpb.sec = 0; - bpb.bspf = bpb.spf; - bpb.spf = 0; + getstdfmt(opt_f, &bpb); + bpb.bsec = bpb.sec; + bpb.sec = 0; + bpb.bspf = bpb.spf; + bpb.spf = 0; } if (opt_h) - bpb.hds = opt_h; + bpb.hds = opt_h; if (opt_u) - bpb.spt = opt_u; + bpb.spt = opt_u; if (opt_S) - bpb.bps = opt_S; + bpb.bps = opt_S; if (opt_s) - bpb.bsec = opt_s; + bpb.bsec = opt_s; if (oflag) - bpb.hid = opt_o; + bpb.hid = opt_o; if (!(opt_f || (opt_h && opt_u && opt_S && opt_s && oflag))) { - off_t delta; - getdiskinfo(fd, fname, dtype, oflag, &bpb); + off_t delta; + getdiskinfo(fd, fname, dtype, oflag, &bpb); if (opt_s) { bpb.bsec = opt_s; } - bpb.bsec -= (opt_ofs / bpb.bps); - delta = bpb.bsec % bpb.spt; - if (delta != 0) { - warnx("trim %d sectors from %d to adjust to a multiple of %d", - (int)delta, bpb.bsec, bpb.spt); - bpb.bsec -= delta; - } - if (bpb.spc == 0) { /* set defaults */ - if (bpb.bsec <= 6000) /* about 3MB -> 512 bytes */ - bpb.spc = 1; - else if (bpb.bsec <= (1<<17)) /* 64M -> 4k */ - bpb.spc = 8; - else if (bpb.bsec <= (1<<19)) /* 256M -> 8k */ - bpb.spc = 16; - else if (bpb.bsec <= (1<<22)) /* 2G -> 16k, some versions of windows - require a minimum of 65527 clusters */ - bpb.spc = 32; - else - bpb.spc = 64; /* otherwise 32k */ - } + bpb.bsec -= (opt_ofs / bpb.bps); + delta = bpb.bsec % bpb.spt; + if (delta != 0) { + warnx("trim %d sectors from %d to adjust to a multiple of %d", + (int)delta, bpb.bsec, bpb.spt); + bpb.bsec -= delta; + } + if (bpb.spc == 0) { /* set defaults */ + if (bpb.bsec <= 6000) /* about 3MB -> 512 bytes */ + bpb.spc = 1; + else if (bpb.bsec <= (1<<17)) /* 64M -> 4k */ + bpb.spc = 8; + else if (bpb.bsec <= (1<<19)) /* 256M -> 8k */ + bpb.spc = 16; + else if (bpb.bsec <= (1<<22)) /* 2G -> 16k, some versions of windows + require a minimum of 65527 clusters */ + bpb.spc = 32; + else + bpb.spc = 64; /* otherwise 32k */ + } } if (!powerof2(bpb.bps)) - errx(1, "bytes/sector (%u) is not a power of 2", bpb.bps); + errx(1, "bytes/sector (%u) is not a power of 2", bpb.bps); if (bpb.bps < MINBPS) - errx(1, "bytes/sector (%u) is too small; minimum is %u", - bpb.bps, MINBPS); + errx(1, "bytes/sector (%u) is too small; minimum is %u", + bpb.bps, MINBPS); if (!(fat = opt_F)) { - if (opt_f) - fat = 12; - else if (!opt_e && (opt_i || opt_k)) - fat = 32; + if (opt_f) + fat = 12; + else if (!opt_e && (opt_i || opt_k)) + fat = 32; } if ((fat == 32 && opt_e) || (fat != 32 && (opt_i || opt_k))) - errx(1, "-%c is not a legal FAT%s option", - fat == 32 ? 'e' : opt_i ? 'i' : 'k', - fat == 32 ? "32" : "12/16"); + errx(1, "-%c is not a legal FAT%s option", + fat == 32 ? 'e' : opt_i ? 'i' : 'k', + fat == 32 ? "32" : "12/16"); if (opt_f && fat == 32) - bpb.rde = 0; + bpb.rde = 0; if (opt_b) { - if (!powerof2(opt_b)) - errx(1, "block size (%u) is not a power of 2", opt_b); - if (opt_b < bpb.bps) - errx(1, "block size (%u) is too small; minimum is %u", - opt_b, bpb.bps); - if (opt_b > bpb.bps * MAXSPC) - errx(1, "block size (%u) is too large; maximum is %u", - opt_b, bpb.bps * MAXSPC); - bpb.spc = opt_b / bpb.bps; + if (!powerof2(opt_b)) + errx(1, "block size (%u) is not a power of 2", opt_b); + if (opt_b < bpb.bps) + errx(1, "block size (%u) is too small; minimum is %u", + opt_b, bpb.bps); + if (opt_b > bpb.bps * MAXSPC) + errx(1, "block size (%u) is too large; maximum is %u", opt_b, bpb.bps * MAXSPC); + bpb.spc = opt_b / bpb.bps; } if (opt_c) { - if (!powerof2(opt_c)) - errx(1, "sectors/cluster (%u) is not a power of 2", opt_c); - bpb.spc = opt_c; + if (!powerof2(opt_c)) + errx(1, "sectors/cluster (%u) is not a power of 2", opt_c); + bpb.spc = opt_c; } if (opt_r) - bpb.res = opt_r; + bpb.res = opt_r; if (opt_n) { - if (opt_n > MAXNFT) - errx(1, "number of FATs (%u) is too large; maximum is %u", - opt_n, MAXNFT); - bpb.nft = opt_n; + if (opt_n > MAXNFT) + errx(1, "number of FATs (%u) is too large; maximum is %u", opt_n, MAXNFT); + bpb.nft = opt_n; } if (opt_e) - bpb.rde = opt_e; + bpb.rde = opt_e; if (mflag) { - if (opt_m < 0xf0) - errx(1, "illegal media descriptor (%#x)", opt_m); - bpb.mid = opt_m; + if (opt_m < 0xf0) + errx(1, "illegal media descriptor (%#x)", opt_m); + bpb.mid = opt_m; } if (opt_a) - bpb.bspf = opt_a; + bpb.bspf = opt_a; if (opt_i) - bpb.infs = opt_i; + bpb.infs = opt_i; if (opt_k) - bpb.bkbs = opt_k; + bpb.bkbs = opt_k; bss = 1; bname = NULL; fd1 = -1; if (opt_B) { - bname = opt_B; - if (!strchr(bname, '/')) { - snprintf(buf, sizeof(buf), "/boot/%s", bname); - if (!(bname = strdup(buf))) - err(1, "%s", buf); - } - if ((fd1 = open(bname, O_RDONLY)) == -1 || fstat(fd1, &sb)) - err(1, "%s", bname); - if (!S_ISREG(sb.st_mode) || sb.st_size % bpb.bps || - sb.st_size < bpb.bps || sb.st_size > bpb.bps * MAXU16) - errx(1, "%s: inappropriate file type or format", bname); - bss = sb.st_size / bpb.bps; + bname = opt_B; + if (!strchr(bname, '/')) { + snprintf(buf, sizeof(buf), "/boot/%s", bname); + if (!(bname = strdup(buf))) + err(1, "%s", buf); + } + if ((fd1 = open(bname, O_RDONLY)) == -1 || fstat(fd1, &sb)) + err(1, "%s", bname); + if (!S_ISREG(sb.st_mode) || sb.st_size % bpb.bps || + sb.st_size < bpb.bps || sb.st_size > bpb.bps * MAXU16) + errx(1, "%s: inappropriate file type or format", bname); + bss = sb.st_size / bpb.bps; } if (!bpb.nft) - bpb.nft = 2; + bpb.nft = 2; if (!fat) { - if (bpb.bsec < (bpb.res ? bpb.res : bss) + - howmany((RESFTE + (bpb.spc ? MINCLS16 : MAXCLS12 + 1)) * - ((bpb.spc ? 16 : 12) / BPN), bpb.bps * NPB) * - bpb.nft + - howmany(bpb.rde ? bpb.rde : DEFRDE, - bpb.bps / sizeof(struct de)) + - (bpb.spc ? MINCLS16 : MAXCLS12 + 1) * - (bpb.spc ? bpb.spc : howmany(DEFBLK, bpb.bps))) - fat = 12; - else if (bpb.rde || bpb.bsec < - (bpb.res ? bpb.res : bss) + - howmany((RESFTE + MAXCLS16) * 2, bpb.bps) * bpb.nft + - howmany(DEFRDE, bpb.bps / sizeof(struct de)) + - (MAXCLS16 + 1) * - (bpb.spc ? bpb.spc : howmany(8192, bpb.bps))) - fat = 16; - else - fat = 32; + if (bpb.bsec < (bpb.res ? bpb.res : bss) + + howmany((RESFTE + (bpb.spc ? MINCLS16 : MAXCLS12 + 1)) * + ((bpb.spc ? 16 : 12) / BPN), bpb.bps * NPB) * + bpb.nft + + howmany(bpb.rde ? bpb.rde : DEFRDE, + bpb.bps / sizeof(struct de)) + + (bpb.spc ? MINCLS16 : MAXCLS12 + 1) * + (bpb.spc ? bpb.spc : howmany(DEFBLK, bpb.bps))) + fat = 12; + else if (bpb.rde || bpb.bsec < + (bpb.res ? bpb.res : bss) + + howmany((RESFTE + MAXCLS16) * 2, bpb.bps) * bpb.nft + + howmany(DEFRDE, bpb.bps / sizeof(struct de)) + + (MAXCLS16 + 1) * + (bpb.spc ? bpb.spc : howmany(8192, bpb.bps))) + fat = 16; + else + fat = 32; } x = bss; if (fat == 32) { - if (!bpb.infs) { - if (x == MAXU16 || x == bpb.bkbs) - errx(1, "no room for info sector"); - bpb.infs = x; - } - if (bpb.infs != MAXU16 && x <= bpb.infs) - x = bpb.infs + 1; - if (!bpb.bkbs) { - if (x == MAXU16) - errx(1, "no room for backup sector"); - bpb.bkbs = x; - } else if (bpb.bkbs != MAXU16 && bpb.bkbs == bpb.infs) - errx(1, "backup sector would overwrite info sector"); - if (bpb.bkbs != MAXU16 && x <= bpb.bkbs) - x = bpb.bkbs + 1; - } - if (!bpb.res) - bpb.res = fat == 32 ? MAX(x, MAX(16384 / bpb.bps, 4)) : x; - else if (bpb.res < x) - errx(1, "too few reserved sectors"); - if (fat != 32 && !bpb.rde) - bpb.rde = DEFRDE; - rds = howmany(bpb.rde, bpb.bps / sizeof(struct de)); - if (!bpb.spc) - for (bpb.spc = howmany(fat == 16 ? DEFBLK16 : DEFBLK, bpb.bps); - bpb.spc < MAXSPC && - bpb.res + - howmany((RESFTE + maxcls(fat)) * (fat / BPN), - bpb.bps * NPB) * bpb.nft + - rds + - (u_int64_t)(maxcls(fat) + 1) * bpb.spc <= bpb.bsec; - bpb.spc <<= 1); - if (fat != 32 && bpb.bspf > MAXU16) - errx(1, "too many sectors/FAT for FAT12/16"); - x1 = bpb.res + rds; - x = bpb.bspf ? bpb.bspf : 1; - if (x1 + (u_int64_t)x * bpb.nft > bpb.bsec) - errx(1, "meta data exceeds file system size"); - x1 += x * bpb.nft; - x = (u_int64_t)(bpb.bsec - x1) * bpb.bps * NPB / - (bpb.spc * bpb.bps * NPB + fat / BPN * bpb.nft); - x2 = howmany((RESFTE + MIN(x, maxcls(fat))) * (fat / BPN), - bpb.bps * NPB); - if (!bpb.bspf) { - bpb.bspf = x2; - x1 += (bpb.bspf - 1) * bpb.nft; + if (!bpb.infs) { + if (x == MAXU16 || x == bpb.bkbs) + errx(1, "no room for info sector"); + bpb.infs = x; + } + if (bpb.infs != MAXU16 && x <= bpb.infs) + x = bpb.infs + 1; + if (!bpb.bkbs) { + if (x == MAXU16) + errx(1, "no room for backup sector"); + bpb.bkbs = x; + } else if (bpb.bkbs != MAXU16 && bpb.bkbs == bpb.infs) + errx(1, "backup sector would overwrite info sector"); + if (bpb.bkbs != MAXU16 && x <= bpb.bkbs) + x = bpb.bkbs + 1; } + + extra_res = 0; + set_res = !bpb.res; + set_spf = !bpb.bspf; + set_spc = !bpb.spc; + tempx = x; + /* + * Attempt to align if opt_A is set. This is done by increasing the number + * of reserved blocks. This can cause other factors to change, which can in + * turn change the alignment. This should take at most 2 iterations, as + * increasing the reserved amount may cause the FAT size to decrease by 1, + * requiring another nft reserved blocks. If spc changes, it will + * be half of its previous size, and thus will not throw off alignment. + */ + do { + x = tempx; + if (set_res) + bpb.res = (fat == 32 ? MAX(x, MAX(16384 / bpb.bps, 4)) : x) + extra_res; + else if (bpb.res < x) + errx(1, "too few reserved sectors"); + if (fat != 32 && !bpb.rde) + bpb.rde = DEFRDE; + rds = howmany(bpb.rde, bpb.bps / sizeof(struct de)); + if (set_spc) + for (bpb.spc = howmany(fat == 16 ? DEFBLK16 : DEFBLK, bpb.bps); + bpb.spc < MAXSPC && + bpb.res + + howmany((RESFTE + maxcls(fat)) * (fat / BPN), + bpb.bps * NPB) * bpb.nft + + rds + + (u_int64_t)(maxcls(fat) + 1) * bpb.spc <= bpb.bsec; + bpb.spc <<= 1); + if (fat != 32 && bpb.bspf > MAXU16) + errx(1, "too many sectors/FAT for FAT12/16"); + x1 = bpb.res + rds; + x = bpb.bspf ? bpb.bspf : 1; + if (x1 + (u_int64_t)x * bpb.nft > bpb.bsec) + errx(1, "meta data exceeds file system size"); + x1 += x * bpb.nft; + x = (u_int64_t)(bpb.bsec - x1) * bpb.bps * NPB / + (bpb.spc * bpb.bps * NPB + fat / BPN * bpb.nft); + x2 = howmany((RESFTE + MIN(x, maxcls(fat))) * (fat / BPN), bpb.bps * NPB); + if (set_spf) { + bpb.bspf = x2; + x1 += (bpb.bspf - 1) * bpb.nft; + } + if(set_res) { + /* attempt to align root directory */ + alignment = (bpb.res + bpb.bspf * bpb.nft) % bpb.spc; + extra_res += bpb.spc - alignment; + } + attempts++; + } while(opt_A && alignment != 0 && attempts < 2); + if (alignment != 0) + warnx("warning: Alignment failed."); + cls = (bpb.bsec - x1) / bpb.spc; x = (u_int64_t)bpb.bspf * bpb.bps * NPB / (fat / BPN) - RESFTE; if (cls > x) - cls = x; + cls = x; if (bpb.bspf < x2) - warnx("warning: sectors/FAT limits file system to %u clusters", - cls); + warnx("warning: sectors/FAT limits file system to %u clusters", cls); if (cls < mincls(fat)) - errx(1, "%u clusters too few clusters for FAT%u, need %u", cls, fat, - mincls(fat)); + errx(1, "%u clusters too few clusters for FAT%u, need %u", cls, fat, mincls(fat)); if (cls > maxcls(fat)) { - cls = maxcls(fat); - bpb.bsec = x1 + (cls + 1) * bpb.spc - 1; - warnx("warning: FAT type limits file system to %u sectors", - bpb.bsec); + cls = maxcls(fat); + bpb.bsec = x1 + (cls + 1) * bpb.spc - 1; + warnx("warning: FAT type limits file system to %u sectors", bpb.bsec); } - printf("%s: %u sector%s in %u FAT%u cluster%s " - "(%u bytes/cluster)\n", fname, cls * bpb.spc, - cls * bpb.spc == 1 ? "" : "s", cls, fat, - cls == 1 ? "" : "s", bpb.bps * bpb.spc); + printf("%s: %u sector%s in %u FAT%u cluster%s (%u bytes/cluster)\n", + fname, cls * bpb.spc, cls * bpb.spc == 1 ? "" : "s", cls, fat, + cls == 1 ? "" : "s", bpb.bps * bpb.spc); if (!bpb.mid) - bpb.mid = !bpb.hid ? 0xf0 : 0xf8; + bpb.mid = !bpb.hid ? 0xf0 : 0xf8; if (fat == 32) - bpb.rdcl = RESFTE; + bpb.rdcl = RESFTE; if (bpb.hid + bpb.bsec <= MAXU16) { - bpb.sec = bpb.bsec; - bpb.bsec = 0; + bpb.sec = bpb.bsec; + bpb.bsec = 0; } if (fat != 32) { - bpb.spf = bpb.bspf; - bpb.bspf = 0; + bpb.spf = bpb.bspf; + bpb.bspf = 0; } print_bpb(&bpb); if (!opt_N) { - gettimeofday(&tv, NULL); - now = tv.tv_sec; - tm = localtime(&now); - if (!(img = malloc(bpb.bps))) - err(1, "%u", bpb.bps); - dir = bpb.res + (bpb.spf ? bpb.spf : bpb.bspf) * bpb.nft; - for (lsn = 0; lsn < dir + (fat == 32 ? bpb.spc : rds); lsn++) { - x = lsn; - if (opt_B && - fat == 32 && bpb.bkbs != MAXU16 && - bss <= bpb.bkbs && x >= bpb.bkbs) { - x -= bpb.bkbs; - if (!x && lseek(fd1, opt_ofs, SEEK_SET)) - err(1, "%s", bname); - } - if (opt_B && x < bss) { - if ((n = read(fd1, img, bpb.bps)) == -1) - err(1, "%s", bname); - if ((unsigned)n != bpb.bps) - errx(1, "%s: can't read sector %u", bname, x); - } else - memset(img, 0, bpb.bps); - if (!lsn || - (fat == 32 && bpb.bkbs != MAXU16 && lsn == bpb.bkbs)) { - x1 = sizeof(struct bs); - bsbpb = (struct bsbpb *)(img + x1); - mk2(bsbpb->bps, bpb.bps); - mk1(bsbpb->spc, bpb.spc); - mk2(bsbpb->res, bpb.res); - mk1(bsbpb->nft, bpb.nft); - mk2(bsbpb->rde, bpb.rde); - mk2(bsbpb->sec, bpb.sec); - mk1(bsbpb->mid, bpb.mid); - mk2(bsbpb->spf, bpb.spf); - mk2(bsbpb->spt, bpb.spt); - mk2(bsbpb->hds, bpb.hds); - mk4(bsbpb->hid, bpb.hid); - mk4(bsbpb->bsec, bpb.bsec); - x1 += sizeof(struct bsbpb); - if (fat == 32) { - bsxbpb = (struct bsxbpb *)(img + x1); - mk4(bsxbpb->bspf, bpb.bspf); - mk2(bsxbpb->xflg, 0); - mk2(bsxbpb->vers, 0); - mk4(bsxbpb->rdcl, bpb.rdcl); - mk2(bsxbpb->infs, bpb.infs); - mk2(bsxbpb->bkbs, bpb.bkbs); - x1 += sizeof(struct bsxbpb); - } - bsx = (struct bsx *)(img + x1); - mk1(bsx->sig, 0x29); - if (Iflag) - x = opt_I; - else - x = (((u_int)(1 + tm->tm_mon) << 8 | - (u_int)tm->tm_mday) + - ((u_int)tm->tm_sec << 8 | - (u_int)(tv.tv_usec / 10))) << 16 | - ((u_int)(1900 + tm->tm_year) + - ((u_int)tm->tm_hour << 8 | - (u_int)tm->tm_min)); - mk4(bsx->volid, x); - mklabel(bsx->label, opt_L ? opt_L : "NO NAME"); - sprintf(buf, "FAT%u", fat); - setstr(bsx->type, buf, sizeof(bsx->type)); - if (!opt_B) { - x1 += sizeof(struct bsx); - bs = (struct bs *)img; - mk1(bs->jmp[0], 0xeb); - mk1(bs->jmp[1], x1 - 2); - mk1(bs->jmp[2], 0x90); - setstr(bs->oem, opt_O ? opt_O : "BSD 4.4", - sizeof(bs->oem)); - memcpy(img + x1, bootcode, sizeof(bootcode)); - mk2(img + MINBPS - 2, DOSMAGIC); - } - } else if (fat == 32 && bpb.infs != MAXU16 && - (lsn == bpb.infs || - (bpb.bkbs != MAXU16 && - lsn == bpb.bkbs + bpb.infs))) { - mk4(img, 0x41615252); - mk4(img + MINBPS - 28, 0x61417272); - mk4(img + MINBPS - 24, 0xffffffff); - mk4(img + MINBPS - 20, bpb.rdcl); - mk2(img + MINBPS - 2, DOSMAGIC); - } else if (lsn >= bpb.res && lsn < dir && - !((lsn - bpb.res) % - (bpb.spf ? bpb.spf : bpb.bspf))) { - mk1(img[0], bpb.mid); - for (x = 1; x < fat * (fat == 32 ? 3 : 2) / 8; x++) - mk1(img[x], fat == 32 && x % 4 == 3 ? 0x0f : 0xff); - } else if (lsn == dir && opt_L) { - de = (struct de *)img; - mklabel(de->namext, opt_L); - mk1(de->attr, 050); - x = (u_int)tm->tm_hour << 11 | - (u_int)tm->tm_min << 5 | - (u_int)tm->tm_sec >> 1; - mk2(de->time, x); - x = (u_int)(tm->tm_year - 80) << 9 | - (u_int)(tm->tm_mon + 1) << 5 | - (u_int)tm->tm_mday; - mk2(de->date, x); - } - if ((n = write(fd, img, bpb.bps)) == -1) - err(1, "%s", fname); - if ((unsigned)n != bpb.bps) { - errx(1, "%s: can't write sector %u", fname, lsn); + gettimeofday(&tv, NULL); + now = tv.tv_sec; + tm = localtime(&now); + if (!(img = malloc(bpb.bps))) + err(1, "%u", bpb.bps); + dir = bpb.res + (bpb.spf ? bpb.spf : bpb.bspf) * bpb.nft; + for (lsn = 0; lsn < dir + (fat == 32 ? bpb.spc : rds); lsn++) { + x = lsn; + if (opt_B && fat == 32 && bpb.bkbs != MAXU16 && bss <= bpb.bkbs && x >= bpb.bkbs) { + x -= bpb.bkbs; + if (!x && lseek(fd1, opt_ofs, SEEK_SET)) + err(1, "%s", bname); + } + if (opt_B && x < bss) { + if ((n = read(fd1, img, bpb.bps)) == -1) + err(1, "%s", bname); + if ((unsigned)n != bpb.bps) + errx(1, "%s: can't read sector %u", bname, x); + } else + memset(img, 0, bpb.bps); + if (!lsn || (fat == 32 && bpb.bkbs != MAXU16 && lsn == bpb.bkbs)) { + x1 = sizeof(struct bs); + bsbpb = (struct bsbpb *)(img + x1); + mk2(bsbpb->bps, bpb.bps); + mk1(bsbpb->spc, bpb.spc); + mk2(bsbpb->res, bpb.res); + mk1(bsbpb->nft, bpb.nft); + mk2(bsbpb->rde, bpb.rde); + mk2(bsbpb->sec, bpb.sec); + mk1(bsbpb->mid, bpb.mid); + mk2(bsbpb->spf, bpb.spf); + mk2(bsbpb->spt, bpb.spt); + mk2(bsbpb->hds, bpb.hds); + mk4(bsbpb->hid, bpb.hid); + mk4(bsbpb->bsec, bpb.bsec); + x1 += sizeof(struct bsbpb); + if (fat == 32) { + bsxbpb = (struct bsxbpb *)(img + x1); + mk4(bsxbpb->bspf, bpb.bspf); + mk2(bsxbpb->xflg, 0); + mk2(bsxbpb->vers, 0); + mk4(bsxbpb->rdcl, bpb.rdcl); + mk2(bsxbpb->infs, bpb.infs); + mk2(bsxbpb->bkbs, bpb.bkbs); + x1 += sizeof(struct bsxbpb); + } + bsx = (struct bsx *)(img + x1); + mk1(bsx->sig, 0x29); + if (Iflag) + x = opt_I; + else + x = (((u_int)(1 + tm->tm_mon) << 8 | + (u_int)tm->tm_mday) + + ((u_int)tm->tm_sec << 8 | + (u_int)(tv.tv_usec / 10))) << 16 | + ((u_int)(1900 + tm->tm_year) + + ((u_int)tm->tm_hour << 8 | + (u_int)tm->tm_min)); + mk4(bsx->volid, x); + mklabel(bsx->label, opt_L ? opt_L : "NO NAME"); + sprintf(buf, "FAT%u", fat); + setstr(bsx->type, buf, sizeof(bsx->type)); + if (!opt_B) { + x1 += sizeof(struct bsx); + bs = (struct bs *)img; + mk1(bs->jmp[0], 0xeb); + mk1(bs->jmp[1], x1 - 2); + mk1(bs->jmp[2], 0x90); + setstr(bs->oem, opt_O ? opt_O : "BSD 4.4", + sizeof(bs->oem)); + memcpy(img + x1, bootcode, sizeof(bootcode)); + mk2(img + MINBPS - 2, DOSMAGIC); + } + } else if (fat == 32 && bpb.infs != MAXU16 && + (lsn == bpb.infs || (bpb.bkbs != MAXU16 && + lsn == bpb.bkbs + bpb.infs))) { + mk4(img, 0x41615252); + mk4(img + MINBPS - 28, 0x61417272); + mk4(img + MINBPS - 24, 0xffffffff); + mk4(img + MINBPS - 20, bpb.rdcl); + mk2(img + MINBPS - 2, DOSMAGIC); + } else if (lsn >= bpb.res && lsn < dir && + !((lsn - bpb.res) % (bpb.spf ? bpb.spf : bpb.bspf))) { + mk1(img[0], bpb.mid); + for (x = 1; x < fat * (fat == 32 ? 3 : 2) / 8; x++) + mk1(img[x], fat == 32 && x % 4 == 3 ? 0x0f : 0xff); + } else if (lsn == dir && opt_L) { + de = (struct de *)img; + mklabel(de->namext, opt_L); + mk1(de->attr, 050); + x = (u_int)tm->tm_hour << 11 | + (u_int)tm->tm_min << 5 | + (u_int)tm->tm_sec >> 1; + mk2(de->time, x); + x = (u_int)(tm->tm_year - 80) << 9 | + (u_int)(tm->tm_mon + 1) << 5 | + (u_int)tm->tm_mday; + mk2(de->date, x); + } + if ((n = write(fd, img, bpb.bps)) == -1) + err(1, "%s", fname); + if ((unsigned)n != bpb.bps) { + errx(1, "%s: can't write sector %u", fname, lsn); exit(1); } - } + } } return 0; } @@ -725,8 +746,7 @@ newfs_msdos_main(int argc, char *argv[]) /* * Exit with error if file system is mounted. */ -static void -check_mounted(const char *fname, mode_t mode) +static void check_mounted(const char *fname, mode_t mode) { #ifdef ANDROID warnx("Skipping mount checks"); @@ -737,19 +757,18 @@ check_mounted(const char *fname, mode_t mode) int n, r; if (!(n = getmntinfo(&mp, MNT_NOWAIT))) - err(1, "getmntinfo"); + err(1, "getmntinfo"); len = strlen(_PATH_DEV); s1 = fname; if (!strncmp(s1, _PATH_DEV, len)) - s1 += len; + s1 += len; r = S_ISCHR(mode) && s1 != fname && *s1 == 'r'; for (; n--; mp++) { - s2 = mp->f_mntfromname; - if (!strncmp(s2, _PATH_DEV, len)) - s2 += len; - if ((r && s2 != mp->f_mntfromname && !strcmp(s1 + 1, s2)) || - !strcmp(s1, s2)) - errx(1, "%s is mounted on %s", fname, mp->f_mntonname); + s2 = mp->f_mntfromname; + if (!strncmp(s2, _PATH_DEV, len)) + s2 += len; + if ((r && s2 != mp->f_mntfromname && !strcmp(s1 + 1, s2)) || !strcmp(s1, s2)) + errx(1, "%s is mounted on %s", fname, mp->f_mntonname); } #endif } @@ -757,15 +776,14 @@ check_mounted(const char *fname, mode_t mode) /* * Get a standard format. */ -static void -getstdfmt(const char *fmt, struct bpb *bpb) +static void getstdfmt(const char *fmt, struct bpb *bpb) { u_int x, i; x = sizeof(stdfmt) / sizeof(stdfmt[0]); for (i = 0; i < x && strcmp(fmt, stdfmt[i].name); i++); if (i == x) - errx(1, "%s: unknown standard format", fmt); + errx(1, "%s: unknown standard format", fmt); *bpb = stdfmt[i].bpb; } @@ -774,9 +792,8 @@ getstdfmt(const char *fmt, struct bpb *bpb) */ #ifdef ANDROID -static void -getdiskinfo(int fd, const char *fname, const char *dtype, __unused int oflag, - struct bpb *bpb) +static void getdiskinfo(int fd, const char *fname, const char *dtype, + __unused int oflag,struct bpb *bpb) { struct hd_geometry geom; @@ -817,9 +834,8 @@ getdiskinfo(int fd, const char *fname, const char *dtype, __unused int oflag, #else -static void -getdiskinfo(int fd, const char *fname, const char *dtype, __unused int oflag, - struct bpb *bpb) +static void getdiskinfo(int fd, const char *fname, const char *dtype, + __unused int oflag, struct bpb *bpb) { struct disklabel *lp, dlp; struct fd_type type; @@ -829,97 +845,96 @@ getdiskinfo(int fd, const char *fname, const char *dtype, __unused int oflag, /* If the user specified a disk type, try to use that */ if (dtype != NULL) { - lp = getdiskbyname(dtype); + lp = getdiskbyname(dtype); } /* Maybe it's a floppy drive */ if (lp == NULL) { - if (ioctl(fd, DIOCGMEDIASIZE, &ms) == -1) { - struct stat st; - - if (fstat(fd, &st)) - err(1, "Cannot get disk size"); - /* create a fake geometry for a file image */ - ms = st.st_size; - dlp.d_secsize = 512; - dlp.d_nsectors = 63; - dlp.d_ntracks = 255; - dlp.d_secperunit = ms / dlp.d_secsize; - lp = &dlp; - } else if (ioctl(fd, FD_GTYPE, &type) != -1) { - dlp.d_secsize = 128 << type.secsize; - dlp.d_nsectors = type.sectrac; - dlp.d_ntracks = type.heads; - dlp.d_secperunit = ms / dlp.d_secsize; - lp = &dlp; - } + if (ioctl(fd, DIOCGMEDIASIZE, &ms) == -1) { + struct stat st; + + if (fstat(fd, &st)) + err(1, "Cannot get disk size"); + /* create a fake geometry for a file image */ + ms = st.st_size; + dlp.d_secsize = 512; + dlp.d_nsectors = 63; + dlp.d_ntracks = 255; + dlp.d_secperunit = ms / dlp.d_secsize; + lp = &dlp; + } else if (ioctl(fd, FD_GTYPE, &type) != -1) { + dlp.d_secsize = 128 << type.secsize; + dlp.d_nsectors = type.sectrac; + dlp.d_ntracks = type.heads; + dlp.d_secperunit = ms / dlp.d_secsize; + lp = &dlp; + } } /* Maybe it's a fixed drive */ if (lp == NULL) { - if (ioctl(fd, DIOCGDINFO, &dlp) == -1) { - if (bpb->bps == 0 && ioctl(fd, DIOCGSECTORSIZE, &dlp.d_secsize) == -1) - errx(1, "Cannot get sector size, %s", strerror(errno)); - - /* XXX Should we use bpb->bps if it's set? */ - dlp.d_secperunit = ms / dlp.d_secsize; - - if (bpb->spt == 0 && ioctl(fd, DIOCGFWSECTORS, &dlp.d_nsectors) == -1) { - warnx("Cannot get number of sectors per track, %s", strerror(errno)); - dlp.d_nsectors = 63; - } - if (bpb->hds == 0 && ioctl(fd, DIOCGFWHEADS, &dlp.d_ntracks) == -1) { - warnx("Cannot get number of heads, %s", strerror(errno)); - if (dlp.d_secperunit <= 63*1*1024) - dlp.d_ntracks = 1; - else if (dlp.d_secperunit <= 63*16*1024) - dlp.d_ntracks = 16; - else - dlp.d_ntracks = 255; - } - } - - hs = (ms / dlp.d_secsize) - dlp.d_secperunit; - lp = &dlp; + if (ioctl(fd, DIOCGDINFO, &dlp) == -1) { + if (bpb->bps == 0 && ioctl(fd, DIOCGSECTORSIZE, &dlp.d_secsize) == -1) + errx(1, "Cannot get sector size, %s", strerror(errno)); + + /* XXX Should we use bpb->bps if it's set? */ + dlp.d_secperunit = ms / dlp.d_secsize; + + if (bpb->spt == 0 && ioctl(fd, DIOCGFWSECTORS, &dlp.d_nsectors) == -1) { + warnx("Cannot get number of sectors per track, %s", strerror(errno)); + dlp.d_nsectors = 63; + } + if (bpb->hds == 0 && ioctl(fd, DIOCGFWHEADS, &dlp.d_ntracks) == -1) { + warnx("Cannot get number of heads, %s", strerror(errno)); + if (dlp.d_secperunit <= 63*1*1024) + dlp.d_ntracks = 1; + else if (dlp.d_secperunit <= 63*16*1024) + dlp.d_ntracks = 16; + else + dlp.d_ntracks = 255; + } + } + + hs = (ms / dlp.d_secsize) - dlp.d_secperunit; + lp = &dlp; } if (bpb->bps == 0) - bpb->bps = ckgeom(fname, lp->d_secsize, "bytes/sector"); + bpb->bps = ckgeom(fname, lp->d_secsize, "bytes/sector"); if (bpb->spt == 0) - bpb->spt = ckgeom(fname, lp->d_nsectors, "sectors/track"); + bpb->spt = ckgeom(fname, lp->d_nsectors, "sectors/track"); if (bpb->hds == 0) - bpb->hds = ckgeom(fname, lp->d_ntracks, "drive heads"); + bpb->hds = ckgeom(fname, lp->d_ntracks, "drive heads"); if (bpb->bsec == 0) - bpb->bsec = lp->d_secperunit; + bpb->bsec = lp->d_secperunit; if (bpb->hid == 0) - bpb->hid = hs; + bpb->hid = hs; } #endif /* * Print out BPB values. */ -static void -print_bpb(struct bpb *bpb) +static void print_bpb(struct bpb *bpb) { printf("bps=%u spc=%u res=%u nft=%u", bpb->bps, bpb->spc, bpb->res, - bpb->nft); + bpb->nft); if (bpb->rde) - printf(" rde=%u", bpb->rde); + printf(" rde=%u", bpb->rde); if (bpb->sec) - printf(" sec=%u", bpb->sec); + printf(" sec=%u", bpb->sec); printf(" mid=%#x", bpb->mid); if (bpb->spf) - printf(" spf=%u", bpb->spf); + printf(" spf=%u", bpb->spf); printf(" spt=%u hds=%u hid=%u", bpb->spt, bpb->hds, bpb->hid); if (bpb->bsec) - printf(" bsec=%u", bpb->bsec); + printf(" bsec=%u", bpb->bsec); if (!bpb->spf) { - printf(" bspf=%u rdcl=%u", bpb->bspf, bpb->rdcl); - printf(" infs="); - printf(bpb->infs == MAXU16 ? "%#x" : "%u", bpb->infs); - printf(" bkbs="); - printf(bpb->bkbs == MAXU16 ? "%#x" : "%u", bpb->bkbs); + printf(" bspf=%u rdcl=%u", bpb->bspf, bpb->rdcl); + printf(" infs="); + printf(bpb->infs == MAXU16 ? "%#x" : "%u", bpb->infs); + printf(" bkbs="); + printf(bpb->bkbs == MAXU16 ? "%#x" : "%u", bpb->bkbs); } printf("\n"); } @@ -927,21 +942,19 @@ print_bpb(struct bpb *bpb) /* * Check a disk geometry value. */ -static u_int -ckgeom(const char *fname, u_int val, const char *msg) +static u_int ckgeom(const char *fname, u_int val, const char *msg) { if (!val) - errx(1, "%s: no default %s", fname, msg); + errx(1, "%s: no default %s", fname, msg); if (val > MAXU16) - errx(1, "%s: illegal %s %d", fname, msg, val); + errx(1, "%s: illegal %s %d", fname, msg, val); return val; } /* * Convert and check a numeric option argument. */ -static u_int -argtou(const char *arg, u_int lo, u_int hi, const char *msg) +static u_int argtou(const char *arg, u_int lo, u_int hi, const char *msg) { char *s; u_long x; @@ -949,15 +962,14 @@ argtou(const char *arg, u_int lo, u_int hi, const char *msg) errno = 0; x = strtoul(arg, &s, 0); if (errno || !*arg || *s || x < lo || x > hi) - errx(1, "%s: bad %s", arg, msg); + errx(1, "%s: bad %s", arg, msg); return x; } /* * Same for off_t, with optional skmgpP suffix */ -static off_t -argtooff(const char *arg, const char *msg) +static off_t argtooff(const char *arg, const char *msg) { char *s; off_t x; @@ -965,40 +977,40 @@ argtooff(const char *arg, const char *msg) x = strtoll(arg, &s, 0); /* allow at most one extra char */ if (errno || x < 0 || (s[0] && s[1]) ) - errx(1, "%s: bad %s", arg, msg); - if (*s) { /* the extra char is the multiplier */ - switch (*s) { - default: - errx(1, "%s: bad %s", arg, msg); - /* notreached */ - - case 's': /* sector */ - case 'S': - x <<= 9; /* times 512 */ - break; - - case 'k': /* kilobyte */ - case 'K': - x <<= 10; /* times 1024 */ - break; - - case 'm': /* megabyte */ - case 'M': - x <<= 20; /* times 1024*1024 */ - break; - - case 'g': /* gigabyte */ - case 'G': - x <<= 30; /* times 1024*1024*1024 */ - break; - - case 'p': /* partition start */ - case 'P': /* partition start */ - case 'l': /* partition length */ - case 'L': /* partition length */ - errx(1, "%s: not supported yet %s", arg, msg); - /* notreached */ - } + errx(1, "%s: bad %s", arg, msg); + if (*s) { /* the extra char is the multiplier */ + switch (*s) { + default: + errx(1, "%s: bad %s", arg, msg); + /* notreached */ + + case 's': /* sector */ + case 'S': + x <<= 9; /* times 512 */ + break; + + case 'k': /* kilobyte */ + case 'K': + x <<= 10; /* times 1024 */ + break; + + case 'm': /* megabyte */ + case 'M': + x <<= 20; /* times 1024*1024 */ + break; + + case 'g': /* gigabyte */ + case 'G': + x <<= 30; /* times 1024*1024*1024 */ + break; + + case 'p': /* partition start */ + case 'P': /* partition start */ + case 'l': /* partition length */ + case 'L': /* partition length */ + errx(1, "%s: not supported yet %s", arg, msg); + /* notreached */ + } } return x; } @@ -1006,15 +1018,14 @@ argtooff(const char *arg, const char *msg) /* * Check a volume label. */ -static int -oklabel(const char *src) +static int oklabel(const char *src) { int c, i; for (i = 0; i <= 11; i++) { - c = (u_char)*src++; - if (c < ' ' + !i || strchr("\"*+,./:;<=>?[\\]|", c)) - break; + c = (u_char)*src++; + if (c < ' ' + !i || strchr("\"*+,./:;<=>?[\\]|", c)) + break; } return i && !c; } @@ -1022,58 +1033,56 @@ oklabel(const char *src) /* * Make a volume label. */ -static void -mklabel(u_int8_t *dest, const char *src) +static void mklabel(u_int8_t *dest, const char *src) { int c, i; for (i = 0; i < 11; i++) { - c = *src ? toupper(*src++) : ' '; - *dest++ = !i && c == '\xe5' ? 5 : c; + c = *src ? toupper(*src++) : ' '; + *dest++ = !i && c == '\xe5' ? 5 : c; } } /* * Copy string, padding with spaces. */ -static void -setstr(u_int8_t *dest, const char *src, size_t len) +static void setstr(u_int8_t *dest, const char *src, size_t len) { while (len--) - *dest++ = *src ? *src++ : ' '; + *dest++ = *src ? *src++ : ' '; } /* * Print usage message. */ -static void -usage(void) +static void usage(void) { - fprintf(stderr, - "usage: newfs_msdos [ -options ] special [disktype]\n" - "where the options are:\n" - "\t-@ create file system at specified offset\n" - "\t-B get bootstrap from file\n" - "\t-C create image file with specified size\n" - "\t-F FAT type (12, 16, or 32)\n" - "\t-I volume ID\n" - "\t-L volume label\n" - "\t-N don't create file system: just print out parameters\n" - "\t-O OEM string\n" - "\t-S bytes/sector\n" - "\t-a sectors/FAT\n" - "\t-b block size\n" - "\t-c sectors/cluster\n" - "\t-e root directory entries\n" - "\t-f standard format\n" - "\t-h drive heads\n" - "\t-i file system info sector\n" - "\t-k backup boot sector\n" - "\t-m media descriptor\n" - "\t-n number of FATs\n" - "\t-o hidden sectors\n" - "\t-r reserved sectors\n" - "\t-s file system size (sectors)\n" - "\t-u sectors/track\n"); - exit(1); + fprintf(stderr, + "usage: newfs_msdos [ -options ] special [disktype]\n" + "where the options are:\n" + "\t-@ create file system at specified offset\n" + "\t-A Attempt to cluster align root directory\n" + "\t-B get bootstrap from file\n" + "\t-C create image file with specified size\n" + "\t-F FAT type (12, 16, or 32)\n" + "\t-I volume ID\n" + "\t-L volume label\n" + "\t-N don't create file system: just print out parameters\n" + "\t-O OEM string\n" + "\t-S bytes/sector\n" + "\t-a sectors/FAT\n" + "\t-b block size\n" + "\t-c sectors/cluster\n" + "\t-e root directory entries\n" + "\t-f standard format\n" + "\t-h drive heads\n" + "\t-i file system info sector\n" + "\t-k backup boot sector\n" + "\t-m media descriptor\n" + "\t-n number of FATs\n" + "\t-o hidden sectors\n" + "\t-r reserved sectors\n" + "\t-s file system size (sectors)\n" + "\t-u sectors/track\n"); + exit(1); } diff --git a/toolbox/ps.c b/toolbox/ps.c index 57b4280..5458f6b 100644 --- a/toolbox/ps.c +++ b/toolbox/ps.c @@ -28,7 +28,8 @@ static char *nexttok(char **strp) #define SHOW_POLICY 4 #define SHOW_CPU 8 #define SHOW_MACLABEL 16 -#define SHOW_ABI 32 +#define SHOW_NUMERIC_UID 32 +#define SHOW_ABI 64 static int display_flags = 0; @@ -48,7 +49,7 @@ static int ps_line(int pid, int tid, char *namefilter) unsigned utime, stime; int prio, nice, rtprio, sched, psr; struct passwd *pw; - + sprintf(statline, "/proc/%d", pid); stat(statline, &stats); @@ -70,7 +71,7 @@ static int ps_line(int pid, int tid, char *namefilter) } cmdline[r] = 0; } - + fd = open(statline, O_RDONLY); if(fd == 0) return -1; r = read(fd, statline, 1023); @@ -92,7 +93,6 @@ static int ps_line(int pid, int tid, char *namefilter) nexttok(&ptr); // pgrp nexttok(&ptr); // sid nexttok(&ptr); // tty - nexttok(&ptr); // tpgid nexttok(&ptr); // flags nexttok(&ptr); // minflt @@ -132,21 +132,21 @@ static int ps_line(int pid, int tid, char *namefilter) psr = atoi(nexttok(&ptr)); // processor rtprio = atoi(nexttok(&ptr)); // rt_priority sched = atoi(nexttok(&ptr)); // scheduling policy - + nexttok(&ptr); // tty - + if(tid != 0) { ppid = pid; pid = tid; } pw = getpwuid(stats.st_uid); - if(pw == 0) { + if(pw == 0 || (display_flags & SHOW_NUMERIC_UID)) { sprintf(user,"%d",(int)stats.st_uid); } else { strcpy(user,pw->pw_name); } - + if(!namefilter || !strncmp(name, namefilter, strlen(namefilter))) { if (display_flags & SHOW_MACLABEL) { fd = open(macline, O_RDONLY); @@ -229,7 +229,7 @@ void ps_threads(int pid, char *namefilter) sprintf(tmp,"/proc/%d/task",pid); d = opendir(tmp); if(d == 0) return; - + while((de = readdir(d)) != 0){ if(isdigit(de->d_name[0])){ int tid = atoi(de->d_name); @@ -237,7 +237,7 @@ void ps_threads(int pid, char *namefilter) ps_line(pid, tid, namefilter); } } - closedir(d); + closedir(d); } int ps_main(int argc, char **argv) @@ -247,13 +247,15 @@ int ps_main(int argc, char **argv) char *namefilter = 0; int pidfilter = 0; int threads = 0; - + d = opendir("/proc"); if(d == 0) return -1; while(argc > 1){ if(!strcmp(argv[1],"-t")) { threads = 1; + } else if(!strcmp(argv[1],"-n")) { + display_flags |= SHOW_NUMERIC_UID; } else if(!strcmp(argv[1],"-x")) { display_flags |= SHOW_TIME; } else if(!strcmp(argv[1], "-Z")) { |