diff options
67 files changed, 3606 insertions, 1325 deletions
diff --git a/adb/Android.mk b/adb/Android.mk index 50e28a6..80427b8 100644 --- a/adb/Android.mk +++ b/adb/Android.mk @@ -102,7 +102,6 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := \ adb.c \ - backup_service.c \ fdevent.c \ transport.c \ transport_local.c \ @@ -326,11 +326,6 @@ int create_jdwp_connection_fd(int jdwp_pid); int handle_forward_request(const char* service, transport_type ttype, char* serial, int reply_fd); #if !ADB_HOST -typedef enum { - BACKUP, - RESTORE -} BackupOperation; -int backup_service(BackupOperation operation, char* args); void framebuffer_service(int fd, void *cookie); void remount_service(int fd, void *cookie); #endif @@ -418,7 +413,7 @@ void adb_qemu_trace(const char* fmt, ...); # define D(...) ((void)0) # define DR(...) ((void)0) # define ADB_TRACING 0 -#endif +#endif /* ADB_TRACE */ #if !DEBUG_PACKETS @@ -476,6 +471,11 @@ int connection_state(atransport *t); extern int HOST; extern int SHELL_EXIT_NOTIFY_FD; +typedef enum { + SUBPROC_PTY = 0, + SUBPROC_RAW = 1, +} subproc_mode; + #define CHUNK_SIZE (64*1024) #if !ADB_HOST diff --git a/adb/backup_service.c b/adb/backup_service.c deleted file mode 100644 index 654e0f3..0000000 --- a/adb/backup_service.c +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright (C) 2011 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 <unistd.h> -#include <stdio.h> - -#include "sysdeps.h" - -#define TRACE_TAG TRACE_ADB -#include "adb.h" - -typedef struct { - pid_t pid; - int fd; -} backup_harvest_params; - -// socketpair but do *not* mark as close_on_exec -static int backup_socketpair(int sv[2]) { - int rc = unix_socketpair( AF_UNIX, SOCK_STREAM, 0, sv ); - if (rc < 0) - return -1; - - return 0; -} - -// harvest the child process then close the read end of the socketpair -static void* backup_child_waiter(void* args) { - int status; - backup_harvest_params* params = (backup_harvest_params*) args; - - waitpid(params->pid, &status, 0); - adb_close(params->fd); - free(params); - return NULL; -} - -/* returns the data socket passing the backup data here for forwarding */ -int backup_service(BackupOperation op, char* args) { - pid_t pid; - int s[2]; - char* operation; - - // Command string depends on our invocation - if (op == BACKUP) { - operation = "backup"; - } else { - operation = "restore"; - } - - D("backup_service(%s, %s)\n", operation, args); - - // set up the pipe from the subprocess to here - // parent will read s[0]; child will write s[1] - if (backup_socketpair(s)) { - D("can't create backup/restore socketpair\n"); - fprintf(stderr, "unable to create backup/restore socketpair\n"); - return -1; - } - - D("Backup/restore socket pair: (send=%d, receive=%d)\n", s[1], s[0]); - close_on_exec(s[0]); // only the side we hold on to - - // spin off the child process to run the backup command - pid = fork(); - if (pid < 0) { - // failure - D("can't fork for %s\n", operation); - fprintf(stderr, "unable to fork for %s\n", operation); - adb_close(s[0]); - adb_close(s[1]); - return -1; - } - - // Great, we're off and running. - if (pid == 0) { - // child -- actually run the backup here - char* p; - int argc; - char portnum[16]; - char** bu_args; - - // fixed args: [0] is 'bu', [1] is the port number, [2] is the 'operation' string - argc = 3; - for (p = (char*)args; p && *p; ) { - argc++; - while (*p && *p != ':') p++; - if (*p == ':') p++; - } - - bu_args = (char**) alloca(argc*sizeof(char*) + 1); - - // run through again to build the argv array - argc = 0; - bu_args[argc++] = "bu"; - snprintf(portnum, sizeof(portnum), "%d", s[1]); - bu_args[argc++] = portnum; - bu_args[argc++] = operation; - for (p = (char*)args; p && *p; ) { - bu_args[argc++] = p; - while (*p && *p != ':') p++; - if (*p == ':') { - *p = 0; - p++; - } - } - bu_args[argc] = NULL; - - // Close the half of the socket that we don't care about, route 'bu's console - // to the output socket, and off we go - adb_close(s[0]); - - // off we go - execvp("/system/bin/bu", (char * const *)bu_args); - // oops error - close up shop and go home - fprintf(stderr, "Unable to exec 'bu', bailing\n"); - exit(-1); - } else { - adb_thread_t t; - backup_harvest_params* params; - - // parent, i.e. adbd -- close the sending half of the socket - D("fork() returned pid %d\n", pid); - adb_close(s[1]); - - // spin a thread to harvest the child process - params = (backup_harvest_params*) malloc(sizeof(backup_harvest_params)); - params->pid = pid; - params->fd = s[0]; - if (adb_thread_create(&t, backup_child_waiter, params)) { - adb_close(s[0]); - free(params); - D("Unable to create child harvester\n"); - return -1; - } - } - - // we'll be reading from s[0] as the data is sent by the child process - return s[0]; -} diff --git a/adb/commandline.c b/adb/commandline.c index 2e4cd37..507f73e 100644 --- a/adb/commandline.c +++ b/adb/commandline.c @@ -110,9 +110,10 @@ void help() " adb push [-p] <local> <remote>\n" " - copy file/dir to device\n" " ('-p' to display the transfer progress)\n" - " adb pull [-p] <remote> [<local>]\n" + " adb pull [-p] [-a] <remote> [<local>]\n" " - copy file/dir from device\n" " ('-p' to display the transfer progress)\n" + " ('-a' means copy timestamp and mode)\n" " adb sync [ <directory> ] - copy host->device only if changed\n" " (-l means list but don't copy)\n" " (see 'adb help all')\n" @@ -286,8 +287,17 @@ static void copy_to_file(int inFd, int outFd) { long total = 0; D("copy_to_file(%d -> %d)\n", inFd, outFd); +#ifdef HAVE_TERMIO_H + if (inFd == STDIN_FILENO) { + stdin_raw_init(STDIN_FILENO); + } +#endif for (;;) { - len = adb_read(inFd, buf, BUFSIZE); + if (inFd == STDIN_FILENO) { + len = unix_read(inFd, buf, BUFSIZE); + } else { + len = adb_read(inFd, buf, BUFSIZE); + } if (len == 0) { D("copy_to_file() : read 0 bytes; exiting\n"); break; @@ -300,9 +310,19 @@ static void copy_to_file(int inFd, int outFd) { D("copy_to_file() : error %d\n", errno); break; } - adb_write(outFd, buf, len); + if (outFd == STDOUT_FILENO) { + fwrite(buf, 1, len, stdout); + fflush(stdout); + } else { + adb_write(outFd, buf, len); + } total += len; } +#ifdef HAVE_TERMIO_H + if (inFd == STDIN_FILENO) { + stdin_raw_restore(STDIN_FILENO); + } +#endif D("copy_to_file() finished after %lu bytes\n", total); free(buf); } @@ -525,39 +545,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; @@ -664,30 +685,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); } @@ -939,13 +954,19 @@ static const char *find_product_out_path(const char *hint) return path_buf; } - -static void parse_push_pull_args(char** arg, int narg, char const** path1, char const** path2, - int* show_progress) { +static void parse_push_pull_args(char **arg, int narg, char const **path1, char const **path2, + int *show_progress, int *copy_attrs) { *show_progress = 0; + *copy_attrs = 0; - if ((narg > 0) && !strcmp(*arg, "-p")) { - *show_progress = 1; + while (narg > 0) { + if (!strcmp(*arg, "-p")) { + *show_progress = 1; + } else if (!strcmp(*arg, "-a")) { + *copy_attrs = 1; + } else { + break; + } ++arg; --narg; } @@ -969,7 +990,6 @@ int adb_commandline(int argc, char **argv) int is_server = 0; int persist = 0; int r; - int quote; transport_type ttype = kTransportAny; char* serial = NULL; char* server_port_str = NULL; @@ -1190,19 +1210,14 @@ top: return r; } - snprintf(buf, sizeof buf, "shell:%s", argv[1]); + snprintf(buf, sizeof(buf), "shell:%s", argv[1]); argc -= 2; argv += 2; - while(argc-- > 0) { - strcat(buf, " "); - - /* quote empty strings and strings with spaces */ - quote = (**argv == 0 || strchr(*argv, ' ')); - if (quote) - strcat(buf, "\""); - strcat(buf, *argv++); - if (quote) - strcat(buf, "\""); + while (argc-- > 0) { + char *quoted = escape_arg(*argv++); + strncat(buf, " ", sizeof(buf) - 1); + strncat(buf, quoted, sizeof(buf) - 1); + free(quoted); } for(;;) { @@ -1234,6 +1249,36 @@ top: } } + if (!strcmp(argv[0], "exec-in") || !strcmp(argv[0], "exec-out")) { + int exec_in = !strcmp(argv[0], "exec-in"); + int fd; + + snprintf(buf, sizeof buf, "exec:%s", argv[1]); + argc -= 2; + argv += 2; + while (argc-- > 0) { + char *quoted = escape_arg(*argv++); + strncat(buf, " ", sizeof(buf) - 1); + strncat(buf, quoted, sizeof(buf) - 1); + free(quoted); + } + + fd = adb_connect(buf); + if (fd < 0) { + fprintf(stderr, "error: %s\n", adb_error()); + return -1; + } + + if (exec_in) { + copy_to_file(STDIN_FILENO, fd); + } else { + copy_to_file(fd, STDOUT_FILENO); + } + + adb_close(fd); + return 0; + } + if(!strcmp(argv[0], "kill-server")) { int fd; fd = _adb_connect("host:kill"); @@ -1416,9 +1461,10 @@ top: if(!strcmp(argv[0], "push")) { int show_progress = 0; + int copy_attrs = 0; // unused const char* lpath = NULL, *rpath = NULL; - parse_push_pull_args(&argv[1], argc - 1, &lpath, &rpath, &show_progress); + 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); @@ -1429,12 +1475,13 @@ top: if(!strcmp(argv[0], "pull")) { int show_progress = 0; + int copy_attrs = 0; const char* rpath = NULL, *lpath = "."; - parse_push_pull_args(&argv[1], argc - 1, &rpath, &lpath, &show_progress); + parse_push_pull_args(&argv[1], argc - 1, &rpath, &lpath, &show_progress, ©_attrs); if (rpath != NULL) { - return do_sync_pull(rpath, lpath, show_progress); + return do_sync_pull(rpath, lpath, show_progress, copy_attrs); } return usage(); @@ -1635,12 +1682,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); } @@ -1672,7 +1716,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); diff --git a/adb/file_sync_client.c b/adb/file_sync_client.c index dc4e77f..d3cb113 100644 --- a/adb/file_sync_client.c +++ b/adb/file_sync_client.c @@ -25,6 +25,7 @@ #include <limits.h> #include <sys/types.h> #include <zipfile/zipfile.h> +#include <utime.h> #include "sysdeps.h" #include "adb.h" @@ -139,7 +140,8 @@ struct syncsendbuf { static syncsendbuf send_buffer; -int sync_readtime(int fd, const char *path, unsigned *timestamp) +int sync_readtime(int fd, const char *path, unsigned int *timestamp, + unsigned int *mode) { syncmsg msg; int len = strlen(path); @@ -161,6 +163,7 @@ int sync_readtime(int fd, const char *path, unsigned *timestamp) } *timestamp = ltohl(msg.stat.time); + *mode = ltohl(msg.stat.mode); return 0; } @@ -237,7 +240,7 @@ static int write_data_file(int fd, const char *path, syncsendbuf *sbuf, int show if (show_progress) { // Determine local file size. struct stat st; - if (lstat(path, &st)) { + if (fstat(lfd, &st)) { fprintf(stderr,"cannot stat '%s': %s\n", path, strerror(errno)); return -1; } @@ -931,8 +934,21 @@ static int remote_build_list(int syncfd, copyinfo **filelist, return 0; } +static int set_time_and_mode(const char *lpath, unsigned int time, unsigned int mode) +{ + struct utimbuf times = { time, time }; + int r1 = utime(lpath, ×); + + /* use umask for permissions */ + mode_t mask=umask(0000); + umask(mask); + int r2 = chmod(lpath, mode & ~mask); + + return r1 ? : r2; +} + static int copy_remote_dir_local(int fd, const char *rpath, const char *lpath, - int checktimestamps) + int copy_attrs) { copyinfo *filelist = 0; copyinfo *ci, *next; @@ -962,26 +978,6 @@ static int copy_remote_dir_local(int fd, const char *rpath, const char *lpath, return -1; } -#if 0 - if (checktimestamps) { - for (ci = filelist; ci != 0; ci = ci->next) { - if (sync_start_readtime(fd, ci->dst)) { - return 1; - } - } - for (ci = filelist; ci != 0; ci = ci->next) { - unsigned int timestamp, mode, size; - if (sync_finish_readtime(fd, ×tamp, &mode, &size)) - return 1; - if (size == ci->size) { - /* for links, we cannot update the atime/mtime */ - if ((S_ISREG(ci->mode & mode) && timestamp == ci->time) || - (S_ISLNK(ci->mode & mode) && timestamp >= ci->time)) - ci->flag = 1; - } - } - } -#endif for (ci = filelist; ci != 0; ci = next) { next = ci->next; if (ci->flag == 0) { @@ -989,6 +985,10 @@ static int copy_remote_dir_local(int fd, const char *rpath, const char *lpath, if (sync_recv(fd, ci->src, ci->dst, 0 /* no show progress */)) { return 1; } + + if (copy_attrs && set_time_and_mode(ci->dst, ci->time, ci->mode)) { + return 1; + } pulled++; } else { skipped++; @@ -1003,9 +1003,9 @@ static int copy_remote_dir_local(int fd, const char *rpath, const char *lpath, return 0; } -int do_sync_pull(const char *rpath, const char *lpath, int show_progress) +int do_sync_pull(const char *rpath, const char *lpath, int show_progress, int copy_attrs) { - unsigned mode; + unsigned mode, time; struct stat st; int fd; @@ -1016,7 +1016,7 @@ int do_sync_pull(const char *rpath, const char *lpath, int show_progress) return 1; } - if(sync_readmode(fd, rpath, &mode)) { + if(sync_readtime(fd, rpath, &time, &mode)) { return 1; } if(mode == 0) { @@ -1047,13 +1047,15 @@ int do_sync_pull(const char *rpath, const char *lpath, int show_progress) if (sync_recv(fd, rpath, lpath, show_progress)) { return 1; } else { + if (copy_attrs && set_time_and_mode(lpath, time, mode)) + return 1; END(); sync_quit(fd); return 0; } } else if(S_ISDIR(mode)) { BEGIN(); - if (copy_remote_dir_local(fd, rpath, lpath, 0)) { + if (copy_remote_dir_local(fd, rpath, lpath, copy_attrs)) { return 1; } else { END(); diff --git a/adb/file_sync_service.h b/adb/file_sync_service.h index 3e7e096..8ea239e 100644 --- a/adb/file_sync_service.h +++ b/adb/file_sync_service.h @@ -80,7 +80,7 @@ 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_sync(const char *lpath, const char *rpath, int listonly); -int do_sync_pull(const char *rpath, const char *lpath, int show_progress); +int do_sync_pull(const char *rpath, const char *lpath, int show_progress, int pullTime); #define SYNC_DATA_MAX (64*1024) diff --git a/adb/services.c b/adb/services.c index 7b809da..d1e8939 100644 --- a/adb/services.c +++ b/adb/services.c @@ -184,11 +184,26 @@ static int create_service_thread(void (*func)(int, void *), void *cookie) } #if !ADB_HOST -static int create_subprocess(const char *cmd, const char *arg0, const char *arg1, pid_t *pid) + +static void init_subproc_child() +{ + setsid(); + + // Set OOM adjustment to prevent killing + int fd = adb_open("/proc/self/oom_adj", O_WRONLY); + if (fd >= 0) { + adb_write(fd, "0", 1); + adb_close(fd); + } else { + D("adb: unable to update oom_adj\n"); + } +} + +static int create_subproc_pty(const char *cmd, const char *arg0, const char *arg1, pid_t *pid) { + D("create_subproc_pty(cmd=%s, arg0=%s, arg1=%s)\n", cmd, arg0, arg1); #ifdef HAVE_WIN32_PROC - D("create_subprocess(cmd=%s, arg0=%s, arg1=%s)\n", cmd, arg0, arg1); - fprintf(stderr, "error: create_subprocess not implemented on Win32 (%s %s %s)\n", cmd, arg0, arg1); + fprintf(stderr, "error: create_subproc_pty not implemented on Win32 (%s %s %s)\n", cmd, arg0, arg1); return -1; #else /* !HAVE_WIN32_PROC */ char *devname; @@ -215,47 +230,74 @@ static int create_subprocess(const char *cmd, const char *arg0, const char *arg1 return -1; } - if(*pid == 0){ - int pts; - - setsid(); + if (*pid == 0) { + init_subproc_child(); - pts = unix_open(devname, O_RDWR); - if(pts < 0) { + int pts = unix_open(devname, O_RDWR); + if (pts < 0) { fprintf(stderr, "child failed to open pseudo-term slave: %s\n", devname); exit(-1); } - dup2(pts, 0); - dup2(pts, 1); - dup2(pts, 2); + dup2(pts, STDIN_FILENO); + dup2(pts, STDOUT_FILENO); + dup2(pts, STDERR_FILENO); adb_close(pts); adb_close(ptm); - // set OOM adjustment to zero - char text[64]; - snprintf(text, sizeof text, "/proc/%d/oom_adj", getpid()); - int fd = adb_open(text, O_WRONLY); - if (fd >= 0) { - adb_write(fd, "0", 1); - adb_close(fd); - } else { - D("adb: unable to open %s\n", text); - } execl(cmd, cmd, arg0, arg1, NULL); fprintf(stderr, "- exec '%s' failed: %s (%d) -\n", cmd, strerror(errno), errno); exit(-1); } else { - // Don't set child's OOM adjustment to zero. - // Let the child do it itself, as sometimes the parent starts - // running before the child has a /proc/pid/oom_adj. - // """adb: unable to open /proc/644/oom_adj""" seen in some logs. return ptm; } #endif /* !HAVE_WIN32_PROC */ } + +static int create_subproc_raw(const char *cmd, const char *arg0, const char *arg1, pid_t *pid) +{ + D("create_subproc_raw(cmd=%s, arg0=%s, arg1=%s)\n", cmd, arg0, arg1); +#ifdef HAVE_WIN32_PROC + fprintf(stderr, "error: create_subproc_raw not implemented on Win32 (%s %s %s)\n", cmd, arg0, arg1); + return -1; +#else /* !HAVE_WIN32_PROC */ + + // 0 is parent socket, 1 is child socket + int sv[2]; + if (unix_socketpair(AF_UNIX, SOCK_STREAM, 0, sv) < 0) { + printf("[ cannot create socket pair - %s ]\n", strerror(errno)); + return -1; + } + + *pid = fork(); + if (*pid < 0) { + printf("- fork failed: %s -\n", strerror(errno)); + adb_close(sv[0]); + adb_close(sv[1]); + return -1; + } + + if (*pid == 0) { + adb_close(sv[0]); + init_subproc_child(); + + // Only hook up stdin/stdout; drop stderr + dup2(sv[1], STDIN_FILENO); + dup2(sv[1], STDOUT_FILENO); + adb_close(sv[1]); + + execl(cmd, cmd, arg0, arg1, NULL); + fprintf(stderr, "- exec '%s' failed: %s (%d) -\n", + cmd, strerror(errno), errno); + exit(-1); + } else { + adb_close(sv[1]); + return sv[0]; + } +#endif /* !HAVE_WIN32_PROC */ +} #endif /* !ABD_HOST */ #if ADB_HOST @@ -296,18 +338,32 @@ static void subproc_waiter_service(int fd, void *cookie) } } -static int create_subproc_thread(const char *name) +static int create_subproc_thread(const char *name, const subproc_mode mode) { stinfo *sti; adb_thread_t t; int ret_fd; - pid_t pid; - if(name) { - ret_fd = create_subprocess(SHELL_COMMAND, "-c", name, &pid); + pid_t pid = -1; + + const char *arg0, *arg1; + if (name == 0 || *name == 0) { + arg0 = "-"; arg1 = 0; } else { - ret_fd = create_subprocess(SHELL_COMMAND, "-", 0, &pid); + arg0 = "-c"; arg1 = name; + } + + switch (mode) { + case SUBPROC_PTY: + ret_fd = create_subproc_pty(SHELL_COMMAND, arg0, arg1, &pid); + break; + case SUBPROC_RAW: + ret_fd = create_subproc_raw(SHELL_COMMAND, arg0, arg1, &pid); + break; + default: + fprintf(stderr, "invalid subproc_mode %d\n", mode); + return -1; } - D("create_subprocess() ret_fd=%d pid=%d\n", ret_fd, pid); + D("create_subproc ret_fd=%d pid=%d\n", ret_fd, pid); sti = malloc(sizeof(stinfo)); if(sti == 0) fatal("cannot allocate stinfo"); @@ -315,14 +371,14 @@ static int create_subproc_thread(const char *name) sti->cookie = (void*) (uintptr_t) pid; sti->fd = ret_fd; - if(adb_thread_create( &t, service_bootstrap_func, sti)){ + if (adb_thread_create(&t, service_bootstrap_func, sti)) { free(sti); adb_close(ret_fd); - printf("cannot create service thread\n"); + fprintf(stderr, "cannot create service thread\n"); return -1; } - D("service thread started, fd=%d pid=%d\n",ret_fd, pid); + D("service thread started, fd=%d pid=%d\n", ret_fd, pid); return ret_fd; } #endif @@ -367,27 +423,35 @@ int service_to_fd(const char *name) } else if (!strncmp(name, "jdwp:", 5)) { ret = create_jdwp_connection_fd(atoi(name+5)); } else if(!HOST && !strncmp(name, "shell:", 6)) { - if(name[6]) { - ret = create_subproc_thread(name + 6); - } else { - ret = create_subproc_thread(0); - } + ret = create_subproc_thread(name + 6, SUBPROC_PTY); + } else if(!HOST && !strncmp(name, "exec:", 5)) { + ret = create_subproc_thread(name + 5, SUBPROC_RAW); } else if(!strncmp(name, "sync:", 5)) { ret = create_service_thread(file_sync_service, NULL); } else if(!strncmp(name, "remount:", 8)) { ret = create_service_thread(remount_service, NULL); } else if(!strncmp(name, "reboot:", 7)) { void* arg = strdup(name + 7); - if(arg == 0) return -1; + if (arg == NULL) return -1; ret = create_service_thread(reboot_service, arg); } else if(!strncmp(name, "root:", 5)) { ret = create_service_thread(restart_root_service, NULL); } else if(!strncmp(name, "backup:", 7)) { - char* arg = strdup(name+7); + char* arg = strdup(name + 7); if (arg == NULL) return -1; - ret = backup_service(BACKUP, arg); + char* c = arg; + for (; *c != '\0'; c++) { + if (*c == ':') + *c = ' '; + } + char* cmd; + if (asprintf(&cmd, "/system/bin/bu backup %s", arg) != -1) { + ret = create_subproc_thread(cmd, SUBPROC_RAW); + free(cmd); + } + free(arg); } else if(!strncmp(name, "restore:", 8)) { - ret = backup_service(RESTORE, NULL); + ret = create_subproc_thread("/system/bin/bu restore", SUBPROC_RAW); } else if(!strncmp(name, "tcpip:", 6)) { int port; if (sscanf(name + 6, "%d", &port) == 0) { 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 fb5f02a..61fd5f0 100755 --- a/debuggerd/tombstone.cpp +++ b/debuggerd/tombstone.cpp @@ -471,16 +471,16 @@ static bool dump_sibling_thread_report( // Reads the contents of the specified log device, filters out the entries // that don't match the specified pid, and writes them to the tombstone file. // -// If "tail" is set, we only print the last few lines. +// If "tail" is non-zero, log the last "tail" number of lines. static EventTagMap* g_eventTagMap = NULL; -static void dump_log_file(log_t* log, pid_t pid, const char* filename, - unsigned int tail) { +static void dump_log_file( + log_t* log, pid_t pid, const char* filename, unsigned int tail) { bool first = true; - struct logger_list *logger_list; + struct logger_list* logger_list; logger_list = android_logger_list_open( - android_name_to_log_id(filename), O_RDONLY | O_NONBLOCK, tail, pid); + android_name_to_log_id(filename), O_RDONLY | O_NONBLOCK, tail, pid); if (!logger_list) { XLOG("Unable to open %s: %s\n", filename, strerror(errno)); @@ -491,6 +491,7 @@ static void dump_log_file(log_t* log, pid_t pid, const char* filename, while (true) { ssize_t actual = android_logger_list_read(logger_list, &log_entry); + struct logger_entry* entry; if (actual < 0) { if (actual == -EINTR) { @@ -500,8 +501,7 @@ static void dump_log_file(log_t* log, pid_t pid, const char* filename, // non-blocking EOF; we're done break; } else { - _LOG(log, 0, "Error while reading log: %s\n", - strerror(-actual)); + _LOG(log, 0, "Error while reading log: %s\n", strerror(-actual)); break; } } else if (actual == 0) { @@ -514,16 +514,11 @@ static void dump_log_file(log_t* log, pid_t pid, const char* filename, // because you will be writing as fast as you're reading. Any // high-frequency debug diagnostics should just be written to // the tombstone file. - struct logger_entry* entry = &log_entry.entry_v1; - if (entry->pid != static_cast<int32_t>(pid)) { - // wrong pid, ignore - continue; - } + entry = &log_entry.entry_v1; if (first) { - _LOG(log, 0, "--------- %slog %s\n", - tail ? "tail end of " : "", filename); + _LOG(log, 0, "--------- %slog %s\n", tail ? "tail end of " : "", filename); first = false; } @@ -565,7 +560,7 @@ static void dump_log_file(log_t* log, pid_t pid, const char* filename, // consume any trailing newlines char* nl = msg + strlen(msg) - 1; while (nl >= msg && *nl == '\n') { - *nl-- = '\0'; + *nl-- = '\0'; } char prioChar = (prio < strlen(kPrioChars) ? kPrioChars[prio] : '?'); @@ -582,7 +577,6 @@ static void dump_log_file(log_t* log, pid_t pid, const char* filename, _LOG(log, 0, "%s.%03d %5d %5d %c %-8s: %s\n", timeBuf, entry->nsec / 1000000, entry->pid, entry->tid, prioChar, tag, msg); - } while ((msg = nl)); } @@ -591,7 +585,7 @@ static void dump_log_file(log_t* log, pid_t pid, const char* filename, // Dumps the logs generated by the specified pid to the tombstone, from both // "system" and "main" log devices. Ideally we'd interleave the output. -static void dump_logs(log_t* log, pid_t pid, unsigned tail) { +static void dump_logs(log_t* log, pid_t pid, unsigned int tail) { dump_log_file(log, pid, "system", tail); dump_log_file(log, pid, "main", tail); } diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c index dcda005..ee3cbf9 100644 --- a/fs_mgr/fs_mgr.c +++ b/fs_mgr/fs_mgr.c @@ -208,16 +208,25 @@ 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; +} + int fs_mgr_mount_all(struct fstab *fstab) { int i = 0; - int encrypted = 0; - int ret = -1; + int encryptable = 0; + int error_count = 0; int mret; int mount_errno; if (!fstab) { - return ret; + return -1; } for (i = 0; i < fstab->num_entries; i++) { @@ -242,7 +251,8 @@ int fs_mgr_mount_all(struct fstab *fstab) 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!"); continue; @@ -250,47 +260,62 @@ int fs_mgr_mount_all(struct fstab *fstab) } 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); + fstab->recs[i].fs_type, fstab->recs[i].flags, + fstab->recs[i].fs_options); if (!mret) { + /* If this is encryptable, need to trigger encryption */ + if ((fstab->recs[i].fs_mgr_flags & MF_FORCECRYPT)) { + if (umount(fstab->recs[i].mount_point) == 0) { + if (!encryptable) { + encryptable = 2; + } else { + ERROR("Only one encryptable/encrypted partition supported"); + encryptable = 1; + } + } else { + INFO("Could not umount %s - allow continue unencrypted", + fstab->recs[i].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) && + /* mount(2) returned an error, check if it's encryptable and deal with it */ + if (mount_errno != EBUSY && mount_errno != EACCES && + (fstab->recs[i].fs_mgr_flags & (MF_CRYPT | MF_FORCECRYPT)) && !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; + MS_NOATIME | MS_NOSUID | MS_NODEV, CRYPTO_TMPFS_OPTIONS) < 0) { + ERROR("Cannot mount tmpfs filesystem for encryptable fs at %s error: %s\n", + fstab->recs[i].mount_point, strerror(errno)); + ++error_count; + continue; } - encrypted = 1; + encryptable = 1; } 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[i].blk_device, fstab->recs[i].mount_point, + fstab->recs[i].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 @@ -332,7 +357,8 @@ 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!"); continue; @@ -504,7 +530,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..6c21425 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. @@ -392,7 +398,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..6bb568a 100644 --- a/fs_mgr/fs_mgr_priv.h +++ b/fs_mgr/fs_mgr_priv.h @@ -72,6 +72,7 @@ #define MF_SWAPPRIO 0x80 #define MF_ZRAMSIZE 0x100 #define MF_VERIFY 0x200 +#define MF_FORCECRYPT 0x400 /* * 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, 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/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 688c7ff..1ee33a1 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,6 +27,9 @@ #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> @@ -37,7 +39,7 @@ namespace android { struct sysfsStringEnumMap { - char* s; + const char* s; int val; }; @@ -170,7 +172,6 @@ int BatteryMonitor::getIntField(const String8& path) { } bool BatteryMonitor::update(void) { - struct BatteryProperties props; bool logthis; props.chargerAcOnline = false; @@ -178,24 +179,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 +241,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 +251,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 +269,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 +402,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 +459,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,25 +509,32 @@ 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("persist.sys.battery.capacity", pval, NULL) > 0) + mBatteryFixedCapacity = (int) strtol(pval, NULL, 10); + + if (property_get("persist.sys.battery.temperature", pval, NULL) > 0) + mBatteryFixedTemperature = (int) strtol(pval, NULL, 10); } }; // namespace android 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 9b84c3e..a1912f1 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 1024 -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 e3cadb1..cfac312 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,11 +33,10 @@ #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> #ifdef CHARGER_ENABLE_SUSPEND #include <suspend/autosuspend.h> @@ -47,6 +44,8 @@ #include "minui/minui.h" +#include "healthd.h" + char *locale; #ifndef max @@ -69,6 +68,7 @@ char *locale; #define BATTERY_FULL_THRESH 95 #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) @@ -81,15 +81,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; @@ -113,69 +104,69 @@ 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; @@ -206,10 +197,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); @@ -220,7 +215,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; @@ -241,114 +236,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) +static int get_battery_capacity() { - 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) -{ - 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 @@ -360,243 +250,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); @@ -616,7 +275,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; @@ -698,8 +357,8 @@ static void update_screen_state(struct charger *charger, int64_t now) 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; } @@ -711,8 +370,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; @@ -741,7 +400,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; @@ -773,7 +432,7 @@ static void update_screen_state(struct charger *charger, int64_t now) 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; @@ -791,13 +450,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; @@ -830,7 +489,7 @@ static void process_key(struct charger *charger, int code, int64_t now) if (key->down) { int64_t reboot_timeout = key->timestamp + POWER_ON_KEY_TIME; if (now >= reboot_timeout) { - LOGI("[%lld] rebooting\n", now); + LOGI("[%" PRId64 "] rebooting\n", now); android_reboot(ANDROID_RB_RESTART, 0, 0); } else { /* if the key is pressed but timeout hasn't expired, @@ -860,14 +519,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 */ @@ -875,21 +537,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); @@ -904,57 +601,38 @@ 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(); @@ -963,15 +641,11 @@ int main(int argc, char **argv) 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) { @@ -1005,13 +679,7 @@ int main(int argc, char **argv) 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/properties.h b/include/cutils/properties.h index 2c70165..798db8b 100644 --- a/include/cutils/properties.h +++ b/include/cutils/properties.h @@ -20,6 +20,7 @@ #include <sys/cdefs.h> #include <stddef.h> #include <sys/system_properties.h> +#include <stdint.h> #ifdef __cplusplus extern "C" { @@ -44,6 +45,64 @@ extern "C" { */ int property_get(const char *key, char *value, const char *default_value); +/* property_get_bool: returns the value of key coerced into a +** boolean. If the property is not set, then the default value is returned. +** +* The following is considered to be true (1): +** "1", "true", "y", "yes", "on" +** +** The following is considered to be false (0): +** "0", "false", "n", "no", "off" +** +** The conversion is whitespace-sensitive (e.g. " off" will not be false). +** +** If no property with this key is set (or the key is NULL) or the boolean +** conversion fails, the default value is returned. +**/ +int8_t property_get_bool(const char *key, int8_t default_value); + +/* property_get_int64: returns the value of key truncated and coerced into a +** int64_t. If the property is not set, then the default value is used. +** +** The numeric conversion is identical to strtoimax with the base inferred: +** - All digits up to the first non-digit characters are read +** - The longest consecutive prefix of digits is converted to a long +** +** Valid strings of digits are: +** - An optional sign character + or - +** - An optional prefix indicating the base (otherwise base 10 is assumed) +** -- 0 prefix is octal +** -- 0x / 0X prefix is hex +** +** Leading/trailing whitespace is ignored. Overflow/underflow will cause +** numeric conversion to fail. +** +** If no property with this key is set (or the key is NULL) or the numeric +** conversion fails, the default value is returned. +**/ +int64_t property_get_int64(const char *key, int64_t default_value); + +/* property_get_int32: returns the value of key truncated and coerced into an +** int32_t. If the property is not set, then the default value is used. +** +** The numeric conversion is identical to strtoimax with the base inferred: +** - All digits up to the first non-digit characters are read +** - The longest consecutive prefix of digits is converted to a long +** +** Valid strings of digits are: +** - An optional sign character + or - +** - An optional prefix indicating the base (otherwise base 10 is assumed) +** -- 0 prefix is octal +** -- 0x / 0X prefix is hex +** +** Leading/trailing whitespace is ignored. Overflow/underflow will cause +** numeric conversion to fail. +** +** If no property with this key is set (or the key is NULL) or the numeric +** conversion fails, the default value is returned. +**/ +int32_t property_get_int32(const char *key, int32_t default_value); + /* property_set: returns 0 on success, < 0 on failure */ int property_set(const char *key, const char *value); 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/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..cc4137e 100644 --- a/include/system/audio.h +++ b/include/system/audio.h @@ -34,11 +34,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, @@ -55,7 +61,58 @@ 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, +}; + +/* 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 +136,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 +160,30 @@ 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; /* 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... */ @@ -129,7 +208,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 @@ -149,21 +228,29 @@ typedef enum { AUDIO_FORMAT_HE_AAC_V1 = 0x05000000UL, AUDIO_FORMAT_HE_AAC_V2 = 0x06000000UL, AUDIO_FORMAT_VORBIS = 0x07000000UL, + AUDIO_FORMAT_OPUS = 0x08000000UL, 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_t; enum { + AUDIO_CHANNEL_NONE = 0x0, /* output channels */ AUDIO_CHANNEL_OUT_FRONT_LEFT = 0x1, AUDIO_CHANNEL_OUT_FRONT_RIGHT = 0x2, @@ -191,16 +278,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 | @@ -264,8 +361,25 @@ 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. + */ typedef uint32_t audio_channel_mask_t; +/* 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 +392,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 +420,25 @@ 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, AUDIO_DEVICE_OUT_DEFAULT = AUDIO_DEVICE_BIT_DEFAULT, AUDIO_DEVICE_OUT_ALL = (AUDIO_DEVICE_OUT_EARPIECE | AUDIO_DEVICE_OUT_SPEAKER | @@ -320,12 +450,17 @@ 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_DEFAULT), AUDIO_DEVICE_OUT_ALL_A2DP = (AUDIO_DEVICE_OUT_BLUETOOTH_A2DP | AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES | @@ -343,13 +478,25 @@ 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_DEFAULT = AUDIO_DEVICE_BIT_IN | AUDIO_DEVICE_BIT_DEFAULT, AUDIO_DEVICE_IN_ALL = (AUDIO_DEVICE_IN_COMMUNICATION | @@ -357,16 +504,23 @@ 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_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 +598,225 @@ static const audio_offload_info_t AUDIO_INFO_INITIALIZER = { is_streaming: false }; + +/* 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)]; /* 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 +841,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 +859,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 +879,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) @@ -524,6 +925,22 @@ static inline bool audio_is_output_channel(audio_channel_mask_t channel) return false; } +/* Returns the number of channels from an input channel mask, + * used in the context of audio input or recording. + */ +static inline uint32_t audio_channel_count_from_in_mask(audio_channel_mask_t channel) +{ + return popcount(channel & AUDIO_CHANNEL_IN_ALL); +} + +/* Returns the number of channels from an output channel mask, + * used in the context of audio output or playback. + */ +static inline uint32_t audio_channel_count_from_out_mask(audio_channel_mask_t channel) +{ + return popcount(channel & AUDIO_CHANNEL_OUT_ALL); +} + /* Derive an output channel mask 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, @@ -534,7 +951,7 @@ static inline bool audio_is_output_channel(audio_channel_mask_t channel) */ static inline audio_channel_mask_t audio_channel_out_mask_from_count(uint32_t channel_count) { - switch(channel_count) { + switch (channel_count) { case 1: return AUDIO_CHANNEL_OUT_MONO; case 2: @@ -573,10 +990,18 @@ 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: @@ -604,12 +1029,18 @@ 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; } diff --git a/include/system/graphics.h b/include/system/graphics.h index be86ae4..82877a0 100644 --- a/include/system/graphics.h +++ b/include/system/graphics.h @@ -165,24 +165,54 @@ 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_RAW_SENSOR = 0x20, + HAL_PIXEL_FORMAT_RAW16 = 0x20, + HAL_PIXEL_FORMAT_RAW_SENSOR = 0x20, // TODO(rubenbrunk): Remove RAW_SENSOR. + + /* + * 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 +327,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..0270baa --- /dev/null +++ b/include/system/sound_trigger.h @@ -0,0 +1,165 @@ +/* + * 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> + +#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 + + +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 */ + 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 recognition_mode; /* recognition modes supported by this key phrase */ + unsigned int num_users; /* number of users in the key phrase */ + 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 */ + 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) */ +}; + +/* + * Specialized recognition event for key phrase detection + */ +struct sound_trigger_phrase_recognition_extra { + unsigned int recognition_modes; + unsigned int num_users; + unsigned int confidence_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]; +}; + + +#endif // ANDROID_SOUND_TRIGGER_H 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 f1d68a0..8c61293 100644 --- a/include/utils/BitSet.h +++ b/include/utils/BitSet.h @@ -75,19 +75,19 @@ struct BitSet32 { // Result is undefined if all bits are unmarked. inline uint32_t firstMarkedBit() const { return firstMarkedBit(value); } - static uint32_t firstMarkedBit(uint32_t value) { return __builtin_clzl(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 firstUnmarkedBit(value); } - static inline uint32_t firstUnmarkedBit(uint32_t value) { return __builtin_clzl(~ 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 lastMarkedBit(value); } - static inline uint32_t lastMarkedBit(uint32_t value) { return 31 - __builtin_ctzl(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. @@ -145,6 +145,25 @@ 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) 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 d9f7bbe..37bbaa3 100644 --- a/init/builtins.c +++ b/init/builtins.c @@ -517,10 +517,14 @@ int do_mount_all(int nargs, char **args) return -1; } - /* ret is 1 if the device is encrypted, 0 if not, and -1 on error */ - if (ret == 1) { + /* ret is 2 if device needs encrypted, 1 if the device appears encrypted, + * 0 if not, and -1 on error */ + if (ret == 2) { + property_set("ro.crypto.state", "unencrypted"); + property_set("vold.decrypt", "trigger_encryption"); + } else if (ret == 1) { property_set("ro.crypto.state", "encrypted"); - property_set("vold.decrypt", "1"); + property_set("vold.decrypt", "trigger_default_encryption"); } else if (ret == 0) { property_set("ro.crypto.state", "unencrypted"); /* If fs_mgr determined this is an unencrypted device, then trigger diff --git a/init/property_service.c b/init/property_service.c index fb3bc8d..97f281f 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> @@ -96,6 +97,8 @@ struct { { "persist.gps.", AID_GPS, 0 }, { "persist.service.bdroid.", AID_BLUETOOTH, 0 }, { "selinux." , AID_SYSTEM, 0 }, + { "build.fingerprint", AID_SYSTEM, 0 }, + { "partition." , AID_SYSTEM, 0}, { NULL, 0, 0 } }; @@ -110,6 +113,7 @@ struct { } control_perms[] = { { "dumpstate",AID_SHELL, AID_LOG }, { "ril-daemon",AID_RADIO, AID_RADIO }, + { "pre-recovery", AID_SYSTEM, AID_SYSTEM }, {NULL, 0, 0 } }; @@ -282,7 +286,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; @@ -292,11 +295,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; @@ -369,6 +371,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; @@ -381,7 +386,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/properties.c b/libcutils/properties.c index 28d8b2f..b283658 100644 --- a/libcutils/properties.c +++ b/libcutils/properties.c @@ -15,17 +15,95 @@ */ #define LOG_TAG "properties" +// #define LOG_NDEBUG 0 #include <stdlib.h> #include <string.h> +#include <ctype.h> #include <unistd.h> #include <cutils/sockets.h> #include <errno.h> #include <assert.h> #include <cutils/properties.h> +#include <stdbool.h> +#include <inttypes.h> #include "loghack.h" +int8_t property_get_bool(const char *key, int8_t default_value) { + if (!key) { + return default_value; + } + + int8_t result = default_value; + char buf[PROPERTY_VALUE_MAX] = {'\0',}; + + int len = property_get(key, buf, ""); + if (len == 1) { + char ch = buf[0]; + if (ch == '0' || ch == 'n') { + result = false; + } else if (ch == '1' || ch == 'y') { + result = true; + } + } else if (len > 1) { + if (!strcmp(buf, "no") || !strcmp(buf, "false") || !strcmp(buf, "off")) { + result = false; + } else if (!strcmp(buf, "yes") || !strcmp(buf, "true") || !strcmp(buf, "on")) { + result = true; + } + } + + return result; +} + +// Convert string property to int (default if fails); return default value if out of bounds +static intmax_t property_get_imax(const char *key, intmax_t lower_bound, intmax_t upper_bound, + intmax_t default_value) { + if (!key) { + return default_value; + } + + intmax_t result = default_value; + char buf[PROPERTY_VALUE_MAX] = {'\0',}; + char *end = NULL; + + int len = property_get(key, buf, ""); + if (len > 0) { + int tmp = errno; + errno = 0; + + // Infer base automatically + result = strtoimax(buf, &end, /*base*/0); + if ((result == INTMAX_MIN || result == INTMAX_MAX) && errno == ERANGE) { + // Over or underflow + result = default_value; + ALOGV("%s(%s,%" PRIdMAX ") - overflow", __FUNCTION__, key, default_value); + } else if (result < lower_bound || result > upper_bound) { + // Out of range of requested bounds + result = default_value; + ALOGV("%s(%s,%" PRIdMAX ") - out of range", __FUNCTION__, key, default_value); + } else if (end == buf) { + // Numeric conversion failed + result = default_value; + ALOGV("%s(%s,%" PRIdMAX ") - numeric conversion failed", + __FUNCTION__, key, default_value); + } + + errno = tmp; + } + + return result; +} + +int64_t property_get_int64(const char *key, int64_t default_value) { + return (int64_t)property_get_imax(key, INT64_MIN, INT64_MAX, default_value); +} + +int32_t property_get_int32(const char *key, int32_t default_value) { + return (int32_t)property_get_imax(key, INT32_MIN, INT32_MAX, default_value); +} + #ifdef HAVE_LIBC_SYSTEM_PROPERTIES #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_ @@ -44,10 +122,13 @@ int property_get(const char *key, char *value, const char *default_value) if(len > 0) { return len; } - if(default_value) { len = strlen(default_value); - memcpy(value, default_value, len + 1); + if (len >= PROPERTY_VALUE_MAX) { + len = PROPERTY_VALUE_MAX - 1; + } + memcpy(value, default_value, len); + value[len] = '\0'; } return len; } diff --git a/libcutils/sched_policy.c b/libcutils/sched_policy.c index 9f092d6..fa0792f 100644 --- a/libcutils/sched_policy.c +++ b/libcutils/sched_policy.c @@ -39,6 +39,7 @@ static inline SchedPolicy _policy(SchedPolicy p) #if defined(HAVE_ANDROID_OS) && defined(HAVE_SCHED_H) && defined(HAVE_PTHREADS) +#include <linux/prctl.h> #include <sched.h> #include <pthread.h> @@ -54,6 +55,9 @@ static inline SchedPolicy _policy(SchedPolicy p) #define CAN_SET_SP_SYSTEM 0 // non-zero means to implement set_sched_policy(tid, SP_SYSTEM) +// 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 +329,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/libcutils/tests/Android.mk b/libcutils/tests/Android.mk new file mode 100644 index 0000000..d3e07f8 --- /dev/null +++ b/libcutils/tests/Android.mk @@ -0,0 +1,32 @@ +# 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. + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +test_src_files := \ + PropertiesTest.cpp \ + +shared_libraries := \ + libutils \ + liblog + +static_libraries := \ + libcutils + +LOCAL_SHARED_LIBRARIES := $(shared_libraries) +LOCAL_STATIC_LIBRARIES := $(static_libraries) +LOCAL_SRC_FILES := $(test_src_files) +LOCAL_MODULE := libcutils_test +include $(BUILD_NATIVE_TEST) diff --git a/libcutils/tests/PropertiesTest.cpp b/libcutils/tests/PropertiesTest.cpp new file mode 100644 index 0000000..659821c --- /dev/null +++ b/libcutils/tests/PropertiesTest.cpp @@ -0,0 +1,309 @@ +/* + * 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. + */ + +#define LOG_TAG "Properties_test" +#include <utils/Log.h> +#include <gtest/gtest.h> + +#include <cutils/properties.h> +#include <limits.h> +#include <string> +#include <sstream> +#include <iostream> + +namespace android { + +#define STRINGIFY_INNER(x) #x +#define STRINGIFY(x) STRINGIFY_INNER(x) +#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0])) +#define ASSERT_OK(x) ASSERT_EQ(0, (x)) +#define EXPECT_OK(x) EXPECT_EQ(0, (x)) + +#define PROPERTY_TEST_KEY "libcutils.test.key" +#define PROPERTY_TEST_VALUE_DEFAULT "<<<default_value>>>" + +template <typename T> +static std::string HexString(T value) { + std::stringstream ss; + ss << "0x" << std::hex << std::uppercase << value; + return ss.str(); +} + +template <typename T> +static ::testing::AssertionResult AssertEqualHex(const char *mExpr, + const char *nExpr, + T m, + T n) { + if (m == n) { + return ::testing::AssertionSuccess(); + } + + return ::testing::AssertionFailure() + << mExpr << " and " << nExpr << " (expected: " << HexString(m) << + ", actual: " << HexString(n) << ") are not equal"; +} + +class PropertiesTest : public testing::Test { +public: + PropertiesTest() : mValue() {} +protected: + virtual void SetUp() { + EXPECT_OK(property_set(PROPERTY_TEST_KEY, /*value*/NULL)); + } + + virtual void TearDown() { + EXPECT_OK(property_set(PROPERTY_TEST_KEY, /*value*/NULL)); + } + + char mValue[PROPERTY_VALUE_MAX]; + + template <typename T> + static std::string ToString(T value) { + std::stringstream ss; + ss << value; + + return ss.str(); + } + + // Return length of property read; value is written into mValue + int SetAndGetProperty(const char* value, const char* defaultValue = PROPERTY_TEST_VALUE_DEFAULT) { + EXPECT_OK(property_set(PROPERTY_TEST_KEY, value)) << "value: '" << value << "'"; + return property_get(PROPERTY_TEST_KEY, mValue, defaultValue); + } + + void ResetValue(unsigned char c = 0xFF) { + for (size_t i = 0; i < ARRAY_SIZE(mValue); ++i) { + mValue[i] = (char) c; + } + } +}; + +TEST_F(PropertiesTest, SetString) { + + // Null key -> unsuccessful set + { + // Null key -> fails + EXPECT_GT(0, property_set(/*key*/NULL, PROPERTY_TEST_VALUE_DEFAULT)); + } + + // Null value -> returns default value + { + // Null value -> OK , and it clears the value + EXPECT_OK(property_set(PROPERTY_TEST_KEY, /*value*/NULL)); + ResetValue(); + + // Since the value is null, default value will be returned + int len = property_get(PROPERTY_TEST_KEY, mValue, PROPERTY_TEST_VALUE_DEFAULT); + EXPECT_EQ(strlen(PROPERTY_TEST_VALUE_DEFAULT), len); + EXPECT_STREQ(PROPERTY_TEST_VALUE_DEFAULT, mValue); + } + + // Trivial case => get returns what was set + { + int len = SetAndGetProperty("hello_world"); + EXPECT_EQ(strlen("hello_world"), len) << "hello_world key"; + EXPECT_STREQ("hello_world", mValue); + ResetValue(); + } + + // Set to empty string => get returns default always + { + const char* EMPTY_STRING_DEFAULT = "EMPTY_STRING"; + int len = SetAndGetProperty("", EMPTY_STRING_DEFAULT); + EXPECT_EQ(strlen(EMPTY_STRING_DEFAULT), len) << "empty key"; + EXPECT_STREQ(EMPTY_STRING_DEFAULT, mValue); + ResetValue(); + } + + // Set to max length => get returns what was set + { + std::string maxLengthString = std::string(PROPERTY_VALUE_MAX-1, 'a'); + + int len = SetAndGetProperty(maxLengthString.c_str()); + EXPECT_EQ(PROPERTY_VALUE_MAX-1, len) << "max length key"; + EXPECT_STREQ(maxLengthString.c_str(), mValue); + ResetValue(); + } + + // Set to max length + 1 => set fails + { + const char* VALID_TEST_VALUE = "VALID_VALUE"; + ASSERT_OK(property_set(PROPERTY_TEST_KEY, VALID_TEST_VALUE)); + + std::string oneLongerString = std::string(PROPERTY_VALUE_MAX, 'a'); + + // Expect that the value set fails since it's too long + EXPECT_GT(0, property_set(PROPERTY_TEST_KEY, oneLongerString.c_str())); + int len = property_get(PROPERTY_TEST_KEY, mValue, PROPERTY_TEST_VALUE_DEFAULT); + + EXPECT_EQ(strlen(VALID_TEST_VALUE), len) << "set should've failed"; + EXPECT_STREQ(VALID_TEST_VALUE, mValue); + ResetValue(); + } +} + +TEST_F(PropertiesTest, GetString) { + + // Try to use a default value that's too long => set fails + { + ASSERT_OK(property_set(PROPERTY_TEST_KEY, "")); + + std::string maxLengthString = std::string(PROPERTY_VALUE_MAX-1, 'a'); + std::string oneLongerString = std::string(PROPERTY_VALUE_MAX, 'a'); + + // Expect that the value is truncated since it's too long (by 1) + int len = property_get(PROPERTY_TEST_KEY, mValue, oneLongerString.c_str()); + EXPECT_EQ(PROPERTY_VALUE_MAX-1, len); + EXPECT_STREQ(maxLengthString.c_str(), mValue); + ResetValue(); + } +} + +TEST_F(PropertiesTest, GetBool) { + /** + * TRUE + */ + const char *valuesTrue[] = { "1", "true", "y", "yes", "on", }; + for (size_t i = 0; i < ARRAY_SIZE(valuesTrue); ++i) { + ASSERT_OK(property_set(PROPERTY_TEST_KEY, valuesTrue[i])); + bool val = property_get_bool(PROPERTY_TEST_KEY, /*default_value*/false); + EXPECT_TRUE(val) << "Property should've been TRUE for value: '" << valuesTrue[i] << "'"; + } + + /** + * FALSE + */ + const char *valuesFalse[] = { "0", "false", "n", "no", "off", }; + for (size_t i = 0; i < ARRAY_SIZE(valuesFalse); ++i) { + ASSERT_OK(property_set(PROPERTY_TEST_KEY, valuesFalse[i])); + bool val = property_get_bool(PROPERTY_TEST_KEY, /*default_value*/true); + EXPECT_FALSE(val) << "Property shoud've been FALSE For string value: '" << valuesFalse[i] << "'"; + } + + /** + * NEITHER + */ + const char *valuesNeither[] = { "x0", "x1", "2", "-2", "True", "False", "garbage", "", " ", + "+1", " 1 ", " true", " true ", " y ", " yes", "yes ", + "+0", "-0", "00", " 00 ", " false", "false ", + }; + for (size_t i = 0; i < ARRAY_SIZE(valuesNeither); ++i) { + ASSERT_OK(property_set(PROPERTY_TEST_KEY, valuesNeither[i])); + + // The default value should always be used + bool val = property_get_bool(PROPERTY_TEST_KEY, /*default_value*/true); + EXPECT_TRUE(val) << "Property should've been NEITHER (true) for string value: '" << valuesNeither[i] << "'"; + + val = property_get_bool(PROPERTY_TEST_KEY, /*default_value*/false); + EXPECT_FALSE(val) << "Property should've been NEITHER (false) for string value: '" << valuesNeither[i] << "'"; + } +} + +TEST_F(PropertiesTest, GetInt64) { + const int64_t DEFAULT_VALUE = INT64_C(0xDEADBEEFBEEFDEAD); + + const std::string longMaxString = ToString(INT64_MAX); + const std::string longStringOverflow = longMaxString + "0"; + + const std::string longMinString = ToString(INT64_MIN); + const std::string longStringUnderflow = longMinString + "0"; + + const char* setValues[] = { + // base 10 + "1", "2", "12345", "-1", "-2", "-12345", + // base 16 + "0xFF", "0x0FF", "0xC0FFEE", + // base 8 + "0", "01234", "07", + // corner cases + " 2", "2 ", "+0", "-0", " +0 ", longMaxString.c_str(), longMinString.c_str(), + // failing cases + NULL, "", " ", " ", "hello", " true ", "y", + longStringOverflow.c_str(), longStringUnderflow.c_str(), + }; + + int64_t getValues[] = { + // base 10 + 1, 2, 12345, -1, -2, -12345, + // base 16 + 0xFF, 0x0FF, 0xC0FFEE, + // base 8 + 0, 01234, 07, + // corner cases + 2, 2, 0, 0, 0, INT64_MAX, INT64_MIN, + // failing cases + DEFAULT_VALUE, DEFAULT_VALUE, DEFAULT_VALUE, DEFAULT_VALUE, DEFAULT_VALUE, DEFAULT_VALUE, DEFAULT_VALUE, + DEFAULT_VALUE, DEFAULT_VALUE, + }; + + ASSERT_EQ(ARRAY_SIZE(setValues), ARRAY_SIZE(getValues)); + + for (size_t i = 0; i < ARRAY_SIZE(setValues); ++i) { + ASSERT_OK(property_set(PROPERTY_TEST_KEY, setValues[i])); + + int64_t val = property_get_int64(PROPERTY_TEST_KEY, DEFAULT_VALUE); + EXPECT_PRED_FORMAT2(AssertEqualHex, getValues[i], val) << "Property was set to '" << setValues[i] << "'"; + } +} + +TEST_F(PropertiesTest, GetInt32) { + const int32_t DEFAULT_VALUE = INT32_C(0xDEADBEEF); + + const std::string intMaxString = ToString(INT32_MAX); + const std::string intStringOverflow = intMaxString + "0"; + + const std::string intMinString = ToString(INT32_MIN); + const std::string intStringUnderflow = intMinString + "0"; + + const char* setValues[] = { + // base 10 + "1", "2", "12345", "-1", "-2", "-12345", + // base 16 + "0xFF", "0x0FF", "0xC0FFEE", "0Xf00", + // base 8 + "0", "01234", "07", + // corner cases + " 2", "2 ", "+0", "-0", " +0 ", intMaxString.c_str(), intMinString.c_str(), + // failing cases + NULL, "", " ", " ", "hello", " true ", "y", + intStringOverflow.c_str(), intStringUnderflow.c_str(), + }; + + int32_t getValues[] = { + // base 10 + 1, 2, 12345, -1, -2, -12345, + // base 16 + 0xFF, 0x0FF, 0xC0FFEE, 0Xf00, + // base 8 + 0, 01234, 07, + // corner cases + 2, 2, 0, 0, 0, INT32_MAX, INT32_MIN, + // failing cases + DEFAULT_VALUE, DEFAULT_VALUE, DEFAULT_VALUE, DEFAULT_VALUE, DEFAULT_VALUE, DEFAULT_VALUE, DEFAULT_VALUE, + DEFAULT_VALUE, DEFAULT_VALUE, + }; + + ASSERT_EQ(ARRAY_SIZE(setValues), ARRAY_SIZE(getValues)); + + for (size_t i = 0; i < ARRAY_SIZE(setValues); ++i) { + ASSERT_OK(property_set(PROPERTY_TEST_KEY, setValues[i])); + + int32_t val = property_get_int32(PROPERTY_TEST_KEY, DEFAULT_VALUE); + EXPECT_PRED_FORMAT2(AssertEqualHex, getValues[i], val) << "Property was set to '" << setValues[i] << "'"; + } +} + +} // namespace android 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/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/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/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 9a50147..9343f8e 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/lmkd/Android.mk b/lmkd/Android.mk index 5d6d1d2..2dd8af2 100644 --- a/lmkd/Android.mk +++ b/lmkd/Android.mk @@ -2,8 +2,8 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES := lmkd.c -LOCAL_STATIC_LIBRARIES := libcutils liblog libm libc -LOCAL_FORCE_STATIC_EXECUTABLE := true +LOCAL_SHARED_LIBRARIES := libcutils liblog libm libc +LOCAL_CFLAGS := -Werror LOCAL_MODULE := lmkd diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c index 376410b..5caf77d 100644 --- a/lmkd/lmkd.c +++ b/lmkd/lmkd.c @@ -16,20 +16,26 @@ #define LOG_TAG "lowmemorykiller" +#include <arpa/inet.h> #include <errno.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> -#include <unistd.h> -#include <arpa/inet.h> +#include <sys/cdefs.h> #include <sys/epoll.h> #include <sys/eventfd.h> #include <sys/socket.h> #include <sys/types.h> -#include <cutils/log.h> +#include <unistd.h> + #include <cutils/sockets.h> +#include <log/log.h> + +#ifndef __unused +#define __unused __attribute__((__unused__)) +#endif #define MEMCG_SYSFS_PATH "/dev/memcg/" #define MEMPRESSURE_WATCH_LEVEL "medium" @@ -75,6 +81,10 @@ static int maxevents; #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; @@ -116,6 +126,14 @@ static time_t kill_lasttime; /* PAGE_SIZE / 1024 */ static long page_k; +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; @@ -219,8 +237,8 @@ static void cmd_procprio(int pid, int oomadj) { return; } - snprintf(path, sizeof(path), "/proc/%d/oom_adj", pid); - snprintf(val, sizeof(val), "%d", oomadj); + 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) @@ -245,8 +263,6 @@ static void cmd_procprio(int pid, int oomadj) { } static void cmd_procremove(int pid) { - struct proc *procp; - if (use_inkernel_interface) return; @@ -370,7 +386,7 @@ static void ctrl_data_handler(uint32_t events) { } } -static void ctrl_connect_handler(uint32_t events) { +static void ctrl_connect_handler(uint32_t events __unused) { struct sockaddr addr; socklen_t alen; struct epoll_event epev; @@ -412,7 +428,7 @@ static int zoneinfo_parse_protection(char *cp) { return 0; if (zoneval > max) max = zoneval; - } while (cp = strtok(NULL, " ")); + } while ((cp = strtok(NULL, " "))); return max; } @@ -443,7 +459,6 @@ static void zoneinfo_parse_line(char *line, struct sysmeminfo *mip) { static int zoneinfo_parse(struct sysmeminfo *mip) { FILE *f; - char *cp; char line[LINE_MAX]; memset(mip, 0, sizeof(struct sysmeminfo)); @@ -507,7 +522,7 @@ static struct proc *proc_adj_lru(int oomadj) { return (struct proc *)adjslot_tail(&procadjslot_list[ADJTOSLOT(oomadj)]); } -static void mp_event(uint32_t events) { +static void mp_event(uint32_t events __unused) { int i; int ret; unsigned long long evcount; @@ -726,7 +741,7 @@ static void mainloop(void) { } } -int main(int argc, char **argv) { +int main(int argc __unused, char **argv __unused) { if (!init()) mainloop(); diff --git a/rootdir/init.rc b/rootdir/init.rc index 6db8cb0..a016657 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 @@ -240,6 +240,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 @@ -312,9 +313,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 @@ -403,11 +404,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 charger class_start charger @@ -463,11 +472,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 @@ -504,6 +508,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 @@ -537,6 +542,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 @@ -548,6 +559,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 @@ -559,7 +584,7 @@ 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 oneshot @@ -601,3 +626,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 7baad63..3a164ed 100644 --- a/sdcard/sdcard.c +++ b/sdcard/sdcard.c @@ -139,6 +139,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; @@ -475,6 +477,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- @@ -486,6 +492,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/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")) { |