diff options
131 files changed, 7504 insertions, 992 deletions
@@ -15,17 +15,4 @@ # LOCAL_PATH := $(my-dir) -ifneq ($(TARGET_SIMULATOR),true) - include $(call first-makefiles-under,$(LOCAL_PATH)) -else - include $(addprefix $(LOCAL_PATH)/,$(addsuffix /Android.mk, \ - adb \ - libcutils \ - libsysutils \ - liblog \ - libnetutils \ - libpixelflinger \ - libusbhost \ - libzipfile \ - )) -endif +include $(call first-makefiles-under,$(LOCAL_PATH)) diff --git a/adb/Android.mk b/adb/Android.mk index 6ed31eb..7744d2b 100644 --- a/adb/Android.mk +++ b/adb/Android.mk @@ -95,11 +95,7 @@ endif # adbd device daemon # ========================================================= -# build adbd in all non-simulator builds -BUILD_ADBD := false -ifneq ($(TARGET_SIMULATOR),true) - BUILD_ADBD := true -endif +BUILD_ADBD := true # build adbd for the Linux simulator build # so we can use it to test the adb USB gadget driver on x86 @@ -113,6 +109,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := \ adb.c \ + backup_service.c \ fdevent.c \ transport.c \ transport_local.c \ @@ -142,21 +139,14 @@ LOCAL_FORCE_STATIC_EXECUTABLE := true LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN) LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_SBIN_UNSTRIPPED) -ifeq ($(TARGET_SIMULATOR),true) - LOCAL_STATIC_LIBRARIES := libcutils - LOCAL_LDLIBS += -lpthread - include $(BUILD_HOST_EXECUTABLE) -else - LOCAL_STATIC_LIBRARIES := libcutils libc - include $(BUILD_EXECUTABLE) -endif +LOCAL_STATIC_LIBRARIES := libcutils libc +include $(BUILD_EXECUTABLE) endif # adb host tool for device-as-host # ========================================================= -ifneq ($(TARGET_SIMULATOR),true) ifneq ($(SDK_ONLY),true) include $(CLEAR_VARS) @@ -195,4 +185,3 @@ LOCAL_STATIC_LIBRARIES := libzipfile libunz libcutils include $(BUILD_EXECUTABLE) endif -endif @@ -36,6 +36,9 @@ #include "usb_vendors.h" #endif +#if ADB_TRACE +ADB_MUTEX_DEFINE( D_lock ); +#endif int HOST = 0; @@ -90,6 +93,7 @@ void adb_trace_init(void) { "sysdeps", TRACE_SYSDEPS }, { "transport", TRACE_TRANSPORT }, { "jdwp", TRACE_JDWP }, + { "services", TRACE_SERVICES }, { NULL, 0 } }; @@ -591,14 +595,6 @@ nomem: return 0; } -#ifdef HAVE_FORKEXEC -static void sigchld_handler(int n) -{ - int status; - while(waitpid(-1, &status, WNOHANG) > 0) ; -} -#endif - #ifdef HAVE_WIN32_PROC static BOOL WINAPI ctrlc_handler(DWORD type) { @@ -641,6 +637,7 @@ void start_logging(void) fd = unix_open("/dev/null", O_RDONLY); dup2(fd, 0); + adb_close(fd); fd = unix_open("/tmp/adb.log", O_WRONLY | O_CREAT | O_APPEND, 0640); if(fd < 0) { @@ -648,6 +645,7 @@ void start_logging(void) } dup2(fd, 1); dup2(fd, 2); + adb_close(fd); fprintf(stderr,"--- adb starting (pid %d) ---\n", getpid()); #endif } @@ -807,9 +805,10 @@ int launch_server(int server_port) // wait for the "OK\n" message adb_close(fd[1]); int ret = adb_read(fd[0], temp, 3); + int saved_errno = errno; adb_close(fd[0]); if (ret < 0) { - fprintf(stderr, "could not read ok from ADB Server, errno = %d\n", errno); + fprintf(stderr, "could not read ok from ADB Server, errno = %d\n", saved_errno); return -1; } if (ret != 3 || temp[0] != 'O' || temp[1] != 'K' || temp[2] != '\n') { @@ -848,7 +847,7 @@ int adb_main(int is_daemon, int server_port) #ifdef HAVE_WIN32_PROC SetConsoleCtrlHandler( ctrlc_handler, TRUE ); #elif defined(HAVE_FORKEXEC) - signal(SIGCHLD, sigchld_handler); + // No SIGCHLD. Let the service subproc handle its children. signal(SIGPIPE, SIG_IGN); #endif @@ -872,7 +871,7 @@ int adb_main(int is_daemon, int server_port) */ property_get("ro.kernel.qemu", value, ""); if (strcmp(value, "1") != 0) { - property_get("ro.secure", value, ""); + property_get("ro.secure", value, "1"); if (strcmp(value, "1") == 0) { // don't run as root if ro.secure is set... secure = 1; @@ -957,7 +956,9 @@ int adb_main(int is_daemon, int server_port) // listen on default port local_init(DEFAULT_ADB_LOCAL_TRANSPORT_PORT); } + D("adb_main(): pre init_jdwp()\n"); init_jdwp(); + D("adb_main(): post init_jdwp()\n"); #endif if (is_daemon) @@ -971,6 +972,7 @@ int adb_main(int is_daemon, int server_port) #endif start_logging(); } + D("Event loop starting\n"); fdevent_loop(); @@ -1269,8 +1271,9 @@ int recovery_mode = 0; int main(int argc, char **argv) { #if ADB_HOST - adb_trace_init(); adb_sysdeps_init(); + adb_trace_init(); + D("Handling commandline()\n"); return adb_commandline(argc - 1, argv + 1); #else if((argc > 1) && (!strcmp(argv[1],"recovery"))) { @@ -1279,6 +1282,7 @@ int main(int argc, char **argv) } start_device_log(); + D("Handling main()\n"); return adb_main(0, DEFAULT_ADB_PORT); #endif } @@ -19,6 +19,8 @@ #include <limits.h> +#include "transport.h" /* readx(), writex() */ + #define MAX_PAYLOAD 4096 #define A_SYNC 0x434e5953 @@ -33,7 +35,7 @@ #define ADB_VERSION_MAJOR 1 // Used for help/version information #define ADB_VERSION_MINOR 0 // Used for help/version information -#define ADB_SERVER_VERSION 26 // Increment this when we want to force users to start a new adb server +#define ADB_SERVER_VERSION 29 // Increment this when we want to force users to start a new adb server typedef struct amessage amessage; typedef struct apacket apacket; @@ -302,6 +304,11 @@ int create_jdwp_connection_fd(int jdwp_pid); #endif #if !ADB_HOST +typedef enum { + BACKUP, + RESTORE +} BackupOperation; +int backup_service(BackupOperation operation, char* args); void framebuffer_service(int fd, void *cookie); void log_service(int fd, void *cookie); void remount_service(int fd, void *cookie); @@ -315,13 +322,6 @@ void put_apacket(apacket *p); int check_header(apacket *p); int check_data(apacket *p); -/* convenience wrappers around read/write that will retry on -** EINTR and/or short read/write. Returns 0 on success, -1 -** on error or EOF. -*/ -int readx(int fd, void *ptr, size_t len); -int writex(int fd, const void *ptr, size_t len); - /* define ADB_TRACE to 1 to enable tracing support, or 0 to disable it */ #define ADB_TRACE 1 @@ -331,33 +331,56 @@ int writex(int fd, const void *ptr, size_t len); * the adb_trace_init() function implemented in adb.c */ typedef enum { - TRACE_ADB = 0, + TRACE_ADB = 0, /* 0x001 */ TRACE_SOCKETS, TRACE_PACKETS, TRACE_TRANSPORT, - TRACE_RWX, + TRACE_RWX, /* 0x010 */ TRACE_USB, TRACE_SYNC, TRACE_SYSDEPS, - TRACE_JDWP, + TRACE_JDWP, /* 0x100 */ + TRACE_SERVICES, } AdbTrace; #if ADB_TRACE - int adb_trace_mask; - + extern int adb_trace_mask; + extern unsigned char adb_trace_output_count; void adb_trace_init(void); # define ADB_TRACING ((adb_trace_mask & (1 << TRACE_TAG)) != 0) /* you must define TRACE_TAG before using this macro */ - #define D(...) \ +# define D(...) \ + do { \ + if (ADB_TRACING) { \ + int save_errno = errno; \ + adb_mutex_lock(&D_lock); \ + fprintf(stderr, "%s::%s():", \ + __FILE__, __FUNCTION__); \ + errno = save_errno; \ + fprintf(stderr, __VA_ARGS__ ); \ + fflush(stderr); \ + adb_mutex_unlock(&D_lock); \ + errno = save_errno; \ + } \ + } while (0) +# define DR(...) \ do { \ - if (ADB_TRACING) \ + if (ADB_TRACING) { \ + int save_errno = errno; \ + adb_mutex_lock(&D_lock); \ + errno = save_errno; \ fprintf(stderr, __VA_ARGS__ ); \ + fflush(stderr); \ + adb_mutex_unlock(&D_lock); \ + errno = save_errno; \ + } \ } while (0) #else # define D(...) ((void)0) +# define DR(...) ((void)0) # define ADB_TRACING 0 #endif @@ -413,6 +436,7 @@ int connection_state(atransport *t); #define CS_NOPERM 5 /* Insufficient permissions to communicate with the device */ extern int HOST; +extern int SHELL_EXIT_NOTIFY_FD; #define CHUNK_SIZE (64*1024) diff --git a/adb/adb_client.c b/adb/adb_client.c index 882810a..9a812f0 100644 --- a/adb/adb_client.c +++ b/adb/adb_client.c @@ -202,6 +202,7 @@ int _adb_connect(const char *service) return -1; } + D("_adb_connect: return fd %d\n", fd); return fd; } @@ -210,6 +211,7 @@ int adb_connect(const char *service) // first query the adb server's version int fd = _adb_connect("host:version"); + D("adb_connect: service %s\n", service); if(fd == -2) { fprintf(stdout,"* daemon not running. starting it now on port %d *\n", __adb_server_port); @@ -266,6 +268,7 @@ int adb_connect(const char *service) if(fd == -2) { fprintf(stderr,"** daemon still not running"); } + D("adb_connect: return fd %d\n", fd); return fd; error: diff --git a/adb/backup_service.c b/adb/backup_service.c new file mode 100644 index 0000000..669ff86 --- /dev/null +++ b/adb/backup_service.c @@ -0,0 +1,155 @@ +/* + * 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; + int socketnum; + + // Command string and choice of stdin/stdout for the pipe depend on our invocation + if (op == BACKUP) { + operation = "backup"; + socketnum = STDOUT_FILENO; + } else { + operation = "restore"; + socketnum = STDIN_FILENO; + } + + 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 b0c2b80..4c15232 100644 --- a/adb/commandline.c +++ b/adb/commandline.c @@ -37,12 +37,6 @@ #include "adb_client.h" #include "file_sync_service.h" -enum { - IGNORE_DATA, - WIPE_DATA, - FLASH_DATA -}; - static int do_cmd(transport_type ttype, char* serial, char *cmd, ...); void get_my_path(char *s, size_t maxLen); @@ -135,14 +129,28 @@ void help() " adb bugreport - return all information from the device\n" " that should be included in a bug report.\n" "\n" + " adb backup [-f <file>] [-apk|-noapk] [-shared|-noshared] [-all] [-system|-nosystem] [<packages...>]\n" + " - write an archive of the device's data to <file>.\n" + " If no -f option is supplied then the data is written\n" + " to \"backup.ab\" in the current directory.\n" + " (-apk|-noapk enable/disable backup of the .apks themselves\n" + " in the archive; the default is noapk.)\n" + " (-shared|-noshared enable/disable backup of the device's\n" + " shared storage / SD card contents; the default is noshared.)\n" + " (-all means to back up all installed applications)\n" + " (-system|-nosystem toggles whether -all automatically includes\n" + " system applications; the default is to include system apps)\n" + " (<packages...> is the list of applications to be backed up. If\n" + " the -all or -shared flags are passed, then the package\n" + " list is optional. Applications explicitly given on the\n" + " command line will be included even if -nosystem would\n" + " ordinarily cause them to be omitted.)\n" + "\n" + " adb restore <file> - restore device contents from the <file> backup archive\n" + "\n" " adb help - show this help message\n" " adb version - show version num\n" "\n" - "DATAOPTS:\n" - " (no option) - don't touch the data partition\n" - " -w - wipe the data partition\n" - " -d - flash the data partition\n" - "\n" "scripting:\n" " adb wait-for-device - block until device is online\n" " adb start-server - ensure that there is a server running\n" @@ -218,7 +226,9 @@ static void read_and_dump(int fd) int len; while(fd >= 0) { + D("read_and_dump(): pre adb_read(fd=%d)\n", fd); len = adb_read(fd, buf, 4096); + D("read_and_dump(): post adb_read(fd=%d): len=%d\n", fd, len); if(len == 0) { break; } @@ -232,6 +242,34 @@ static void read_and_dump(int fd) } } +static void copy_to_file(int inFd, int outFd) { + const size_t BUFSIZE = 32 * 1024; + char* buf = (char*) malloc(BUFSIZE); + int len; + long total = 0; + + D("copy_to_file(%d -> %d)\n", inFd, outFd); + for (;;) { + len = adb_read(inFd, buf, BUFSIZE); + if (len == 0) { + D("copy_to_file() : read 0 bytes; exiting\n"); + break; + } + if (len < 0) { + if (errno == EINTR) { + D("copy_to_file() : EINTR, retrying\n"); + continue; + } + D("copy_to_file() : error %d\n", errno); + break; + } + adb_write(outFd, buf, len); + total += len; + } + D("copy_to_file() finished after %lu bytes\n", total); + free(buf); +} + static void *stdin_read_thread(void *x) { int fd, fdi; @@ -246,7 +284,9 @@ static void *stdin_read_thread(void *x) for(;;) { /* fdi is really the client's stdin, so use read, not adb_read here */ + D("stdin_read_thread(): pre unix_read(fdi=%d,...)\n", fdi); r = unix_read(fdi, buf, 1024); + D("stdin_read_thread(): post unix_read(fdi=%d,...)\n", fdi); if(r == 0) break; if(r < 0) { if(errno == EINTR) continue; @@ -537,6 +577,85 @@ static int logcat(transport_type transport, char* serial, int argc, char **argv) return 0; } +static int backup(int argc, char** argv) { + char buf[4096]; + const char* filename = "./backup.ab"; + int fd, outFd; + int i, j; + + /* find, extract, and use any -f argument */ + for (i = 1; i < argc; i++) { + if (!strcmp("-f", argv[i])) { + if (i == argc-1) { + fprintf(stderr, "adb: -f passed with no filename\n"); + return usage(); + } + filename = argv[i+1]; + for (j = i+2; j <= argc; ) { + argv[i++] = argv[j++]; + } + argc -= 2; + argv[argc] = NULL; + } + } + + /* bare "adb backup" or "adb backup -f filename" are not valid invocations */ + if (argc < 2) return usage(); + + outFd = adb_open_mode(filename, O_WRONLY | O_CREAT | O_TRUNC, 0640); + if (outFd < 0) { + fprintf(stderr, "adb: unable to open file %s\n", filename); + return -1; + } + + snprintf(buf, sizeof(buf), "backup"); + for (argc--, argv++; argc; argc--, argv++) { + strncat(buf, ":", sizeof(buf) - strlen(buf) - 1); + strncat(buf, argv[0], sizeof(buf) - strlen(buf) - 1); + } + + D("backup. filename=%s buf=%s\n", filename, buf); + fd = adb_connect(buf); + if (fd < 0) { + fprintf(stderr, "adb: unable to connect for backup\n"); + adb_close(outFd); + return -1; + } + + copy_to_file(fd, outFd); + + adb_close(fd); + adb_close(outFd); + return 0; +} + +static int restore(int argc, char** argv) { + const char* filename; + int fd, tarFd; + + if (argc != 2) return usage(); + + filename = argv[1]; + tarFd = adb_open(filename, O_RDONLY); + if (tarFd < 0) { + fprintf(stderr, "adb: unable to open file %s\n", filename); + return -1; + } + + fd = adb_connect("restore:"); + if (fd < 0) { + fprintf(stderr, "adb: unable to connect for backup\n"); + adb_close(tarFd); + return -1; + } + + copy_to_file(tarFd, fd); + + adb_close(fd); + adb_close(tarFd); + return 0; +} + #define SENTINEL_FILE "config" OS_PATH_SEPARATOR_STR "envsetup.make" static int top_works(const char *top) { @@ -853,6 +972,7 @@ top: } if(argc < 2) { + D("starting interactive shell\n"); r = interactive_shell(); if (h) { printf("\x1b[0m"); @@ -877,9 +997,12 @@ top: } for(;;) { + D("interactive shell loop. buff=%s\n", buf); fd = adb_connect(buf); if(fd >= 0) { + D("about to read_and_dump(fd=%d)\n", fd); read_and_dump(fd); + D("read_and_dump() done.\n"); adb_close(fd); r = 0; } else { @@ -896,6 +1019,7 @@ top: printf("\x1b[0m"); fflush(stdout); } + D("interactive shell loop. return r=%d\n", r); return r; } } @@ -1091,6 +1215,14 @@ top: return adb_connect("host:start-server"); } + if (!strcmp(argv[0], "backup")) { + return backup(argc, argv); + } + + if (!strcmp(argv[0], "restore")) { + return restore(argc, argv); + } + if (!strcmp(argv[0], "jdwp")) { int fd = adb_connect("jdwp"); if (fd >= 0) { @@ -1242,50 +1374,115 @@ static int delete_file(transport_type transport, char* serial, char* filename) return 0; } -int install_app(transport_type transport, char* serial, int argc, char** argv) +static const char* get_basename(const char* filename) +{ + const char* basename = adb_dirstop(filename); + if (basename) { + basename++; + return basename; + } else { + return filename; + } +} + +static int check_file(const char* filename) { struct stat st; - int err; - const char *const DATA_DEST = "/data/local/tmp/%s"; - const char *const SD_DEST = "/sdcard/tmp/%s"; + + if (filename == NULL) { + return 0; + } + + if (stat(filename, &st) != 0) { + fprintf(stderr, "can't find '%s' to install\n", filename); + return 1; + } + + if (!S_ISREG(st.st_mode)) { + fprintf(stderr, "can't install '%s' because it's not a file\n", filename); + return 1; + } + + return 0; +} + +int install_app(transport_type transport, char* serial, int argc, char** argv) +{ + static const char *const DATA_DEST = "/data/local/tmp/%s"; + static const char *const SD_DEST = "/sdcard/tmp/%s"; const char* where = DATA_DEST; - char to[PATH_MAX]; - char* filename = argv[argc - 1]; - const char* p; + char apk_dest[PATH_MAX]; + char verification_dest[PATH_MAX]; + char* apk_file; + char* verification_file = NULL; + int file_arg = -1; + int err; int i; - for (i = 0; i < argc; i++) { - if (!strcmp(argv[i], "-s")) + for (i = 1; i < argc; i++) { + if (*argv[i] != '-') { + file_arg = i; + break; + } else if (!strcmp(argv[i], "-i")) { + // Skip the installer package name. + i++; + } else if (!strcmp(argv[i], "-s")) { where = SD_DEST; + } } - p = adb_dirstop(filename); - if (p) { - p++; - snprintf(to, sizeof to, where, p); - } else { - snprintf(to, sizeof to, where, filename); + if (file_arg < 0) { + fprintf(stderr, "can't find filename in arguments\n"); + return 1; + } else if (file_arg + 2 < argc) { + fprintf(stderr, "too many files specified; only takes APK file and verifier file\n"); + return 1; } - if (p[0] == '\0') { + + apk_file = argv[file_arg]; + + if (file_arg != argc - 1) { + verification_file = argv[file_arg + 1]; } - err = stat(filename, &st); - if (err != 0) { - fprintf(stderr, "can't find '%s' to install\n", filename); + if (check_file(apk_file) || check_file(verification_file)) { return 1; } - if (!S_ISREG(st.st_mode)) { - fprintf(stderr, "can't install '%s' because it's not a file\n", - filename); - return 1; + + snprintf(apk_dest, sizeof apk_dest, where, get_basename(apk_file)); + if (verification_file != NULL) { + snprintf(verification_dest, sizeof(verification_dest), where, get_basename(verification_file)); + + if (!strcmp(apk_dest, verification_dest)) { + fprintf(stderr, "APK and verification file can't have the same name\n"); + return 1; + } + } + + err = do_sync_push(apk_file, apk_dest, 1 /* verify APK */); + if (err) { + return err; + } else { + argv[file_arg] = apk_dest; /* destination name, not source location */ } - if (!(err = do_sync_push(filename, to, 1 /* verify APK */))) { - /* file in place; tell the Package Manager to install it */ - argv[argc - 1] = to; /* destination name, not source location */ - pm_command(transport, serial, argc, argv); - delete_file(transport, serial, to); + if (verification_file != NULL) { + err = do_sync_push(verification_file, verification_dest, 0 /* no verify APK */); + if (err) { + goto cleanup_apk; + } else { + argv[file_arg + 1] = verification_dest; /* destination name, not source location */ + } + } + + pm_command(transport, serial, argc, argv); + + if (verification_file != NULL) { + delete_file(transport, serial, verification_dest); } +cleanup_apk: + delete_file(transport, serial, apk_dest); + return err; } diff --git a/adb/fdevent.c b/adb/fdevent.c index c179b20..5c374a7 100644 --- a/adb/fdevent.c +++ b/adb/fdevent.c @@ -15,6 +15,8 @@ ** limitations under the License. */ +#include <sys/ioctl.h> + #include <stdlib.h> #include <stdio.h> #include <string.h> @@ -27,10 +29,20 @@ #include <stddef.h> #include "fdevent.h" +#include "transport.h" +#include "sysdeps.h" + -#define TRACE(x...) fprintf(stderr,x) +/* !!! Do not enable DEBUG for the adb that will run as the server: +** both stdout and stderr are used to communicate between the client +** and server. Any extra output will cause failures. +*/ +#define DEBUG 0 /* non-0 will break adb server */ -#define DEBUG 0 +// This socket is used when a subproc shell service exists. +// It wakes up the fdevent_loop() and cause the correct handling +// of the shell's pseudo-tty master. I.e. force close it. +int SHELL_EXIT_NOTIFY_FD = -1; static void fatal(const char *fn, const char *fmt, ...) { @@ -45,15 +57,28 @@ static void fatal(const char *fn, const char *fmt, ...) #define FATAL(x...) fatal(__FUNCTION__, x) #if DEBUG +#define D(...) \ + do { \ + adb_mutex_lock(&D_lock); \ + int save_errno = errno; \ + fprintf(stderr, "%s::%s():", __FILE__, __FUNCTION__); \ + errno = save_errno; \ + fprintf(stderr, __VA_ARGS__); \ + adb_mutex_unlock(&D_lock); \ + errno = save_errno; \ + } while(0) static void dump_fde(fdevent *fde, const char *info) { + adb_mutex_lock(&D_lock); fprintf(stderr,"FDE #%03d %c%c%c %s\n", fde->fd, fde->state & FDE_READ ? 'R' : ' ', fde->state & FDE_WRITE ? 'W' : ' ', fde->state & FDE_ERROR ? 'E' : ' ', info); + adb_mutex_unlock(&D_lock); } #else +#define D(...) ((void)0) #define dump_fde(fde, info) do { } while(0) #endif @@ -67,6 +92,7 @@ static void dump_fde(fdevent *fde, const char *info) static void fdevent_plist_enqueue(fdevent *node); static void fdevent_plist_remove(fdevent *node); static fdevent *fdevent_plist_dequeue(void); +static void fdevent_subproc_event_func(int fd, unsigned events, void *userdata); static fdevent list_pending = { .next = &list_pending, @@ -270,9 +296,72 @@ static void fdevent_update(fdevent *fde, unsigned events) FD_CLR(fde->fd, &error_fds); } - fde->state = (fde->state & FDE_STATEMASK) | events; + fde->state = (fde->state & FDE_STATEMASK) | events; +} + +/* Looks at fd_table[] for bad FDs and sets bit in fds. +** Returns the number of bad FDs. +*/ +static int fdevent_fd_check(fd_set *fds) +{ + int i, n = 0; + fdevent *fde; + + for(i = 0; i < select_n; i++) { + fde = fd_table[i]; + if(fde == 0) continue; + if(fcntl(i, F_GETFL, NULL) < 0) { + FD_SET(i, fds); + n++; + // fde->state |= FDE_DONT_CLOSE; + + } + } + return n; } +#if !DEBUG +static inline void dump_all_fds(const char *extra_msg) {} +#else +static void dump_all_fds(const char *extra_msg) +{ +int i; + fdevent *fde; + // per fd: 4 digits (but really: log10(FD_SETSIZE)), 1 staus, 1 blank + char msg_buff[FD_SETSIZE*6 + 1], *pb=msg_buff; + size_t max_chars = FD_SETSIZE * 6 + 1; + int printed_out; +#define SAFE_SPRINTF(...) \ + do { \ + printed_out = snprintf(pb, max_chars, __VA_ARGS__); \ + if (printed_out <= 0) { \ + D("... snprintf failed.\n"); \ + return; \ + } \ + if (max_chars < (unsigned int)printed_out) { \ + D("... snprintf out of space.\n"); \ + return; \ + } \ + pb += printed_out; \ + max_chars -= printed_out; \ + } while(0) + + for(i = 0; i < select_n; i++) { + fde = fd_table[i]; + SAFE_SPRINTF("%d", i); + if(fde == 0) { + SAFE_SPRINTF("? "); + continue; + } + if(fcntl(i, F_GETFL, NULL) < 0) { + SAFE_SPRINTF("b"); + } + SAFE_SPRINTF(" "); + } + D("%s fd_table[]->fd = {%s}\n", extra_msg, msg_buff); +} +#endif + static void fdevent_process() { int i, n; @@ -284,28 +373,49 @@ static void fdevent_process() memcpy(&wfd, &write_fds, sizeof(fd_set)); memcpy(&efd, &error_fds, sizeof(fd_set)); - n = select(select_n, &rfd, &wfd, &efd, 0); + dump_all_fds("pre select()"); + + n = select(select_n, &rfd, &wfd, &efd, NULL); + int saved_errno = errno; + D("select() returned n=%d, errno=%d\n", n, n<0?saved_errno:0); + + dump_all_fds("post select()"); if(n < 0) { - if(errno == EINTR) return; - perror("select"); - return; + switch(saved_errno) { + case EINTR: return; + case EBADF: + // Can't trust the FD sets after an error. + FD_ZERO(&wfd); + FD_ZERO(&efd); + FD_ZERO(&rfd); + break; + default: + D("Unexpected select() error=%d\n", saved_errno); + return; + } + } + if(n <= 0) { + // We fake a read, as the rest of the code assumes + // that errors will be detected at that point. + n = fdevent_fd_check(&rfd); } for(i = 0; (i < select_n) && (n > 0); i++) { events = 0; - if(FD_ISSET(i, &rfd)) events |= FDE_READ; - if(FD_ISSET(i, &wfd)) events |= FDE_WRITE; - if(FD_ISSET(i, &efd)) events |= FDE_ERROR; + if(FD_ISSET(i, &rfd)) { events |= FDE_READ; n--; } + if(FD_ISSET(i, &wfd)) { events |= FDE_WRITE; n--; } + if(FD_ISSET(i, &efd)) { events |= FDE_ERROR; n--; } if(events) { - n--; - fde = fd_table[i]; - if(fde == 0) FATAL("missing fde for fd %d\n", i); + if(fde == 0) + FATAL("missing fde for fd %d\n", i); fde->events |= events; + D("got events fde->fd=%d events=%04x, state=%04x\n", + fde->fd, fde->events, fde->state); if(fde->state & FDE_PENDING) continue; fde->state |= FDE_PENDING; fdevent_plist_enqueue(fde); @@ -350,14 +460,14 @@ static void fdevent_unregister(fdevent *fde) } if(fd_table[fde->fd] != fde) { - FATAL("fd_table out of sync"); + FATAL("fd_table out of sync [%d]\n", fde->fd); } fd_table[fde->fd] = 0; if(!(fde->state & FDE_DONT_CLOSE)) { dump_fde(fde, "close"); - close(fde->fd); + adb_close(fde->fd); } } @@ -394,6 +504,74 @@ static fdevent *fdevent_plist_dequeue(void) return node; } +static void fdevent_call_fdfunc(fdevent* fde) +{ + unsigned events = fde->events; + fde->events = 0; + if(!(fde->state & FDE_PENDING)) return; + fde->state &= (~FDE_PENDING); + dump_fde(fde, "callback"); + fde->func(fde->fd, events, fde->arg); +} + +static void fdevent_subproc_event_func(int fd, unsigned ev, void *userdata) +{ + + D("subproc handling on fd=%d ev=%04x\n", fd, ev); + + // Hook oneself back into the fde's suitable for select() on read. + if((fd < 0) || (fd >= fd_table_max)) { + FATAL("fd %d out of range for fd_table \n", fd); + } + fdevent *fde = fd_table[fd]; + fdevent_add(fde, FDE_READ); + + if(ev & FDE_READ){ + int subproc_fd; + + if(readx(fd, &subproc_fd, sizeof(subproc_fd))) { + FATAL("Failed to read the subproc's fd from fd=%d\n", fd); + } + if((subproc_fd < 0) || (subproc_fd >= fd_table_max)) { + D("subproc_fd %d out of range 0, fd_table_max=%d\n", + subproc_fd, fd_table_max); + return; + } + fdevent *subproc_fde = fd_table[subproc_fd]; + if(!subproc_fde) { + D("subproc_fd %d cleared from fd_table\n", subproc_fd); + return; + } + if(subproc_fde->fd != subproc_fd) { + // Already reallocated? + D("subproc_fd %d != fd_table[].fd %d\n", subproc_fd, subproc_fde->fd); + return; + } + + subproc_fde->force_eof = 1; + + int rcount = 0; + ioctl(subproc_fd, FIONREAD, &rcount); + D("subproc with fd=%d has rcount=%d err=%d\n", + subproc_fd, rcount, errno); + + if(rcount) { + // If there is data left, it will show up in the select(). + // This works because there is no other thread reading that + // data when in this fd_func(). + return; + } + + D("subproc_fde.state=%04x\n", subproc_fde->state); + subproc_fde->events |= FDE_READ; + if(subproc_fde->state & FDE_PENDING) { + return; + } + subproc_fde->state |= FDE_PENDING; + fdevent_call_fdfunc(subproc_fde); + } +} + fdevent *fdevent_create(int fd, fd_func func, void *arg) { fdevent *fde = (fdevent*) malloc(sizeof(fdevent)); @@ -412,11 +590,12 @@ void fdevent_destroy(fdevent *fde) fdevent_remove(fde); } -void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg) +void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg) { memset(fde, 0, sizeof(fdevent)); fde->state = FDE_ACTIVE; fde->fd = fd; + fde->force_eof = 0; fde->func = func; fde->arg = arg; @@ -437,7 +616,7 @@ void fdevent_remove(fdevent *fde) if(fde->state & FDE_ACTIVE) { fdevent_disconnect(fde); - dump_fde(fde, "disconnect"); + dump_fde(fde, "disconnect"); fdevent_unregister(fde); } @@ -484,23 +663,33 @@ void fdevent_del(fdevent *fde, unsigned events) fde, (fde->state & FDE_EVENTMASK) & (~(events & FDE_EVENTMASK))); } +void fdevent_subproc_setup() +{ + int s[2]; + + if(adb_socketpair(s)) { + FATAL("cannot create shell-exit socket-pair\n"); + } + SHELL_EXIT_NOTIFY_FD = s[0]; + fdevent *fde; + fde = fdevent_create(s[1], fdevent_subproc_event_func, NULL); + if(!fde) + FATAL("cannot create fdevent for shell-exit handler\n"); + fdevent_add(fde, FDE_READ); +} + void fdevent_loop() { fdevent *fde; + fdevent_subproc_setup(); for(;;) { -#if DEBUG - fprintf(stderr,"--- ---- waiting for events\n"); -#endif + D("--- ---- waiting for events\n"); + fdevent_process(); while((fde = fdevent_plist_dequeue())) { - unsigned events = fde->events; - fde->events = 0; - fde->state &= (~FDE_PENDING); - dump_fde(fde, "callback"); - fde->func(fde->fd, events, fde->arg); + fdevent_call_fdfunc(fde); } } } - diff --git a/adb/fdevent.h b/adb/fdevent.h index 6b7e7ec..a0ebe2a 100644 --- a/adb/fdevent.h +++ b/adb/fdevent.h @@ -70,6 +70,8 @@ struct fdevent fdevent *prev; int fd; + int force_eof; + unsigned short state; unsigned short events; diff --git a/adb/file_sync_client.c b/adb/file_sync_client.c index 5c7a26f..64e393c 100644 --- a/adb/file_sync_client.c +++ b/adb/file_sync_client.c @@ -641,8 +641,9 @@ static int local_build_list(copyinfo **filelist, } else { ci = mkcopyinfo(lpath, rpath, name, 0); if(lstat(ci->src, &st)) { - closedir(d); fprintf(stderr,"cannot stat '%s': %s\n", ci->src, strerror(errno)); + closedir(d); + return -1; } if(!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) { diff --git a/adb/file_sync_service.c b/adb/file_sync_service.c index a231e93..d3e841b 100644 --- a/adb/file_sync_service.c +++ b/adb/file_sync_service.c @@ -193,9 +193,11 @@ static int handle_send_file(int s, char *path, mode_t mode, char *buffer) if(fd < 0) continue; if(writex(fd, buffer, len)) { + int saved_errno = errno; adb_close(fd); adb_unlink(path); fd = -1; + errno = saved_errno; if(fail_errno(s)) return -1; } } diff --git a/adb/mutex_list.h b/adb/mutex_list.h index eebe0df..652dd73 100644 --- a/adb/mutex_list.h +++ b/adb/mutex_list.h @@ -1,8 +1,11 @@ -/* the list of mutexes used by addb */ +/* the list of mutexes used by adb */ +/* #ifndef __MUTEX_LIST_H + * Do not use an include-guard. This file is included once to declare the locks + * and once in win32 to actually do the runtime initialization. + */ #ifndef ADB_MUTEX #error ADB_MUTEX not defined when including this file #endif - ADB_MUTEX(dns_lock) ADB_MUTEX(socket_list_lock) ADB_MUTEX(transport_lock) @@ -11,4 +14,13 @@ ADB_MUTEX(local_transports_lock) #endif ADB_MUTEX(usb_lock) +// Sadly logging to /data/adb/adb-... is not thread safe. +// After modifying adb.h::D() to count invocations: +// DEBUG(jpa):0:Handling main() +// DEBUG(jpa):1:[ usb_init - starting thread ] +// (Oopsies, no :2:, and matching message is also gone.) +// DEBUG(jpa):3:[ usb_thread - opening device ] +// DEBUG(jpa):4:jdwp control socket started (10) +ADB_MUTEX(D_lock) + #undef ADB_MUTEX diff --git a/adb/services.c b/adb/services.c index 7eab17a..6940be8 100644 --- a/adb/services.c +++ b/adb/services.c @@ -22,7 +22,7 @@ #include "sysdeps.h" -#define TRACE_TAG TRACE_ADB +#define TRACE_TAG TRACE_SERVICES #include "adb.h" #include "file_sync_service.h" @@ -30,6 +30,7 @@ # ifndef HAVE_WINSOCK # include <netinet/in.h> # include <netdb.h> +# include <sys/ioctl.h> # endif #else # include <cutils/android_reboot.h> @@ -124,14 +125,12 @@ void restart_root_service(int fd, void *cookie) return; } - property_set("service.adb.root", "1"); snprintf(buf, sizeof(buf), "restarting adbd as root\n"); writex(fd, buf, strlen(buf)); adb_close(fd); - // quit, and init will restart us as root - sleep(1); - exit(1); + // This will cause a property trigger in init.rc to restart us + property_set("service.adb.root", "1"); } } @@ -267,15 +266,16 @@ static int create_service_thread(void (*func)(int, void *), void *cookie) return s[0]; } -static int create_subprocess(const char *cmd, const char *arg0, const char *arg1) +#if !ADB_HOST +static int create_subprocess(const char *cmd, const char *arg0, const char *arg1, pid_t *pid) { #ifdef HAVE_WIN32_PROC - fprintf(stderr, "error: create_subprocess not implemented on Win32 (%s %s %s)\n", cmd, arg0, arg1); - return -1; + 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); + return -1; #else /* !HAVE_WIN32_PROC */ char *devname; int ptm; - pid_t pid; ptm = unix_open("/dev/ptmx", O_RDWR); // | O_NOCTTY); if(ptm < 0){ @@ -287,22 +287,27 @@ static int create_subprocess(const char *cmd, const char *arg0, const char *arg1 if(grantpt(ptm) || unlockpt(ptm) || ((devname = (char*) ptsname(ptm)) == 0)){ printf("[ trouble with /dev/ptmx - %s ]\n", strerror(errno)); + adb_close(ptm); return -1; } - pid = fork(); - if(pid < 0) { + *pid = fork(); + if(*pid < 0) { printf("- fork failed: %s -\n", strerror(errno)); + adb_close(ptm); return -1; } - if(pid == 0){ + if(*pid == 0){ int pts; setsid(); pts = unix_open(devname, O_RDWR); - if(pts < 0) exit(-1); + if(pts < 0) { + fprintf(stderr, "child failed to open pseudo-term slave: %s\n", devname); + exit(-1); + } dup2(pts, 0); dup2(pts, 1); @@ -311,15 +316,9 @@ static int create_subprocess(const char *cmd, const char *arg0, const char *arg1 adb_close(pts); adb_close(ptm); - execl(cmd, cmd, arg0, arg1, NULL); - fprintf(stderr, "- exec '%s' failed: %s (%d) -\n", - cmd, strerror(errno), errno); - exit(-1); - } else { -#if !ADB_HOST - // set child's OOM adjustment to zero + // set OOM adjustment to zero char text[64]; - snprintf(text, sizeof text, "/proc/%d/oom_adj", pid); + snprintf(text, sizeof text, "/proc/%d/oom_adj", getpid()); int fd = adb_open(text, O_WRONLY); if (fd >= 0) { adb_write(fd, "0", 1); @@ -327,11 +326,20 @@ static int create_subprocess(const char *cmd, const char *arg0, const char *arg1 } else { D("adb: unable to open %s\n", text); } -#endif + 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 */ } +#endif /* !ABD_HOST */ #if ADB_HOST #define SHELL_COMMAND "/bin/sh" @@ -339,6 +347,70 @@ static int create_subprocess(const char *cmd, const char *arg0, const char *arg1 #define SHELL_COMMAND "/system/bin/sh" #endif +#if !ADB_HOST +static void subproc_waiter_service(int fd, void *cookie) +{ + pid_t pid = (pid_t)cookie; + + D("entered. fd=%d of pid=%d\n", fd, pid); + for (;;) { + int status; + pid_t p = waitpid(pid, &status, 0); + if (p == pid) { + D("fd=%d, post waitpid(pid=%d) status=%04x\n", fd, p, status); + if (WIFSIGNALED(status)) { + D("*** Killed by signal %d\n", WTERMSIG(status)); + break; + } else if (!WIFEXITED(status)) { + D("*** Didn't exit!!. status %d\n", status); + break; + } else if (WEXITSTATUS(status) >= 0) { + D("*** Exit code %d\n", WEXITSTATUS(status)); + break; + } + } + usleep(100000); // poll every 0.1 sec + } + D("shell exited fd=%d of pid=%d err=%d\n", fd, pid, errno); + if (SHELL_EXIT_NOTIFY_FD >=0) { + int res; + res = writex(SHELL_EXIT_NOTIFY_FD, &fd, sizeof(fd)); + D("notified shell exit via fd=%d for pid=%d res=%d errno=%d\n", + SHELL_EXIT_NOTIFY_FD, pid, res, errno); + } +} + +static int create_subproc_thread(const char *name) +{ + stinfo *sti; + adb_thread_t t; + int ret_fd; + pid_t pid; + if(name) { + ret_fd = create_subprocess(SHELL_COMMAND, "-c", name, &pid); + } else { + ret_fd = create_subprocess(SHELL_COMMAND, "-", 0, &pid); + } + D("create_subprocess() ret_fd=%d pid=%d\n", ret_fd, pid); + + sti = malloc(sizeof(stinfo)); + if(sti == 0) fatal("cannot allocate stinfo"); + sti->func = subproc_waiter_service; + sti->cookie = (void*)pid; + sti->fd = ret_fd; + + if(adb_thread_create( &t, service_bootstrap_func, sti)){ + free(sti); + adb_close(ret_fd); + printf("cannot create service thread\n"); + return -1; + } + + D("service thread started, fd=%d pid=%d\n",ret_fd, pid); + return ret_fd; +} +#endif + int service_to_fd(const char *name) { int ret = -1; @@ -389,14 +461,12 @@ int service_to_fd(const char *name) ret = create_jdwp_connection_fd(atoi(name+5)); } else if (!strncmp(name, "log:", 4)) { ret = create_service_thread(log_service, get_log_file_path(name + 4)); -#endif } else if(!HOST && !strncmp(name, "shell:", 6)) { if(name[6]) { - ret = create_subprocess(SHELL_COMMAND, "-c", name + 6); + ret = create_subproc_thread(name + 6); } else { - ret = create_subprocess(SHELL_COMMAND, "-", 0); + ret = create_subproc_thread(0); } -#if !ADB_HOST } else if(!strncmp(name, "sync:", 5)) { ret = create_service_thread(file_sync_service, NULL); } else if(!strncmp(name, "remount:", 8)) { @@ -407,6 +477,12 @@ int service_to_fd(const char *name) 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); + if (arg == NULL) return -1; + ret = backup_service(BACKUP, arg); + } else if(!strncmp(name, "restore:", 8)) { + ret = backup_service(RESTORE, NULL); } else if(!strncmp(name, "tcpip:", 6)) { int port; if (sscanf(name + 6, "%d", &port) == 0) { diff --git a/adb/sockets.c b/adb/sockets.c index f0357d6..df223b1 100644 --- a/adb/sockets.c +++ b/adb/sockets.c @@ -199,6 +199,7 @@ static void local_socket_close(asocket *s) static void local_socket_destroy(asocket *s) { apacket *p, *n; + D("LS(%d): destroying fde.fd=%d\n", s->id, s->fde.fd); /* IMPORTANT: the remove closes the fd ** that belongs to this socket @@ -218,7 +219,10 @@ static void local_socket_destroy(asocket *s) static void local_socket_close_locked(asocket *s) { + D("entered. LS(%d) fd=%d\n", s->id, s->fd); if(s->peer) { + D("LS(%d): closing peer. peer->id=%d peer->fd=%d\n", + s->id, s->peer->id, s->peer->fd); s->peer->peer = 0; // tweak to avoid deadlock if (s->peer->close == local_socket_close) { @@ -245,6 +249,7 @@ static void local_socket_close_locked(asocket *s) s->closing = 1; fdevent_del(&s->fde, FDE_READ); remove_socket(s); + D("LS(%d): put on socket_closing_list fd=%d\n", s->id, s->fd); insert_local_socket(s, &local_socket_closing_list); } @@ -252,6 +257,8 @@ static void local_socket_event_func(int fd, unsigned ev, void *_s) { asocket *s = _s; + D("LS(%d): event_func(fd=%d(==%d), ev=%04x)\n", s->id, s->fd, fd, ev); + /* put the FDE_WRITE processing before the FDE_READ ** in order to simplify the code. */ @@ -273,6 +280,7 @@ static void local_socket_event_func(int fd, unsigned ev, void *_s) if(errno == EAGAIN) return; if(errno == EINTR) continue; } + D(" closing after write because r=%d and errno is %d\n", r, errno); s->close(s); return; } @@ -288,6 +296,7 @@ static void local_socket_event_func(int fd, unsigned ev, void *_s) ** we can now destroy it. */ if (s->closing) { + D(" closing because 'closing' is set after write\n"); s->close(s); return; } @@ -310,6 +319,7 @@ static void local_socket_event_func(int fd, unsigned ev, void *_s) while(avail > 0) { r = adb_read(fd, x, avail); + D("LS(%d): post adb_read(fd=%d,...) r=%d (errno=%d) avail=%d\n", s->id, s->fd, r, r<0?errno:0, avail); if(r > 0) { avail -= r; x += r; @@ -324,13 +334,15 @@ static void local_socket_event_func(int fd, unsigned ev, void *_s) is_eof = 1; break; } - + D("LS(%d): fd=%d post avail loop. r=%d is_eof=%d forced_eof=%d\n", + s->id, s->fd, r, is_eof, s->fde.force_eof); if((avail == MAX_PAYLOAD) || (s->peer == 0)) { put_apacket(p); } else { p->len = MAX_PAYLOAD - avail; r = s->peer->enqueue(s->peer, p); + D("LS(%d): fd=%d post peer->enqueue(). r=%d\n", s->id, s->fd, r); if(r < 0) { /* error return means they closed us as a side-effect @@ -352,8 +364,9 @@ static void local_socket_event_func(int fd, unsigned ev, void *_s) fdevent_del(&s->fde, FDE_READ); } } - - if(is_eof) { + /* Don't allow a forced eof if data is still there */ + if((s->fde.force_eof && !r) || is_eof) { + D(" closing because is_eof=%d r=%d s->fde.force_eof=%d\n", is_eof, r, s->fde.force_eof); s->close(s); } } @@ -364,6 +377,8 @@ static void local_socket_event_func(int fd, unsigned ev, void *_s) ** bytes of readable data. */ // s->close(s); + D("LS(%d): FDE_ERROR (fd=%d)\n", s->id, s->fd); + return; } } @@ -372,11 +387,11 @@ asocket *create_local_socket(int fd) { asocket *s = calloc(1, sizeof(asocket)); if (s == NULL) fatal("cannot allocate socket"); - install_local_socket(s); s->fd = fd; s->enqueue = local_socket_enqueue; s->ready = local_socket_ready; s->close = local_socket_close; + install_local_socket(s); fdevent_install(&s->fde, fd, local_socket_event_func, s); /* fdevent_add(&s->fde, FDE_ERROR); */ @@ -402,7 +417,7 @@ asocket *create_local_service_socket(const char *name) if(fd < 0) return 0; s = create_local_socket(fd); - D("LS(%d): bound to '%s'\n", s->id, name); + D("LS(%d): bound to '%s' via %d\n", s->id, name, fd); return s; } @@ -432,7 +447,8 @@ typedef struct aremotesocket { static int remote_socket_enqueue(asocket *s, apacket *p) { - D("Calling remote_socket_enqueue\n"); + D("entered remote_socket_enqueue RS(%d) WRITE fd=%d peer.fd=%d\n", + s->id, s->fd, s->peer->fd); p->msg.command = A_WRTE; p->msg.arg0 = s->peer->id; p->msg.arg1 = s->id; @@ -443,7 +459,8 @@ static int remote_socket_enqueue(asocket *s, apacket *p) static void remote_socket_ready(asocket *s) { - D("Calling remote_socket_ready\n"); + D("entered remote_socket_ready RS(%d) OKAY fd=%d peer.fd=%d\n", + s->id, s->fd, s->peer->fd); apacket *p = get_apacket(); p->msg.command = A_OKAY; p->msg.arg0 = s->peer->id; @@ -453,12 +470,15 @@ static void remote_socket_ready(asocket *s) static void remote_socket_close(asocket *s) { - D("Calling remote_socket_close\n"); + D("entered remote_socket_close RS(%d) CLOSE fd=%d peer->fd=%d\n", + s->id, s->fd, s->peer?s->peer->fd:-1); apacket *p = get_apacket(); p->msg.command = A_CLSE; if(s->peer) { p->msg.arg0 = s->peer->id; s->peer->peer = 0; + D("RS(%d) peer->close()ing peer->id=%d peer->fd=%d\n", + s->id, s->peer->id, s->peer->fd); s->peer->close(s->peer); } p->msg.arg1 = s->id; @@ -503,7 +523,7 @@ asocket *create_remote_socket(unsigned id, atransport *t) void connect_to_remote(asocket *s, const char *destination) { - D("Connect_to_remote call \n"); + D("Connect_to_remote call RS(%d) fd=%d\n", s->id, s->fd); apacket *p = get_apacket(); int len = strlen(destination) + 1; diff --git a/adb/sysdeps.h b/adb/sysdeps.h index 74f4ed1..b518076 100644 --- a/adb/sysdeps.h +++ b/adb/sysdeps.h @@ -44,6 +44,7 @@ typedef CRITICAL_SECTION adb_mutex_t; #define ADB_MUTEX_DEFINE(x) adb_mutex_t x /* declare all mutexes */ +/* For win32, adb_sysdeps_init() will do the mutex runtime initialization. */ #define ADB_MUTEX(x) extern adb_mutex_t x; #include "mutex_list.h" @@ -195,6 +196,8 @@ struct fdevent { fdevent *prev; int fd; + int force_eof; + unsigned short state; unsigned short events; @@ -274,13 +277,14 @@ static __inline__ int adb_is_absolute_host_path( const char* path ) #define OS_PATH_SEPARATOR_STR "/" typedef pthread_mutex_t adb_mutex_t; + #define ADB_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER #define adb_mutex_init pthread_mutex_init #define adb_mutex_lock pthread_mutex_lock #define adb_mutex_unlock pthread_mutex_unlock #define adb_mutex_destroy pthread_mutex_destroy -#define ADB_MUTEX_DEFINE(m) static adb_mutex_t m = PTHREAD_MUTEX_INITIALIZER +#define ADB_MUTEX_DEFINE(m) adb_mutex_t m = PTHREAD_MUTEX_INITIALIZER #define adb_cond_t pthread_cond_t #define adb_cond_init pthread_cond_init @@ -289,6 +293,10 @@ typedef pthread_mutex_t adb_mutex_t; #define adb_cond_signal pthread_cond_signal #define adb_cond_destroy pthread_cond_destroy +/* declare all mutexes */ +#define ADB_MUTEX(x) extern adb_mutex_t x; +#include "mutex_list.h" + static __inline__ void close_on_exec(int fd) { fcntl( fd, F_SETFD, FD_CLOEXEC ); diff --git a/adb/transport.c b/adb/transport.c index 2baf340..83a349a 100644 --- a/adb/transport.c +++ b/adb/transport.c @@ -35,24 +35,30 @@ static atransport transport_list = { ADB_MUTEX_DEFINE( transport_lock ); #if ADB_TRACE +#define MAX_DUMP_HEX_LEN 16 static void dump_hex( const unsigned char* ptr, size_t len ) { int nn, len2 = len; + // Build a string instead of logging each character. + // MAX chars in 2 digit hex, one space, MAX chars, one '\0'. + char buffer[MAX_DUMP_HEX_LEN *2 + 1 + MAX_DUMP_HEX_LEN + 1 ], *pb = buffer; - if (len2 > 16) len2 = 16; + if (len2 > MAX_DUMP_HEX_LEN) len2 = MAX_DUMP_HEX_LEN; - for (nn = 0; nn < len2; nn++) - D("%02x", ptr[nn]); - D(" "); + for (nn = 0; nn < len2; nn++) { + sprintf(pb, "%02x", ptr[nn]); + pb += 2; + } + sprintf(pb++, " "); for (nn = 0; nn < len2; nn++) { int c = ptr[nn]; if (c < 32 || c > 127) c = '.'; - D("%c", c); + *pb++ = c; } - D("\n"); - fflush(stdout); + *pb++ = '\0'; + DR("%s\n", buffer); } #endif @@ -192,6 +198,7 @@ write_packet(int fd, const char* name, apacket** ppacket) static void transport_socket_events(int fd, unsigned events, void *_t) { atransport *t = _t; + D("transport_socket_events(fd=%d, events=%04x,...)\n", fd, events); if(events & FDE_READ){ apacket *p = 0; if(read_packet(fd, t->serial, &p)){ @@ -221,8 +228,10 @@ void send_packet(apacket *p, atransport *t) print_packet("send", p); if (t == NULL) { - fatal_errno("Transport is null"); D("Transport is null \n"); + // Zap errno because print_packet() and other stuff have errno effect. + errno = 0; + fatal_errno("Transport is null"); } if(write_packet(t->transport_socket, t->serial, &p)){ @@ -1069,4 +1078,3 @@ int check_data(apacket *p) return 0; } } - diff --git a/adb/transport.h b/adb/transport.h new file mode 100644 index 0000000..992e052 --- /dev/null +++ b/adb/transport.h @@ -0,0 +1,26 @@ +/* + * 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. + */ + +#ifndef __TRANSPORT_H +#define __TRANSPORT_H + +/* convenience wrappers around read/write that will retry on +** EINTR and/or short read/write. Returns 0 on success, -1 +** on error or EOF. +*/ +int readx(int fd, void *ptr, size_t len); +int writex(int fd, const void *ptr, size_t len); +#endif /* __TRANSPORT_H */ diff --git a/adb/usb_linux.c b/adb/usb_linux.c index cd61083..4d55b74 100644 --- a/adb/usb_linux.c +++ b/adb/usb_linux.c @@ -45,7 +45,7 @@ /* usb scan debugging is waaaay too verbose */ #define DBGX(x...) -static adb_mutex_t usb_lock = ADB_MUTEX_INITIALIZER; +ADB_MUTEX_DEFINE( usb_lock ); struct usb_handle { @@ -369,6 +369,7 @@ static int usb_bulk_read(usb_handle *h, void *data, int len) h->reaper_thread = pthread_self(); adb_mutex_unlock(&h->lock); res = ioctl(h->desc, USBDEVFS_REAPURB, &out); + int saved_errno = errno; adb_mutex_lock(&h->lock); h->reaper_thread = 0; if(h->dead) { @@ -376,7 +377,7 @@ static int usb_bulk_read(usb_handle *h, void *data, int len) break; } if(res < 0) { - if(errno == EINTR) { + if(saved_errno == EINTR) { continue; } D("[ reap urb - error ]\n"); @@ -604,6 +605,7 @@ static void register_device(const char *dev_name, ctrl.wIndex = 0; ctrl.wLength = sizeof(languages); ctrl.data = languages; + ctrl.timeout = 1000; result = ioctl(usb->desc, USBDEVFS_CONTROL, &ctrl); if (result > 0) @@ -619,6 +621,7 @@ static void register_device(const char *dev_name, ctrl.wIndex = __le16_to_cpu(languages[i]); ctrl.wLength = sizeof(buffer); ctrl.data = buffer; + ctrl.timeout = 1000; result = ioctl(usb->desc, USBDEVFS_CONTROL, &ctrl); if (result > 0) { @@ -685,4 +688,3 @@ void usb_init() fatal_errno("cannot create input thread"); } } - diff --git a/adb/usb_linux_client.c b/adb/usb_linux_client.c index 0a21c6f..635fa4b 100644 --- a/adb/usb_linux_client.c +++ b/adb/usb_linux_client.c @@ -83,14 +83,14 @@ int usb_write(usb_handle *h, const void *data, int len) { int n; - D("[ write %d ]\n", len); + D("about to write (fd=%d, len=%d)\n", h->fd, len); n = adb_write(h->fd, data, len); if(n != len) { - D("ERROR: n = %d, errno = %d (%s)\n", - n, errno, strerror(errno)); + D("ERROR: fd = %d, n = %d, errno = %d (%s)\n", + h->fd, n, errno, strerror(errno)); return -1; } - D("[ done ]\n"); + D("[ done fd=%d ]\n", h->fd); return 0; } @@ -98,13 +98,14 @@ int usb_read(usb_handle *h, void *data, int len) { int n; - D("[ read %d ]\n", len); + D("about to read (fd=%d, len=%d)\n", h->fd, len); n = adb_read(h->fd, data, len); if(n != len) { - D("ERROR: n = %d, errno = %d (%s)\n", - n, errno, strerror(errno)); + D("ERROR: fd = %d, n = %d, errno = %d (%s)\n", + h->fd, n, errno, strerror(errno)); return -1; } + D("[ done fd=%d ]\n", h->fd); return 0; } diff --git a/adb/usb_vendors.c b/adb/usb_vendors.c index e0c6a47..06f21dd 100644 --- a/adb/usb_vendors.c +++ b/adb/usb_vendors.c @@ -98,11 +98,19 @@ // T & A Mobile Phones' USB Vendor ID #define VENDOR_ID_T_AND_A 0x1BBB // LenovoMobile's USB Vendor ID -#define VENDOR_ID_LENOVOMOBILE 0x2006 +#define VENDOR_ID_LENOVOMOBILE 0x2006 // Lenovo's USB Vendor ID #define VENDOR_ID_LENOVO 0x17EF // Vizio's USB Vendor ID #define VENDOR_ID_VIZIO 0xE040 +// K-Touch's USB Vendor ID +#define VENDOR_ID_K_TOUCH 0x24E3 +// Pegatron's USB Vendor ID +#define VENDOR_ID_PEGATRON 0x1D4D +// Archos's USB Vendor ID +#define VENDOR_ID_ARCHOS 0x0E79 +// Positivo's USB Vendor ID +#define VENDOR_ID_POSITIVO 0x1662 /** built-in vendor list */ @@ -141,6 +149,10 @@ int builtInVendorIds[] = { VENDOR_ID_LENOVOMOBILE, VENDOR_ID_LENOVO, VENDOR_ID_VIZIO, + VENDOR_ID_K_TOUCH, + VENDOR_ID_PEGATRON, + VENDOR_ID_ARCHOS, + VENDOR_ID_POSITIVO, }; #define BUILT_IN_VENDOR_COUNT (sizeof(builtInVendorIds)/sizeof(builtInVendorIds[0])) diff --git a/adb/usb_windows.c b/adb/usb_windows.c index 38c4cf4..b216999 100644 --- a/adb/usb_windows.c +++ b/adb/usb_windows.c @@ -246,10 +246,10 @@ usb_handle* do_usb_open(const wchar_t* interface_name) { } // Something went wrong. - errno = GetLastError(); + int saved_errno = GetLastError(); usb_cleanup_handle(ret); free(ret); - SetLastError(errno); + SetLastError(saved_errno); return NULL; } @@ -267,7 +267,7 @@ int usb_write(usb_handle* handle, const void* data, int len) { (unsigned long)len, &written, time_out); - errno = GetLastError(); + int saved_errno = GetLastError(); if (ret) { // Make sure that we've written what we were asked to write @@ -285,9 +285,10 @@ int usb_write(usb_handle* handle, const void* data, int len) { } } else { // assume ERROR_INVALID_HANDLE indicates we are disconnected - if (errno == ERROR_INVALID_HANDLE) + if (saved_errno == ERROR_INVALID_HANDLE) usb_kick(handle); } + errno = saved_errno; } else { D("usb_write NULL handle\n"); SetLastError(ERROR_INVALID_HANDLE); @@ -313,20 +314,21 @@ int usb_read(usb_handle *handle, void* data, int len) { (unsigned long)xfer, &read, time_out); - errno = GetLastError(); - D("usb_write got: %ld, expected: %d, errno: %d\n", read, xfer, errno); + int saved_errno = GetLastError(); + D("usb_write got: %ld, expected: %d, errno: %d\n", read, xfer, saved_errno); if (ret) { data += read; len -= read; if (len == 0) return 0; - } else if (errno != ERROR_SEM_TIMEOUT) { + } else if (saved_errno != ERROR_SEM_TIMEOUT) { // assume ERROR_INVALID_HANDLE indicates we are disconnected - if (errno == ERROR_INVALID_HANDLE) + if (saved_errno == ERROR_INVALID_HANDLE) usb_kick(handle); break; } + errno = saved_errno; } } else { D("usb_read NULL handle\n"); diff --git a/charger/Android.mk b/charger/Android.mk new file mode 100644 index 0000000..5367a98 --- /dev/null +++ b/charger/Android.mk @@ -0,0 +1,50 @@ +# 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 + +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 libpixelflinger_static libpng +LOCAL_STATIC_LIBRARIES += libz libstdc++ libcutils 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/charger/charger.c b/charger/charger.c new file mode 100644 index 0000000..03280bf --- /dev/null +++ b/charger/charger.c @@ -0,0 +1,979 @@ +/* + * 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. + */ + +//#define DEBUG_UEVENTS +#define CHARGER_KLOG_LEVEL 6 + +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <linux/input.h> +#include <linux/netlink.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/poll.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/un.h> +#include <time.h> +#include <unistd.h> + +#include <cutils/android_reboot.h> +#include <cutils/klog.h> +#include <cutils/list.h> +#include <cutils/misc.h> +#include <cutils/uevent.h> + +#include "minui/minui.h" + +#ifndef max +#define max(a,b) ((a) > (b) ? (a) : (b)) +#endif + +#ifndef min +#define min(a,b) ((a) < (b) ? (a) : (b)) +#endif + +#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) + +#define MSEC_PER_SEC (1000LL) +#define NSEC_PER_MSEC (1000000LL) + +#define BATTERY_UNKNOWN_TIME (2 * MSEC_PER_SEC) +#define POWER_ON_KEY_TIME (2 * MSEC_PER_SEC) +#define UNPLUGGED_SHUTDOWN_TIME (10 * MSEC_PER_SEC) + +#define BATTERY_FULL_THRESH 95 + +#define LAST_KMSG_PATH "/proc/last_kmsg" +#define LAST_KMSG_MAX_SZ (32 * 1024) + +#define LOGE(x...) do { KLOG_ERROR("charger", x); } while (0) +#define LOGI(x...) do { KLOG_INFO("charger", x); } while (0) +#define LOGV(x...) do { KLOG_DEBUG("charger", x); } while (0) + +struct key_state { + bool pending; + bool down; + 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 { + const char *name; + int disp_time; + int min_capacity; + + gr_surface surface; +}; + +struct animation { + bool run; + + struct frame *frames; + int cur_frame; + int num_frames; + + int cur_cycle; + int num_cycles; + + /* current capacity being animated */ + int capacity; +}; + +struct charger { + 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[] = { + { + .name = "charger/battery_0", + .disp_time = 750, + .min_capacity = 0, + }, + { + .name = "charger/battery_1", + .disp_time = 750, + .min_capacity = 20, + }, + { + .name = "charger/battery_2", + .disp_time = 750, + .min_capacity = 40, + }, + { + .name = "charger/battery_3", + .disp_time = 750, + .min_capacity = 60, + }, + { + .name = "charger/battery_4", + .disp_time = 750, + .min_capacity = 80, + }, + { + .name = "charger/battery_5", + .disp_time = 750, + .min_capacity = BATTERY_FULL_THRESH, + }, +}; + +static struct animation battery_animation = { + .frames = batt_anim_frames, + .num_frames = ARRAY_SIZE(batt_anim_frames), + .num_cycles = 3, +}; + +static struct charger charger_state = { + .batt_anim = &battery_animation, +}; + +static int char_width; +static int char_height; + +/* current time in milliseconds */ +static int64_t curr_time_ms(void) +{ + struct timespec tm; + clock_gettime(CLOCK_MONOTONIC, &tm); + return tm.tv_sec * MSEC_PER_SEC + (tm.tv_nsec / NSEC_PER_MSEC); +} + +static void clear_screen(void) +{ + gr_color(0, 0, 0, 255); + gr_fill(0, 0, gr_fb_width(), gr_fb_height()); +}; + +#define MAX_KLOG_WRITE_BUF_SZ 256 + +static void dump_last_kmsg(void) +{ + char *buf; + char *ptr; + unsigned sz = 0; + int len; + + LOGI("\n"); + LOGI("*************** LAST KMSG ***************\n"); + LOGI("\n"); + buf = 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); + ptr = buf + (sz - len); + + while (len > 0) { + int cnt = min(len, MAX_KLOG_WRITE_BUF_SZ); + char yoink; + char *nl; + + nl = memrchr(ptr, '\n', cnt - 1); + if (nl) + cnt = nl - ptr + 1; + + yoink = ptr[cnt]; + ptr[cnt] = '\0'; + klog_write(6, "<6>%s", ptr); + ptr[cnt] = yoink; + + len -= cnt; + ptr += cnt; + } + + free(buf); + +out: + LOGI("\n"); + LOGI("************* END LAST KMSG *************\n"); + LOGI("\n"); +} + +static int read_file(const char *path, char *buf, size_t sz) +{ + int fd; + size_t cnt; + + fd = open(path, O_RDONLY, 0); + if (fd < 0) + goto err; + + cnt = read(fd, buf, sz - 1); + if (cnt <= 0) + goto err; + buf[cnt] = '\0'; + if (buf[cnt - 1] == '\n') { + cnt--; + buf[cnt] = '\0'; + } + + close(fd); + return cnt; + +err: + if (fd >= 0) + close(fd); + return -1; +} + +static int read_file_int(const char *path, int *val) +{ + char buf[32]; + int ret; + int tmp; + char *end; + + ret = read_file(path, buf, sizeof(buf)); + if (ret < 0) + return -1; + + tmp = strtol(buf, &end, 0); + if (end == buf || + ((end < buf+sizeof(buf)) && (*end != '\n' && *end != '\0'))) + goto err; + + *val = tmp; + return 0; + +err: + return -1; +} + +static int get_battery_capacity(struct charger *charger) +{ + 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); +} + +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); + + if (x < 0) + x = (gr_fb_width() - str_len_px) / 2; + if (y < 0) + y = (gr_fb_height() - char_height) / 2; + gr_text(x, y, str); + + return y + char_height; +} + +static void android_green(void) +{ + gr_color(0xa4, 0xc6, 0x39, 255); +} + +/* returns the last y-offset of where the surface ends */ +static int draw_surface_centered(struct charger *charger, gr_surface surface) +{ + int w; + int h; + int x; + int y; + + w = gr_get_width(surface); + h = gr_get_height(surface); + x = (gr_fb_width() - w) / 2 ; + y = (gr_fb_height() - h) / 2 ; + + LOGV("drawing surface %dx%d+%d+%d\n", w, h, x, y); + gr_blit(surface, 0, 0, w, h, x, y); + return y + h; +} + +static void draw_unknown(struct charger *charger) +{ + int y; + if (charger->surf_unknown) { + draw_surface_centered(charger, charger->surf_unknown); + } else { + android_green(); + y = draw_text("Charging!", -1, -1); + draw_text("?\?/100", -1, y + 25); + } +} + +static void draw_battery(struct charger *charger) +{ + struct animation *batt_anim = charger->batt_anim; + struct frame *frame = &batt_anim->frames[batt_anim->cur_frame]; + + if (batt_anim->num_frames != 0) { + draw_surface_centered(charger, frame->surface); + LOGV("drawing frame #%d name=%s min_cap=%d time=%d\n", + batt_anim->cur_frame, frame->name, frame->min_capacity, + frame->disp_time); + } +} + +static void redraw_screen(struct charger *charger) +{ + struct animation *batt_anim = charger->batt_anim; + + clear_screen(); + + /* try to display *something* */ + if (batt_anim->capacity < 0 || batt_anim->num_frames == 0) + draw_unknown(charger); + else + draw_battery(charger); + gr_flip(); +} + +static void kick_animation(struct animation *anim) +{ + anim->run = true; +} + +static void reset_animation(struct animation *anim) +{ + anim->cur_cycle = 0; + anim->cur_frame = 0; + anim->run = false; +} + +static void update_screen_state(struct charger *charger, int64_t now) +{ + struct animation *batt_anim = charger->batt_anim; + int cur_frame; + int disp_time; + + if (!batt_anim->run || now < charger->next_screen_transition) + return; + + /* animation is over, blank screen and leave */ + if (batt_anim->cur_cycle == batt_anim->num_cycles) { + reset_animation(batt_anim); + charger->next_screen_transition = -1; + gr_fb_blank(true); + LOGV("[%lld] animation done\n", now); + return; + } + + disp_time = batt_anim->frames[batt_anim->cur_frame].disp_time; + + /* animation starting, set up the animation */ + if (batt_anim->cur_frame == 0) { + int batt_cap; + int ret; + + LOGV("[%lld] animation starting\n", now); + batt_cap = get_battery_capacity(charger); + if (batt_cap >= 0 && batt_anim->num_frames != 0) { + int i; + + /* find first frame given current capacity */ + for (i = 1; i < batt_anim->num_frames; i++) { + if (batt_cap < batt_anim->frames[i].min_capacity) + break; + } + batt_anim->cur_frame = i - 1; + + /* show the first frame for twice as long */ + disp_time = batt_anim->frames[batt_anim->cur_frame].disp_time * 2; + } + + batt_anim->capacity = batt_cap; + } + + /* unblank the screen on first cycle */ + if (batt_anim->cur_cycle == 0) + gr_fb_blank(false); + + /* draw the new frame (@ cur_frame) */ + redraw_screen(charger); + + /* if we don't have anim frames, we only have one image, so just bump + * 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); + charger->next_screen_transition = now + BATTERY_UNKNOWN_TIME; + batt_anim->cur_cycle++; + return; + } + + /* schedule next screen transition */ + charger->next_screen_transition = now + disp_time; + + /* advance frame cntr to the next valid frame + * if necessary, advance cycle cntr, and reset frame cntr + */ + batt_anim->cur_frame++; + if (batt_anim->cur_frame == batt_anim->num_frames) { + batt_anim->cur_cycle++; + batt_anim->cur_frame = 0; + + /* don't reset the cycle counter, since we use that as a signal + * in a test above to check if animation is over + */ + } +} + +static int set_key_callback(int code, int value, void *data) +{ + struct charger *charger = data; + int64_t now = curr_time_ms(); + int down = !!value; + + if (code > KEY_MAX) + return -1; + + /* ignore events that don't modify our state */ + if (charger->keys[code].down == down) + return 0; + + /* only record the down even timestamp, as the amount + * of time the key spent not being pressed is not useful */ + if (down) + charger->keys[code].timestamp = now; + charger->keys[code].down = down; + charger->keys[code].pending = true; + if (down) { + LOGV("[%lld] 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); + } + + return 0; +} + +static void update_input_state(struct charger *charger, + struct input_event *ev) +{ + if (ev->type != EV_KEY) + return; + set_key_callback(ev->code, ev->value, charger); +} + +static void set_next_key_check(struct charger *charger, + struct key_state *key, + int64_t timeout) +{ + int64_t then = key->timestamp + timeout; + + if (charger->next_key_check == -1 || then < charger->next_key_check) + charger->next_key_check = then; +} + +static void process_key(struct charger *charger, int code, int64_t now) +{ + struct key_state *key = &charger->keys[code]; + int64_t next_key_check; + + if (code == KEY_POWER) { + if (key->down) { + int64_t reboot_timeout = key->timestamp + POWER_ON_KEY_TIME; + if (now >= reboot_timeout) { + LOGI("[%lld] rebooting\n", now); + android_reboot(ANDROID_RB_RESTART, 0, 0); + } else { + /* if the key is pressed but timeout hasn't expired, + * make sure we wake up at the right-ish time to check + */ + set_next_key_check(charger, key, POWER_ON_KEY_TIME); + } + } else { + /* if the power key got released, force screen state cycle */ + if (key->pending) + kick_animation(charger->batt_anim); + } + } + + key->pending = false; +} + +static void handle_input_state(struct charger *charger, int64_t now) +{ + process_key(charger, KEY_POWER, now); + + if (charger->next_key_check != -1 && now > charger->next_key_check) + charger->next_key_check = -1; +} + +static void handle_power_supply_state(struct charger *charger, int64_t now) +{ + if (charger->num_supplies_online == 0) { + 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); + } else if (now >= charger->next_pwr_check) { + LOGI("[%lld] shutting down\n", now); + android_reboot(ANDROID_RB_POWEROFF, 0, 0); + } else { + /* otherwise we already have a shutdown timer scheduled */ + } + } else { + /* online supply present, reset shutdown timer if set */ + if (charger->next_pwr_check != -1) { + LOGI("[%lld] 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) +{ + 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, + charger->next_screen_transition, charger->next_key_check, + charger->next_pwr_check); + + if (charger->next_screen_transition != -1) + next_event = charger->next_screen_transition; + if (charger->next_key_check != -1 && charger->next_key_check < next_event) + next_event = charger->next_key_check; + if (charger->next_pwr_check != -1 && charger->next_pwr_check < next_event) + next_event = charger->next_pwr_check; + + if (next_event != -1 && next_event != INT64_MAX) + 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(); +} + +static int input_callback(int fd, short revents, void *data) +{ + struct charger *charger = data; + struct input_event ev; + int ret; + + ret = ev_get_input(fd, revents, &ev); + if (ret) + return -1; + update_input_state(charger, &ev); + return 0; +} + +static void event_loop(struct charger *charger) +{ + 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); + } +} + +int main(int argc, char **argv) +{ + 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); + + dump_last_kmsg(); + + LOGI("--------------- STARTING CHARGER MODE ---------------\n"); + + gr_init(); + gr_font_size(&char_width, &char_height); + + ev_init(input_callback, charger); + + fd = uevent_open_socket(64*1024, true); + if (fd >= 0) { + fcntl(fd, F_SETFL, O_NONBLOCK); + ev_add_fd(fd, uevent_callback, charger); + } + charger->uevent_fd = fd; + coldboot(charger, "/sys/class/power_supply", "add"); + + ret = res_create_surface("charger/battery_fail", &charger->surf_unknown); + if (ret < 0) { + LOGE("Cannot load image\n"); + charger->surf_unknown = NULL; + } + + for (i = 0; i < charger->batt_anim->num_frames; i++) { + struct frame *frame = &charger->batt_anim->frames[i]; + + ret = res_create_surface(frame->name, &frame->surface); + if (ret < 0) { + LOGE("Cannot load image %s\n", frame->name); + /* TODO: free the already allocated surfaces... */ + charger->batt_anim->num_frames = 0; + charger->batt_anim->num_cycles = 1; + break; + } + } + + ev_sync_key_state(set_key_callback, charger); + + gr_fb_blank(true); + + charger->next_screen_transition = now - 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_0.png b/charger/images/battery_0.png Binary files differnew file mode 100644 index 0000000..2347074 --- /dev/null +++ b/charger/images/battery_0.png diff --git a/charger/images/battery_1.png b/charger/images/battery_1.png Binary files differnew file mode 100644 index 0000000..cd34620 --- /dev/null +++ b/charger/images/battery_1.png diff --git a/charger/images/battery_2.png b/charger/images/battery_2.png Binary files differnew file mode 100644 index 0000000..3e4095e --- /dev/null +++ b/charger/images/battery_2.png diff --git a/charger/images/battery_3.png b/charger/images/battery_3.png Binary files differnew file mode 100644 index 0000000..08c1551 --- /dev/null +++ b/charger/images/battery_3.png diff --git a/charger/images/battery_4.png b/charger/images/battery_4.png Binary files differnew file mode 100644 index 0000000..3a678da --- /dev/null +++ b/charger/images/battery_4.png diff --git a/charger/images/battery_5.png b/charger/images/battery_5.png Binary files differnew file mode 100644 index 0000000..d8dc40e --- /dev/null +++ b/charger/images/battery_5.png diff --git a/charger/images/battery_charge.png b/charger/images/battery_charge.png Binary files differnew file mode 100644 index 0000000..b501933 --- /dev/null +++ b/charger/images/battery_charge.png diff --git a/charger/images/battery_fail.png b/charger/images/battery_fail.png Binary files differnew file mode 100644 index 0000000..36fc254 --- /dev/null +++ b/charger/images/battery_fail.png diff --git a/debuggerd/arm/machine.c b/debuggerd/arm/machine.c index 88bf054..58a7839 100644 --- a/debuggerd/arm/machine.c +++ b/debuggerd/arm/machine.c @@ -50,6 +50,74 @@ extern int unwind_backtrace_with_ptrace(int tfd, pid_t pid, mapinfo *map, int *frame0_pc_sane, bool at_fault); +/* + * If this isn't clearly a null pointer dereference, dump the + * /proc/maps entries near the fault address. + */ +static void show_nearby_maps(int tfd, int pid, mapinfo *map) +{ + siginfo_t si; + + memset(&si, 0, sizeof(si)); + if (ptrace(PTRACE_GETSIGINFO, pid, 0, &si)) { + _LOG(tfd, false, "cannot get siginfo: %s\n", strerror(errno)); + return; + } + if (!signal_has_address(si.si_signo)) + return; + + uintptr_t addr = (uintptr_t) si.si_addr; + addr &= ~0xfff; /* round to 4K page boundary */ + if (addr == 0) /* null-pointer deref */ + return; + + _LOG(tfd, false, "\nmemory map around addr %08x:\n", si.si_addr); + + /* + * Search for a match, or for a hole where the match would be. The list + * is backward from the file content, so it starts at high addresses. + */ + bool found = false; + mapinfo *next = NULL; + mapinfo *prev = NULL; + while (map != NULL) { + if (addr >= map->start && addr < map->end) { + found = true; + next = map->next; + break; + } else if (addr >= map->end) { + /* map would be between "prev" and this entry */ + next = map; + map = NULL; + break; + } + + prev = map; + map = map->next; + } + + /* + * Show "next" then "match" then "prev" so that the addresses appear in + * ascending order (like /proc/pid/maps). + */ + if (next != NULL) { + _LOG(tfd, false, "%08x-%08x %s\n", next->start, next->end, next->name); + } else { + _LOG(tfd, false, "(no map below)\n"); + } + if (map != NULL) { + _LOG(tfd, false, "%08x-%08x %s\n", map->start, map->end, map->name); + } else { + _LOG(tfd, false, "(no map for address)\n"); + } + if (prev != NULL) { + _LOG(tfd, false, "%08x-%08x %s\n", prev->start, prev->end, prev->name); + } else { + _LOG(tfd, false, "(no map above)\n"); + } +} + + void dump_stack_and_code(int tfd, int pid, mapinfo *map, int unwind_depth, unsigned int sp_list[], bool at_fault) @@ -123,6 +191,8 @@ void dump_stack_and_code(int tfd, int pid, mapinfo *map, } } + show_nearby_maps(tfd, pid, map); + p = sp - 64; if (p > sp) p = 0; diff --git a/debuggerd/debuggerd.c b/debuggerd/debuggerd.c index 7a3e781..91d9dda 100644 --- a/debuggerd/debuggerd.c +++ b/debuggerd/debuggerd.c @@ -31,13 +31,13 @@ #include <cutils/sockets.h> #include <cutils/logd.h> +#include <cutils/logger.h> #include <cutils/properties.h> #include <linux/input.h> #include <private/android_filesystem_config.h> -#include <byteswap.h> #include "debuggerd.h" #include "utility.h" @@ -71,14 +71,17 @@ mapinfo *parse_maps_line(char *line) mapinfo *mi; int len = strlen(line); - if(len < 1) return 0; + if (len < 1) return 0; /* not expected */ line[--len] = 0; - if(len < 50) return 0; - if(line[20] != 'x') return 0; + if (len < 50) { + mi = malloc(sizeof(mapinfo) + 1); + } else { + mi = malloc(sizeof(mapinfo) + (len - 47)); + } + if (mi == 0) return 0; - mi = malloc(sizeof(mapinfo) + (len - 47)); - if(mi == 0) return 0; + mi->isExecutable = (line[20] == 'x'); mi->start = strtoul(line, 0, 16); mi->end = strtoul(line + 9, 0, 16); @@ -88,7 +91,11 @@ mapinfo *parse_maps_line(char *line) mi->exidx_start = mi->exidx_end = 0; mi->symbols = 0; mi->next = 0; - strcpy(mi->name, line + 49); + if (len < 50) { + mi->name[0] = '\0'; + } else { + strcpy(mi->name, line + 49); + } return mi; } @@ -166,11 +173,14 @@ void dump_fault_addr(int tfd, int pid, int sig) memset(&si, 0, sizeof(si)); if(ptrace(PTRACE_GETSIGINFO, pid, 0, &si)){ _LOG(tfd, false, "cannot get siginfo: %s\n", strerror(errno)); - } else { + } else if (signal_has_address(sig)) { _LOG(tfd, false, "signal %d (%s), code %d (%s), fault addr %08x\n", sig, get_signame(sig), si.si_code, get_sigcode(sig, si.si_code), si.si_addr); + } else { + _LOG(tfd, false, "signal %d (%s), code %d (%s), fault addr --------\n", + sig, get_signame(sig), si.si_code, get_sigcode(sig, si.si_code)); } } @@ -196,77 +206,13 @@ void dump_crash_banner(int tfd, unsigned pid, unsigned tid, int sig) if(sig) dump_fault_addr(tfd, tid, sig); } -/* After randomization (ASLR), stack contents that point to randomized - * code become uninterpretable (e.g. can't be resolved to line numbers). - * Here, we bundle enough information so that stack analysis on the - * server side can still be performed. This means we are leaking some - * information about the device (its randomization base). We have to make - * sure an attacker has no way of intercepting the tombstone. - */ - -typedef struct { - int32_t mmap_addr; - char tag[4]; /* 'P', 'R', 'E', ' ' */ -} prelink_info_t __attribute__((packed)); - -static inline void set_prelink(long *prelink_addr, - prelink_info_t *info) -{ - // We will assume the binary is little-endian, and test the - // host endianness here. - unsigned long test_endianness = 0xFF; - - if (sizeof(prelink_info_t) == 8 && prelink_addr) { - if (*(unsigned char *)&test_endianness) - *prelink_addr = info->mmap_addr; - else - *prelink_addr = bswap_32(info->mmap_addr); - } -} - -static int check_prelinked(const char *fname, - long *prelink_addr) -{ - *prelink_addr = 0; - if (sizeof(prelink_info_t) != 8) return 0; - - int fd = open(fname, O_RDONLY); - if (fd < 0) return 0; - off_t end = lseek(fd, 0, SEEK_END); - int nr = sizeof(prelink_info_t); - - off_t sz = lseek(fd, -nr, SEEK_CUR); - if ((long)(end - sz) != (long)nr) return 0; - if (sz == (off_t)-1) return 0; - - prelink_info_t info; - int num_read = read(fd, &info, nr); - if (num_read < 0) return 0; - if (num_read != sizeof(info)) return 0; - - int prelinked = 0; - if (!strncmp(info.tag, "PRE ", 4)) { - set_prelink(prelink_addr, &info); - prelinked = 1; - } - if (close(fd) < 0) return 0; - return prelinked; -} - -void dump_randomization_base(int tfd, bool at_fault) { - bool only_in_tombstone = !at_fault; - long prelink_addr; - check_prelinked("/system/lib/libc.so", &prelink_addr); - _LOG(tfd, only_in_tombstone, - "\nlibc base address: %08x\n", prelink_addr); -} - -/* End of ASLR-related logic. */ - static void parse_elf_info(mapinfo *milist, pid_t pid) { mapinfo *mi; for (mi = milist; mi != NULL; mi = mi->next) { + if (!mi->isExecutable) + continue; + Elf32_Ehdr ehdr; memset(&ehdr, 0, sizeof(Elf32_Ehdr)); @@ -353,7 +299,6 @@ void dump_crash_report(int tfd, unsigned pid, unsigned tid, bool at_fault) dump_pc_and_lr(tfd, tid, milist, stack_depth, at_fault); } - dump_randomization_base(tfd, at_fault); dump_stack_and_code(tfd, tid, milist, stack_depth, sp_list, at_fault); #elif __i386__ /* If stack unwinder fails, use the default solution to dump the stack @@ -469,6 +414,101 @@ static bool dump_sibling_thread_report(int tfd, unsigned pid, unsigned tid) return need_cleanup != 0; } +/* + * 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. + */ +static void dump_log_file(int tfd, unsigned pid, const char* filename) +{ + int logfd = open(filename, O_RDONLY | O_NONBLOCK); + if (logfd < 0) { + XLOG("Unable to open %s: %s\n", filename, strerror(errno)); + return; + } + _LOG(tfd, true, "--------- log %s\n", filename); + + union { + unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1]; + struct logger_entry entry; + } log_entry; + + while (true) { + ssize_t actual = read(logfd, log_entry.buf, LOGGER_ENTRY_MAX_LEN); + if (actual < 0) { + if (errno == EINTR) { + /* interrupted by signal, retry */ + continue; + } else if (errno == EAGAIN) { + /* non-blocking EOF; we're done */ + break; + } else { + _LOG(tfd, true, "Error while reading log: %s\n", + strerror(errno)); + break; + } + } else if (actual == 0) { + _LOG(tfd, true, "Got zero bytes while reading log: %s\n", + strerror(errno)); + break; + } + + /* + * NOTE: if you XLOG something here, this will spin forever, + * 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; + + if (entry->pid != (int32_t) pid) { + /* wrong pid, ignore */ + continue; + } + + /* + * Msg format is: <priority:1><tag:N>\0<message:N>\0 + * + * We want to display it in the same format as "logcat -v threadtime" + * (although in this case the pid is redundant). + * + * TODO: scan for line breaks ('\n') and display each text line + * on a separate line, prefixed with the header, like logcat does. + */ + static const char* kPrioChars = "!.VDIWEFS"; + unsigned char prio = entry->msg[0]; + const char* tag = entry->msg + 1; + const char* msg = tag + strlen(tag) + 1; + + log_entry.entry.msg[entry->len] = '\0'; + + char timeBuf[32]; + time_t sec = (time_t) entry->sec; + struct tm tmBuf; + struct tm* ptm; + ptm = localtime_r(&sec, &tmBuf); + strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm); + + _LOG(tfd, true, "%s.%03ld %5d %5d %c %-8s: %s\n", + timeBuf, entry->nsec / 1000000, + entry->pid, entry->tid, + (prio < strlen(kPrioChars) ? kPrioChars[prio] : '?'), + tag, msg); + } + + close(logfd); +} + +/* + * 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(int tfd, unsigned pid) +{ + dump_log_file(tfd, pid, "/dev/log/system"); + dump_log_file(tfd, pid, "/dev/log/main"); +} + /* Return true if some thread is not detached cleanly */ static bool engrave_tombstone(unsigned pid, unsigned tid, int debug_uid, int signal) @@ -493,6 +533,13 @@ static bool engrave_tombstone(unsigned pid, unsigned tid, int debug_uid, need_cleanup = dump_sibling_thread_report(fd, pid, tid); } + /* don't copy log to tombstone unless this is a dev device */ + char value[PROPERTY_VALUE_MAX]; + property_get("ro.debuggable", value, "0"); + if (value[0] == '1') { + dump_logs(fd, pid); + } + close(fd); return need_cleanup; } @@ -662,19 +709,39 @@ static void handle_crashing_process(int fd) * debugger_signal_handler(). */ tid_attach_status = ptrace(PTRACE_ATTACH, tid, 0, 0); + int ptrace_error = errno; - TEMP_FAILURE_RETRY(write(fd, &tid, 1)); + if (TEMP_FAILURE_RETRY(write(fd, &tid, 1)) != 1) { + XLOG("failed responding to client: %s\n", + strerror(errno)); + goto done; + } if(tid_attach_status < 0) { - LOG("ptrace attach failed: %s\n", strerror(errno)); + LOG("ptrace attach failed: %s\n", strerror(ptrace_error)); goto done; } close(fd); fd = -1; + const int sleep_time_usec = 200000; /* 0.2 seconds */ + const int max_total_sleep_usec = 3000000; /* 3 seconds */ + int loop_limit = max_total_sleep_usec / sleep_time_usec; for(;;) { - n = waitpid(tid, &status, __WALL); + if (loop_limit-- == 0) { + LOG("timed out waiting for pid=%d tid=%d uid=%d to die\n", + cr.pid, tid, cr.uid); + goto done; + } + n = waitpid(tid, &status, __WALL | WNOHANG); + + if (n == 0) { + /* not ready yet */ + XLOG("not ready yet\n"); + usleep(sleep_time_usec); + continue; + } if(n < 0) { if(errno == EAGAIN) continue; @@ -764,6 +831,18 @@ int main() struct sigaction act; int logsocket = -1; + /* + * debuggerd crashes can't be reported to debuggerd. Reset all of the + * crash handlers. + */ + signal(SIGILL, SIG_DFL); + signal(SIGABRT, SIG_DFL); + signal(SIGBUS, SIG_DFL); + signal(SIGFPE, SIG_DFL); + signal(SIGSEGV, SIG_DFL); + signal(SIGSTKFLT, SIG_DFL); + signal(SIGPIPE, SIG_DFL); + logsocket = socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_DGRAM); if(logsocket < 0) { @@ -791,8 +870,12 @@ int main() int fd; alen = sizeof(addr); + XLOG("waiting for connection\n"); fd = accept(s, &addr, &alen); - if(fd < 0) continue; + if(fd < 0) { + XLOG("accept failed: %s\n", strerror(errno)); + continue; + } fcntl(fd, F_SETFD, FD_CLOEXEC); diff --git a/debuggerd/utility.c b/debuggerd/utility.c index 2afdb46..409209c 100644 --- a/debuggerd/utility.c +++ b/debuggerd/utility.c @@ -17,6 +17,7 @@ #include <sys/ptrace.h> #include <sys/exec_elf.h> +#include <signal.h> #include <assert.h> #include <string.h> #include <errno.h> @@ -82,3 +83,20 @@ const mapinfo *pc_to_mapinfo(mapinfo *mi, unsigned pc, unsigned *rel_pc) } return NULL; } + +/* + * Returns true if the specified signal has an associated address (i.e. it + * sets siginfo_t.si_addr). + */ +bool signal_has_address(int sig) +{ + switch (sig) { + case SIGILL: + case SIGFPE: + case SIGSEGV: + case SIGBUS: + return true; + default: + return false; + } +} diff --git a/debuggerd/utility.h b/debuggerd/utility.h index 45e2067..4a935d2 100644 --- a/debuggerd/utility.h +++ b/debuggerd/utility.h @@ -36,6 +36,7 @@ typedef struct mapinfo { unsigned exidx_start; unsigned exidx_end; struct symbol_table *symbols; + bool isExecutable; char name[]; } mapinfo; @@ -56,6 +57,9 @@ const char *map_to_name(mapinfo *mi, unsigned pc, const char* def); /* Log information onto the tombstone */ extern void _LOG(int tfd, bool in_tombstone_only, const char *fmt, ...); +/* Determine whether si_addr is valid for this signal */ +bool signal_has_address(int sig); + #define LOG(fmt...) _LOG(-1, 0, fmt) /* Set to 1 for normal debug traces */ diff --git a/include/arch/darwin-x86/AndroidConfig.h b/include/arch/darwin-x86/AndroidConfig.h index d99072a..c8ccc7e 100644 --- a/include/arch/darwin-x86/AndroidConfig.h +++ b/include/arch/darwin-x86/AndroidConfig.h @@ -305,12 +305,4 @@ */ #define HAVE_PRINTF_ZD 1 -/* - * We need to open binary files using O_BINARY on Windows. - * Most systems lack (and actually don't need) this flag. - */ -#ifndef O_BINARY -#define O_BINARY 0 -#endif - #endif /*_ANDROID_CONFIG_H*/ diff --git a/include/arch/freebsd-x86/AndroidConfig.h b/include/arch/freebsd-x86/AndroidConfig.h index 9703c76..d828bd5 100644 --- a/include/arch/freebsd-x86/AndroidConfig.h +++ b/include/arch/freebsd-x86/AndroidConfig.h @@ -363,12 +363,4 @@ */ #define HAVE_PRINTF_ZD 1 -/* - * We need to open binary files using O_BINARY on Windows. - * Most systems lack (and actually don't need) this flag. - */ -#ifndef O_BINARY -#define O_BINARY 0 -#endif - #endif /*_ANDROID_CONFIG_H*/ diff --git a/include/arch/linux-arm/AndroidConfig.h b/include/arch/linux-arm/AndroidConfig.h index 5138d90..cae112b 100644 --- a/include/arch/linux-arm/AndroidConfig.h +++ b/include/arch/linux-arm/AndroidConfig.h @@ -190,7 +190,7 @@ * with a memory address. If not defined, stack crawls will not have symbolic * information. */ -#define HAVE_DLADDR 0 +#define HAVE_DLADDR 1 /* * Defined if we have the cxxabi.h header for demangling C++ symbols. If @@ -361,12 +361,4 @@ */ #define HAVE_PRINTF_ZD 1 -/* - * We need to open binary files using O_BINARY on Windows. - * Most systems lack (and actually don't need) this flag. - */ -#ifndef O_BINARY -#define O_BINARY 0 -#endif - #endif /* _ANDROID_CONFIG_H */ diff --git a/include/arch/linux-ppc/AndroidConfig.h b/include/arch/linux-ppc/AndroidConfig.h index 60bddd6..00706dc 100644 --- a/include/arch/linux-ppc/AndroidConfig.h +++ b/include/arch/linux-ppc/AndroidConfig.h @@ -323,12 +323,4 @@ */ #define HAVE_PREAD 1 -/* - * We need to open binary files using O_BINARY on Windows. - * Most systems lack (and actually don't need) this flag. - */ -#ifndef O_BINARY -#define O_BINARY 0 -#endif - #endif /*_ANDROID_CONFIG_H*/ diff --git a/include/arch/linux-sh/AndroidConfig.h b/include/arch/linux-sh/AndroidConfig.h index 9303bb6..5562eae 100644 --- a/include/arch/linux-sh/AndroidConfig.h +++ b/include/arch/linux-sh/AndroidConfig.h @@ -366,12 +366,4 @@ */ #define HAVE_PRINTF_ZD 1 -/* - * We need to open binary files using O_BINARY on Windows. - * Most systems lack (and actually don't need) this flag. - */ -#ifndef O_BINARY -#define O_BINARY 0 -#endif - #endif /* _ANDROID_CONFIG_H */ diff --git a/include/arch/linux-x86/AndroidConfig.h b/include/arch/linux-x86/AndroidConfig.h index 6fd26ea..7dcaa98 100644 --- a/include/arch/linux-x86/AndroidConfig.h +++ b/include/arch/linux-x86/AndroidConfig.h @@ -333,12 +333,4 @@ */ #define HAVE_PRINTF_ZD 1 -/* - * We need to open binary files using O_BINARY on Windows. - * Most systems lack (and actually don't need) this flag. - */ -#ifndef O_BINARY -#define O_BINARY 0 -#endif - #endif /*_ANDROID_CONFIG_H*/ diff --git a/include/arch/target_linux-x86/AndroidConfig.h b/include/arch/target_linux-x86/AndroidConfig.h index a6f7090..05dd220 100644 --- a/include/arch/target_linux-x86/AndroidConfig.h +++ b/include/arch/target_linux-x86/AndroidConfig.h @@ -350,12 +350,4 @@ */ #define HAVE_PRINTF_ZD 1 -/* - * We need to open binary files using O_BINARY on Windows. - * Most systems lack (and actually don't need) this flag. - */ -#ifndef O_BINARY -#define O_BINARY 0 -#endif - #endif /* _ANDROID_CONFIG_H */ diff --git a/include/arch/windows/AndroidConfig.h b/include/arch/windows/AndroidConfig.h index 8a7e062..ad890b4 100644 --- a/include/arch/windows/AndroidConfig.h +++ b/include/arch/windows/AndroidConfig.h @@ -338,10 +338,4 @@ */ /* #define HAVE_PRINTF_ZD 1 */ -/* - * We need to open binary files using O_BINARY on Windows. - * We don't define it on Windows since it is part of the io headers. - */ -/* #define O_BINARY 0 */ - #endif /*_ANDROID_CONFIG_H*/ diff --git a/include/cutils/atomic-inline.h b/include/cutils/atomic-inline.h index 6acb67c..49f3e70 100644 --- a/include/cutils/atomic-inline.h +++ b/include/cutils/atomic-inline.h @@ -17,6 +17,10 @@ #ifndef ANDROID_CUTILS_ATOMIC_INLINE_H #define ANDROID_CUTILS_ATOMIC_INLINE_H +#ifdef __cplusplus +extern "C" { +#endif + /* * Inline declarations and macros for some special-purpose atomic * operations. These are intended for rare circumstances where a @@ -61,4 +65,8 @@ #define ANDROID_MEMBAR_STORE android_memory_store_barrier #endif +#ifdef __cplusplus +} +#endif + #endif /* ANDROID_CUTILS_ATOMIC_INLINE_H */ diff --git a/include/cutils/compiler.h b/include/cutils/compiler.h index 09112d5..70f884a 100644 --- a/include/cutils/compiler.h +++ b/include/cutils/compiler.h @@ -29,4 +29,16 @@ # define CC_UNLIKELY( exp ) (__builtin_expect( !!(exp), 0 )) #endif +/** + * exports marked symbols + * + * if used on a C++ class declaration, this macro must be inserted + * after the "class" keyword. For instance: + * + * template <typename TYPE> + * class ANDROID_API Singleton { } + */ + +#define ANDROID_API __attribute__((visibility("default"))) + #endif // ANDROID_CUTILS_COMPILER_H diff --git a/include/cutils/config_utils.h b/include/cutils/config_utils.h index f3fb370..2dea6f1 100644 --- a/include/cutils/config_utils.h +++ b/include/cutils/config_utils.h @@ -54,6 +54,9 @@ const char* config_str(cnode *root, const char *name, const char *_default); /* add a named child to a config node (or modify it if it already exists) */ void config_set(cnode *root, const char *name, const char *value); +/* free a config node tree */ +void config_free(cnode *root); + #ifdef __cplusplus } #endif diff --git a/include/cutils/klog.h b/include/cutils/klog.h new file mode 100644 index 0000000..1335543 --- /dev/null +++ b/include/cutils/klog.h @@ -0,0 +1,34 @@ +/* + * 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. + */ + +#ifndef _CUTILS_KLOG_H_ +#define _CUTILS_KLOG_H_ + +void klog_init(void); +void klog_set_level(int level); +void klog_close(void); +void klog_write(int level, const char *fmt, ...) + __attribute__ ((format(printf, 2, 3))); + +#define KLOG_ERROR(tag,x...) klog_write(3, "<3>" tag ": " x) +#define KLOG_WARNING(tag,x...) klog_write(4, "<4>" tag ": " x) +#define KLOG_NOTICE(tag,x...) klog_write(5, "<5>" tag ": " x) +#define KLOG_INFO(tag,x...) klog_write(6, "<6>" tag ": " x) +#define KLOG_DEBUG(tag,x...) klog_write(7, "<7>" tag ": " x) + +#define KLOG_DEFAULT_LEVEL 3 /* messages <= this level are logged */ + +#endif diff --git a/init/list.h b/include/cutils/list.h index 7b9ef32..eb5a3c8 100644 --- a/init/list.h +++ b/include/cutils/list.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef _INIT_LIST_H_ -#define _INIT_LIST_H_ +#ifndef _CUTILS_LIST_H_ +#define _CUTILS_LIST_H_ #include <stddef.h> diff --git a/include/cutils/log.h b/include/cutils/log.h index f602017..42d7382 100644 --- a/include/cutils/log.h +++ b/include/cutils/log.h @@ -289,13 +289,17 @@ extern "C" { * It is NOT stripped from release builds. Note that the condition test * is -inverted- from the normal assert() semantics. */ +#ifndef LOG_ALWAYS_FATAL_IF #define LOG_ALWAYS_FATAL_IF(cond, ...) \ ( (CONDITION(cond)) \ ? ((void)android_printAssert(#cond, LOG_TAG, ## __VA_ARGS__)) \ : (void)0 ) +#endif +#ifndef LOG_ALWAYS_FATAL #define LOG_ALWAYS_FATAL(...) \ ( ((void)android_printAssert(NULL, LOG_TAG, ## __VA_ARGS__)) ) +#endif /* * Versions of LOG_ALWAYS_FATAL_IF and LOG_ALWAYS_FATAL that @@ -303,13 +307,21 @@ extern "C" { */ #if LOG_NDEBUG +#ifndef LOG_FATAL_IF #define LOG_FATAL_IF(cond, ...) ((void)0) +#endif +#ifndef LOG_FATAL #define LOG_FATAL(...) ((void)0) +#endif #else +#ifndef LOG_FATAL_IF #define LOG_FATAL_IF(cond, ...) LOG_ALWAYS_FATAL_IF(cond, ## __VA_ARGS__) +#endif +#ifndef LOG_FATAL #define LOG_FATAL(...) LOG_ALWAYS_FATAL(__VA_ARGS__) +#endif #endif @@ -317,8 +329,10 @@ extern "C" { * Assertion that generates a log message when the assertion fails. * Stripped out of release builds. Uses the current LOG_TAG. */ +#ifndef LOG_ASSERT #define LOG_ASSERT(cond, ...) LOG_FATAL_IF(!(cond), ## __VA_ARGS__) //#define LOG_ASSERT(cond) LOG_FATAL_IF(!(cond), "Assertion failed: " #cond) +#endif // --------------------------------------------------------------------- @@ -377,18 +391,24 @@ typedef enum { } AndroidEventLogType; +#ifndef LOG_EVENT_INT #define LOG_EVENT_INT(_tag, _value) { \ int intBuf = _value; \ (void) android_btWriteLog(_tag, EVENT_TYPE_INT, &intBuf, \ sizeof(intBuf)); \ } +#endif +#ifndef LOG_EVENT_LONG #define LOG_EVENT_LONG(_tag, _value) { \ long long longBuf = _value; \ (void) android_btWriteLog(_tag, EVENT_TYPE_LONG, &longBuf, \ sizeof(longBuf)); \ } +#endif +#ifndef LOG_EVENT_STRING #define LOG_EVENT_STRING(_tag, _value) \ ((void) 0) /* not implemented -- must combine len with string */ +#endif /* TODO: something for LIST */ /* diff --git a/include/cutils/native_handle.h b/include/cutils/native_handle.h index 89d6b65..268c5d3 100644 --- a/include/cutils/native_handle.h +++ b/include/cutils/native_handle.h @@ -21,7 +21,7 @@ extern "C" { #endif -typedef struct +typedef struct native_handle { int version; /* sizeof(native_handle_t) */ int numFds; /* number of file-descriptors at &data[0] */ @@ -29,10 +29,6 @@ typedef struct int data[0]; /* numFds + numInts ints */ } native_handle_t; - -/* keep the old definition for backward source-compatibility */ -typedef native_handle_t native_handle; - /* * native_handle_close * diff --git a/include/cutils/partition_utils.h b/include/cutils/partition_utils.h new file mode 100644 index 0000000..597df92 --- /dev/null +++ b/include/cutils/partition_utils.h @@ -0,0 +1,27 @@ +/* + * Copyright 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. + */ + +#ifndef __CUTILS_PARTITION_WIPED_H__ +#define __CUTILS_PARTITION_WIPED_H__ + +__BEGIN_DECLS + +int partition_wiped(char *source); +void erase_footer(const char *dev_path, long long size); + +__END_DECLS + +#endif /* __CUTILS_PARTITION_WIPED_H__ */ diff --git a/include/cutils/qtaguid.h b/include/cutils/qtaguid.h index e6d61e6..f8550fd 100644 --- a/include/cutils/qtaguid.h +++ b/include/cutils/qtaguid.h @@ -35,6 +35,30 @@ extern int qtaguid_tagSocket(int sockfd, int tag, uid_t uid); */ extern int qtaguid_untagSocket(int sockfd); +/* + * For the given uid, switch counter sets. + * The kernel only keeps a limited number of sets. + * 2 for now. + */ +extern int qtaguid_setCounterSet(int counterSetNum, uid_t uid); + +/* + * Delete all tag info that relates to the given tag an uid. + * If the tag is 0, then ALL info about the uid is freeded. + * The delete data also affects active tagged socketd, which are + * then untagged. + * The calling process can only operate on its own tags. + * Unless it is part of the happy AID_NET_BW_ACCT group. + * In which case it can clobber everything. + */ +extern int qtaguid_deleteTagData(int tag, uid_t uid); + +/* + * Enable/disable qtaguid functionnality at a lower level. + * When pacified, the kernel will accept commands but do nothing. + */ +extern int qtaguid_setPacifier(int on); + #ifdef __cplusplus } #endif diff --git a/include/cutils/sockets.h b/include/cutils/sockets.h index aa8682e..19cae0c 100644 --- a/include/cutils/sockets.h +++ b/include/cutils/sockets.h @@ -20,6 +20,7 @@ #include <errno.h> #include <stdlib.h> #include <string.h> +#include <stdbool.h> #ifdef HAVE_WINSOCK #include <winsock2.h> @@ -92,7 +93,18 @@ extern int socket_local_client_connect(int fd, const char *name, int namespaceId, int type); extern int socket_local_client(const char *name, int namespaceId, int type); extern int socket_inaddr_any_server(int port, int type); - + +/* + * socket_peer_is_trusted - Takes a socket which is presumed to be a + * connected local socket (e.g. AF_LOCAL) and returns whether the peer + * (the userid that owns the process on the other end of that socket) + * is one of the two trusted userids, root or shell. + * + * Note: This only works as advertised on the Android OS and always + * just returns true when called on other operating systems. + */ +extern bool socket_peer_is_trusted(int fd); + #ifdef __cplusplus } #endif diff --git a/include/cutils/uevent.h b/include/cutils/uevent.h index 587149c..4ebc300 100644 --- a/include/cutils/uevent.h +++ b/include/cutils/uevent.h @@ -17,13 +17,15 @@ #ifndef __CUTILS_UEVENT_H #define __CUTILS_UEVENT_H +#include <stdbool.h> #include <sys/socket.h> #ifdef __cplusplus extern "C" { #endif -ssize_t uevent_checked_recv(int socket, void *buffer, size_t length); +int uevent_open_socket(int buf_sz, bool passcred); +ssize_t uevent_kernel_multicast_recv(int socket, void *buffer, size_t length); #ifdef __cplusplus } diff --git a/include/netutils/ifc.h b/include/netutils/ifc.h index 575e72e..67a4a45 100644 --- a/include/netutils/ifc.h +++ b/include/netutils/ifc.h @@ -38,9 +38,13 @@ extern int ifc_reset_connections(const char *ifname, const int reset_mask); extern int ifc_get_addr(const char *name, in_addr_t *addr); extern int ifc_set_addr(const char *name, in_addr_t addr); -extern int ifc_get_prefixLength(const char *name, uint32_t *prefixLength); -extern int ifc_set_prefixLength(const char *name, uint32_t prefixLength); +extern int ifc_add_address(const char *name, const char *address, + int prefixlen); +extern int ifc_del_address(const char *name, const char *address, + int prefixlen); +extern int ifc_set_prefixLength(const char *name, int prefixLength); extern int ifc_set_hwaddr(const char *name, const void *ptr); +extern int ifc_clear_addresses(const char *name); /* This function is deprecated. Use ifc_add_route instead. */ extern int ifc_add_host_route(const char *name, in_addr_t addr); @@ -53,9 +57,10 @@ extern int ifc_create_default_route(const char *name, in_addr_t addr); extern int ifc_remove_default_route(const char *ifname); extern int ifc_add_route(const char *name, const char *addr, int prefix_length, const char *gw); - -extern int ifc_get_info(const char *name, in_addr_t *addr, in_addr_t *mask, - in_addr_t *flags); +extern int ifc_remove_route(const char *ifname, const char *dst, + int prefix_length, const char *gw); +extern int ifc_get_info(const char *name, in_addr_t *addr, int *prefixLength, + unsigned *flags); extern int ifc_configure(const char *ifname, in_addr_t address, uint32_t prefixLength, in_addr_t gateway, diff --git a/include/private/android_filesystem_config.h b/include/private/android_filesystem_config.h index f23c235..2cc673f 100644 --- a/include/private/android_filesystem_config.h +++ b/include/private/android_filesystem_config.h @@ -59,6 +59,7 @@ #define AID_MEDIA_RW 1023 /* internal media storage write access */ #define AID_MTP 1024 /* MTP USB driver access */ #define AID_NFC 1025 /* nfc subsystem */ +#define AID_DRMRPC 1026 /* group for drm rpc */ #define AID_SHELL 2000 /* adb and debug shell user */ #define AID_CACHE 2001 /* cache access */ @@ -71,6 +72,8 @@ #define AID_INET 3003 /* can create AF_INET and AF_INET6 sockets */ #define AID_NET_RAW 3004 /* can create raw INET sockets */ #define AID_NET_ADMIN 3005 /* can configure interfaces and routing tables. */ +#define AID_NET_BW_STATS 3006 /* read bandwidth statistics */ +#define AID_NET_BW_ACCT 3007 /* change bandwidth statistics accounting */ #define AID_MISC 9998 /* access to misc storage */ #define AID_NOBODY 9999 @@ -103,6 +106,7 @@ static const struct android_id_info android_ids[] = { { "drm", AID_DRM, }, { "available", AID_AVAILABLE, }, { "nfc", AID_NFC, }, + { "drmrpc", AID_DRMRPC, }, { "shell", AID_SHELL, }, { "cache", AID_CACHE, }, { "diag", AID_DIAG, }, @@ -118,6 +122,8 @@ static const struct android_id_info android_ids[] = { { "inet", AID_INET, }, { "net_raw", AID_NET_RAW, }, { "net_admin", AID_NET_ADMIN, }, + { "net_bw_stats", AID_NET_BW_STATS, }, + { "net_bw_acct", AID_NET_BW_ACCT, }, { "misc", AID_MISC, }, { "nobody", AID_NOBODY, }, }; @@ -209,6 +215,7 @@ static struct fs_path_config android_files[] = { { 00750, AID_ROOT, AID_SHELL, "sbin/*" }, { 00755, AID_ROOT, AID_ROOT, "bin/*" }, { 00750, AID_ROOT, AID_SHELL, "init*" }, + { 00750, AID_ROOT, AID_SHELL, "charger*" }, { 00644, AID_ROOT, AID_ROOT, 0 }, }; diff --git a/include/system/audio.h b/include/system/audio.h index 8f2ac0c..52ba5e7 100644 --- a/include/system/audio.h +++ b/include/system/audio.h @@ -88,8 +88,10 @@ typedef enum { /* PCM sub formats */ typedef enum { - AUDIO_FORMAT_PCM_SUB_16_BIT = 0x1, /* DO NOT CHANGE */ - AUDIO_FORMAT_PCM_SUB_8_BIT = 0x2, /* DO NOT CHANGE */ + 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_fmt_t; /* MP3 sub format field definition : can use 11 LSBs in the same way as MP3 @@ -144,21 +146,32 @@ typedef enum { AUDIO_FORMAT_PCM_SUB_16_BIT), 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_t; -/* Channel mask definitions must be kept in sync with JAVA values in - * frameworks/base/media/java/android/media/AudioFormat.java */ typedef enum { /* output channels */ - AUDIO_CHANNEL_OUT_FRONT_LEFT = 0x4, - AUDIO_CHANNEL_OUT_FRONT_RIGHT = 0x8, - AUDIO_CHANNEL_OUT_FRONT_CENTER = 0x10, - AUDIO_CHANNEL_OUT_LOW_FREQUENCY = 0x20, - AUDIO_CHANNEL_OUT_BACK_LEFT = 0x40, - AUDIO_CHANNEL_OUT_BACK_RIGHT = 0x80, - AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER = 0x100, - AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER = 0x200, - AUDIO_CHANNEL_OUT_BACK_CENTER = 0x400, + AUDIO_CHANNEL_OUT_FRONT_LEFT = 0x1, + AUDIO_CHANNEL_OUT_FRONT_RIGHT = 0x2, + AUDIO_CHANNEL_OUT_FRONT_CENTER = 0x4, + AUDIO_CHANNEL_OUT_LOW_FREQUENCY = 0x8, + AUDIO_CHANNEL_OUT_BACK_LEFT = 0x10, + AUDIO_CHANNEL_OUT_BACK_RIGHT = 0x20, + AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER = 0x40, + AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER = 0x80, + AUDIO_CHANNEL_OUT_BACK_CENTER = 0x100, + AUDIO_CHANNEL_OUT_SIDE_LEFT = 0x200, + AUDIO_CHANNEL_OUT_SIDE_RIGHT = 0x400, + AUDIO_CHANNEL_OUT_TOP_CENTER = 0x800, + AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT = 0x1000, + AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER = 0x2000, + AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT = 0x4000, + AUDIO_CHANNEL_OUT_TOP_BACK_LEFT = 0x8000, + AUDIO_CHANNEL_OUT_TOP_BACK_CENTER = 0x10000, + AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT = 0x20000, AUDIO_CHANNEL_OUT_MONO = AUDIO_CHANNEL_OUT_FRONT_LEFT, AUDIO_CHANNEL_OUT_STEREO = (AUDIO_CHANNEL_OUT_FRONT_LEFT | @@ -177,14 +190,15 @@ typedef enum { AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_BACK_LEFT | AUDIO_CHANNEL_OUT_BACK_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 | AUDIO_CHANNEL_OUT_FRONT_CENTER | AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_BACK_LEFT | AUDIO_CHANNEL_OUT_BACK_RIGHT | - AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER | - AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER), + AUDIO_CHANNEL_OUT_SIDE_LEFT | + AUDIO_CHANNEL_OUT_SIDE_RIGHT), AUDIO_CHANNEL_OUT_ALL = (AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT | AUDIO_CHANNEL_OUT_FRONT_CENTER | @@ -193,7 +207,16 @@ typedef enum { AUDIO_CHANNEL_OUT_BACK_RIGHT | AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER | AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER | - AUDIO_CHANNEL_OUT_BACK_CENTER), + AUDIO_CHANNEL_OUT_BACK_CENTER| + AUDIO_CHANNEL_OUT_SIDE_LEFT| + AUDIO_CHANNEL_OUT_SIDE_RIGHT| + AUDIO_CHANNEL_OUT_TOP_CENTER| + AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT| + AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER| + AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT| + AUDIO_CHANNEL_OUT_TOP_BACK_LEFT| + AUDIO_CHANNEL_OUT_TOP_BACK_CENTER| + AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT), /* input channels */ AUDIO_CHANNEL_IN_LEFT = 0x4, @@ -363,6 +386,10 @@ static inline bool audio_is_valid_format(uint32_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) { + return false; + } case AUDIO_FORMAT_MP3: case AUDIO_FORMAT_AMR_NB: case AUDIO_FORMAT_AMR_WB: @@ -378,16 +405,30 @@ static inline bool audio_is_valid_format(uint32_t format) static inline bool audio_is_linear_pcm(uint32_t format) { + return ((format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_PCM); +} + +static inline size_t audio_bytes_per_sample(uint32_t format) +{ + size_t size = 0; + switch (format) { - case AUDIO_FORMAT_PCM_16_BIT: - case AUDIO_FORMAT_PCM_8_BIT: - return true; - default: - return false; + case AUDIO_FORMAT_PCM_32_BIT: + case AUDIO_FORMAT_PCM_8_24_BIT: + size = sizeof(int32_t); + break; + case AUDIO_FORMAT_PCM_16_BIT: + size = sizeof(int16_t); + break; + case AUDIO_FORMAT_PCM_8_BIT: + size = sizeof(uint8_t); + break; + default: + break; } + return size; } - __END_DECLS #endif // ANDROID_AUDIO_CORE_H diff --git a/include/system/audio_policy.h b/include/system/audio_policy.h new file mode 100644 index 0000000..1e0af7d --- /dev/null +++ b/include/system/audio_policy.h @@ -0,0 +1,106 @@ +/* + * 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. + */ + + +#ifndef ANDROID_AUDIO_POLICY_CORE_H +#define ANDROID_AUDIO_POLICY_CORE_H + +#include <stdint.h> +#include <sys/cdefs.h> +#include <sys/types.h> + +#include <cutils/bitops.h> + +__BEGIN_DECLS + +/* The enums were moved here mostly from + * frameworks/base/include/media/AudioSystem.h + */ + +/* request to open a direct output with get_output() (by opposition to + * sharing an output with other AudioTracks) + */ +typedef enum { + AUDIO_POLICY_OUTPUT_FLAG_INDIRECT = 0x0, + AUDIO_POLICY_OUTPUT_FLAG_DIRECT = 0x1 +} audio_policy_output_flags_t; + +/* device categories used for audio_policy->set_force_use() */ +typedef enum { + AUDIO_POLICY_FORCE_NONE, + AUDIO_POLICY_FORCE_SPEAKER, + AUDIO_POLICY_FORCE_HEADPHONES, + AUDIO_POLICY_FORCE_BT_SCO, + AUDIO_POLICY_FORCE_BT_A2DP, + AUDIO_POLICY_FORCE_WIRED_ACCESSORY, + AUDIO_POLICY_FORCE_BT_CAR_DOCK, + AUDIO_POLICY_FORCE_BT_DESK_DOCK, + AUDIO_POLICY_FORCE_ANALOG_DOCK, + AUDIO_POLICY_FORCE_DIGITAL_DOCK, + + AUDIO_POLICY_FORCE_CFG_CNT, + AUDIO_POLICY_FORCE_CFG_MAX = AUDIO_POLICY_FORCE_CFG_CNT - 1, + + AUDIO_POLICY_FORCE_DEFAULT = AUDIO_POLICY_FORCE_NONE, +} audio_policy_forced_cfg_t; + +/* usages used for audio_policy->set_force_use() */ +typedef enum { + AUDIO_POLICY_FORCE_FOR_COMMUNICATION, + AUDIO_POLICY_FORCE_FOR_MEDIA, + AUDIO_POLICY_FORCE_FOR_RECORD, + AUDIO_POLICY_FORCE_FOR_DOCK, + + AUDIO_POLICY_FORCE_USE_CNT, + AUDIO_POLICY_FORCE_USE_MAX = AUDIO_POLICY_FORCE_USE_CNT - 1, +} audio_policy_force_use_t; + +/* device connection states used for audio_policy->set_device_connection_state() + */ +typedef enum { + AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, + AUDIO_POLICY_DEVICE_STATE_AVAILABLE, + + AUDIO_POLICY_DEVICE_STATE_CNT, + AUDIO_POLICY_DEVICE_STATE_MAX = AUDIO_POLICY_DEVICE_STATE_CNT - 1, +} audio_policy_dev_state_t; + +typedef enum { + /* Used to generate a tone to notify the user of a + * notification/alarm/ringtone while they are in a call. */ + AUDIO_POLICY_TONE_IN_CALL_NOTIFICATION = 0, + + AUDIO_POLICY_TONE_CNT, + AUDIO_POLICY_TONE_MAX = AUDIO_POLICY_TONE_CNT - 1, +} audio_policy_tone_t; + + +static inline bool audio_is_low_visibility(audio_stream_type_t stream) +{ + switch (stream) { + case AUDIO_STREAM_SYSTEM: + case AUDIO_STREAM_NOTIFICATION: + case AUDIO_STREAM_RING: + return true; + default: + return false; + } +} + + +__END_DECLS + +#endif // ANDROID_AUDIO_POLICY_CORE_H diff --git a/include/system/camera.h b/include/system/camera.h new file mode 100644 index 0000000..cdfa256 --- /dev/null +++ b/include/system/camera.h @@ -0,0 +1,237 @@ +/* + * 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. + */ + +#ifndef SYSTEM_CORE_INCLUDE_ANDROID_CAMERA_H +#define SYSTEM_CORE_INCLUDE_ANDROID_CAMERA_H + +#include <stdint.h> +#include <sys/cdefs.h> +#include <sys/types.h> +#include <cutils/native_handle.h> +#include <hardware/hardware.h> +#include <hardware/gralloc.h> + +__BEGIN_DECLS + +/** + * A set of bit masks for specifying how the received preview frames are + * handled before the previewCallback() call. + * + * The least significant 3 bits of an "int" value are used for this purpose: + * + * ..... 0 0 0 + * ^ ^ ^ + * | | |---------> determine whether the callback is enabled or not + * | |-----------> determine whether the callback is one-shot or not + * |-------------> determine whether the frame is copied out or not + * + * WARNING: When a frame is sent directly without copying, it is the frame + * receiver's responsiblity to make sure that the frame data won't get + * corrupted by subsequent preview frames filled by the camera. This flag is + * recommended only when copying out data brings significant performance price + * and the handling/processing of the received frame data is always faster than + * the preview frame rate so that data corruption won't occur. + * + * For instance, + * 1. 0x00 disables the callback. In this case, copy out and one shot bits + * are ignored. + * 2. 0x01 enables a callback without copying out the received frames. A + * typical use case is the Camcorder application to avoid making costly + * frame copies. + * 3. 0x05 is enabling a callback with frame copied out repeatedly. A typical + * use case is the Camera application. + * 4. 0x07 is enabling a callback with frame copied out only once. A typical + * use case is the Barcode scanner application. + */ + +enum { + CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK = 0x01, + CAMERA_FRAME_CALLBACK_FLAG_ONE_SHOT_MASK = 0x02, + CAMERA_FRAME_CALLBACK_FLAG_COPY_OUT_MASK = 0x04, + /** Typical use cases */ + CAMERA_FRAME_CALLBACK_FLAG_NOOP = 0x00, + CAMERA_FRAME_CALLBACK_FLAG_CAMCORDER = 0x01, + CAMERA_FRAME_CALLBACK_FLAG_CAMERA = 0x05, + CAMERA_FRAME_CALLBACK_FLAG_BARCODE_SCANNER = 0x07 +}; + +/** msgType in notifyCallback and dataCallback functions */ +enum { + CAMERA_MSG_ERROR = 0x0001, // notifyCallback + CAMERA_MSG_SHUTTER = 0x0002, // notifyCallback + CAMERA_MSG_FOCUS = 0x0004, // notifyCallback + CAMERA_MSG_ZOOM = 0x0008, // notifyCallback + CAMERA_MSG_PREVIEW_FRAME = 0x0010, // dataCallback + CAMERA_MSG_VIDEO_FRAME = 0x0020, // data_timestamp_callback + CAMERA_MSG_POSTVIEW_FRAME = 0x0040, // dataCallback + CAMERA_MSG_RAW_IMAGE = 0x0080, // dataCallback + CAMERA_MSG_COMPRESSED_IMAGE = 0x0100, // dataCallback + CAMERA_MSG_RAW_IMAGE_NOTIFY = 0x0200, // dataCallback + // Preview frame metadata. This can be combined with + // CAMERA_MSG_PREVIEW_FRAME in dataCallback. For example, the apps can + // request FRAME and METADATA. Or the apps can request only FRAME or only + // METADATA. + CAMERA_MSG_PREVIEW_METADATA = 0x0400, // dataCallback + CAMERA_MSG_ALL_MSGS = 0xFFFF +}; + +/** cmdType in sendCommand functions */ +enum { + CAMERA_CMD_START_SMOOTH_ZOOM = 1, + CAMERA_CMD_STOP_SMOOTH_ZOOM = 2, + + /** + * Set the clockwise rotation of preview display (setPreviewDisplay) in + * degrees. This affects the preview frames and the picture displayed after + * snapshot. This method is useful for portrait mode applications. Note + * that preview display of front-facing cameras is flipped horizontally + * before the rotation, that is, the image is reflected along the central + * vertical axis of the camera sensor. So the users can see themselves as + * looking into a mirror. + * + * This does not affect the order of byte array of + * CAMERA_MSG_PREVIEW_FRAME, CAMERA_MSG_VIDEO_FRAME, + * CAMERA_MSG_POSTVIEW_FRAME, CAMERA_MSG_RAW_IMAGE, or + * CAMERA_MSG_COMPRESSED_IMAGE. This is allowed to be set during preview + * since API level 14. + */ + CAMERA_CMD_SET_DISPLAY_ORIENTATION = 3, + + /** + * cmdType to disable/enable shutter sound. In sendCommand passing arg1 = + * 0 will disable, while passing arg1 = 1 will enable the shutter sound. + */ + CAMERA_CMD_ENABLE_SHUTTER_SOUND = 4, + + /* cmdType to play recording sound */ + CAMERA_CMD_PLAY_RECORDING_SOUND = 5, + + /** + * Start the face detection. This should be called after preview is started. + * The camera will notify the listener of CAMERA_MSG_FACE and the detected + * faces in the preview frame. The detected faces may be the same as the + * previous ones. Apps should call CAMERA_CMD_STOP_FACE_DETECTION to stop + * the face detection. This method is supported if CameraParameters + * KEY_MAX_NUM_HW_DETECTED_FACES or KEY_MAX_NUM_SW_DETECTED_FACES is + * bigger than 0. Hardware and software face detection should not be running + * at the same time. If the face detection has started, apps should not send + * this again. + * + * In hardware face detection mode, CameraParameters KEY_WHITE_BALANCE, + * KEY_FOCUS_AREAS and KEY_METERING_AREAS have no effect. + * + * arg1 is the face detection type. It can be CAMERA_FACE_DETECTION_HW or + * CAMERA_FACE_DETECTION_SW. + */ + CAMERA_CMD_START_FACE_DETECTION = 6, + + /** + * Stop the face detection. + */ + CAMERA_CMD_STOP_FACE_DETECTION = 7, +}; + +/** camera fatal errors */ +enum { + CAMERA_ERROR_UNKNOWN = 1, + CAMERA_ERROR_SERVER_DIED = 100 +}; + +enum { + /** The facing of the camera is opposite to that of the screen. */ + CAMERA_FACING_BACK = 0, + /** The facing of the camera is the same as that of the screen. */ + CAMERA_FACING_FRONT = 1 +}; + +enum { + /** Hardware face detection. It does not use much CPU. */ + CAMERA_FACE_DETECTION_HW = 0, + /** + * Software face detection. It uses some CPU. Applications must use + * Camera.setPreviewTexture for preview in this mode. + */ + CAMERA_FACE_DETECTION_SW = 1 +}; + +/** + * The information of a face from camera face detection. + */ +typedef struct camera_face { + /** + * Bounds of the face [left, top, right, bottom]. (-1000, -1000) represents + * the top-left of the camera field of view, and (1000, 1000) represents the + * bottom-right of the field of view. The width and height cannot be 0 or + * negative. This is supported by both hardware and software face detection. + * + * The direction is relative to the sensor orientation, that is, what the + * sensor sees. The direction is not affected by the rotation or mirroring + * of CAMERA_CMD_SET_DISPLAY_ORIENTATION. + */ + int32_t rect[4]; + + /** + * The confidence level of the face. The range is 1 to 100. 100 is the + * highest confidence. This is supported by both hardware and software + * face detection. + */ + int32_t score; + + /** + * An unique id per face while the face is visible to the tracker. If + * the face leaves the field-of-view and comes back, it will get a new + * id. If the value is 0, id is not supported. + */ + int32_t id; + + /** + * The coordinates of the center of the left eye. The range is -1000 to + * 1000. -2000, -2000 if this is not supported. + */ + int32_t left_eye[2]; + + /** + * The coordinates of the center of the right eye. The range is -1000 to + * 1000. -2000, -2000 if this is not supported. + */ + int32_t right_eye[2]; + + /** + * The coordinates of the center of the mouth. The range is -1000 to 1000. + * -2000, -2000 if this is not supported. + */ + int32_t mouth[2]; + +} camera_face_t; + +/** + * The metadata of the frame data. + */ +typedef struct camera_frame_metadata { + /** + * The number of detected faces in the frame. + */ + int32_t number_of_faces; + + /** + * An array of the detected faces. The length is number_of_faces. + */ + camera_face_t *faces; +} camera_frame_metadata_t; + +__END_DECLS + +#endif /* SYSTEM_CORE_INCLUDE_ANDROID_CAMERA_H */ diff --git a/include/system/graphics.h b/include/system/graphics.h new file mode 100644 index 0000000..729e92c --- /dev/null +++ b/include/system/graphics.h @@ -0,0 +1,123 @@ +/* + * 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. + */ + +#ifndef SYSTEM_CORE_INCLUDE_ANDROID_GRAPHICS_H +#define SYSTEM_CORE_INCLUDE_ANDROID_GRAPHICS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * If the HAL needs to create service threads to handle graphics related + * tasks, these threads need to run at HAL_PRIORITY_URGENT_DISPLAY priority + * if they can block the main rendering thread in any way. + * + * the priority of the current thread can be set with: + * + * #include <sys/resource.h> + * setpriority(PRIO_PROCESS, 0, HAL_PRIORITY_URGENT_DISPLAY); + * + */ + +#define HAL_PRIORITY_URGENT_DISPLAY (-8) + +/** + * pixel format definitions + */ + +enum { + HAL_PIXEL_FORMAT_RGBA_8888 = 1, + HAL_PIXEL_FORMAT_RGBX_8888 = 2, + HAL_PIXEL_FORMAT_RGB_888 = 3, + HAL_PIXEL_FORMAT_RGB_565 = 4, + HAL_PIXEL_FORMAT_BGRA_8888 = 5, + HAL_PIXEL_FORMAT_RGBA_5551 = 6, + HAL_PIXEL_FORMAT_RGBA_4444 = 7, + + /* 0x8 - 0xFF range unavailable */ + + /* + * 0x100 - 0x1FF + * + * This range is reserved for pixel formats that are specific to the HAL + * implementation. Implementations can use any value in this range to + * communicate video pixel formats between their HAL modules. These formats + * must not have an alpha channel. Additionally, an EGLimage created from a + * gralloc buffer of one of these formats must be supported for use with the + * GL_OES_EGL_image_external OpenGL ES extension. + */ + + /* + * Android YUV format: + * + * This format is exposed outside of the HAL to software decoders and + * applications. EGLImageKHR must support it in conjunction with the + * OES_EGL_image_external extension. + * + * YV12 is a 4:2:0 YCrCb planar format comprised of a WxH Y plane followed + * by (W/2) x (H/2) Cr and Cb planes. + * + * This format assumes + * - an even width + * - an even height + * - a horizontal stride multiple of 16 pixels + * - a vertical stride equal to the height + * + * y_size = stride * height + * c_size = ALIGN(stride/2, 16) * height/2 + * size = y_size + c_size * 2 + * cr_offset = y_size + * cb_offset = y_size + c_size + * + */ + HAL_PIXEL_FORMAT_YV12 = 0x32315659, // YCrCb 4:2:0 Planar + + + + /* Legacy formats (deprecated), used by ImageFormat.java */ + HAL_PIXEL_FORMAT_YCbCr_422_SP = 0x10, // NV16 + HAL_PIXEL_FORMAT_YCrCb_420_SP = 0x11, // NV21 + HAL_PIXEL_FORMAT_YCbCr_422_I = 0x14, // YUY2 +}; + + +/** + * Transformation definitions + * + * IMPORTANT NOTE: + * HAL_TRANSFORM_ROT_90 is applied CLOCKWISE and AFTER HAL_TRANSFORM_FLIP_{H|V}. + * + */ + +enum { + /* flip source image horizontally (around the vertical axis) */ + HAL_TRANSFORM_FLIP_H = 0x01, + /* flip source image vertically (around the horizontal axis)*/ + HAL_TRANSFORM_FLIP_V = 0x02, + /* rotate source image 90 degrees clockwise */ + HAL_TRANSFORM_ROT_90 = 0x04, + /* rotate source image 180 degrees */ + HAL_TRANSFORM_ROT_180 = 0x03, + /* rotate source image 270 degrees clockwise */ + HAL_TRANSFORM_ROT_270 = 0x07, +}; + +#ifdef __cplusplus +} +#endif + +#endif /* SYSTEM_CORE_INCLUDE_ANDROID_GRAPHICS_H */ diff --git a/include/system/window.h b/include/system/window.h new file mode 100644 index 0000000..959bd23 --- /dev/null +++ b/include/system/window.h @@ -0,0 +1,614 @@ +/* + * 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. + */ + +#ifndef SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H +#define SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H + +#include <stdint.h> +#include <sys/cdefs.h> +#include <system/graphics.h> +#include <cutils/native_handle.h> + +__BEGIN_DECLS + +/*****************************************************************************/ + +#define ANDROID_NATIVE_MAKE_CONSTANT(a,b,c,d) \ + (((unsigned)(a)<<24)|((unsigned)(b)<<16)|((unsigned)(c)<<8)|(unsigned)(d)) + +#define ANDROID_NATIVE_WINDOW_MAGIC \ + ANDROID_NATIVE_MAKE_CONSTANT('_','w','n','d') + +#define ANDROID_NATIVE_BUFFER_MAGIC \ + ANDROID_NATIVE_MAKE_CONSTANT('_','b','f','r') + +// --------------------------------------------------------------------------- + +typedef const native_handle_t* buffer_handle_t; + +// --------------------------------------------------------------------------- + +typedef struct android_native_rect_t +{ + int32_t left; + int32_t top; + int32_t right; + int32_t bottom; +} android_native_rect_t; + +// --------------------------------------------------------------------------- + +typedef struct android_native_base_t +{ + /* a magic value defined by the actual EGL native type */ + int magic; + + /* the sizeof() of the actual EGL native type */ + int version; + + void* reserved[4]; + + /* reference-counting interface */ + void (*incRef)(struct android_native_base_t* base); + void (*decRef)(struct android_native_base_t* base); +} android_native_base_t; + +typedef struct ANativeWindowBuffer +{ +#ifdef __cplusplus + ANativeWindowBuffer() { + common.magic = ANDROID_NATIVE_BUFFER_MAGIC; + common.version = sizeof(ANativeWindowBuffer); + memset(common.reserved, 0, sizeof(common.reserved)); + } + + // Implement the methods that sp<ANativeWindowBuffer> expects so that it + // can be used to automatically refcount ANativeWindowBuffer's. + void incStrong(const void* id) const { + common.incRef(const_cast<android_native_base_t*>(&common)); + } + void decStrong(const void* id) const { + common.decRef(const_cast<android_native_base_t*>(&common)); + } +#endif + + struct android_native_base_t common; + + int width; + int height; + int stride; + int format; + int usage; + + void* reserved[2]; + + buffer_handle_t handle; + + void* reserved_proc[8]; +} ANativeWindowBuffer_t; + +// Old typedef for backwards compatibility. +typedef ANativeWindowBuffer_t android_native_buffer_t; + +// --------------------------------------------------------------------------- + +/* attributes queriable with query() */ +enum { + NATIVE_WINDOW_WIDTH = 0, + NATIVE_WINDOW_HEIGHT = 1, + NATIVE_WINDOW_FORMAT = 2, + + /* The minimum number of buffers that must remain un-dequeued after a buffer + * has been queued. This value applies only if set_buffer_count was used to + * override the number of buffers and if a buffer has since been queued. + * Users of the set_buffer_count ANativeWindow method should query this + * value before calling set_buffer_count. If it is necessary to have N + * buffers simultaneously dequeued as part of the steady-state operation, + * and this query returns M then N+M buffers should be requested via + * native_window_set_buffer_count. + * + * Note that this value does NOT apply until a single buffer has been + * queued. In particular this means that it is possible to: + * + * 1. Query M = min undequeued buffers + * 2. Set the buffer count to N + M + * 3. Dequeue all N + M buffers + * 4. Cancel M buffers + * 5. Queue, dequeue, queue, dequeue, ad infinitum + */ + NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS = 3, + + /* Check whether queueBuffer operations on the ANativeWindow send the buffer + * to the window compositor. The query sets the returned 'value' argument + * to 1 if the ANativeWindow DOES send queued buffers directly to the window + * compositor and 0 if the buffers do not go directly to the window + * compositor. + * + * This can be used to determine whether protected buffer content should be + * sent to the ANativeWindow. Note, however, that a result of 1 does NOT + * indicate that queued buffers will be protected from applications or users + * capturing their contents. If that behavior is desired then some other + * mechanism (e.g. the GRALLOC_USAGE_PROTECTED flag) should be used in + * conjunction with this query. + */ + NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER = 4, + + /* Get the concrete type of a ANativeWindow. See below for the list of + * possible return values. + * + * This query should not be used outside the Android framework and will + * likely be removed in the near future. + */ + NATIVE_WINDOW_CONCRETE_TYPE = 5, + + + /* + * Default width and height of the ANativeWindow, these are the dimensions + * of the window irrespective of the NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS + * call. + */ + NATIVE_WINDOW_DEFAULT_WIDTH = 6, + NATIVE_WINDOW_DEFAULT_HEIGHT = 7, + + /* + * transformation that will most-likely be applied to buffers. This is only + * a hint, the actual transformation applied might be different. + * + * INTENDED USE: + * + * The transform hint can be used by a producer, for instance the GLES + * driver, to pre-rotate the rendering such that the final transformation + * in the composer is identity. This can be very useful when used in + * conjunction with the h/w composer HAL, in situations where it + * cannot handle arbitrary rotations. + * + * 1. Before dequeuing a buffer, the GL driver (or any other ANW client) + * queries the ANW for NATIVE_WINDOW_TRANSFORM_HINT. + * + * 2. The GL driver overrides the width and height of the ANW to + * account for NATIVE_WINDOW_TRANSFORM_HINT. This is done by querying + * NATIVE_WINDOW_DEFAULT_{WIDTH | HEIGHT}, swapping the dimensions + * according to NATIVE_WINDOW_TRANSFORM_HINT and calling + * native_window_set_buffers_dimensions(). + * + * 3. The GL driver dequeues a buffer of the new pre-rotated size. + * + * 4. The GL driver renders to the buffer such that the image is + * already transformed, that is applying NATIVE_WINDOW_TRANSFORM_HINT + * to the rendering. + * + * 5. The GL driver calls native_window_set_transform to apply + * inverse transformation to the buffer it just rendered. + * In order to do this, the GL driver needs + * to calculate the inverse of NATIVE_WINDOW_TRANSFORM_HINT, this is + * done easily: + * + * int hintTransform, inverseTransform; + * query(..., NATIVE_WINDOW_TRANSFORM_HINT, &hintTransform); + * inverseTransform = hintTransform; + * if (hintTransform & HAL_TRANSFORM_ROT_90) + * inverseTransform ^= HAL_TRANSFORM_ROT_180; + * + * + * 6. The GL driver queues the pre-transformed buffer. + * + * 7. The composer combines the buffer transform with the display + * transform. If the buffer transform happens to cancel out the + * display transform then no rotation is needed. + * + */ + NATIVE_WINDOW_TRANSFORM_HINT = 8, +}; + +/* valid operations for the (*perform)() hook */ +enum { + NATIVE_WINDOW_SET_USAGE = 0, + NATIVE_WINDOW_CONNECT = 1, /* deprecated */ + NATIVE_WINDOW_DISCONNECT = 2, /* deprecated */ + NATIVE_WINDOW_SET_CROP = 3, + NATIVE_WINDOW_SET_BUFFER_COUNT = 4, + NATIVE_WINDOW_SET_BUFFERS_GEOMETRY = 5, /* deprecated */ + NATIVE_WINDOW_SET_BUFFERS_TRANSFORM = 6, + NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP = 7, + NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS = 8, + NATIVE_WINDOW_SET_BUFFERS_FORMAT = 9, + NATIVE_WINDOW_SET_SCALING_MODE = 10, + NATIVE_WINDOW_LOCK = 11, /* private */ + NATIVE_WINDOW_UNLOCK_AND_POST = 12, /* private */ + NATIVE_WINDOW_API_CONNECT = 13, /* private */ + NATIVE_WINDOW_API_DISCONNECT = 14, /* private */ +}; + +/* parameter for NATIVE_WINDOW_[API_][DIS]CONNECT */ +enum { + /* Buffers will be queued by EGL via eglSwapBuffers after being filled using + * OpenGL ES. + */ + NATIVE_WINDOW_API_EGL = 1, + + /* Buffers will be queued after being filled using the CPU + */ + NATIVE_WINDOW_API_CPU = 2, + + /* Buffers will be queued by Stagefright after being filled by a video + * decoder. The video decoder can either be a software or hardware decoder. + */ + NATIVE_WINDOW_API_MEDIA = 3, + + /* Buffers will be queued by the the camera HAL. + */ + NATIVE_WINDOW_API_CAMERA = 4, +}; + +/* parameter for NATIVE_WINDOW_SET_BUFFERS_TRANSFORM */ +enum { + /* flip source image horizontally */ + NATIVE_WINDOW_TRANSFORM_FLIP_H = HAL_TRANSFORM_FLIP_H , + /* flip source image vertically */ + NATIVE_WINDOW_TRANSFORM_FLIP_V = HAL_TRANSFORM_FLIP_V, + /* rotate source image 90 degrees clock-wise */ + NATIVE_WINDOW_TRANSFORM_ROT_90 = HAL_TRANSFORM_ROT_90, + /* rotate source image 180 degrees */ + NATIVE_WINDOW_TRANSFORM_ROT_180 = HAL_TRANSFORM_ROT_180, + /* rotate source image 270 degrees clock-wise */ + NATIVE_WINDOW_TRANSFORM_ROT_270 = HAL_TRANSFORM_ROT_270, +}; + +/* parameter for NATIVE_WINDOW_SET_SCALING_MODE */ +enum { + /* the window content is not updated (frozen) until a buffer of + * the window size is received (enqueued) + */ + NATIVE_WINDOW_SCALING_MODE_FREEZE = 0, + /* the buffer is scaled in both dimensions to match the window size */ + NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW = 1, +}; + +/* values returned by the NATIVE_WINDOW_CONCRETE_TYPE query */ +enum { + NATIVE_WINDOW_FRAMEBUFFER = 0, /* FramebufferNativeWindow */ + NATIVE_WINDOW_SURFACE = 1, /* Surface */ + NATIVE_WINDOW_SURFACE_TEXTURE_CLIENT = 2, /* SurfaceTextureClient */ +}; + +/* parameter for NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP + * + * Special timestamp value to indicate that timestamps should be auto-generated + * by the native window when queueBuffer is called. This is equal to INT64_MIN, + * defined directly to avoid problems with C99/C++ inclusion of stdint.h. + */ +static const int64_t NATIVE_WINDOW_TIMESTAMP_AUTO = (-9223372036854775807LL-1); + +struct ANativeWindow +{ +#ifdef __cplusplus + ANativeWindow() + : flags(0), minSwapInterval(0), maxSwapInterval(0), xdpi(0), ydpi(0) + { + common.magic = ANDROID_NATIVE_WINDOW_MAGIC; + common.version = sizeof(ANativeWindow); + memset(common.reserved, 0, sizeof(common.reserved)); + } + + /* Implement the methods that sp<ANativeWindow> expects so that it + can be used to automatically refcount ANativeWindow's. */ + void incStrong(const void* id) const { + common.incRef(const_cast<android_native_base_t*>(&common)); + } + void decStrong(const void* id) const { + common.decRef(const_cast<android_native_base_t*>(&common)); + } +#endif + + struct android_native_base_t common; + + /* flags describing some attributes of this surface or its updater */ + const uint32_t flags; + + /* min swap interval supported by this updated */ + const int minSwapInterval; + + /* max swap interval supported by this updated */ + const int maxSwapInterval; + + /* horizontal and vertical resolution in DPI */ + const float xdpi; + const float ydpi; + + /* Some storage reserved for the OEM's driver. */ + intptr_t oem[4]; + + /* + * Set the swap interval for this surface. + * + * Returns 0 on success or -errno on error. + */ + int (*setSwapInterval)(struct ANativeWindow* window, + int interval); + + /* + * hook called by EGL to acquire a buffer. After this call, the buffer + * is not locked, so its content cannot be modified. + * this call may block if no buffers are available. + * + * Returns 0 on success or -errno on error. + */ + int (*dequeueBuffer)(struct ANativeWindow* window, + struct ANativeWindowBuffer** buffer); + + /* + * hook called by EGL to lock a buffer. This MUST be called before modifying + * the content of a buffer. The buffer must have been acquired with + * dequeueBuffer first. + * + * Returns 0 on success or -errno on error. + */ + int (*lockBuffer)(struct ANativeWindow* window, + struct ANativeWindowBuffer* buffer); + /* + * hook called by EGL when modifications to the render buffer are done. + * This unlocks and post the buffer. + * + * Buffers MUST be queued in the same order than they were dequeued. + * + * Returns 0 on success or -errno on error. + */ + int (*queueBuffer)(struct ANativeWindow* window, + struct ANativeWindowBuffer* buffer); + + /* + * hook used to retrieve information about the native window. + * + * Returns 0 on success or -errno on error. + */ + int (*query)(const struct ANativeWindow* window, + int what, int* value); + + /* + * hook used to perform various operations on the surface. + * (*perform)() is a generic mechanism to add functionality to + * ANativeWindow while keeping backward binary compatibility. + * + * DO NOT CALL THIS HOOK DIRECTLY. Instead, use the helper functions + * defined below. + * + * (*perform)() returns -ENOENT if the 'what' parameter is not supported + * by the surface's implementation. + * + * The valid operations are: + * NATIVE_WINDOW_SET_USAGE + * NATIVE_WINDOW_CONNECT (deprecated) + * NATIVE_WINDOW_DISCONNECT (deprecated) + * NATIVE_WINDOW_SET_CROP + * NATIVE_WINDOW_SET_BUFFER_COUNT + * NATIVE_WINDOW_SET_BUFFERS_GEOMETRY (deprecated) + * NATIVE_WINDOW_SET_BUFFERS_TRANSFORM + * NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP + * NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS + * NATIVE_WINDOW_SET_BUFFERS_FORMAT + * NATIVE_WINDOW_SET_SCALING_MODE + * NATIVE_WINDOW_LOCK (private) + * NATIVE_WINDOW_UNLOCK_AND_POST (private) + * NATIVE_WINDOW_API_CONNECT (private) + * NATIVE_WINDOW_API_DISCONNECT (private) + * + */ + + int (*perform)(struct ANativeWindow* window, + int operation, ... ); + + /* + * hook used to cancel a buffer that has been dequeued. + * No synchronization is performed between dequeue() and cancel(), so + * either external synchronization is needed, or these functions must be + * called from the same thread. + */ + int (*cancelBuffer)(struct ANativeWindow* window, + struct ANativeWindowBuffer* buffer); + + + void* reserved_proc[2]; +}; + + /* Backwards compatibility: use ANativeWindow (struct ANativeWindow in C). + * android_native_window_t is deprecated. + */ +typedef struct ANativeWindow ANativeWindow; +typedef struct ANativeWindow android_native_window_t; + +/* + * native_window_set_usage(..., usage) + * Sets the intended usage flags for the next buffers + * acquired with (*lockBuffer)() and on. + * By default (if this function is never called), a usage of + * GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE + * is assumed. + * Calling this function will usually cause following buffers to be + * reallocated. + */ + +static inline int native_window_set_usage( + struct ANativeWindow* window, int usage) +{ + return window->perform(window, NATIVE_WINDOW_SET_USAGE, usage); +} + +/* deprecated. Always returns 0. Don't call. */ +static inline int native_window_connect( + struct ANativeWindow* window, int api) { + return 0; +} + +/* deprecated. Always returns 0. Don't call. */ +static inline int native_window_disconnect( + struct ANativeWindow* window, int api) { + return 0; +} + +/* + * native_window_set_crop(..., crop) + * Sets which region of the next queued buffers needs to be considered. + * A buffer's crop region is scaled to match the surface's size. + * + * The specified crop region applies to all buffers queued after it is called. + * + * if 'crop' is NULL, subsequently queued buffers won't be cropped. + * + * An error is returned if for instance the crop region is invalid, + * out of the buffer's bound or if the window is invalid. + */ +static inline int native_window_set_crop( + struct ANativeWindow* window, + android_native_rect_t const * crop) +{ + return window->perform(window, NATIVE_WINDOW_SET_CROP, crop); +} + +/* + * native_window_set_buffer_count(..., count) + * Sets the number of buffers associated with this native window. + */ +static inline int native_window_set_buffer_count( + struct ANativeWindow* window, + size_t bufferCount) +{ + return window->perform(window, NATIVE_WINDOW_SET_BUFFER_COUNT, bufferCount); +} + +/* + * native_window_set_buffers_geometry(..., int w, int h, int format) + * All buffers dequeued after this call will have the dimensions and format + * specified. A successful call to this function has the same effect as calling + * native_window_set_buffers_size and native_window_set_buffers_format. + * + * XXX: This function is deprecated. The native_window_set_buffers_dimensions + * and native_window_set_buffers_format functions should be used instead. + */ +static inline int native_window_set_buffers_geometry( + struct ANativeWindow* window, + int w, int h, int format) +{ + return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_GEOMETRY, + w, h, format); +} + +/* + * native_window_set_buffers_dimensions(..., int w, int h) + * All buffers dequeued after this call will have the dimensions specified. + * In particular, all buffers will have a fixed-size, independent form the + * native-window size. They will be scaled according to the scaling mode + * (see native_window_set_scaling_mode) upon window composition. + * + * If w and h are 0, the normal behavior is restored. That is, dequeued buffers + * following this call will be sized to match the window's size. + * + * Calling this function will reset the window crop to a NULL value, which + * disables cropping of the buffers. + */ +static inline int native_window_set_buffers_dimensions( + struct ANativeWindow* window, + int w, int h) +{ + return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS, + w, h); +} + +/* + * native_window_set_buffers_format(..., int format) + * All buffers dequeued after this call will have the format specified. + * + * If the specified format is 0, the default buffer format will be used. + */ +static inline int native_window_set_buffers_format( + struct ANativeWindow* window, + int format) +{ + return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_FORMAT, format); +} + +/* + * native_window_set_buffers_transform(..., int transform) + * All buffers queued after this call will be displayed transformed according + * to the transform parameter specified. + */ +static inline int native_window_set_buffers_transform( + struct ANativeWindow* window, + int transform) +{ + return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_TRANSFORM, + transform); +} + +/* + * native_window_set_buffers_timestamp(..., int64_t timestamp) + * All buffers queued after this call will be associated with the timestamp + * parameter specified. If the timestamp is set to NATIVE_WINDOW_TIMESTAMP_AUTO + * (the default), timestamps will be generated automatically when queueBuffer is + * called. The timestamp is measured in nanoseconds, and is normally monotonically + * increasing. The timestamp should be unaffected by time-of-day adjustments, + * and for a camera should be strictly monotonic but for a media player may be + * reset when the position is set. + */ +static inline int native_window_set_buffers_timestamp( + struct ANativeWindow* window, + int64_t timestamp) +{ + return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP, + timestamp); +} + +/* + * native_window_set_scaling_mode(..., int mode) + * All buffers queued after this call will be associated with the scaling mode + * specified. + */ +static inline int native_window_set_scaling_mode( + struct ANativeWindow* window, + int mode) +{ + return window->perform(window, NATIVE_WINDOW_SET_SCALING_MODE, + mode); +} + + +/* + * native_window_api_connect(..., int api) + * connects an API to this window. only one API can be connected at a time. + * Returns -EINVAL if for some reason the window cannot be connected, which + * can happen if it's connected to some other API. + */ +static inline int native_window_api_connect( + struct ANativeWindow* window, int api) +{ + return window->perform(window, NATIVE_WINDOW_API_CONNECT, api); +} + +/* + * native_window_api_disconnect(..., int api) + * disconnect the API from this window. + * An error is returned if for instance the window wasn't connected in the + * first place. + */ +static inline int native_window_api_disconnect( + struct ANativeWindow* window, int api) +{ + return window->perform(window, NATIVE_WINDOW_API_DISCONNECT, api); +} + + +__END_DECLS + +#endif /* SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H */ diff --git a/include/sysutils/NetlinkEvent.h b/include/sysutils/NetlinkEvent.h index b329b09..25a56f7 100644 --- a/include/sysutils/NetlinkEvent.h +++ b/include/sysutils/NetlinkEvent.h @@ -16,6 +16,8 @@ #ifndef _NETLINKEVENT_H #define _NETLINKEVENT_H +#include <sysutils/NetlinkListener.h> + #define NL_PARAMS_MAX 32 class NetlinkEvent { @@ -30,17 +32,23 @@ public: const static int NlActionAdd; const static int NlActionRemove; const static int NlActionChange; + const static int NlActionLinkDown; + const static int NlActionLinkUp; NetlinkEvent(); virtual ~NetlinkEvent(); - bool decode(char *buffer, int size); + bool decode(char *buffer, int size, int format = NetlinkListener::NETLINK_FORMAT_ASCII); const char *findParam(const char *paramName); const char *getSubsystem() { return mSubsystem; } int getAction() { return mAction; } void dump(); + + protected: + bool parseBinaryNetlinkMessage(char *buffer, int size); + bool parseAsciiNetlinkMessage(char *buffer, int size); }; #endif diff --git a/include/sysutils/NetlinkListener.h b/include/sysutils/NetlinkListener.h index 2880046..beb8bda 100644 --- a/include/sysutils/NetlinkListener.h +++ b/include/sysutils/NetlinkListener.h @@ -22,13 +22,27 @@ class NetlinkEvent; class NetlinkListener : public SocketListener { char mBuffer[64 * 1024]; + int mFormat; public: + static const int NETLINK_FORMAT_ASCII = 0; + static const int NETLINK_FORMAT_BINARY = 1; + +#if 1 + /* temporary version until we can get Motorola to update their + * ril.so. Their prebuilt ril.so is using this private class + * so changing the NetlinkListener() constructor breaks their ril. + */ NetlinkListener(int socket); + NetlinkListener(int socket, int format); +#else + NetlinkListener(int socket, int format = NETLINK_FORMAT_ASCII); +#endif virtual ~NetlinkListener() {} protected: virtual bool onDataAvailable(SocketClient *cli); virtual void onEvent(NetlinkEvent *evt) = 0; }; + #endif diff --git a/include/sysutils/SocketClient.h b/include/sysutils/SocketClient.h index d6bb7d5..7d2b1d6 100644 --- a/include/sysutils/SocketClient.h +++ b/include/sysutils/SocketClient.h @@ -8,6 +8,7 @@ class SocketClient { int mSocket; + bool mSocketOwned; pthread_mutex_t mWriteMutex; /* Peer process ID */ @@ -24,8 +25,8 @@ class SocketClient { int mRefCount; public: - SocketClient(int sock); - virtual ~SocketClient() {} + SocketClient(int sock, bool owned); + virtual ~SocketClient(); int getSocket() { return mSocket; } pid_t getPid() const { return mPid; } diff --git a/init/builtins.c b/init/builtins.c index f2f76b7..eccda3f 100644 --- a/init/builtins.c +++ b/init/builtins.c @@ -30,6 +30,7 @@ #include <sys/mount.h> #include <sys/resource.h> #include <linux/loop.h> +#include <cutils/partition_utils.h> #include "init.h" #include "keywords.h" @@ -225,14 +226,10 @@ int do_insmod(int nargs, char **args) return do_insmod_inner(nargs, args, size); } -int do_import(int nargs, char **args) -{ - return init_parse_config_file(args[1]); -} - int do_mkdir(int nargs, char **args) { mode_t mode = 0755; + int ret; /* mkdir <path> [mode] [owner] [group] */ @@ -240,7 +237,12 @@ int do_mkdir(int nargs, char **args) mode = strtoul(args[2], 0, 8); } - if (mkdir(args[1], mode)) { + ret = mkdir(args[1], mode); + /* chmod in case the directory already exists */ + if (ret == -1 && errno == EEXIST) { + ret = chmod(args[1], mode); + } + if (ret == -1) { return -errno; } @@ -367,7 +369,9 @@ int do_mount(int nargs, char **args) if (wait) wait_for_file(source, COMMAND_RETRY_TIMEOUT); if (mount(source, target, system, flags, options) < 0) { - /* If this fails, it may be an encrypted filesystem. + /* If this fails, it may be an encrypted filesystem + * or it could just be wiped. If wiped, that will be + * handled later in the boot process. * We only support encrypting /data. Check * if we're trying to mount it, and if so, * assume it's encrypted, mount a tmpfs instead. @@ -375,7 +379,7 @@ int do_mount(int nargs, char **args) * for vold to query when it mounts the real * encrypted /data. */ - if (!strcmp(target, DATA_MNT_POINT)) { + if (!strcmp(target, DATA_MNT_POINT) && !partition_wiped(source)) { const char *tmpfs_options; tmpfs_options = property_get("ro.crypto.tmpfs_options"); @@ -442,7 +446,24 @@ int do_setkey(int nargs, char **args) int do_setprop(int nargs, char **args) { - property_set(args[1], args[2]); + const char *name = args[1]; + const char *value = args[2]; + + if (value[0] == '$') { + /* Use the value of a system property if value starts with '$' */ + value++; + if (value[0] != '$') { + value = property_get(value); + if (!value) { + ERROR("property %s has no value for assigning to %s\n", value, name); + return -EINVAL; + } + } /* else fall through to support double '$' prefix for setting properties + * to string literals that start with '$' + */ + } + + property_set(name, value); return 0; } @@ -524,7 +545,23 @@ int do_sysclktz(int nargs, char **args) int do_write(int nargs, char **args) { - return write_file(args[1], args[2]); + const char *path = args[1]; + const char *value = args[2]; + if (value[0] == '$') { + /* Write the value of a system property if value starts with '$' */ + value++; + if (value[0] != '$') { + value = property_get(value); + if (!value) { + ERROR("property %s has no value for writing to %s\n", value, path); + return -EINVAL; + } + } /* else fall through to support double '$' prefix for writing + * string literals that start with '$' + */ + } + + return write_file(path, value); } int do_copy(int nargs, char **args) @@ -626,7 +663,7 @@ int do_chmod(int nargs, char **args) { int do_loglevel(int nargs, char **args) { if (nargs == 2) { - log_set_level(atoi(args[1])); + klog_set_level(atoi(args[1])); return 0; } return -1; diff --git a/init/devices.c b/init/devices.c index 9c07e99..a2f84aa 100644 --- a/init/devices.c +++ b/init/devices.c @@ -34,12 +34,12 @@ #include <asm/page.h> #include <sys/wait.h> +#include <cutils/list.h> #include <cutils/uevent.h> #include "devices.h" #include "util.h" #include "log.h" -#include "list.h" #define SYSFS_PREFIX "/sys" #define FIRMWARE_DIR1 "/etc/firmware" @@ -58,33 +58,6 @@ struct uevent { int minor; }; -static int open_uevent_socket(void) -{ - struct sockaddr_nl addr; - int sz = 64*1024; // XXX larger? udev uses 16MB! - int on = 1; - int s; - - memset(&addr, 0, sizeof(addr)); - addr.nl_family = AF_NETLINK; - addr.nl_pid = getpid(); - addr.nl_groups = 0xffffffff; - - s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); - if(s < 0) - return -1; - - setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)); - setsockopt(s, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)); - - if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - close(s); - return -1; - } - - return s; -} - struct perms_ { char *name; char *attr; @@ -99,8 +72,15 @@ struct perm_node { struct listnode plist; }; +struct platform_node { + char *name; + int name_len; + struct listnode list; +}; + static list_declare(sys_perms); static list_declare(dev_perms); +static list_declare(platform_names); int add_dev_perms(const char *name, const char *attr, mode_t perm, unsigned int uid, unsigned int gid, @@ -214,6 +194,68 @@ static void make_device(const char *path, setegid(AID_ROOT); } +static void add_platform_device(const char *name) +{ + int name_len = strlen(name); + struct listnode *node; + struct platform_node *bus; + + list_for_each_reverse(node, &platform_names) { + bus = node_to_item(node, struct platform_node, list); + if ((bus->name_len < name_len) && + (name[bus->name_len] == '/') && + !strncmp(name, bus->name, bus->name_len)) + /* subdevice of an existing platform, ignore it */ + return; + } + + INFO("adding platform device %s\n", name); + + bus = calloc(1, sizeof(struct platform_node)); + bus->name = strdup(name); + bus->name_len = name_len; + list_add_tail(&platform_names, &bus->list); +} + +/* + * given a name that may start with a platform device, find the length of the + * platform device prefix. If it doesn't start with a platform device, return + * 0. + */ +static const char *find_platform_device(const char *name) +{ + int name_len = strlen(name); + struct listnode *node; + struct platform_node *bus; + + list_for_each_reverse(node, &platform_names) { + bus = node_to_item(node, struct platform_node, list); + if ((bus->name_len < name_len) && + (name[bus->name_len] == '/') && + !strncmp(name, bus->name, bus->name_len)) + return bus->name; + } + + return NULL; +} + +static void remove_platform_device(const char *name) +{ + struct listnode *node; + struct platform_node *bus; + + list_for_each_reverse(node, &platform_names) { + bus = node_to_item(node, struct platform_node, list); + if (!strcmp(name, bus->name)) { + INFO("removing platform device %s\n", name); + free(bus->name); + list_remove(node); + free(bus); + return; + } + } +} + #if LOG_UEVENTS static inline suseconds_t get_usecs(void) @@ -334,7 +376,7 @@ err: static char **parse_platform_block_device(struct uevent *uevent) { - const char *driver; + const char *device; const char *path; char *slash; int width; @@ -354,16 +396,14 @@ static char **parse_platform_block_device(struct uevent *uevent) /* Drop "/devices/platform/" */ path = uevent->path; - driver = path + 18; - slash = strchr(driver, '/'); - if (!slash) - goto err; - width = slash - driver; - if (width <= 0) + device = path + 18; + device = find_platform_device(device); + if (!device) goto err; - snprintf(link_path, sizeof(link_path), "/dev/block/platform/%.*s", - width, driver); + INFO("found platform device %s\n", device); + + snprintf(link_path, sizeof(link_path), "/dev/block/platform/%s", device); if (uevent->partition_name) { p = strdup(uevent->partition_name); @@ -395,104 +435,20 @@ err: return NULL; } -static void handle_device_event(struct uevent *uevent) +static void handle_device(const char *action, const char *devpath, + const char *path, int block, int major, int minor, char **links) { - char devpath[96]; - int devpath_ready = 0; - char *base, *name; - char **links = NULL; - int block; int i; - if (!strcmp(uevent->action,"add")) - fixup_sys_perms(uevent->path); - - /* if it's not a /dev device, nothing else to do */ - if((uevent->major < 0) || (uevent->minor < 0)) - return; - - /* do we have a name? */ - name = strrchr(uevent->path, '/'); - if(!name) - return; - name++; - - /* too-long names would overrun our buffer */ - if(strlen(name) > 64) - return; - - /* are we block or char? where should we live? */ - if(!strncmp(uevent->subsystem, "block", 5)) { - block = 1; - base = "/dev/block/"; - mkdir(base, 0755); - if (!strncmp(uevent->path, "/devices/platform/", 18)) - links = parse_platform_block_device(uevent); - } else { - block = 0; - /* this should probably be configurable somehow */ - if (!strncmp(uevent->subsystem, "usb", 3)) { - if (!strcmp(uevent->subsystem, "usb")) { - /* This imitates the file system that would be created - * if we were using devfs instead. - * Minors are broken up into groups of 128, starting at "001" - */ - int bus_id = uevent->minor / 128 + 1; - int device_id = uevent->minor % 128 + 1; - /* build directories */ - mkdir("/dev/bus", 0755); - mkdir("/dev/bus/usb", 0755); - snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d", bus_id); - mkdir(devpath, 0755); - snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d/%03d", bus_id, device_id); - devpath_ready = 1; - } else { - /* ignore other USB events */ - return; - } - } else if (!strncmp(uevent->subsystem, "graphics", 8)) { - base = "/dev/graphics/"; - mkdir(base, 0755); - } else if (!strncmp(uevent->subsystem, "oncrpc", 6)) { - base = "/dev/oncrpc/"; - mkdir(base, 0755); - } else if (!strncmp(uevent->subsystem, "adsp", 4)) { - base = "/dev/adsp/"; - mkdir(base, 0755); - } else if (!strncmp(uevent->subsystem, "msm_camera", 10)) { - base = "/dev/msm_camera/"; - mkdir(base, 0755); - } else if(!strncmp(uevent->subsystem, "input", 5)) { - base = "/dev/input/"; - mkdir(base, 0755); - } else if(!strncmp(uevent->subsystem, "mtd", 3)) { - base = "/dev/mtd/"; - mkdir(base, 0755); - } else if(!strncmp(uevent->subsystem, "sound", 5)) { - base = "/dev/snd/"; - mkdir(base, 0755); - } else if(!strncmp(uevent->subsystem, "misc", 4) && - !strncmp(name, "log_", 4)) { - base = "/dev/log/"; - mkdir(base, 0755); - name += 4; - } else - base = "/dev/"; - links = get_character_device_symlinks(uevent); - } - - if (!devpath_ready) - snprintf(devpath, sizeof(devpath), "%s%s", base, name); - - if(!strcmp(uevent->action, "add")) { - make_device(devpath, uevent->path, block, uevent->major, uevent->minor); + if(!strcmp(action, "add")) { + make_device(devpath, path, block, major, minor); if (links) { for (i = 0; links[i]; i++) make_link(devpath, links[i]); } } - if(!strcmp(uevent->action, "remove")) { + if(!strcmp(action, "remove")) { if (links) { for (i = 0; links[i]; i++) remove_link(devpath, links[i]); @@ -507,6 +463,138 @@ static void handle_device_event(struct uevent *uevent) } } +static void handle_platform_device_event(struct uevent *uevent) +{ + const char *name = uevent->path + 18; /* length of /devices/platform/ */ + + if (!strcmp(uevent->action, "add")) + add_platform_device(name); + else if (!strcmp(uevent->action, "remove")) + remove_platform_device(name); +} + +static const char *parse_device_name(struct uevent *uevent, unsigned int len) +{ + const char *name; + + /* if it's not a /dev device, nothing else to do */ + if((uevent->major < 0) || (uevent->minor < 0)) + return NULL; + + /* do we have a name? */ + name = strrchr(uevent->path, '/'); + if(!name) + return NULL; + name++; + + /* too-long names would overrun our buffer */ + if(strlen(name) > len) + return NULL; + + return name; +} + +static void handle_block_device_event(struct uevent *uevent) +{ + const char *base = "/dev/block/"; + const char *name; + char devpath[96]; + char **links = NULL; + + name = parse_device_name(uevent, 64); + if (!name) + return; + + snprintf(devpath, sizeof(devpath), "%s%s", base, name); + mkdir(base, 0755); + + if (!strncmp(uevent->path, "/devices/platform/", 18)) + links = parse_platform_block_device(uevent); + + handle_device(uevent->action, devpath, uevent->path, 1, + uevent->major, uevent->minor, links); +} + +static void handle_generic_device_event(struct uevent *uevent) +{ + char *base; + const char *name; + char devpath[96] = {0}; + char **links = NULL; + + name = parse_device_name(uevent, 64); + if (!name) + return; + + if (!strncmp(uevent->subsystem, "usb", 3)) { + if (!strcmp(uevent->subsystem, "usb")) { + /* This imitates the file system that would be created + * if we were using devfs instead. + * Minors are broken up into groups of 128, starting at "001" + */ + int bus_id = uevent->minor / 128 + 1; + int device_id = uevent->minor % 128 + 1; + /* build directories */ + mkdir("/dev/bus", 0755); + mkdir("/dev/bus/usb", 0755); + snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d", bus_id); + mkdir(devpath, 0755); + snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d/%03d", bus_id, device_id); + } else { + /* ignore other USB events */ + return; + } + } else if (!strncmp(uevent->subsystem, "graphics", 8)) { + base = "/dev/graphics/"; + mkdir(base, 0755); + } else if (!strncmp(uevent->subsystem, "oncrpc", 6)) { + base = "/dev/oncrpc/"; + mkdir(base, 0755); + } else if (!strncmp(uevent->subsystem, "adsp", 4)) { + base = "/dev/adsp/"; + mkdir(base, 0755); + } else if (!strncmp(uevent->subsystem, "msm_camera", 10)) { + base = "/dev/msm_camera/"; + mkdir(base, 0755); + } else if(!strncmp(uevent->subsystem, "input", 5)) { + base = "/dev/input/"; + mkdir(base, 0755); + } else if(!strncmp(uevent->subsystem, "mtd", 3)) { + base = "/dev/mtd/"; + mkdir(base, 0755); + } else if(!strncmp(uevent->subsystem, "sound", 5)) { + base = "/dev/snd/"; + mkdir(base, 0755); + } else if(!strncmp(uevent->subsystem, "misc", 4) && + !strncmp(name, "log_", 4)) { + base = "/dev/log/"; + mkdir(base, 0755); + name += 4; + } else + base = "/dev/"; + links = get_character_device_symlinks(uevent); + + if (!devpath[0]) + snprintf(devpath, sizeof(devpath), "%s%s", base, name); + + handle_device(uevent->action, devpath, uevent->path, 0, + uevent->major, uevent->minor, links); +} + +static void handle_device_event(struct uevent *uevent) +{ + if (!strcmp(uevent->action,"add")) + fixup_sys_perms(uevent->path); + + if (!strncmp(uevent->subsystem, "block", 5)) { + handle_block_device_event(uevent); + } else if (!strncmp(uevent->subsystem, "platform", 8)) { + handle_platform_device_event(uevent); + } else { + handle_generic_device_event(uevent); + } +} + static int load_firmware(int fw_fd, int loading_fd, int data_fd) { struct stat st; @@ -553,13 +641,19 @@ out: return ret; } +static int is_booting(void) +{ + return access("/dev/.booting", F_OK) == 0; +} + static void process_firmware_event(struct uevent *uevent) { char *root, *loading, *data, *file1 = NULL, *file2 = NULL; int l, loading_fd, data_fd, fw_fd; + int booting = is_booting(); - log_event_print("firmware event { '%s', '%s' }\n", - uevent->path, uevent->firmware); + INFO("firmware: loading '%s' for '%s'\n", + uevent->firmware, uevent->path); l = asprintf(&root, SYSFS_PREFIX"%s/", uevent->path); if (l == -1) @@ -589,19 +683,29 @@ static void process_firmware_event(struct uevent *uevent) if(data_fd < 0) goto loading_close_out; +try_loading_again: fw_fd = open(file1, O_RDONLY); if(fw_fd < 0) { fw_fd = open(file2, O_RDONLY); if (fw_fd < 0) { + if (booting) { + /* If we're not fully booted, we may be missing + * filesystems needed for firmware, wait and retry. + */ + usleep(100000); + booting = is_booting(); + goto try_loading_again; + } + INFO("firmware: could not open '%s' %d\n", uevent->firmware, errno); write(loading_fd, "-1", 2); goto data_close_out; } } if(!load_firmware(fw_fd, loading_fd, data_fd)) - log_event_print("firmware copy success { '%s', '%s' }\n", root, uevent->firmware); + INFO("firmware: copy success { '%s', '%s' }\n", root, uevent->firmware); else - log_event_print("firmware copy failure { '%s', '%s' }\n", root, uevent->firmware); + INFO("firmware: copy failure { '%s', '%s' }\n", root, uevent->firmware); close(fw_fd); data_close_out: @@ -622,7 +726,6 @@ root_free_out: static void handle_firmware_event(struct uevent *uevent) { pid_t pid; - int status; int ret; if(strcmp(uevent->subsystem, "firmware")) @@ -636,10 +739,6 @@ static void handle_firmware_event(struct uevent *uevent) if (!pid) { process_firmware_event(uevent); exit(EXIT_SUCCESS); - } else { - do { - ret = waitpid(pid, &status, 0); - } while (ret == -1 && errno == EINTR); } } @@ -648,7 +747,7 @@ void handle_device_fd() { char msg[UEVENT_MSG_LEN+2]; int n; - while ((n = uevent_checked_recv(device_fd, msg, UEVENT_MSG_LEN)) > 0) { + while ((n = uevent_kernel_multicast_recv(device_fd, msg, UEVENT_MSG_LEN)) > 0) { if(n >= UEVENT_MSG_LEN) /* overflow -- discard */ continue; @@ -721,7 +820,8 @@ void device_init(void) struct stat info; int fd; - device_fd = open_uevent_socket(); + /* is 64K enough? udev uses 16MB! */ + device_fd = uevent_open_socket(64*1024, true); if(device_fd < 0) return; diff --git a/init/init.c b/init/init.c index bb27c4e..7b4a963 100755 --- a/init/init.c +++ b/init/init.c @@ -33,6 +33,7 @@ #include <sys/un.h> #include <libgen.h> +#include <cutils/list.h> #include <cutils/sockets.h> #include <cutils/iosched_policy.h> #include <private/android_filesystem_config.h> @@ -42,7 +43,6 @@ #include "devices.h" #include "init.h" -#include "list.h" #include "log.h" #include "property_service.h" #include "bootchart.h" @@ -92,7 +92,7 @@ static const char *ENV[32]; int add_environment(const char *key, const char *val) { int n; - + for (n = 0; n < 31; n++) { if (!ENV[n]) { size_t len = strlen(key) + strlen(val) + 2; @@ -156,7 +156,7 @@ void service_start(struct service *svc, const char *dynamic_args) */ svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET)); svc->time_started = 0; - + /* running processes require no additional work -- if * they're in the process of exiting, we've ensured * that they will immediately restart on exit, unless @@ -381,7 +381,7 @@ static void msg_start(const char *name) svc = service_find_by_name(tmp); } - + if (svc) { service_start(svc, args); } else { @@ -455,43 +455,11 @@ static void import_kernel_nv(char *name, int in_qemu) } } -static void import_kernel_cmdline(int in_qemu) -{ - char cmdline[1024]; - char *ptr; - int fd; - - fd = open("/proc/cmdline", O_RDONLY); - if (fd >= 0) { - int n = read(fd, cmdline, 1023); - if (n < 0) n = 0; - - /* get rid of trailing newline, it happens */ - if (n > 0 && cmdline[n-1] == '\n') n--; - - cmdline[n] = 0; - close(fd); - } else { - cmdline[0] = 0; - } - - ptr = cmdline; - while (ptr && *ptr) { - char *x = strchr(ptr, ' '); - if (x != 0) *x++ = 0; - import_kernel_nv(ptr, in_qemu); - ptr = x; - } - - /* don't expose the raw commandline to nonpriv processes */ - chmod("/proc/cmdline", 0440); -} - static struct command *get_first_command(struct action *act) { struct listnode *node; node = list_head(&act->commands); - if (!node) + if (!node || list_empty(&act->commands)) return NULL; return node_to_item(node, struct command, clist); @@ -548,8 +516,12 @@ static int wait_for_coldboot_done_action(int nargs, char **args) static int property_init_action(int nargs, char **args) { + bool load_defaults = true; + INFO("property init\n"); - property_init(); + if (!strcmp(bootmode, "charger")) + load_defaults = false; + property_init(load_defaults); return 0; } @@ -605,7 +577,7 @@ static int set_init_properties_action(int nargs, char **args) char tmp[PROP_VALUE_MAX]; if (qemu[0]) - import_kernel_cmdline(1); + import_kernel_cmdline(1, import_kernel_nv); if (!strcmp(bootmode,"factory")) property_set("ro.factorytest", "1"); @@ -651,6 +623,10 @@ static int check_startup_action(int nargs, char **args) ERROR("init startup failure\n"); exit(1); } + + /* signal that we hit this point */ + unlink("/dev/.booting"); + return 0; } @@ -710,6 +686,9 @@ int main(int argc, char **argv) mount("proc", "/proc", "proc", 0, NULL); mount("sysfs", "/sys", "sysfs", 0, NULL); + /* indicate that booting is in progress to background fw loaders, etc */ + close(open("/dev/.booting", O_WRONLY | O_CREAT, 0000)); + /* We must have some place other than / to create the * device nodes for kmsg and null, otherwise we won't * be able to remount / read-only later on. @@ -717,14 +696,15 @@ int main(int argc, char **argv) * talk to the outside world. */ open_devnull_stdio(); - log_init(); - + klog_init(); + INFO("reading config file\n"); init_parse_config_file("/init.rc"); /* pull the kernel commandline and ramdisk properties file in */ - import_kernel_cmdline(0); - + import_kernel_cmdline(0, import_kernel_nv); + /* don't expose the raw commandline to nonpriv processes */ + chmod("/proc/cmdline", 0440); get_hardware_name(hardware, &revision); snprintf(tmp, sizeof(tmp), "/init.%s.rc", hardware); init_parse_config_file(tmp); @@ -737,20 +717,27 @@ int main(int argc, char **argv) queue_builtin_action(console_init_action, "console_init"); queue_builtin_action(set_init_properties_action, "set_init_properties"); - /* execute all the boot actions to get us started */ + /* execute all the boot actions to get us started */ action_for_each_trigger("init", action_add_queue_tail); - action_for_each_trigger("early-fs", action_add_queue_tail); - action_for_each_trigger("fs", action_add_queue_tail); - action_for_each_trigger("post-fs", action_add_queue_tail); - action_for_each_trigger("post-fs-data", action_add_queue_tail); + + /* skip mounting filesystems in charger mode */ + if (strcmp(bootmode, "charger") != 0) { + action_for_each_trigger("early-fs", action_add_queue_tail); + action_for_each_trigger("fs", action_add_queue_tail); + action_for_each_trigger("post-fs", action_add_queue_tail); + action_for_each_trigger("post-fs-data", action_add_queue_tail); + } queue_builtin_action(property_service_init_action, "property_service_init"); queue_builtin_action(signal_init_action, "signal_init"); queue_builtin_action(check_startup_action, "check_startup"); - /* execute all the boot actions to get us started */ - action_for_each_trigger("early-boot", action_add_queue_tail); - action_for_each_trigger("boot", action_add_queue_tail); + if (!strcmp(bootmode, "charger")) { + action_for_each_trigger("charger", action_add_queue_tail); + } else { + action_for_each_trigger("early-boot", action_add_queue_tail); + action_for_each_trigger("boot", action_add_queue_tail); + } /* run all property triggers based on current state of the properties */ queue_builtin_action(queue_property_triggers_action, "queue_propety_triggers"); diff --git a/init/init.h b/init/init.h index 05cdfaa..2d98c7c 100644 --- a/init/init.h +++ b/init/init.h @@ -17,7 +17,7 @@ #ifndef _INIT_INIT_H #define _INIT_INIT_H -#include "list.h" +#include <cutils/list.h> #include <sys/stat.h> diff --git a/init/init_parser.c b/init/init_parser.c index e8e65ac..fa813b9 100644 --- a/init/init_parser.c +++ b/init/init_parser.c @@ -27,11 +27,11 @@ #include "parser.h" #include "init_parser.h" #include "log.h" -#include "list.h" #include "property_service.h" #include "util.h" #include <cutils/iosched_policy.h> +#include <cutils/list.h> #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_ #include <sys/_system_properties.h> @@ -179,6 +179,14 @@ void parse_new_section(struct parse_state *state, int kw, return; } break; + case K_import: + if (nargs != 2) { + ERROR("single argument needed for import\n"); + } else { + int ret = init_parse_config_file(args[1]); + if (ret) + ERROR("could not import file %s\n", args[1]); + } } state->parse_line = parse_line_no_op; } @@ -347,7 +355,8 @@ void queue_property_triggers(const char *name, const char *value) if (!strncmp(name, test, name_length) && test[name_length] == '=' && - !strcmp(test + name_length + 1, value)) { + (!strcmp(test + name_length + 1, value) || + !strcmp(test + name_length + 1, "*"))) { action_add_queue_tail(act); } } @@ -377,7 +386,8 @@ void queue_all_property_triggers() /* does the property exist, and match the trigger value? */ value = property_get(prop_name); - if (value && !strcmp(equals + 1, value)) { + if (value && (!strcmp(equals + 1, value) || + !strcmp(equals + 1, "*"))) { action_add_queue_tail(act); } } diff --git a/init/keywords.h b/init/keywords.h index 95acd01..3e3733f 100644 --- a/init/keywords.h +++ b/init/keywords.h @@ -11,7 +11,6 @@ int do_export(int nargs, char **args); int do_hostname(int nargs, char **args); int do_ifup(int nargs, char **args); int do_insmod(int nargs, char **args); -int do_import(int nargs, char **args); int do_mkdir(int nargs, char **args); int do_mount(int nargs, char **args); int do_restart(int nargs, char **args); @@ -54,7 +53,7 @@ enum { KEYWORD(hostname, COMMAND, 1, do_hostname) KEYWORD(ifup, COMMAND, 1, do_ifup) KEYWORD(insmod, COMMAND, 1, do_insmod) - KEYWORD(import, COMMAND, 1, do_import) + KEYWORD(import, SECTION, 1, 0) KEYWORD(keycodes, OPTION, 0, 0) KEYWORD(mkdir, COMMAND, 1, do_mkdir) KEYWORD(mount, COMMAND, 3, do_mount) @@ -17,17 +17,12 @@ #ifndef _INIT_LOG_H_ #define _INIT_LOG_H_ -void log_init(void); -void log_set_level(int level); -void log_close(void); -void log_write(int level, const char *fmt, ...) - __attribute__ ((format(printf, 2, 3))); +#include <cutils/klog.h> -#define ERROR(x...) log_write(3, "<3>init: " x) -#define NOTICE(x...) log_write(5, "<5>init: " x) -#define INFO(x...) log_write(6, "<6>init: " x) +#define ERROR(x...) KLOG_ERROR("init", x) +#define NOTICE(x...) KLOG_NOTICE("init", x) +#define INFO(x...) KLOG_INFO("init", x) -#define LOG_DEFAULT_LEVEL 3 /* messages <= this level are logged */ #define LOG_UEVENTS 0 /* log uevent messages if 1. verbose */ #endif diff --git a/init/parser.c b/init/parser.c index 3c2ec00..48e7aec 100644 --- a/init/parser.c +++ b/init/parser.c @@ -3,7 +3,6 @@ #include <string.h> #include "parser.h" -#include "list.h" #include "log.h" #define RAW(x...) log_write(6, x) diff --git a/init/property_service.c b/init/property_service.c index c8d6c09..6733437 100644 --- a/init/property_service.c +++ b/init/property_service.c @@ -62,10 +62,13 @@ struct { { "net.gprs.", AID_RADIO, 0 }, { "net.ppp", AID_RADIO, 0 }, { "net.qmi", AID_RADIO, 0 }, + { "net.lte", AID_RADIO, 0 }, + { "net.cdma", AID_RADIO, 0 }, { "ril.", AID_RADIO, 0 }, { "gsm.", AID_RADIO, 0 }, { "persist.radio", AID_RADIO, 0 }, { "net.dns", AID_RADIO, 0 }, + { "sys.usb.config", AID_RADIO, 0 }, { "net.", AID_SYSTEM, 0 }, { "dev.", AID_SYSTEM, 0 }, { "runtime.", AID_SYSTEM, 0 }, @@ -75,8 +78,6 @@ struct { { "wlan.", AID_SYSTEM, 0 }, { "dhcp.", AID_SYSTEM, 0 }, { "dhcp.", AID_DHCP, 0 }, - { "vpn.", AID_SYSTEM, 0 }, - { "vpn.", AID_VPN, 0 }, { "debug.", AID_SHELL, 0 }, { "log.", AID_SHELL, 0 }, { "service.adb.root", AID_SHELL, 0 }, @@ -374,11 +375,11 @@ void handle_property_set_fd() return; } - r = recv(s, &msg, sizeof(msg), 0); + r = TEMP_FAILURE_RETRY(recv(s, &msg, sizeof(msg), 0)); if(r != sizeof(prop_msg)) { + ERROR("sys_prop: mis-match msg size recieved: %d expected: %d errno: %d\n", + r, sizeof(prop_msg), errno); close(s); - ERROR("sys_prop: mis-match msg size recieved: %d expected: %d\n", - r, sizeof(prop_msg)); return; } @@ -504,10 +505,11 @@ static void load_persistent_properties() persistent_properties_loaded = 1; } -void property_init(void) +void property_init(bool load_defaults) { init_property_area(); - load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT); + if (load_defaults) + load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT); } int properties_inited(void) diff --git a/init/property_service.h b/init/property_service.h index bc97cc4..37c2788 100644 --- a/init/property_service.h +++ b/init/property_service.h @@ -17,8 +17,10 @@ #ifndef _INIT_PROPERTY_H #define _INIT_PROPERTY_H +#include <stdbool.h> + extern void handle_property_set_fd(void); -extern void property_init(void); +extern void property_init(bool load_defaults); extern void load_persist_props(void); extern void start_property_service(void); void get_property_workspace(int *fd, int *sz); diff --git a/init/signal_handler.c b/init/signal_handler.c index f89d058..b170132 100644 --- a/init/signal_handler.c +++ b/init/signal_handler.c @@ -24,9 +24,9 @@ #include <sys/wait.h> #include <cutils/sockets.h> #include <cutils/android_reboot.h> +#include <cutils/list.h> #include "init.h" -#include "list.h" #include "util.h" #include "log.h" diff --git a/init/ueventd.c b/init/ueventd.c index 0e97be7..ecf3b9b 100644 --- a/init/ueventd.c +++ b/init/ueventd.c @@ -20,6 +20,8 @@ #include <stdlib.h> #include <stdio.h> #include <ctype.h> +#include <signal.h> + #include <private/android_filesystem_config.h> #include "ueventd.h" @@ -31,17 +33,43 @@ static char hardware[32]; static unsigned revision = 0; +static void import_kernel_nv(char *name, int in_qemu) +{ + if (*name != '\0') { + char *value = strchr(name, '='); + if (value != NULL) { + *value++ = 0; + if (!strcmp(name,"androidboot.hardware")) + { + strlcpy(hardware, value, sizeof(hardware)); + } + } + } +} + int ueventd_main(int argc, char **argv) { struct pollfd ufd; int nr; char tmp[32]; + /* Prevent fire-and-forget children from becoming zombies. + * If we should need to wait() for some children in the future + * (as opposed to none right now), double-forking here instead + * of ignoring SIGCHLD may be the better solution. + */ + signal(SIGCHLD, SIG_IGN); + open_devnull_stdio(); - log_init(); + klog_init(); INFO("starting ueventd\n"); + /* Respect hardware passed in through the kernel cmd line. Here we will look + * for androidboot.hardware param in kernel cmdline, and save its value in + * hardware[]. */ + import_kernel_cmdline(0, import_kernel_nv); + get_hardware_name(hardware, &revision); ueventd_parse_config_file("/ueventd.rc"); diff --git a/init/ueventd_parser.c b/init/ueventd_parser.c index 0dd8b4d..3e60df5 100644 --- a/init/ueventd_parser.c +++ b/init/ueventd_parser.c @@ -22,7 +22,6 @@ #include "ueventd_parser.h" #include "parser.h" #include "log.h" -#include "list.h" #include "util.h" static void parse_line_device(struct parse_state *state, int nargs, char **args); diff --git a/init/util.c b/init/util.c index d8ec88e..13c9ca2 100755 --- a/init/util.c +++ b/init/util.c @@ -34,45 +34,8 @@ #include <private/android_filesystem_config.h> #include "log.h" -#include "list.h" #include "util.h" -static int log_fd = -1; -/* Inital log level before init.rc is parsed and this this is reset. */ -static int log_level = LOG_DEFAULT_LEVEL; - - -void log_set_level(int level) { - log_level = level; -} - -void log_init(void) -{ - static const char *name = "/dev/__kmsg__"; - if (mknod(name, S_IFCHR | 0600, (1 << 8) | 11) == 0) { - log_fd = open(name, O_WRONLY); - fcntl(log_fd, F_SETFD, FD_CLOEXEC); - unlink(name); - } -} - -#define LOG_BUF_MAX 512 - -void log_write(int level, const char *fmt, ...) -{ - char buf[LOG_BUF_MAX]; - va_list ap; - - if (level > log_level) return; - if (log_fd < 0) return; - - va_start(ap, fmt); - vsnprintf(buf, LOG_BUF_MAX, fmt, ap); - buf[LOG_BUF_MAX - 1] = 0; - va_end(ap); - write(log_fd, buf, strlen(buf)); -} - /* * android_name_to_id - returns the integer uid/gid associated with the given * name, or -1U on error. @@ -192,26 +155,6 @@ oops: return 0; } -void list_init(struct listnode *node) -{ - node->next = node; - node->prev = node; -} - -void list_add_tail(struct listnode *head, struct listnode *item) -{ - item->next = head; - item->prev = head->prev; - head->prev->next = item; - head->prev = item; -} - -void list_remove(struct listnode *item) -{ - item->next->prev = item->prev; - item->prev->next = item->next; -} - #define MAX_MTD_PARTITIONS 16 static struct { @@ -456,3 +399,33 @@ void get_hardware_name(char *hardware, unsigned int *revision) } } } + +void import_kernel_cmdline(int in_qemu, + void (*import_kernel_nv)(char *name, int in_qemu)) +{ + char cmdline[1024]; + char *ptr; + int fd; + + fd = open("/proc/cmdline", O_RDONLY); + if (fd >= 0) { + int n = read(fd, cmdline, 1023); + if (n < 0) n = 0; + + /* get rid of trailing newline, it happens */ + if (n > 0 && cmdline[n-1] == '\n') n--; + + cmdline[n] = 0; + close(fd); + } else { + cmdline[0] = 0; + } + + ptr = cmdline; + while (ptr && *ptr) { + char *x = strchr(ptr, ' '); + if (x != 0) *x++ = 0; + import_kernel_nv(ptr, in_qemu); + ptr = x; + } +} diff --git a/init/util.h b/init/util.h index 2e47369..9247739 100644 --- a/init/util.h +++ b/init/util.h @@ -38,4 +38,5 @@ void remove_link(const char *oldpath, const char *newpath); int wait_for_file(const char *filename, int timeout); void open_devnull_stdio(void); void get_hardware_name(char *hardware, unsigned int *revision); +void import_kernel_cmdline(int in_qemu, void (*import_kernel_nv)(char *name, int in_qemu)); #endif diff --git a/libcutils/Android.mk b/libcutils/Android.mk index 283a6bf..effaae0 100644 --- a/libcutils/Android.mk +++ b/libcutils/Android.mk @@ -35,9 +35,11 @@ commonSources := \ socket_loopback_client.c \ socket_loopback_server.c \ socket_network_client.c \ + sockets.c \ config_utils.c \ cpu_info.c \ load_file.c \ + list.c \ open_memstream.c \ strdup16to8.c \ strdup8to16.c \ @@ -92,25 +94,11 @@ LOCAL_CFLAGS += $(hostSmpFlag) include $(BUILD_HOST_STATIC_LIBRARY) -ifeq ($(TARGET_SIMULATOR),true) - -# Shared library for simulator -# ======================================================== -include $(CLEAR_VARS) -LOCAL_MODULE := libcutils -LOCAL_SRC_FILES := $(commonSources) $(commonHostSources) memory.c dlmalloc_stubs.c -LOCAL_LDLIBS := -lpthread -LOCAL_SHARED_LIBRARIES := liblog -LOCAL_CFLAGS += $(targetSmpFlag) -include $(BUILD_SHARED_LIBRARY) - -else #!sim - # Shared and static library for target # ======================================================== include $(CLEAR_VARS) LOCAL_MODULE := libcutils -LOCAL_SRC_FILES := $(commonSources) ashmem-dev.c mq.c android_reboot.c uevent.c qtaguid.c +LOCAL_SRC_FILES := $(commonSources) ashmem-dev.c mq.c android_reboot.c partition_utils.c uevent.c qtaguid.c klog.c ifeq ($(TARGET_ARCH),arm) LOCAL_SRC_FILES += arch-arm/memset32.S @@ -139,4 +127,10 @@ LOCAL_SHARED_LIBRARIES := liblog LOCAL_CFLAGS += $(targetSmpFlag) include $(BUILD_SHARED_LIBRARY) -endif #!sim +include $(CLEAR_VARS) +LOCAL_MODULE := tst_str_parms +LOCAL_CFLAGS += -DTEST_STR_PARMS +LOCAL_SRC_FILES := str_parms.c hashmap.c memory.c +LOCAL_SHARED_LIBRARIES := liblog +LOCAL_MODULE_TAGS := optional +include $(BUILD_EXECUTABLE) diff --git a/libcutils/config_utils.c b/libcutils/config_utils.c index 75fa6c6..fc5ca78 100644 --- a/libcutils/config_utils.c +++ b/libcutils/config_utils.c @@ -315,3 +315,15 @@ void config_load_file(cnode *root, const char *fn) data = load_file(fn, 0); config_load(root, data); } + +void config_free(cnode *root) +{ + cnode *cur = root->first_child; + + while (cur) { + cnode *prev = cur; + config_free(cur); + cur = cur->next; + free(prev); + } +} diff --git a/libcutils/hashmap.c b/libcutils/hashmap.c index e29bc24..65539ea 100644 --- a/libcutils/hashmap.c +++ b/libcutils/hashmap.c @@ -310,10 +310,11 @@ void hashmapForEach(Hashmap* map, for (i = 0; i < map->bucketCount; i++) { Entry* entry = map->buckets[i]; while (entry != NULL) { + Entry *next = entry->next; if (!callback(entry->key, entry->value, context)) { return; } - entry = entry->next; + entry = next; } } } diff --git a/libcutils/klog.c b/libcutils/klog.c new file mode 100644 index 0000000..b586a57 --- /dev/null +++ b/libcutils/klog.c @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2008 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 <sys/stat.h> +#include <sys/types.h> +#include <fcntl.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <cutils/klog.h> + +static int klog_fd = -1; +static int klog_level = KLOG_DEFAULT_LEVEL; + +void klog_set_level(int level) { + klog_level = level; +} + +void klog_init(void) +{ + static const char *name = "/dev/__kmsg__"; + if (mknod(name, S_IFCHR | 0600, (1 << 8) | 11) == 0) { + klog_fd = open(name, O_WRONLY); + fcntl(klog_fd, F_SETFD, FD_CLOEXEC); + unlink(name); + } +} + +#define LOG_BUF_MAX 512 + +void klog_write(int level, const char *fmt, ...) +{ + char buf[LOG_BUF_MAX]; + va_list ap; + + if (level > klog_level) return; + if (klog_fd < 0) return; + + va_start(ap, fmt); + vsnprintf(buf, LOG_BUF_MAX, fmt, ap); + buf[LOG_BUF_MAX - 1] = 0; + va_end(ap); + write(klog_fd, buf, strlen(buf)); +} diff --git a/libcutils/list.c b/libcutils/list.c new file mode 100644 index 0000000..e13452d --- /dev/null +++ b/libcutils/list.c @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2008 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 <cutils/list.h> + +void list_init(struct listnode *node) +{ + node->next = node; + node->prev = node; +} + +void list_add_tail(struct listnode *head, struct listnode *item) +{ + item->next = head; + item->prev = head->prev; + head->prev->next = item; + head->prev = item; +} + +void list_remove(struct listnode *item) +{ + item->next->prev = item->prev; + item->prev->next = item->next; +} diff --git a/libcutils/partition_utils.c b/libcutils/partition_utils.c new file mode 100644 index 0000000..10539fa --- /dev/null +++ b/libcutils/partition_utils.c @@ -0,0 +1,67 @@ +/* + * Copyright 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 <sys/types.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <sys/mount.h> /* for BLKGETSIZE */ +#include <cutils/properties.h> + +static int only_one_char(char *buf, int len, char c) +{ + int i, ret; + + ret = 1; + for (i=0; i<len; i++) { + if (buf[i] != c) { + ret = 0; + break; + } + } + return ret; +} + +int partition_wiped(char *source) +{ + char buf[4096]; + int fd, ret, wiped; + + if ((fd = open(source, O_RDONLY)) < 0) { + return 0; + } + + ret = read(fd, buf, sizeof(buf)); + close(fd); + + if (ret != sizeof(buf)) { + return 0; + } + + /* Check for all zeros */ + if (only_one_char(buf, sizeof(buf), 0)) { + return 1; + } + + /* Check for all ones */ + if (only_one_char(buf, sizeof(buf), 0xff)) { + return 1; + } + + return 0; +} + diff --git a/libcutils/qtaguid.c b/libcutils/qtaguid.c index 218a21f..994ad32 100644 --- a/libcutils/qtaguid.c +++ b/libcutils/qtaguid.c @@ -15,6 +15,8 @@ ** limitations under the License. */ +// #define LOG_NDEBUG 0 + #define LOG_TAG "qtaguid" #include <cutils/qtaguid.h> @@ -24,44 +26,154 @@ #include <stdio.h> #include <string.h> #include <unistd.h> +#include <pthread.h> -extern int qtaguid_tagSocket(int sockfd, int tag, uid_t uid) { - char lineBuf[128]; - int fd, cnt = 0, res = 0; - uint64_t kTag = (uint64_t)tag << 32; - snprintf(lineBuf, sizeof(lineBuf), "t %d %llu %d", sockfd, kTag, uid); +static const char* CTRL_PROCPATH = "/proc/net/xt_qtaguid/ctrl"; +static const int CTRL_MAX_INPUT_LEN = 128; +static const char *GLOBAL_PACIFIER_PARAM = "/sys/module/xt_qtaguid/parameters/passive"; +static const char *TAG_PACIFIER_PARAM = "/sys/module/xt_qtaguid/parameters/tag_tracking_passive"; + +/* + * One per proccess. + * Once the device is open, this process will have its socket tags tracked. + * And on exit or untimely death, all socket tags will be removed. + * A process can only open /dev/xt_qtaguid once. + * It should not close it unless it is really done with all the socket tags. + * Failure to open it will be visible when socket tagging will be attempted. + */ +static int resTrackFd = -1; +pthread_once_t resTrackInitDone = PTHREAD_ONCE_INIT; + +/* Only call once per process. */ +void qtaguid_resTrack(void) { + resTrackFd = TEMP_FAILURE_RETRY(open("/dev/xt_qtaguid", O_RDONLY)); + if (resTrackFd >=0) { + TEMP_FAILURE_RETRY(fcntl(resTrackFd, F_SETFD, FD_CLOEXEC)); + } +} + +/* + * Returns: + * 0 on success. + * -errno on failure. + */ +static int write_ctrl(const char *cmd) { + int fd, res, savedErrno; - LOGI("Tagging socket %d with tag %llx(%d) for uid %d", sockfd, kTag, tag, uid); - fd = open("/proc/net/xt_qtaguid/ctrl", O_WRONLY); + LOGV("write_ctrl(%s)", cmd); + + fd = TEMP_FAILURE_RETRY(open(CTRL_PROCPATH, O_WRONLY)); if (fd < 0) { return -errno; } - cnt = write(fd, lineBuf, strlen(lineBuf)); - if (cnt < 0) { - res = -errno; + res = TEMP_FAILURE_RETRY(write(fd, cmd, strlen(cmd))); + if (res < 0) { + savedErrno = errno; + } else { + savedErrno = 0; + } + if (res < 0) { + LOGI("Failed write_ctrl(%s) res=%d errno=%d", cmd, res, savedErrno); } - close(fd); - return res; + return -savedErrno; } -extern int qtaguid_untagSocket(int sockfd) { - char lineBuf[128]; - int fd, cnt = 0, res = 0; - snprintf(lineBuf, sizeof(lineBuf), "u %d", sockfd); +static int write_param(const char *param_path, const char *value) { + int param_fd; + int res; - LOGI("Untagging socket %d", sockfd); - fd = open("/proc/net/xt_qtaguid/ctrl", O_WRONLY); - if (fd < 0) { + param_fd = TEMP_FAILURE_RETRY(open(param_path, O_WRONLY)); + if (param_fd < 0) { + return -errno; + } + res = TEMP_FAILURE_RETRY(write(param_fd, value, strlen(value))); + if (res < 0) { return -errno; } + close(param_fd); + return 0; +} + +int qtaguid_tagSocket(int sockfd, int tag, uid_t uid) { + char lineBuf[CTRL_MAX_INPUT_LEN]; + int res; + /* Doing java-land a favor, enforcing "long" */ + uint64_t kTag = ((uint64_t)tag << 32) & ~(1LLU<<63); + - cnt = write(fd, lineBuf, strlen(lineBuf)); - if (cnt < 0) { - res = -errno; + pthread_once(&resTrackInitDone, qtaguid_resTrack); + + snprintf(lineBuf, sizeof(lineBuf), "t %d %llu %d", sockfd, kTag, uid); + + LOGV("Tagging socket %d with tag %llx{%u,0} for uid %d", sockfd, kTag, tag, uid); + + res = write_ctrl(lineBuf); + if (res < 0) { + LOGI("Tagging socket %d with tag %llx(%d) for uid %d failed errno=%d", + sockfd, kTag, tag, uid, res); + } + + return res; +} + +int qtaguid_untagSocket(int sockfd) { + char lineBuf[CTRL_MAX_INPUT_LEN]; + int res; + + LOGV("Untagging socket %d", sockfd); + + snprintf(lineBuf, sizeof(lineBuf), "u %d", sockfd); + res = write_ctrl(lineBuf); + if (res < 0) { + LOGI("Untagging socket %d failed errno=%d", sockfd, res); + } + + return res; +} + +int qtaguid_setCounterSet(int counterSetNum, uid_t uid) { + char lineBuf[CTRL_MAX_INPUT_LEN]; + int res; + + LOGV("Setting counters to set %d for uid %d", counterSetNum, uid); + + snprintf(lineBuf, sizeof(lineBuf), "s %d %d", counterSetNum, uid); + res = write_ctrl(lineBuf); + return res; +} + +int qtaguid_deleteTagData(int tag, uid_t uid) { + char lineBuf[CTRL_MAX_INPUT_LEN]; + int fd, cnt = 0, res = 0; + uint64_t kTag = (uint64_t)tag << 32; + + LOGV("Deleting tag data with tag %llx{%d,0} for uid %d", kTag, tag, uid); + + pthread_once(&resTrackInitDone, qtaguid_resTrack); + + snprintf(lineBuf, sizeof(lineBuf), "d %llu %d", kTag, uid); + res = write_ctrl(lineBuf); + if (res < 0) { + LOGI("Deleteing tag data with tag %llx/%d for uid %d failed with cnt=%d errno=%d", + kTag, tag, uid, cnt, errno); } - close(fd); return res; } + +int qtaguid_setPacifier(int on) { + int param_fd; + int res; + const char *value; + + value = on ? "Y" : "N"; + if (write_param(GLOBAL_PACIFIER_PARAM, value) < 0) { + return -errno; + } + if (write_param(TAG_PACIFIER_PARAM, value) < 0) { + return -errno; + } + return 0; +} diff --git a/libcutils/sockets.c b/libcutils/sockets.c new file mode 100644 index 0000000..101a382 --- /dev/null +++ b/libcutils/sockets.c @@ -0,0 +1,44 @@ +/* + * 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 <cutils/log.h> +#include <cutils/sockets.h> + +#ifdef HAVE_ANDROID_OS +/* For the socket trust (credentials) check */ +#include <private/android_filesystem_config.h> +#endif + +bool socket_peer_is_trusted(int fd) +{ +#ifdef HAVE_ANDROID_OS + struct ucred cr; + socklen_t len = sizeof(cr); + int n = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &len); + + if (n != 0) { + LOGE("could not get socket credentials: %s\n", strerror(errno)); + return false; + } + + if ((cr.uid != AID_ROOT) && (cr.uid != AID_SHELL)) { + LOGE("untrusted userid on other end of socket: userid %d\n", cr.uid); + return false; + } +#endif + + return true; +} diff --git a/libcutils/uevent.c b/libcutils/uevent.c index 3533c00..4add29c 100644 --- a/libcutils/uevent.c +++ b/libcutils/uevent.c @@ -17,14 +17,19 @@ #include <cutils/uevent.h> #include <errno.h> +#include <stdbool.h> +#include <string.h> #include <strings.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <unistd.h> #include <linux/netlink.h> /** * Like recv(), but checks that messages actually originate from the kernel. */ -ssize_t uevent_checked_recv(int socket, void *buffer, size_t length) { +ssize_t uevent_kernel_multicast_recv(int socket, void *buffer, size_t length) { struct iovec iov = { buffer, length }; struct sockaddr_nl addr; char control[CMSG_SPACE(sizeof(struct ucred))]; @@ -68,3 +73,29 @@ out: errno = EIO; return -1; } + +int uevent_open_socket(int buf_sz, bool passcred) +{ + struct sockaddr_nl addr; + int on = passcred; + int s; + + memset(&addr, 0, sizeof(addr)); + addr.nl_family = AF_NETLINK; + addr.nl_pid = getpid(); + addr.nl_groups = 0xffffffff; + + s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); + if(s < 0) + return -1; + + setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, &buf_sz, sizeof(buf_sz)); + setsockopt(s, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)); + + if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + close(s); + return -1; + } + + return s; +} diff --git a/libdiskconfig/Android.mk b/libdiskconfig/Android.mk index 714606a..9decbb9 100644 --- a/libdiskconfig/Android.mk +++ b/libdiskconfig/Android.mk @@ -1,8 +1,6 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -ifneq ($(TARGET_SIMULATOR),true) - commonSources := \ diskconfig.c \ diskutils.c \ @@ -25,5 +23,3 @@ LOCAL_SYSTEM_SHARED_LIBRARIES := libcutils LOCAL_CFLAGS := -O2 -g -W -Wall -Werror -D_LARGEFILE64_SOURCE include $(BUILD_HOST_STATIC_LIBRARY) endif # HOST_OS == linux - -endif # ! TARGET_SIMULATOR diff --git a/liblog/Android.mk b/liblog/Android.mk index 0eec87f..bd4fed4 100644 --- a/liblog/Android.mk +++ b/liblog/Android.mk @@ -48,25 +48,14 @@ LOCAL_LDLIBS := -lpthread LOCAL_CFLAGS := -DFAKE_LOG_DEVICE=1 include $(BUILD_HOST_STATIC_LIBRARY) -ifeq ($(TARGET_SIMULATOR),true) - # Shared library for simulator - # ======================================================== - include $(CLEAR_VARS) - LOCAL_MODULE := liblog - LOCAL_SRC_FILES := $(liblog_host_sources) - LOCAL_LDLIBS := -lpthread - LOCAL_CFLAGS := -DFAKE_LOG_DEVICE=1 - include $(BUILD_SHARED_LIBRARY) -else # !sim - # Shared and static library for target - # ======================================================== - include $(CLEAR_VARS) - LOCAL_MODULE := liblog - LOCAL_SRC_FILES := $(liblog_sources) - include $(BUILD_STATIC_LIBRARY) +# Shared and static library for target +# ======================================================== +include $(CLEAR_VARS) +LOCAL_MODULE := liblog +LOCAL_SRC_FILES := $(liblog_sources) +include $(BUILD_STATIC_LIBRARY) - include $(CLEAR_VARS) - LOCAL_MODULE := liblog - LOCAL_WHOLE_STATIC_LIBRARIES := liblog - include $(BUILD_SHARED_LIBRARY) -endif # !sim +include $(CLEAR_VARS) +LOCAL_MODULE := liblog +LOCAL_WHOLE_STATIC_LIBRARIES := liblog +include $(BUILD_SHARED_LIBRARY) diff --git a/liblog/logprint.c b/liblog/logprint.c index 4c5b3e5..f2dd79f 100644 --- a/liblog/logprint.c +++ b/liblog/logprint.c @@ -350,18 +350,37 @@ static inline char * strip_end(char *str) int android_log_processLogBuffer(struct logger_entry *buf, AndroidLogEntry *entry) { - size_t tag_len; - entry->tv_sec = buf->sec; entry->tv_nsec = buf->nsec; entry->priority = buf->msg[0]; entry->pid = buf->pid; entry->tid = buf->tid; + + /* + * format: <priority:1><tag:N>\0<message:N>\0 + * + * tag str + * starts at msg+1 + * msg + * starts at msg+1+len(tag)+1 + */ entry->tag = buf->msg + 1; - tag_len = strlen(entry->tag); - entry->messageLen = buf->len - tag_len - 3; + const size_t tag_len = strlen(entry->tag); + const size_t preambleAndNullLen = tag_len + 3; + if (buf->len <= preambleAndNullLen) { + fprintf(stderr, "+++ LOG: entry corrupt or truncated\n"); + return -1; + } + entry->messageLen = buf->len - preambleAndNullLen; entry->message = entry->tag + tag_len + 1; + if (entry->messageLen != strlen(entry->message)) { + fprintf(stderr, + "+++ LOG: Message length inconsistent. Expected %d, got %d\n", + entry->messageLen, strlen(entry->message)); + return -1; + } + return 0; } @@ -818,7 +837,6 @@ char *android_log_formatLogLine ( while(pm < (entry->message + entry->messageLen)) { const char *lineStart; size_t lineLen; - lineStart = pm; // Find the next end-of-line in message diff --git a/libnetutils/Android.mk b/libnetutils/Android.mk index 46102d5..5f5849f 100644 --- a/libnetutils/Android.mk +++ b/libnetutils/Android.mk @@ -6,17 +6,10 @@ LOCAL_SRC_FILES:= \ dhcpmsg.c \ dhcp_utils.c \ ifc_utils.c \ - packet.c + packet.c LOCAL_SHARED_LIBRARIES := \ - libcutils - -# need "-lrt" on Linux simulator to pick up clock_gettime -ifeq ($(TARGET_SIMULATOR),true) - ifeq ($(HOST_OS),linux) - LOCAL_LDLIBS += -lrt -lpthread - endif -endif + libcutils LOCAL_MODULE:= libnetutils diff --git a/libnetutils/dhcp_utils.c b/libnetutils/dhcp_utils.c index 8496725..3ab5d1b 100644 --- a/libnetutils/dhcp_utils.c +++ b/libnetutils/dhcp_utils.c @@ -33,6 +33,8 @@ static const int NAP_TIME = 200; /* wait for 200ms at a time */ /* when polling for property values */ static const char DAEMON_NAME_RENEW[] = "iprenew"; static char errmsg[100]; +/* interface suffix on dhcpcd */ +#define MAX_DAEMON_SUFFIX 25 /* * Wait for a system property to be assigned a specified value. @@ -131,6 +133,15 @@ static const char *ipaddr_to_string(in_addr_t addr) return inet_ntoa(in_addr); } +void get_daemon_suffix(const char *interface, char *daemon_suffix) { + /* Use p2p suffix for any p2p interface. */ + if (strncmp(interface, "p2p",3) == 0) { + sprintf(daemon_suffix, "p2p"); + } else { + snprintf(daemon_suffix, MAX_DAEMON_SUFFIX, "%s", interface); + } +} + /* * Start the dhcp client daemon, and wait for it to finish * configuring the interface. @@ -150,6 +161,9 @@ int dhcp_do_request(const char *interface, char daemon_cmd[PROPERTY_VALUE_MAX * 2]; const char *ctrl_prop = "ctl.start"; const char *desired_status = "running"; + char daemon_suffix[MAX_DAEMON_SUFFIX]; + + get_daemon_suffix(interface, daemon_suffix); snprintf(result_prop_name, sizeof(result_prop_name), "%s.%s.result", DHCP_PROP_NAME_PREFIX, @@ -157,17 +171,17 @@ int dhcp_do_request(const char *interface, snprintf(daemon_prop_name, sizeof(daemon_prop_name), "%s_%s", DAEMON_PROP_NAME, - interface); + daemon_suffix); /* Erase any previous setting of the dhcp result property */ property_set(result_prop_name, ""); /* Start the daemon and wait until it's ready */ if (property_get(HOSTNAME_PROP_NAME, prop_value, NULL) && (prop_value[0] != '\0')) - snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s:-h %s %s", DAEMON_NAME, interface, + snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s:-h %s %s", DAEMON_NAME, daemon_suffix, prop_value, interface); else - snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s:%s", DAEMON_NAME, interface, interface); + snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s:%s", DAEMON_NAME, daemon_suffix, interface); memset(prop_value, '\0', PROPERTY_VALUE_MAX); property_set(ctrl_prop, daemon_cmd); if (wait_for_property(daemon_prop_name, desired_status, 10) < 0) { @@ -217,15 +231,19 @@ int dhcp_stop(const char *interface) const char *ctrl_prop = "ctl.stop"; const char *desired_status = "stopped"; + char daemon_suffix[MAX_DAEMON_SUFFIX]; + + get_daemon_suffix(interface, daemon_suffix); + snprintf(result_prop_name, sizeof(result_prop_name), "%s.%s.result", DHCP_PROP_NAME_PREFIX, interface); snprintf(daemon_prop_name, sizeof(daemon_prop_name), "%s_%s", DAEMON_PROP_NAME, - interface); + daemon_suffix); - snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s", DAEMON_NAME, interface); + snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s", DAEMON_NAME, daemon_suffix); /* Stop the daemon and wait until it's reported to be stopped */ property_set(ctrl_prop, daemon_cmd); @@ -246,11 +264,15 @@ int dhcp_release_lease(const char *interface) const char *ctrl_prop = "ctl.stop"; const char *desired_status = "stopped"; + char daemon_suffix[MAX_DAEMON_SUFFIX]; + + get_daemon_suffix(interface, daemon_suffix); + snprintf(daemon_prop_name, sizeof(daemon_prop_name), "%s_%s", DAEMON_PROP_NAME, - interface); + daemon_suffix); - snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s", DAEMON_NAME, interface); + snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s", DAEMON_NAME, daemon_suffix); /* Stop the daemon and wait until it's reported to be stopped */ property_set(ctrl_prop, daemon_cmd); @@ -265,15 +287,16 @@ char *dhcp_get_errmsg() { } /** - * DHCP renewal request + * Run WiMAX dhcp renew service. + * "wimax_renew" service shoud be included in init.rc. */ int dhcp_do_request_renew(const char *interface, - char *ipaddr, - char *gateway, - uint32_t *prefixLength, - char *dns1, - char *dns2, - char *server, + in_addr_t *ipaddr, + in_addr_t *gateway, + in_addr_t *mask, + in_addr_t *dns1, + in_addr_t *dns2, + in_addr_t *server, uint32_t *lease) { char result_prop_name[PROPERTY_KEY_MAX]; @@ -281,6 +304,10 @@ int dhcp_do_request_renew(const char *interface, char daemon_cmd[PROPERTY_VALUE_MAX * 2]; const char *ctrl_prop = "ctl.start"; + char daemon_suffix[MAX_DAEMON_SUFFIX]; + + get_daemon_suffix(interface, daemon_suffix); + snprintf(result_prop_name, sizeof(result_prop_name), "%s.%s.result", DHCP_PROP_NAME_PREFIX, interface); @@ -289,7 +316,8 @@ int dhcp_do_request_renew(const char *interface, property_set(result_prop_name, ""); /* Start the renew daemon and wait until it's ready */ - snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s:%s", DAEMON_NAME_RENEW, interface, interface); + snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s:%s", DAEMON_NAME_RENEW, + daemon_suffix, interface); memset(prop_value, '\0', PROPERTY_VALUE_MAX); property_set(ctrl_prop, daemon_cmd); @@ -305,10 +333,7 @@ int dhcp_do_request_renew(const char *interface, return -1; } if (strcmp(prop_value, "ok") == 0) { - if(fill_ip_info(interface, ipaddr, gateway, prefixLength, dns1, dns2, server, lease) - == -1) { - return -1; - } + fill_ip_info(interface, ipaddr, gateway, mask, dns1, dns2, server, lease); return 0; } else { snprintf(errmsg, sizeof(errmsg), "DHCP Renew result was %s", prop_value); diff --git a/libnetutils/dhcpclient.c b/libnetutils/dhcpclient.c index 5039e26..4f2d1c1 100644 --- a/libnetutils/dhcpclient.c +++ b/libnetutils/dhcpclient.c @@ -197,7 +197,7 @@ int decode_dhcp_msg(dhcp_msg *msg, int len, dhcp_info *info) } switch(opt) { case OPT_SUBNET_MASK: - if (optlen >= 4) info->prefixLength = ipv4NetmaskToPrefixLength((int)x); + if (optlen >= 4) info->prefixLength = ipv4NetmaskToPrefixLength(*((uint32_t*)x)); break; case OPT_GATEWAY: if (optlen >= 4) memcpy(&info->gateway, x, 4); diff --git a/libnetutils/ifc_utils.c b/libnetutils/ifc_utils.c index c9d6ed2..0a2f760 100644 --- a/libnetutils/ifc_utils.c +++ b/libnetutils/ifc_utils.c @@ -26,15 +26,18 @@ #include <netinet/in.h> #include <arpa/inet.h> #include <net/if.h> +#include <netdb.h> #include <linux/if.h> #include <linux/if_ether.h> #include <linux/if_arp.h> -#include <linux/sockios.h> +#include <linux/netlink.h> #include <linux/route.h> #include <linux/ipv6_route.h> -#include <netdb.h> -#include <linux/wireless.h> +#include <linux/rtnetlink.h> +#include <linux/sockios.h> + +#include "netutils/ifc.h" #ifdef ANDROID #define LOG_TAG "NetUtils" @@ -51,6 +54,10 @@ static int ifc_ctl_sock = -1; static int ifc_ctl_sock6 = -1; void printerr(char *fmt, ...); +#define DBG 0 +#define INET_ADDRLEN 4 +#define INET6_ADDRLEN 16 + in_addr_t prefixLengthToIpv4Netmask(int prefix_length) { in_addr_t mask = 0; @@ -86,15 +93,41 @@ static const char *ipaddr_to_string(in_addr_t addr) return inet_ntoa(in_addr); } +int string_to_ip(const char *string, struct sockaddr_storage *ss) { + struct addrinfo hints, *ai; + int ret; + + if (ss == NULL) { + return -EFAULT; + } + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_flags = AI_NUMERICHOST; + hints.ai_socktype = SOCK_DGRAM; + + ret = getaddrinfo(string, NULL, &hints, &ai); + if (ret == 0) { + memcpy(ss, ai->ai_addr, ai->ai_addrlen); + freeaddrinfo(ai); + } + + return ret; +} + int ifc_init(void) { + int ret; if (ifc_ctl_sock == -1) { - ifc_ctl_sock = socket(AF_INET, SOCK_DGRAM, 0); + ifc_ctl_sock = socket(AF_INET, SOCK_DGRAM, 0); if (ifc_ctl_sock < 0) { printerr("socket() failed: %s\n", strerror(errno)); } } - return ifc_ctl_sock < 0 ? -1 : 0; + + ret = ifc_ctl_sock < 0 ? -1 : 0; + if (DBG) printerr("ifc_init_returning %d", ret); + return ret; } int ifc_init6(void) @@ -110,6 +143,7 @@ int ifc_init6(void) void ifc_close(void) { + if (DBG) printerr("ifc_close"); if (ifc_ctl_sock != -1) { (void)close(ifc_ctl_sock); ifc_ctl_sock = -1; @@ -141,7 +175,7 @@ int ifc_get_hwaddr(const char *name, void *ptr) if(r < 0) return -1; memcpy(ptr, &ifr.ifr_hwaddr.sa_data, ETH_ALEN); - return 0; + return 0; } int ifc_get_ifindex(const char *name, int *if_indexp) @@ -169,12 +203,16 @@ static int ifc_set_flags(const char *name, unsigned set, unsigned clr) int ifc_up(const char *name) { - return ifc_set_flags(name, IFF_UP, 0); + int ret = ifc_set_flags(name, IFF_UP, 0); + if (DBG) printerr("ifc_up(%s) = %d", name, ret); + return ret; } int ifc_down(const char *name) { - return ifc_set_flags(name, 0, IFF_UP); + int ret = ifc_set_flags(name, 0, IFF_UP); + if (DBG) printerr("ifc_down(%s) = %d", name, ret); + return ret; } static void init_sockaddr_in(struct sockaddr *sa, in_addr_t addr) @@ -188,11 +226,193 @@ static void init_sockaddr_in(struct sockaddr *sa, in_addr_t addr) int ifc_set_addr(const char *name, in_addr_t addr) { struct ifreq ifr; + int ret; ifc_init_ifr(name, &ifr); init_sockaddr_in(&ifr.ifr_addr, addr); - return ioctl(ifc_ctl_sock, SIOCSIFADDR, &ifr); + ret = ioctl(ifc_ctl_sock, SIOCSIFADDR, &ifr); + if (DBG) printerr("ifc_set_addr(%s, xx) = %d", name, ret); + return ret; +} + +/* + * Adds or deletes an IP address on an interface. + * + * Action is one of: + * - RTM_NEWADDR (to add a new address) + * - RTM_DELADDR (to delete an existing address) + * + * Returns zero on success and negative errno on failure. + */ +int ifc_act_on_address(int action, const char *name, const char *address, + int prefixlen) { + int ifindex, s, len, ret; + struct sockaddr_storage ss; + void *addr; + size_t addrlen; + struct { + struct nlmsghdr n; + struct ifaddrmsg r; + // Allow for IPv6 address, headers, and padding. + char attrbuf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + + NLMSG_ALIGN(sizeof(struct rtattr)) + + NLMSG_ALIGN(INET6_ADDRLEN)]; + } req; + struct rtattr *rta; + struct nlmsghdr *nh; + struct nlmsgerr *err; + char buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + + NLMSG_ALIGN(sizeof(struct nlmsgerr)) + + NLMSG_ALIGN(sizeof(struct nlmsghdr))]; + + // Get interface ID. + ifindex = if_nametoindex(name); + if (ifindex == 0) { + return -errno; + } + + // Convert string representation to sockaddr_storage. + ret = string_to_ip(address, &ss); + if (ret) { + return ret; + } + + // Determine address type and length. + if (ss.ss_family == AF_INET) { + struct sockaddr_in *sin = (struct sockaddr_in *) &ss; + addr = &sin->sin_addr; + addrlen = INET_ADDRLEN; + } else if (ss.ss_family == AF_INET6) { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) &ss; + addr = &sin6->sin6_addr; + addrlen = INET6_ADDRLEN; + } else { + return -EAFNOSUPPORT; + } + + // Fill in netlink structures. + memset(&req, 0, sizeof(req)); + + // Netlink message header. + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.r)); + req.n.nlmsg_type = action; + req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + req.n.nlmsg_pid = getpid(); + + // Interface address message header. + req.r.ifa_family = ss.ss_family; + req.r.ifa_prefixlen = prefixlen; + req.r.ifa_index = ifindex; + + // Routing attribute. Contains the actual IP address. + rta = (struct rtattr *) (((char *) &req) + NLMSG_ALIGN(req.n.nlmsg_len)); + rta->rta_type = IFA_LOCAL; + rta->rta_len = RTA_LENGTH(addrlen); + req.n.nlmsg_len = NLMSG_ALIGN(req.n.nlmsg_len) + RTA_LENGTH(addrlen); + memcpy(RTA_DATA(rta), addr, addrlen); + + s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (send(s, &req, req.n.nlmsg_len, 0) < 0) { + close(s); + return -errno; + } + + len = recv(s, buf, sizeof(buf), 0); + close(s); + if (len < 0) { + return -errno; + } + + // Parse the acknowledgement to find the return code. + nh = (struct nlmsghdr *) buf; + if (!NLMSG_OK(nh, (unsigned) len) || nh->nlmsg_type != NLMSG_ERROR) { + return -EINVAL; + } + err = NLMSG_DATA(nh); + + // Return code is negative errno. + return err->error; +} + +int ifc_add_address(const char *name, const char *address, int prefixlen) { + return ifc_act_on_address(RTM_NEWADDR, name, address, prefixlen); +} + +int ifc_del_address(const char *name, const char * address, int prefixlen) { + return ifc_act_on_address(RTM_DELADDR, name, address, prefixlen); +} + +/* + * Clears IPv6 addresses on the specified interface. + */ +int ifc_clear_ipv6_addresses(const char *name) { + char rawaddrstr[INET6_ADDRSTRLEN], addrstr[INET6_ADDRSTRLEN]; + unsigned int prefixlen; + int lasterror = 0, i, j, ret; + char ifname[64]; // Currently, IFNAMSIZ = 16. + FILE *f = fopen("/proc/net/if_inet6", "r"); + if (!f) { + return -errno; + } + + // Format: + // 20010db8000a0001fc446aa4b5b347ed 03 40 00 01 wlan0 + while (fscanf(f, "%32s %*02x %02x %*02x %*02x %63s\n", + rawaddrstr, &prefixlen, ifname) == 3) { + // Is this the interface we're looking for? + if (strcmp(name, ifname)) { + continue; + } + + // Put the colons back into the address. + for (i = 0, j = 0; i < 32; i++, j++) { + addrstr[j] = rawaddrstr[i]; + if (i % 4 == 3) { + addrstr[++j] = ':'; + } + } + addrstr[j - 1] = '\0'; + + // Don't delete the link-local address as well, or it will disable IPv6 + // on the interface. + if (strncmp(addrstr, "fe80:", 5) == 0) { + continue; + } + + ret = ifc_del_address(ifname, addrstr, prefixlen); + if (ret) { + LOGE("Deleting address %s/%d on %s: %s", addrstr, prefixlen, ifname, + strerror(-ret)); + lasterror = ret; + } + } + + fclose(f); + return lasterror; +} + +/* + * Clears IPv4 addresses on the specified interface. + */ +void ifc_clear_ipv4_addresses(const char *name) { + unsigned count, addr; + ifc_init(); + for (count=0, addr=1;((addr != 0) && (count < 255)); count++) { + if (ifc_get_addr(name, &addr) < 0) + break; + if (addr) + ifc_set_addr(name, 0); + } + ifc_close(); +} + +/* + * Clears all IP addresses on the specified interface. + */ +int ifc_clear_addresses(const char *name) { + ifc_clear_ipv4_addresses(name); + return ifc_clear_ipv6_addresses(name); } int ifc_set_hwaddr(const char *name, const void *ptr) @@ -206,6 +426,19 @@ int ifc_set_hwaddr(const char *name, const void *ptr) return ioctl(ifc_ctl_sock, SIOCSIFHWADDR, &ifr); } +int ifc_set_mask(const char *name, in_addr_t mask) +{ + struct ifreq ifr; + int ret; + + ifc_init_ifr(name, &ifr); + init_sockaddr_in(&ifr.ifr_addr, mask); + + ret = ioctl(ifc_ctl_sock, SIOCSIFNETMASK, &ifr); + if (DBG) printerr("ifc_set_mask(%s, xx) = %d", name, ret); + return ret; +} + int ifc_set_prefixLength(const char *name, int prefixLength) { struct ifreq ifr; @@ -313,6 +546,7 @@ int ifc_act_on_ipv4_route(int action, const char *ifname, struct in_addr dst, in return result; } +/* deprecated - v4 only */ int ifc_create_default_route(const char *name, in_addr_t gw) { struct in_addr in_dst, in_gw; @@ -320,7 +554,20 @@ int ifc_create_default_route(const char *name, in_addr_t gw) in_dst.s_addr = 0; in_gw.s_addr = gw; - return ifc_act_on_route(SIOCADDRT, name, in_dst, 0, in_gw); + int ret = ifc_act_on_ipv4_route(SIOCADDRT, name, in_dst, 0, in_gw); + if (DBG) printerr("ifc_create_default_route(%s, %d) = %d", name, gw, ret); + return ret; +} + +/* deprecated v4-only */ +int ifc_add_host_route(const char *name, in_addr_t dst) +{ + struct in_addr in_dst, in_gw; + + in_dst.s_addr = dst; + in_gw.s_addr = 0; + + return ifc_act_on_ipv4_route(SIOCADDRT, name, in_dst, 32, in_gw); } int ifc_enable(const char *ifname) @@ -449,6 +696,70 @@ int ifc_remove_host_routes(const char *name) } /* + * Return the address of the default gateway + * + * TODO: factor out common code from this and remove_host_routes() + * so that we only scan /proc/net/route in one place. + * + * DEPRECATED + */ +int ifc_get_default_route(const char *ifname) +{ + char name[64]; + in_addr_t dest, gway, mask; + int flags, refcnt, use, metric, mtu, win, irtt; + int result; + FILE *fp; + + fp = fopen("/proc/net/route", "r"); + if (fp == NULL) + return 0; + /* Skip the header line */ + if (fscanf(fp, "%*[^\n]\n") < 0) { + fclose(fp); + return 0; + } + ifc_init(); + result = 0; + for (;;) { + int nread = fscanf(fp, "%63s%X%X%X%d%d%d%X%d%d%d\n", + name, &dest, &gway, &flags, &refcnt, &use, &metric, &mask, + &mtu, &win, &irtt); + if (nread != 11) { + break; + } + if ((flags & (RTF_UP|RTF_GATEWAY)) == (RTF_UP|RTF_GATEWAY) + && dest == 0 + && strcmp(ifname, name) == 0) { + result = gway; + break; + } + } + fclose(fp); + ifc_close(); + return result; +} + +/* + * Sets the specified gateway as the default route for the named interface. + * DEPRECATED + */ +int ifc_set_default_route(const char *ifname, in_addr_t gateway) +{ + struct in_addr addr; + int result; + + ifc_init(); + addr.s_addr = gateway; + if ((result = ifc_create_default_route(ifname, gateway)) < 0) { + LOGD("failed to add %s as default route for %s: %s", + inet_ntoa(addr), ifname, strerror(errno)); + } + ifc_close(); + return result; +} + +/* * Removes the default route for the named interface. */ int ifc_remove_default_route(const char *ifname) @@ -627,9 +938,31 @@ int ifc_act_on_route(int action, const char *ifname, const char *dst, int prefix return ret; } +/* + * DEPRECATED + */ +int ifc_add_ipv4_route(const char *ifname, struct in_addr dst, int prefix_length, + struct in_addr gw) +{ + int i =ifc_act_on_ipv4_route(SIOCADDRT, ifname, dst, prefix_length, gw); + if (DBG) printerr("ifc_add_ipv4_route(%s, xx, %d, xx) = %d", ifname, prefix_length, i); + return i; +} + +/* + * DEPRECATED + */ +int ifc_add_ipv6_route(const char *ifname, struct in6_addr dst, int prefix_length, + struct in6_addr gw) +{ + return ifc_act_on_ipv6_route(SIOCADDRT, ifname, dst, prefix_length, gw); +} + int ifc_add_route(const char *ifname, const char *dst, int prefix_length, const char *gw) { - return ifc_act_on_route(SIOCADDRT, ifname, dst, prefix_length, gw); + int i = ifc_act_on_route(SIOCADDRT, ifname, dst, prefix_length, gw); + if (DBG) printerr("ifc_add_route(%s, %s, %d, %s) = %d", ifname, dst, prefix_length, gw, i); + return i; } int ifc_remove_route(const char *ifname, const char*dst, int prefix_length, const char *gw) diff --git a/libnl_2/.gitignore b/libnl_2/.gitignore new file mode 100644 index 0000000..d4ca744 --- /dev/null +++ b/libnl_2/.gitignore @@ -0,0 +1,2 @@ +include/netlink/version.h.in +cscope.* diff --git a/libnl_2/Android.mk b/libnl_2/Android.mk new file mode 100644 index 0000000..1745f5a --- /dev/null +++ b/libnl_2/Android.mk @@ -0,0 +1,30 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + attr.c \ + cache.c \ + genl/genl.c \ + genl/family.c \ + handlers.c \ + msg.c \ + netlink.c \ + object.c \ + socket.c \ + dbg.c + +LOCAL_C_INCLUDES += \ + external/libnl-headers + +# Static Library +LOCAL_MODULE := libnl_2 +LOCAL_MODULE_TAGS := optional +include $(BUILD_STATIC_LIBRARY) + +####################################### +# Shared library currently unavailiable +# * Netlink cache not implemented +# * Library is not thread safe +####################################### + diff --git a/libnl_2/README b/libnl_2/README new file mode 100644 index 0000000..14db6db --- /dev/null +++ b/libnl_2/README @@ -0,0 +1,88 @@ +Netlink Protocol Library + +This library is a clean room re-implementation of libnl 2.0 and +re-licensed under Apache 2.0. It was developed primarily to support +wpa_supplicant. However, with additional development can be extended +to support other netlink applications. + +Netlink Protocol Format (RFC3549) + ++-----------------+-+-------------------+-+ +|Netlink Message |P| Generic Netlink |P| +| Header |A| Message Header |A| +|(struct nlmsghdr)|D|(struct genlmsghdr)|D| ++-----------------+-+-------------------+-+-------------+ +|len:4|type:2|flags:2|seq:4 pid:4|cmd:1|ver:1|reserved:2| ++--------------------------------+----------------------+ ++-----------------+-+-----------------+-+-----------------+-+-----------------+-+---+ +|Netlink Attribute|P|Netlink Attribute|P|Netlink Attribute|P|Netlink Attribute|P|...| +| #0 Header |A| #0 Payload |A| #1 Header |A| #1 Payload |A| | +| (struct nlattr) |D| (void) |D| (struct nlattr) |D| (void) |D| | ++-----------------+-+-----------------+-+-----------------+-+-----------------+-+---+ +|len:2(==4+payload)|type:2|payload|pad| ++-------------------------+-------+---+ + +NETLINK OVERVIEW + +* Each netlink message consists of a bitstream with a netlink header. +* After this header a second header *can* be used specific to the netlink + family in use. This library was tested using the generic netlink + protocol defined by struct genlmsghdr to support nl80211. +* After the header(s) netlink attributes can be appended to the message + which hold can hold basic types such as unsigned integers and strings. +* Attributes can also be nested. This is accomplished by calling "nla_nest_start" + which creates an empty attribute with nest attributes as its payload. Then to + close the nest, "nla_nest_end" is called. +* All data structures in this implementation are byte-aligned (Currently 4 bytes). +* Acknowledgements (ACKs) are sent as NLMSG_ERROR netlink message types (0x2) and + have an error value of 0. + +KNOWN ISSUES + + GENERAL + * Not tested for thread safety + + Android.mk + * No dynamic library because of netlink cache not implemented and + not tested for thread safety + + attr.c + * nla_parse - does not use nla_policy argument + + cache.c + * netlink cache not implemented and only supports one netlink family id + which is stored in the nl_cache pointer instead of an actual cache + + netlink.c + * nl_recvmsgs - does not support nl_cb_overwrite_recv() + * nl_recv - sets/unsets asynchronous socket flag + +SOURCE FILES + +* Android.mk - Android makefile +* README - This file +* attr.c - Netlink attributes +* cache.c - Netlink cache +* genl/family.c - Generic netlink family id +* genl/genl.c - Generic netlink +* handlers.c - Netlink callbacks +* msg.c - Netlink messages construction +* netlink.c - Netlink socket communication +* object.c - libnl object wrapper +* socket.c - Netlink kernel socket utils + +IMPORTANT HEADER FILES - NOTE: These are based on the the origin GPL libnl headers + +* netlink-types.h - Contains many important structs for libnl + to represent netlink objects +* netlink/netlink-kernel.h - Netlink kernel headers and field constants. +* netlink/msg.h - macros for iterating over netlink messages +* netlink/attr.h - netlink attribute constants, iteration macros and setters + +REFERENCES + +* nl80211.h +* netlink_types.h +* $LINUX_KERNEL/net/wireless/nl80211.c +* http://www.infradead.org/~tgr/libnl/doc-3.0/index.html +* http://www.netfilter.org/projects/libmnl/doxygen/index.html diff --git a/libnl_2/attr.c b/libnl_2/attr.c new file mode 100644 index 0000000..f3a2b58 --- /dev/null +++ b/libnl_2/attr.c @@ -0,0 +1,217 @@ +/* + * 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. + */ + +/* NOTICE: This is a clean room re-implementation of libnl */ + +#include <errno.h> +#include "netlink/netlink.h" +#include "netlink/msg.h" +#include "netlink/attr.h" +#include "netlink-types.h" + +/* Return payload of string attribute. */ +char *nla_get_string(struct nlattr *nla) +{ + return (char *) nla_data(nla); +} + +/* Return payload of 16 bit integer attribute. */ +uint16_t nla_get_u16(struct nlattr *nla) +{ + return *((uint16_t *) nla_data(nla)); +} + +/* Return payload of 32 bit integer attribute. */ +uint32_t nla_get_u32(struct nlattr *nla) +{ + return *((uint32_t *) nla_data(nla)); +} + +/* Return value of 8 bit integer attribute. */ +uint8_t nla_get_u8(struct nlattr *nla) +{ + return *((uint8_t *) nla_data(nla)); +} + +/* Return payload of uint64_t attribute. */ +uint64_t nla_get_u64(struct nlattr *nla) +{ + uint64_t tmp; + nla_memcpy(&tmp, nla, sizeof(tmp)); + return tmp; +} + +/* Head of payload */ +void *nla_data(const struct nlattr *nla) +{ + return (void *) ((char *) nla + NLA_HDRLEN); +} + +/* Return length of the payload . */ +int nla_len(const struct nlattr *nla) +{ + return nla->nla_len - NLA_HDRLEN; +} + +int nla_padlen(int payload) +{ + return NLA_ALIGN(payload) - payload; +} + +/* Start a new level of nested attributes. */ +struct nlattr *nla_nest_start(struct nl_msg *msg, int attrtype) +{ + struct nlattr *start = (struct nlattr *)nlmsg_tail(msg->nm_nlh); + int rc; + + rc = nla_put(msg, attrtype, 0, NULL); + if (rc < 0) + return NULL; + + return start; +} + +/* Finalize nesting of attributes. */ +int nla_nest_end(struct nl_msg *msg, struct nlattr *start) +{ + /* Set attribute size */ + start->nla_len = (unsigned char *)nlmsg_tail(nlmsg_hdr(msg)) - + (unsigned char *)start; + return 0; +} + +/* Return next attribute in a stream of attributes. */ +struct nlattr *nla_next(const struct nlattr *nla, int *remaining) +{ + struct nlattr *next_nla = NULL; + if (nla->nla_len >= sizeof(struct nlattr) && + nla->nla_len <= *remaining){ + next_nla = (struct nlattr *) \ + ((char *) nla + NLA_ALIGN(nla->nla_len)); + *remaining = *remaining - NLA_ALIGN(nla->nla_len); + } + + return next_nla; + +} + +/* Check if the attribute header and payload can be accessed safely. */ +int nla_ok(const struct nlattr *nla, int remaining) +{ + return remaining > 0 && + nla->nla_len >= sizeof(struct nlattr) && + sizeof(struct nlattr) <= (unsigned int) remaining && + nla->nla_len <= remaining; +} + +/* Create attribute index based on a stream of attributes. */ +/* NOTE: Policy not used ! */ +int nla_parse(struct nlattr *tb[], int maxtype, struct nlattr *head, + int len, struct nla_policy *policy) +{ + struct nlattr *pos; + int rem; + + /* First clear table */ + memset(tb, 0, (maxtype + 1) * sizeof(struct nlattr *)); + + nla_for_each_attr(pos, head, len, rem) { + int type = nla_type(pos); + + if ((type <= maxtype) && (type != 0)) + tb[type] = pos; + } + + return 0; +} + + +/* Create attribute index based on nested attribute. */ +int nla_parse_nested(struct nlattr *tb[], int maxtype, + struct nlattr *nla, struct nla_policy *policy) +{ + return nla_parse(tb, maxtype, nla_data(nla), nla_len(nla), policy); +} + + +/* Add a unspecific attribute to netlink message. */ +int nla_put(struct nl_msg *msg, int attrtype, int datalen, const void *data) +{ + struct nlattr *nla; + + /* Reserve space and init nla header */ + nla = nla_reserve(msg, attrtype, datalen); + if (nla) { + memcpy(nla_data(nla), data, datalen); + return 0; + } + + return -EINVAL; + +} + + +/* Add nested attributes to netlink message. */ +/* Takes the attributes found in the nested message and appends them + * to the message msg nested in a container of the type attrtype. The + * nested message may not have a family specific header */ +int nla_put_nested(struct nl_msg *msg, int attrtype, struct nl_msg *nested) +{ + int rc; + + rc = nla_put(msg, attrtype, nlmsg_attrlen(nlmsg_hdr(nested), 0), + nlmsg_attrdata(nlmsg_hdr(nested), 0)); + return rc; + +} + +/* Return type of the attribute. */ +int nla_type(const struct nlattr *nla) +{ + return (int)nla->nla_type & NLA_TYPE_MASK; +} + +/* Reserves room for an attribute in specified netlink message and fills + * in the attribute header (type,length). Return NULL if insufficient space */ +struct nlattr *nla_reserve(struct nl_msg *msg, int attrtype, int data_len) +{ + + struct nlattr *nla; + const unsigned int NEW_SIZE = NLMSG_ALIGN(msg->nm_nlh->nlmsg_len) + + NLA_ALIGN(NLA_HDRLEN + data_len); + + /* Check enough space for attribute */ + if (NEW_SIZE > msg->nm_size) + return NULL; + + nla = (struct nlattr *)nlmsg_tail(msg->nm_nlh); + nla->nla_type = attrtype; + nla->nla_len = NLA_HDRLEN + data_len; + memset((unsigned char *)nla + nla->nla_len, 0, nla_padlen(data_len)); + msg->nm_nlh->nlmsg_len = NEW_SIZE; + return nla; +} + +/* Copy attribute payload to another memory area. */ +int nla_memcpy(void *dest, struct nlattr *src, int count) +{ + if (!src || !dest) + return 0; + if (count > nla_len(src)) + count = nla_len(src); + memcpy(dest, nla_data(src), count); + return count; +} diff --git a/libnl_2/cache.c b/libnl_2/cache.c new file mode 100644 index 0000000..c21974d --- /dev/null +++ b/libnl_2/cache.c @@ -0,0 +1,37 @@ +/* + * 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. + */ + +/* NOTICE: This is a clean room re-implementation of libnl */ + +#include "netlink/cache.h" +#include "netlink/object.h" + +void nl_cache_free(struct nl_cache *cache) +{ + +} + +void nl_cache_clear(struct nl_cache *cache) +{ + +} + +void nl_cache_remove(struct nl_object *obj) +{ + +} + + diff --git a/libnl_2/dbg.c b/libnl_2/dbg.c new file mode 100644 index 0000000..9764de6 --- /dev/null +++ b/libnl_2/dbg.c @@ -0,0 +1,12 @@ +#include "netlink/netlink.h" +#include <android/log.h> + +void libnl_printf(int level, char *format, ...) +{ + va_list ap; + + level = ANDROID_LOG_ERROR; + va_start(ap, format); + __android_log_vprint(level, "libnl_2", format, ap); + va_end(ap); +} diff --git a/libnl_2/genl/family.c b/libnl_2/genl/family.c new file mode 100644 index 0000000..1beee6e --- /dev/null +++ b/libnl_2/genl/family.c @@ -0,0 +1,44 @@ +/* + * 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. + */ + +/* NOTICE: This is a clean room re-implementation of libnl */ + +#include "netlink-types.h" + +static struct genl_family *genl_family_find_byname(const char *name) +{ + return NULL; +} + +/* Release reference and none outstanding */ +void genl_family_put(struct genl_family *family) +{ + family->ce_refcnt--; + if (family->ce_refcnt <= 0) + free(family); +} + +unsigned int genl_family_get_id(struct genl_family *family) +{ + const int NO_FAMILY_ID = 0; + + if (!family) + return NO_FAMILY_ID; + else + return family->gf_id; + +} + diff --git a/libnl_2/genl/genl.c b/libnl_2/genl/genl.c new file mode 100644 index 0000000..2442993 --- /dev/null +++ b/libnl_2/genl/genl.c @@ -0,0 +1,286 @@ +/* + * 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. + */ + +/* NOTICE: This is a clean room re-implementation of libnl */ + +#include <errno.h> +#include <unistd.h> +#include <stdio.h> +#include <sys/time.h> +#include <linux/netlink.h> +#include "netlink-types.h" + +/* Get head of attribute data. */ +struct nlattr *genlmsg_attrdata(const struct genlmsghdr *gnlh, int hdrlen) +{ + return (struct nlattr *) \ + ((char *) gnlh + GENL_HDRLEN + NLMSG_ALIGN(hdrlen)); + +} + +/* Get length of attribute data. */ +int genlmsg_attrlen(const struct genlmsghdr *gnlh, int hdrlen) +{ + struct nlattr *nla; + struct nlmsghdr *nlh; + + nla = genlmsg_attrdata(gnlh, hdrlen); + nlh = (struct nlmsghdr *) ((char *) gnlh - NLMSG_HDRLEN); + return (char *) nlmsg_tail(nlh) - (char *) nla; +} + +/* Add generic netlink header to netlink message. */ +void *genlmsg_put(struct nl_msg *msg, uint32_t pid, uint32_t seq, int family, + int hdrlen, int flags, uint8_t cmd, uint8_t version) +{ + int new_size; + struct nlmsghdr *nlh; + struct timeval tv; + struct genlmsghdr *gmh; + + /* Make sure nl_msg has enough space */ + new_size = NLMSG_HDRLEN + GENL_HDRLEN + hdrlen; + if ((sizeof(struct nl_msg) + new_size) > msg->nm_size) + goto fail; + + /* Fill in netlink header */ + nlh = msg->nm_nlh; + nlh->nlmsg_len = new_size; + nlh->nlmsg_type = family; + nlh->nlmsg_pid = getpid(); + nlh->nlmsg_flags = flags | NLM_F_REQUEST | NLM_F_ACK; + + /* Get current time for sequence number */ + if (gettimeofday(&tv, NULL)) + nlh->nlmsg_seq = 1; + else + nlh->nlmsg_seq = (int) tv.tv_sec; + + /* Setup genlmsghdr in new message */ + gmh = (struct genlmsghdr *) ((char *)nlh + NLMSG_HDRLEN); + gmh->cmd = (__u8) cmd; + gmh->version = version; + + return gmh; +fail: + return NULL; + +} + +/* Socket has already been alloced to connect it to kernel? */ +int genl_connect(struct nl_sock *sk) +{ + return nl_connect(sk, NETLINK_GENERIC); + +} + +int genl_ctrl_alloc_cache(struct nl_sock *sock, struct nl_cache **result) +{ + int rc = -1; + int nl80211_genl_id = -1; + char sendbuf[sizeof(struct nlmsghdr)+sizeof(struct genlmsghdr)]; + struct nlmsghdr nlmhdr; + struct genlmsghdr gmhhdr; + struct iovec sendmsg_iov; + struct msghdr msg; + int num_char; + const int RECV_BUF_SIZE = getpagesize(); + char *recvbuf; + struct iovec recvmsg_iov; + int nl80211_flag = 0, nlm_f_multi = 0, nlmsg_done = 0; + struct nlmsghdr *nlh; + + /* REQUEST GENERIC NETLINK FAMILY ID */ + /* Message buffer */ + nlmhdr.nlmsg_len = sizeof(sendbuf); + nlmhdr.nlmsg_type = NETLINK_GENERIC; + nlmhdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP; + nlmhdr.nlmsg_seq = sock->s_seq_next; + nlmhdr.nlmsg_pid = sock->s_local.nl_pid; + + /* Generic netlink header */ + memset(&gmhhdr, 0, sizeof(gmhhdr)); + gmhhdr.cmd = CTRL_CMD_GETFAMILY; + gmhhdr.version = CTRL_ATTR_FAMILY_ID; + + /* Combine netlink and generic netlink headers */ + memcpy(&sendbuf[0], &nlmhdr, sizeof(nlmhdr)); + memcpy(&sendbuf[0]+sizeof(nlmhdr), &gmhhdr, sizeof(gmhhdr)); + + /* Create IO vector with Netlink message */ + sendmsg_iov.iov_base = &sendbuf; + sendmsg_iov.iov_len = sizeof(sendbuf); + + /* Socket message */ + msg.msg_name = (void *) &sock->s_peer; + msg.msg_namelen = sizeof(sock->s_peer); + msg.msg_iov = &sendmsg_iov; + msg.msg_iovlen = 1; /* Only sending one iov */ + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + /* Send message and verify sent */ + num_char = sendmsg(sock->s_fd, &msg, 0); + if (num_char == -1) + return -errno; + + /* RECEIVE GENL CMD RESPONSE */ + + /* Create receive iov buffer */ + recvbuf = (char *) malloc(RECV_BUF_SIZE); + + /* Attach to iov */ + recvmsg_iov.iov_base = recvbuf; + recvmsg_iov.iov_len = RECV_BUF_SIZE; + + msg.msg_iov = &recvmsg_iov; + msg.msg_iovlen = 1; + + /***************************************************************/ + /* Receive message. If multipart message, keep receiving until */ + /* message type is NLMSG_DONE */ + /***************************************************************/ + + do { + + int recvmsg_len, nlmsg_rem; + + /* Receive message */ + memset(recvbuf, 0, RECV_BUF_SIZE); + recvmsg_len = recvmsg(sock->s_fd, &msg, 0); + + /* Make sure receive successful */ + if (recvmsg_len < 0) { + rc = -errno; + goto error_recvbuf; + } + + /* Parse nlmsghdr */ + nlmsg_for_each_msg(nlh, (struct nlmsghdr *) recvbuf, \ + recvmsg_len, nlmsg_rem) { + struct nlattr *nla; + int nla_rem; + + /* Check type */ + switch (nlh->nlmsg_type) { + case NLMSG_DONE: + goto return_genl_id; + break; + case NLMSG_ERROR: + + /* Should check nlmsgerr struct received */ + fprintf(stderr, "Receive message error\n"); + goto error_recvbuf; + case NLMSG_OVERRUN: + fprintf(stderr, "Receive data partly lost\n"); + goto error_recvbuf; + case NLMSG_MIN_TYPE: + case NLMSG_NOOP: + break; + default: + break; + } + + + + /* Check flags */ + if (nlh->nlmsg_flags & NLM_F_MULTI) + nlm_f_multi = 1; + else + nlm_f_multi = 0; + + if (nlh->nlmsg_type & NLMSG_DONE) + nlmsg_done = 1; + else + nlmsg_done = 0; + + /* Iteratve over attributes */ + nla_for_each_attr(nla, + nlmsg_attrdata(nlh, GENL_HDRLEN), + nlmsg_attrlen(nlh, GENL_HDRLEN), + nla_rem){ + + /* If this family is nl80211 */ + if (nla->nla_type == CTRL_ATTR_FAMILY_NAME && + !strcmp((char *)nla_data(nla), + "nl80211")) + nl80211_flag = 1; + + /* Save the family id */ + else if (nl80211_flag && + nla->nla_type == CTRL_ATTR_FAMILY_ID) { + nl80211_genl_id = + *((int *)nla_data(nla)); + nl80211_flag = 0; + } + + } + + } + + } while (nlm_f_multi && !nlmsg_done); + +return_genl_id: + /* Return family id as cache pointer */ + *result = (struct nl_cache *) nl80211_genl_id; + rc = 0; +error_recvbuf: + free(recvbuf); +error: + return rc; +} + +/* Checks the netlink cache to find family reference by name string */ +/* NOTE: Caller needs to call genl_family_put() when done with * + * returned object */ +struct genl_family *genl_ctrl_search_by_name(struct nl_cache *cache, \ + const char *name) +{ + /* TODO: When will we release this memory ? */ + struct genl_family *gf = (struct genl_family *) \ + malloc(sizeof(struct genl_family)); + if (!gf) + goto fail; + memset(gf, 0, sizeof(*gf)); + + /* Add ref */ + gf->ce_refcnt++; + + /* Overriding cache pointer as family id for now */ + gf->gf_id = (uint16_t) ((uint32_t) cache); + strcpy(gf->gf_name, "nl80211"); + + return gf; +fail: + return NULL; + +} + +int genl_ctrl_resolve(struct nl_sock *sk, const char *name) +{ + /* Hack to support wpa_supplicant */ + if (strcmp(name, "nlctrl") == 0) + return NETLINK_GENERIC; + else { + int errsv = errno; + fprintf(stderr, \ + "Only nlctrl supported by genl_ctrl_resolve!\n"); + return -errsv; + } + +} + diff --git a/libnl_2/handlers.c b/libnl_2/handlers.c new file mode 100644 index 0000000..ec8d512 --- /dev/null +++ b/libnl_2/handlers.c @@ -0,0 +1,92 @@ +/* + * 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. + */ + +/* NOTICE: This is a clean room re-implementation of libnl */ + +#include <malloc.h> +#include "netlink-types.h" +#include "netlink/handlers.h" + +/* Allocate a new callback handle. */ +struct nl_cb *nl_cb_alloc(enum nl_cb_kind kind) +{ + struct nl_cb *cb; + + cb = (struct nl_cb *) malloc(sizeof(struct nl_cb)); + if (cb == NULL) + goto fail; + memset(cb, 0, sizeof(*cb)); + + return nl_cb_get(cb); +fail: + return NULL; +} + +/* Clone an existing callback handle */ +struct nl_cb *nl_cb_clone(struct nl_cb *orig) +{ + struct nl_cb *new_cb; + int new_refcnt; + + new_cb = nl_cb_alloc(NL_CB_DEFAULT); + if (new_cb == NULL) + goto fail; + + /* Preserve reference count and copy original */ + new_refcnt = new_cb->cb_refcnt; + memcpy(new_cb, orig, sizeof(*orig)); + new_cb->cb_refcnt = new_refcnt; + + return new_cb; +fail: + return NULL; +} + +/* Set up a callback. */ +int nl_cb_set(struct nl_cb *cb, enum nl_cb_type type, enum nl_cb_kind kind, \ + nl_recvmsg_msg_cb_t func, void *arg) +{ + cb->cb_set[type] = func; + cb->cb_args[type] = arg; + return 0; +} + + + +/* Set up an error callback. */ +int nl_cb_err(struct nl_cb *cb, enum nl_cb_kind kind, \ + nl_recvmsg_err_cb_t func, void *arg) +{ + cb->cb_err = func; + cb->cb_err_arg = arg; + return 0; + +} + +struct nl_cb *nl_cb_get(struct nl_cb *cb) +{ + cb->cb_refcnt++; + return cb; +} + +void nl_cb_put(struct nl_cb *cb) +{ + cb->cb_refcnt--; + if (cb->cb_refcnt <= 0) + free(cb); + +} + diff --git a/libnl_2/msg.c b/libnl_2/msg.c new file mode 100644 index 0000000..283da6e --- /dev/null +++ b/libnl_2/msg.c @@ -0,0 +1,149 @@ +/* + * 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. + */ + +/* NOTICE: This is a clean room re-implementation of libnl */ + +#include <malloc.h> +#include <unistd.h> +#include <linux/netlink.h> +#include "netlink-types.h" + +/* Allocate a new netlink message with the default maximum payload size. */ +struct nl_msg *nlmsg_alloc(void) +{ + /* Whole page will store nl_msg + nlmsghdr + genlmsghdr + payload */ + const int page_sz = getpagesize(); + struct nl_msg *nm; + struct nlmsghdr *nlh; + + /* Netlink message */ + nm = (struct nl_msg *) malloc(page_sz); + if (!nm) + goto fail; + + /* Netlink message header pointer */ + nlh = (struct nlmsghdr *) ((char *) nm + sizeof(struct nl_msg)); + + /* Initialize */ + memset(nm, 0, page_sz); + nm->nm_size = page_sz; + + nm->nm_src.nl_family = AF_NETLINK; + nm->nm_src.nl_pid = getpid(); + + nm->nm_dst.nl_family = AF_NETLINK; + nm->nm_dst.nl_pid = 0; /* Kernel */ + + /* Initialize and add to netlink message */ + nlh->nlmsg_len = NLMSG_HDRLEN; + nm->nm_nlh = nlh; + + /* Add to reference count and return nl_msg */ + nlmsg_get(nm); + return nm; +fail: + return NULL; +} + +/* Return pointer to message payload. */ +void *nlmsg_data(const struct nlmsghdr *nlh) +{ + return (char *) nlh + NLMSG_HDRLEN; +} + +/* Add reference count to nl_msg */ +void nlmsg_get(struct nl_msg *nm) +{ + nm->nm_refcnt++; +} + +/* Release a reference from an netlink message. */ +void nlmsg_free(struct nl_msg *nm) +{ + if (nm) { + nm->nm_refcnt--; + if (nm->nm_refcnt <= 0) + free(nm); + } + +} + +/* Return actual netlink message. */ +struct nlmsghdr *nlmsg_hdr(struct nl_msg *n) +{ + return n->nm_nlh; +} + +/* Return head of attributes data / payload section */ +struct nlattr *nlmsg_attrdata(const struct nlmsghdr *nlh, int hdrlen) +{ + unsigned char *data = nlmsg_data(nlh); + return (struct nlattr *)(data + NLMSG_ALIGN(hdrlen)); +} + +/* Returns pointer to end of netlink message */ +void *nlmsg_tail(const struct nlmsghdr *nlh) +{ + return (void *)((char *)nlh + NLMSG_ALIGN(nlh->nlmsg_len)); +} + +/* Next netlink message in message stream */ +struct nlmsghdr *nlmsg_next(struct nlmsghdr *nlh, int *remaining) +{ + struct nlmsghdr *next_nlh = NULL; + int len = nlmsg_len(nlh); + + len = NLMSG_ALIGN(len); + if (*remaining > 0 && + len <= *remaining && + len >= (int) sizeof(struct nlmsghdr)) { + next_nlh = (struct nlmsghdr *)((char *)nlh + len); + *remaining -= len; + } + + return next_nlh; +} + +int nlmsg_datalen(const struct nlmsghdr *nlh) +{ + return nlh->nlmsg_len - NLMSG_HDRLEN; +} + +/* Length of attributes data */ +int nlmsg_attrlen(const struct nlmsghdr *nlh, int hdrlen) +{ + return nlmsg_datalen(nlh) - NLMSG_ALIGN(hdrlen); +} + +/* Length of netlink message */ +int nlmsg_len(const struct nlmsghdr *nlh) +{ + return nlh->nlmsg_len; +} + +/* Check if the netlink message fits into the remaining bytes */ +int nlmsg_ok(const struct nlmsghdr *nlh, int rem) +{ + return rem >= (int)sizeof(struct nlmsghdr) && + rem >= nlmsg_len(nlh) && + nlmsg_len(nlh) >= (int) sizeof(struct nlmsghdr) && + nlmsg_len(nlh) <= (rem); +} + +int nlmsg_padlen(int payload) +{ + return NLMSG_ALIGN(payload) - payload; +} diff --git a/libnl_2/netlink.c b/libnl_2/netlink.c new file mode 100644 index 0000000..cc2f88e --- /dev/null +++ b/libnl_2/netlink.c @@ -0,0 +1,273 @@ +/* + * 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. + */ + +/* NOTICE: This is a clean room re-implementation of libnl */ + +#include <errno.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/socket.h> +#include "netlink-types.h" + +#define NL_BUFFER_SZ (32768U) + +/* Checks message for completeness and sends it out */ +int nl_send_auto_complete(struct nl_sock *sk, struct nl_msg *msg) +{ + struct nlmsghdr *nlh = msg->nm_nlh; + struct timeval tv; + + if (!nlh) { + int errsv = errno; + fprintf(stderr, "Netlink message header is NULL!\n"); + return -errsv; + } + + /* Complete the nl_msg header */ + if (gettimeofday(&tv, NULL)) + nlh->nlmsg_seq = 1; + else + nlh->nlmsg_seq = (int) tv.tv_sec; + nlh->nlmsg_pid = sk->s_local.nl_pid; + nlh->nlmsg_flags |= NLM_F_REQUEST | NLM_F_ACK; + + return nl_send(sk, msg); +} + +/* Receives a netlink message, allocates a buffer in *buf and stores + * the message content. The peer's netlink address is stored in + * *nla. The caller is responsible for freeing the buffer allocated in + * *buf if a positive value is returned. Interrupted system calls are + * handled by repeating the read. The input buffer size is determined + * by peeking before the actual read is done */ +int nl_recv(struct nl_sock *sk, struct sockaddr_nl *nla, \ + unsigned char **buf, struct ucred **creds) +{ + int rc = -1; + int sk_flags; + int RECV_BUF_SIZE; + int errsv; + struct iovec recvmsg_iov; + struct msghdr msg; + + /* Allocate buffer */ + RECV_BUF_SIZE = getpagesize(); + *buf = (unsigned char *) malloc(RECV_BUF_SIZE); + if (!buf) { + rc = -ENOMEM; + goto fail; + } + + /* Prepare to receive message */ + recvmsg_iov.iov_base = *buf; + recvmsg_iov.iov_len = RECV_BUF_SIZE; + + msg.msg_name = (void *) &sk->s_peer; + msg.msg_namelen = sizeof(sk->s_peer); + msg.msg_iov = &recvmsg_iov; + msg.msg_iovlen = 1; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + /* Make non blocking and then restore previous setting */ + sk_flags = fcntl(sk->s_fd, F_GETFL, 0); + fcntl(sk->s_fd, F_SETFL, O_NONBLOCK); + rc = recvmsg(sk->s_fd, &msg, 0); + errsv = errno; + fcntl(sk->s_fd, F_SETFL, sk_flags); + + if (rc < 0) + rc = -errsv; + +fail: + return rc; +} + +/* Receive a set of messages from a netlink socket */ +/* NOTE: Does not currently support callback replacements!!! */ +int nl_recvmsgs(struct nl_sock *sk, struct nl_cb *cb) +{ + struct sockaddr_nl nla; + struct ucred *creds; + + int rc, cb_rc = NL_OK, done = 0; + + do { + + unsigned char *buf; + int i, rem, flags; + struct nlmsghdr *nlh; + struct nlmsgerr *nlme; + struct nl_msg *msg; + + done = 0; + rc = nl_recv(sk, &nla, &buf, &creds); + if (rc < 0) + break; + + nlmsg_for_each_msg(nlh, (struct nlmsghdr *) buf, rc, rem) { + + if (rc <= 0 || cb_rc == NL_STOP) + break; + + /* Check for callbacks */ + + msg = (struct nl_msg *)malloc(sizeof(struct nl_msg)); + memset(msg, 0, sizeof(*msg)); + msg->nm_nlh = nlh; + + /* Check netlink message type */ + + switch (msg->nm_nlh->nlmsg_type) { + case NLMSG_ERROR: /* Used for ACK too */ + /* Certainly we should be doing some + * checking here to make sure this + * message is intended for us */ + nlme = nlmsg_data(msg->nm_nlh); + if (nlme->error == 0) + msg->nm_nlh->nlmsg_flags |= NLM_F_ACK; + + rc = nlme->error; + cb_rc = cb->cb_err(&nla, nlme, cb->cb_err_arg); + nlme = NULL; + break; + + case NLMSG_DONE: + done = 1; + + case NLMSG_OVERRUN: + case NLMSG_NOOP: + default: + break; + }; + + for (i = 0; i <= NL_CB_TYPE_MAX; i++) { + + if (cb->cb_set[i]) { + switch (i) { + case NL_CB_VALID: + if (rc > 0) + cb_rc = cb->cb_set[i](msg, cb->cb_args[i]); + break; + + case NL_CB_FINISH: + if ((msg->nm_nlh->nlmsg_flags & NLM_F_MULTI) && + (msg->nm_nlh->nlmsg_type & NLMSG_DONE)) + cb_rc = cb->cb_set[i](msg, cb->cb_args[i]); + + break; + + case NL_CB_ACK: + if (msg->nm_nlh->nlmsg_flags & NLM_F_ACK) + cb_rc = cb->cb_set[i](msg, cb->cb_args[i]); + + break; + default: + break; + } + } + } + + free(msg); + if (done) + break; + } + + free(buf); + buf = NULL; + + if (done) + break; + } while (rc > 0 && cb_rc != NL_STOP); + +success: +fail: + return rc; +} + +/* Send raw data over netlink socket */ +int nl_send(struct nl_sock *sk, struct nl_msg *msg) +{ + struct nlmsghdr *nlh = nlmsg_hdr(msg); + struct iovec msg_iov; + + /* Create IO vector with Netlink message */ + msg_iov.iov_base = nlh; + msg_iov.iov_len = nlh->nlmsg_len; + + return nl_send_iovec(sk, msg, &msg_iov, 1); +} + +/* Send netlink message */ +int nl_send_iovec(struct nl_sock *sk, struct nl_msg *msg, + struct iovec *iov, unsigned iovlen) +{ + int rc; + + /* Socket message */ + struct msghdr mh = { + .msg_name = (void *) &sk->s_peer, + .msg_namelen = sizeof(sk->s_peer), + .msg_iov = iov, + .msg_iovlen = iovlen, + .msg_control = NULL, + .msg_controllen = 0, + .msg_flags = 0 + }; + + /* Send message and verify sent */ + rc = nl_sendmsg(sk, (struct nl_msg *) &mh, 0); + if (rc < 0) + fprintf(stderr, "Error sending netlink message: %d\n", errno); + return rc; + +} + +/* Send netlink message with control over sendmsg() message header */ +int nl_sendmsg(struct nl_sock *sk, struct nl_msg *msg, struct msghdr *hdr) +{ + return sendmsg(sk->s_fd, (struct msghdr *) msg, (int) hdr); +} + +/* Create and connect netlink socket */ +int nl_connect(struct nl_sock *sk, int protocol) +{ + struct sockaddr addr; + socklen_t addrlen; + int rc; + + /* Create RX socket */ + sk->s_fd = socket(PF_NETLINK, SOCK_RAW, protocol); + if (sk->s_fd < 0) + return -errno; + + /* Set size of RX and TX buffers */ + if (nl_socket_set_buffer_size(sk, NL_BUFFER_SZ, NL_BUFFER_SZ) < 0) + return -errno; + + /* Bind RX socket */ + rc = bind(sk->s_fd, (struct sockaddr *)&sk->s_local, \ + sizeof(sk->s_local)); + if (rc < 0) + return -errno; + addrlen = sizeof(addr); + getsockname(sk->s_fd, &addr, &addrlen); + + return 0; + +} diff --git a/libnl_2/object.c b/libnl_2/object.c new file mode 100644 index 0000000..c53accf --- /dev/null +++ b/libnl_2/object.c @@ -0,0 +1,33 @@ +/* + * 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. + */ + +/* NOTICE: This is a clean room re-implementation of libnl */ + +#include "netlink-types.h" + +void nl_object_put(struct nl_object *obj) +{ + obj->ce_refcnt--; + if (!obj->ce_refcnt) + nl_object_free(obj); +} + +void nl_object_free(struct nl_object *obj) +{ + nl_cache_remove(obj); +} + + diff --git a/libnl_2/socket.c b/libnl_2/socket.c new file mode 100644 index 0000000..ce54f19 --- /dev/null +++ b/libnl_2/socket.c @@ -0,0 +1,120 @@ +/* + * 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. + */ + +/* NOTICE: This is a clean room re-implementation of libnl */ + +#include <errno.h> +#include <unistd.h> +#include <malloc.h> +#include <sys/time.h> +#include <sys/socket.h> +#include "netlink-types.h" + +/* Join group */ +int nl_socket_add_membership(struct nl_sock *sk, int group) +{ + return setsockopt(sk->s_fd, SOL_NETLINK, + NETLINK_ADD_MEMBERSHIP, &group, sizeof(group)); +} + +/* Allocate new netlink socket. */ +struct nl_sock *nl_socket_alloc(void) +{ + struct nl_sock *sk; + struct timeval tv; + struct nl_cb *cb; + + sk = (struct nl_sock *) malloc(sizeof(struct nl_sock)); + if (!sk) + goto fail; + memset(sk, 0, sizeof(*sk)); + + /* Get current time */ + + if (gettimeofday(&tv, NULL)) + return NULL; + else + sk->s_seq_next = (int) tv.tv_sec; + + /* Create local socket */ + sk->s_local.nl_family = AF_NETLINK; + sk->s_local.nl_pid = 0; /* Kernel fills in pid */ + sk->s_local.nl_groups = 0; /* No groups */ + + /* Create peer socket */ + sk->s_peer.nl_family = AF_NETLINK; + sk->s_peer.nl_pid = 0; /* Kernel */ + sk->s_peer.nl_groups = 0; /* No groups */ + + cb = (struct nl_cb *) malloc(sizeof(struct nl_cb)); + if (!cb) + goto cb_fail; + memset(cb, 0, sizeof(*cb)); + sk->s_cb = nl_cb_alloc(NL_CB_DEFAULT); + + + return sk; +cb_fail: + free(sk); +fail: + return NULL; +} + +/* Allocate new socket with custom callbacks. */ +struct nl_sock *nl_socket_alloc_cb(struct nl_cb *cb) +{ + struct nl_sock *sk = nl_socket_alloc(); + if (!sk) + return NULL; + + sk->s_cb = cb; + nl_cb_get(cb); + + return sk; + +} + +/* Free a netlink socket. */ +void nl_socket_free(struct nl_sock *sk) +{ + nl_cb_put(sk->s_cb); + close(sk->s_fd); + free(sk); +} + +/* Sets socket buffer size of netlink socket */ +int nl_socket_set_buffer_size(struct nl_sock *sk, int rxbuf, int txbuf) +{ + if (setsockopt(sk->s_fd, SOL_SOCKET, SO_SNDBUF, \ + &rxbuf, (socklen_t) sizeof(rxbuf))) + goto error; + + if (setsockopt(sk->s_fd, SOL_SOCKET, SO_RCVBUF, \ + &txbuf, (socklen_t) sizeof(txbuf))) + goto error; + + return 0; +error: + return -errno; + +} + +int nl_socket_get_fd(struct nl_sock *sk) +{ + return sk->s_fd; +} + + diff --git a/libpixelflinger/scanline.cpp b/libpixelflinger/scanline.cpp index 8fba147..a37b47e 100644 --- a/libpixelflinger/scanline.cpp +++ b/libpixelflinger/scanline.cpp @@ -350,7 +350,7 @@ static void pick_scanline(context_t* c) } } -#ifdef DEBUG_NEEDS +#if DEBUG_NEEDS LOGI("Needs: n=0x%08x p=0x%08x t0=0x%08x t1=0x%08x", c->state.needs.n, c->state.needs.p, c->state.needs.t[0], c->state.needs.t[1]); diff --git a/libsysutils/Android.mk b/libsysutils/Android.mk index 3b1f618..cccf484 100644 --- a/libsysutils/Android.mk +++ b/libsysutils/Android.mk @@ -1,10 +1,4 @@ ifneq ($(BUILD_TINY_ANDROID),true) -BUILD_LIBSYSUTILS := false -ifneq ($(TARGET_SIMULATOR),true) - BUILD_LIBSYSUTILS := true -endif - -ifeq ($(BUILD_LIBSYSUTILS),true) LOCAL_PATH:= $(call my-dir) @@ -27,11 +21,6 @@ LOCAL_CFLAGS := LOCAL_SHARED_LIBRARIES := libcutils -ifeq ($(TARGET_SIMULATOR),true) - LOCAL_LDLIBS += -lpthread -endif - include $(BUILD_SHARED_LIBRARY) endif -endif diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp index c8d3b1f..fe96976 100644 --- a/libsysutils/src/NetlinkEvent.cpp +++ b/libsysutils/src/NetlinkEvent.cpp @@ -21,10 +21,23 @@ #include <sysutils/NetlinkEvent.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <linux/if.h> +#include <linux/netfilter/nfnetlink.h> +#include <linux/netfilter_ipv4/ipt_ULOG.h> +/* From kernel's net/netfilter/xt_quota2.c */ +const int QLOG_NL_EVENT = 112; + +#include <linux/netlink.h> +#include <linux/rtnetlink.h> + const int NetlinkEvent::NlActionUnknown = 0; const int NetlinkEvent::NlActionAdd = 1; const int NetlinkEvent::NlActionRemove = 2; const int NetlinkEvent::NlActionChange = 3; +const int NetlinkEvent::NlActionLinkUp = 4; +const int NetlinkEvent::NlActionLinkDown = 5; NetlinkEvent::NetlinkEvent() { mAction = NlActionUnknown; @@ -56,6 +69,74 @@ void NetlinkEvent::dump() { } } +/* + * Parse an binary message from a NETLINK_ROUTE netlink socket. + */ +bool NetlinkEvent::parseBinaryNetlinkMessage(char *buffer, int size) { + size_t sz = size; + const struct nlmsghdr *nh = (struct nlmsghdr *) buffer; + + while (NLMSG_OK(nh, sz) && (nh->nlmsg_type != NLMSG_DONE)) { + + if (nh->nlmsg_type == RTM_NEWLINK) { + int len = nh->nlmsg_len - sizeof(*nh); + struct ifinfomsg *ifi; + + if (sizeof(*ifi) > (size_t) len) { + SLOGE("Got a short RTM_NEWLINK message\n"); + continue; + } + + ifi = (ifinfomsg *)NLMSG_DATA(nh); + if ((ifi->ifi_flags & IFF_LOOPBACK) != 0) { + continue; + } + + struct rtattr *rta = (struct rtattr *) + ((char *) ifi + NLMSG_ALIGN(sizeof(*ifi))); + len = NLMSG_PAYLOAD(nh, sizeof(*ifi)); + + while(RTA_OK(rta, len)) { + switch(rta->rta_type) { + case IFLA_IFNAME: + char buffer[16 + IFNAMSIZ]; + snprintf(buffer, sizeof(buffer), "INTERFACE=%s", + (char *) RTA_DATA(rta)); + mParams[0] = strdup(buffer); + mAction = (ifi->ifi_flags & IFF_LOWER_UP) ? + NlActionLinkUp : NlActionLinkDown; + mSubsystem = strdup("net"); + break; + } + + rta = RTA_NEXT(rta, len); + } + + } else if (nh->nlmsg_type == QLOG_NL_EVENT) { + char *devname; + ulog_packet_msg_t *pm; + size_t len = nh->nlmsg_len - sizeof(*nh); + if (sizeof(*pm) > len) { + SLOGE("Got a short QLOG message\n"); + continue; + } + pm = (ulog_packet_msg_t *)NLMSG_DATA(nh); + devname = pm->indev_name[0] ? pm->indev_name : pm->outdev_name; + SLOGD("QLOG prefix=%s dev=%s\n", pm->prefix, devname); + asprintf(&mParams[0], "ALERT_NAME=%s", pm->prefix); + asprintf(&mParams[1], "INTERFACE=%s", devname); + mSubsystem = strdup("qlog"); + mAction = NlActionChange; + + } else { + SLOGD("Unexpected netlink message. type=0x%x\n", nh->nlmsg_type); + } + nh = NLMSG_NEXT(nh, size); + } + + return true; +} + /* If the string between 'str' and 'end' begins with 'prefixlen' characters * from the 'prefix' array, then return 'str + prefixlen', otherwise return * NULL. @@ -76,7 +157,11 @@ has_prefix(const char* str, const char* end, const char* prefix, size_t prefixle #define HAS_CONST_PREFIX(str,end,prefix) has_prefix((str),(end),prefix,CONST_STRLEN(prefix)) -bool NetlinkEvent::decode(char *buffer, int size) { +/* + * Parse an ASCII-formatted message from a NETLINK_KOBJECT_UEVENT + * netlink socket. + */ +bool NetlinkEvent::parseAsciiNetlinkMessage(char *buffer, int size) { const char *s = buffer; const char *end; int param_idx = 0; @@ -123,6 +208,14 @@ bool NetlinkEvent::decode(char *buffer, int size) { return true; } +bool NetlinkEvent::decode(char *buffer, int size, int format) { + if (format == NetlinkListener::NETLINK_FORMAT_BINARY) { + return parseBinaryNetlinkMessage(buffer, size); + } else { + return parseAsciiNetlinkMessage(buffer, size); + } +} + const char *NetlinkEvent::findParam(const char *paramName) { size_t len = strlen(paramName); for (int i = 0; i < NL_PARAMS_MAX && mParams[i] != NULL; ++i) { diff --git a/libsysutils/src/NetlinkListener.cpp b/libsysutils/src/NetlinkListener.cpp index ddf6537..e67b5c6 100644 --- a/libsysutils/src/NetlinkListener.cpp +++ b/libsysutils/src/NetlinkListener.cpp @@ -22,55 +22,43 @@ #define LOG_TAG "NetlinkListener" #include <cutils/log.h> +#include <cutils/uevent.h> -#include <sysutils/NetlinkListener.h> #include <sysutils/NetlinkEvent.h> +#if 1 +/* temporary version until we can get Motorola to update their + * ril.so. Their prebuilt ril.so is using this private class + * so changing the NetlinkListener() constructor breaks their ril. + */ NetlinkListener::NetlinkListener(int socket) : SocketListener(socket, false) { + mFormat = NETLINK_FORMAT_ASCII; +} +#endif + +NetlinkListener::NetlinkListener(int socket, int format) : + SocketListener(socket, false), mFormat(format) { } bool NetlinkListener::onDataAvailable(SocketClient *cli) { int socket = cli->getSocket(); ssize_t count; - char cred_msg[CMSG_SPACE(sizeof(struct ucred))]; - struct sockaddr_nl snl; - struct iovec iov = {mBuffer, sizeof(mBuffer)}; - struct msghdr hdr = {&snl, sizeof(snl), &iov, 1, cred_msg, sizeof(cred_msg), 0}; - count = TEMP_FAILURE_RETRY(recvmsg(socket, &hdr, 0)); + count = TEMP_FAILURE_RETRY(uevent_kernel_multicast_recv(socket, mBuffer, sizeof(mBuffer))); if (count < 0) { SLOGE("recvmsg failed (%s)", strerror(errno)); return false; } - if ((snl.nl_groups != 1) || (snl.nl_pid != 0)) { - SLOGE("ignoring non-kernel netlink multicast message"); - return false; - } - - struct cmsghdr * cmsg = CMSG_FIRSTHDR(&hdr); - - if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) { - SLOGE("ignoring message with no sender credentials"); - return false; - } - - struct ucred * cred = (struct ucred *)CMSG_DATA(cmsg); - if (cred->uid != 0) { - SLOGE("ignoring message from non-root UID %d", cred->uid); - return false; - } - NetlinkEvent *evt = new NetlinkEvent(); - if (!evt->decode(mBuffer, count)) { + if (!evt->decode(mBuffer, count, mFormat)) { SLOGE("Error decoding NetlinkEvent"); - goto out; + } else { + onEvent(evt); } - onEvent(evt); -out: delete evt; return true; } diff --git a/libsysutils/src/SocketClient.cpp b/libsysutils/src/SocketClient.cpp index 90ca52e..722dcb2 100644 --- a/libsysutils/src/SocketClient.cpp +++ b/libsysutils/src/SocketClient.cpp @@ -10,8 +10,9 @@ #include <sysutils/SocketClient.h> -SocketClient::SocketClient(int socket) +SocketClient::SocketClient(int socket, bool owned) : mSocket(socket) + , mSocketOwned(owned) , mPid(-1) , mUid(-1) , mGid(-1) @@ -32,6 +33,13 @@ SocketClient::SocketClient(int socket) } } +SocketClient::~SocketClient() +{ + if (mSocketOwned) { + close(mSocket); + } +} + int SocketClient::sendMsg(int code, const char *msg, bool addErrno) { char *buf; const char* arg; diff --git a/libsysutils/src/SocketListener.cpp b/libsysutils/src/SocketListener.cpp index fcad624..3f871ea 100644 --- a/libsysutils/src/SocketListener.cpp +++ b/libsysutils/src/SocketListener.cpp @@ -79,7 +79,7 @@ int SocketListener::startListener() { SLOGE("Unable to listen on socket (%s)", strerror(errno)); return -1; } else if (!mListen) - mClients->push_back(new SocketClient(mSock)); + mClients->push_back(new SocketClient(mSock, false)); if (pipe(mCtrlPipe)) { SLOGE("pipe failed (%s)", strerror(errno)); @@ -191,7 +191,7 @@ void SocketListener::runListener() { continue; } pthread_mutex_lock(&mClientsLock); - mClients->push_back(new SocketClient(c)); + mClients->push_back(new SocketClient(c, true)); pthread_mutex_unlock(&mClientsLock); } @@ -225,12 +225,8 @@ void SocketListener::runListener() { } } pthread_mutex_unlock(&mClientsLock); - /* Destroy the client */ - int socket = c->getSocket(); - if (c->decRef()) { - // Note: 'c' is deleted memory at this point. - close(socket); - } + /* Remove our reference to the client */ + c->decRef(); } } } diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp index 96b68ef..4cd2151 100644 --- a/logcat/logcat.cpp +++ b/logcat/logcat.cpp @@ -323,7 +323,7 @@ static void readLogLines(log_device_t* devices) // the caller requested to just dump the log and exit if (g_nonblock) { - exit(0); + return; } } else { // print all that aren't the last in their list @@ -765,10 +765,10 @@ int main(int argc, char **argv) } if (getLogSize) { - return 0; + exit(0); } if (clearLog) { - return 0; + exit(0); } //LOG_EVENT_INT(10, 12345); diff --git a/netcfg/netcfg.c b/netcfg/netcfg.c index c520075..3738f24 100644 --- a/netcfg/netcfg.c +++ b/netcfg/netcfg.c @@ -50,8 +50,9 @@ void usage(void) int dump_interface(const char *name) { - unsigned addr, prefixLength, flags; + unsigned addr, flags; unsigned char hwbuf[ETH_ALEN]; + int prefixLength; if(ifc_get_info(name, &addr, &prefixLength, &flags)) { return 0; diff --git a/rootdir/etc/init.goldfish.rc b/rootdir/etc/init.goldfish.rc index 1ac09ca..83b7f8a 100644 --- a/rootdir/etc/init.goldfish.rc +++ b/rootdir/etc/init.goldfish.rc @@ -52,6 +52,18 @@ service goldfish-setup /system/etc/init.goldfish.sh group root oneshot +# The qemu-props program is used to set various system +# properties on boot. It must be run early during the boot +# process to avoid race conditions with other daemons that +# might read them (e.g. surface flinger), so define it in +# class 'core' +# +service qemu-props /system/bin/qemu-props + class core + user root + group root + oneshot + service qemud /system/bin/qemud socket qemud stream 666 oneshot diff --git a/rootdir/etc/init.goldfish.sh b/rootdir/etc/init.goldfish.sh index 1156dd7..ece75b4 100755 --- a/rootdir/etc/init.goldfish.sh +++ b/rootdir/etc/init.goldfish.sh @@ -57,10 +57,6 @@ case "$boot_anim" in ;; esac -# call 'qemu-props' to set system properties from the emulator. -# -/system/bin/qemu-props - # set up the second interface (for inter-emulator connections) # if required my_ip=`getprop net.shared_net_ip` diff --git a/rootdir/init.lowmem.rc b/rootdir/init.lowmem.rc deleted file mode 100644 index 7c08054..0000000 --- a/rootdir/init.lowmem.rc +++ /dev/null @@ -1,19 +0,0 @@ -# Adjustments to the out-of-memory killer, for devices that are -# tight on memory. These should not be used if not needed, as they -# can result in more paging. - -on early-boot - - setprop ro.FOREGROUND_APP_MEM 1536 - setprop ro.VISIBLE_APP_MEM 2048 - setprop ro.PERCEPTIBLE_APP_MEM 2048 - setprop ro.HEAVY_WEIGHT_APP_MEM 2048 - setprop ro.SECONDARY_SERVER_MEM 4096 - setprop ro.BACKUP_APP_MEM 4096 - setprop ro.HOME_APP_MEM 4096 - setprop ro.HIDDEN_APP_MEM 5120 - setprop ro.EMPTY_APP_MEM 6144 - -on boot - - write /sys/module/lowmemorykiller/parameters/minfree 1536,2048,3072,4096,5120,6144 diff --git a/rootdir/init.rc b/rootdir/init.rc index 54f4be2..af4967e 100644 --- a/rootdir/init.rc +++ b/rootdir/init.rc @@ -19,7 +19,7 @@ loglevel 3 export ANDROID_DATA /data export ASEC_MOUNTPOINT /mnt/asec export LOOP_MOUNTPOINT /mnt/obb - export BOOTCLASSPATH /system/framework/core.jar:/system/framework/apache-xml.jar:/system/framework/bouncycastle.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/android.policy.jar:/system/framework/services.jar:/system/framework/core-junit.jar + export BOOTCLASSPATH /system/framework/core.jar:/system/framework/core-junit.jar:/system/framework/bouncycastle.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/android.policy.jar:/system/framework/services.jar:/system/framework/apache-xml.jar:/system/framework/filterfw.jar # Backward compatibility symlink /system/etc /etc @@ -64,6 +64,7 @@ loglevel 3 write /proc/sys/kernel/sched_wakeup_granularity_ns 2000000 write /proc/sys/kernel/sched_compat_yield 1 write /proc/sys/kernel/sched_child_runs_first 0 + write /proc/sys/kernel/randomize_va_space 2 # Create cgroup mount points for process groups mkdir /dev/cpuctl @@ -84,6 +85,10 @@ loglevel 3 # 5.0 % write /dev/cpuctl/bg_non_interactive/cpu.shares 52 +# Allow everybody to read the xt_qtaguid resource tracking misc dev. +# This is needed by any process that uses socket tagging. + chmod 0644 /dev/xt_qtaguid + on fs # mount mtd partitions # Mount /system rw first to give the filesystem a chance to save a checkpoint @@ -115,11 +120,7 @@ on post-fs chmod 0220 /proc/sysrq-trigger # create the lost+found directories, so as to enforce our permissions - mkdir /cache/lost+found 0770 - - # double check the perms, in case lost+found already exists, and set owner - chown root root /cache/lost+found - chmod 0770 /cache/lost+found + mkdir /cache/lost+found 0770 root root on post-fs-data # We chown/chmod /data again so because mount is run as root + defaults @@ -129,10 +130,7 @@ on post-fs-data # Create dump dir and collect dumps. # Do this before we mount cache so eventually we can use cache for # storing dumps on platforms which do not have a dedicated dump partition. - - mkdir /data/dontpanic - chown root log /data/dontpanic - chmod 0750 /data/dontpanic + mkdir /data/dontpanic 0750 root log # Collect apanic data, free resources and re-arm trigger copy /proc/apanic_console /data/dontpanic/apanic_console @@ -150,12 +148,11 @@ on post-fs-data mkdir /data/misc/bluetoothd 0770 bluetooth bluetooth mkdir /data/misc/bluetooth 0770 system system mkdir /data/misc/keystore 0700 keystore keystore - mkdir /data/misc/vpn 0770 system system + mkdir /data/misc/keychain 0771 system system + mkdir /data/misc/vpn 0770 system vpn mkdir /data/misc/systemkeys 0700 system system - mkdir /data/misc/vpn/profiles 0770 system system # give system access to wpa_supplicant.conf for backup and restore mkdir /data/misc/wifi 0770 wifi wifi - chmod 0770 /data/misc/wifi chmod 0660 /data/misc/wifi/wpa_supplicant.conf mkdir /data/local 0771 shell shell mkdir /data/local/tmp 0771 shell shell @@ -164,10 +161,8 @@ on post-fs-data mkdir /data/app 0771 system system mkdir /data/property 0700 root root - # create dalvik-cache and double-check the perms + # create dalvik-cache, so as to enforce our permissions mkdir /data/dalvik-cache 0771 system system - chown system system /data/dalvik-cache - chmod 0771 /data/dalvik-cache # create resource-cache and double-check the perms mkdir /data/resource-cache 0771 system system @@ -175,11 +170,7 @@ on post-fs-data chmod 0771 /data/resource-cache # create the lost+found directories, so as to enforce our permissions - mkdir /data/lost+found 0770 - - # double check the perms, in case lost+found already exists, and set owner - chown root root /data/lost+found - chmod 0770 /data/lost+found + mkdir /data/lost+found 0770 root root # create directory for DRM plug-ins mkdir /data/drm 0774 drm drm @@ -190,6 +181,11 @@ on post-fs-data # Set indication (checked by vold) that we have finished this action #setprop vold.post_fs_data_done 1 + chown system system /sys/class/android_usb/android0/f_mass_storage/lun/file + chmod 0660 /sys/class/android_usb/android0/f_mass_storage/lun/file + chown system system /sys/class/android_usb/android0/f_rndis/ethaddr + chmod 0660 /sys/class/android_usb/android0/f_rndis/ethaddr + on boot # basic network init ifup lo @@ -199,54 +195,17 @@ on boot # set RLIMIT_NICE to allow priorities from 19 to -20 setrlimit 13 40 40 -# Define the oom_adj values for the classes of processes that can be -# killed by the kernel. These are used in ActivityManagerService. - setprop ro.FOREGROUND_APP_ADJ 0 - setprop ro.VISIBLE_APP_ADJ 1 - setprop ro.PERCEPTIBLE_APP_ADJ 2 - setprop ro.HEAVY_WEIGHT_APP_ADJ 3 - setprop ro.SECONDARY_SERVER_ADJ 4 - setprop ro.BACKUP_APP_ADJ 5 - setprop ro.HOME_APP_ADJ 6 - setprop ro.HIDDEN_APP_MIN_ADJ 7 - setprop ro.EMPTY_APP_ADJ 15 - -# Define the memory thresholds at which the above process classes will -# be killed. These numbers are in pages (4k). - # These are currently tuned for tablets with approx 1GB RAM. - setprop ro.FOREGROUND_APP_MEM 8192 - setprop ro.VISIBLE_APP_MEM 10240 - setprop ro.PERCEPTIBLE_APP_MEM 12288 - setprop ro.HEAVY_WEIGHT_APP_MEM 12288 - setprop ro.SECONDARY_SERVER_MEM 14336 - setprop ro.BACKUP_APP_MEM 14336 - setprop ro.HOME_APP_MEM 14336 - setprop ro.HIDDEN_APP_MEM 16384 - setprop ro.EMPTY_APP_MEM 20480 - - # Old values for phones. Should probably be adjusted up for the next - # phone version. - #setprop ro.FOREGROUND_APP_MEM 2048 - #setprop ro.VISIBLE_APP_MEM 3072 - #setprop ro.PERCEPTIBLE_APP_MEM 4096 - #setprop ro.HEAVY_WEIGHT_APP_MEM 4096 - #setprop ro.SECONDARY_SERVER_MEM 6144 - #setprop ro.BACKUP_APP_MEM 6144 - #setprop ro.HOME_APP_MEM 6144 - #setprop ro.HIDDEN_APP_MEM 7168 - #setprop ro.EMPTY_APP_MEM 8192 - -# Write value must be consistent with the above properties. -# Note that the driver only supports 6 slots, so we have combined some of -# the classes into the same memory level; the associated processes of higher -# classes will still be killed first. - write /sys/module/lowmemorykiller/parameters/adj 0,1,2,4,7,15 - +# Memory management. Basic kernel parameters, and allow the high +# level system server to be able to adjust the kernel OOM driver +# paramters to match how it is managing things. write /proc/sys/vm/overcommit_memory 1 write /proc/sys/vm/min_free_order_shift 4 - write /sys/module/lowmemorykiller/parameters/minfree 8192,10240,12288,14336,16384,20480 + chown root system /sys/module/lowmemorykiller/parameters/adj + chmod 0664 /sys/module/lowmemorykiller/parameters/adj + chown root system /sys/module/lowmemorykiller/parameters/minfree + chmod 0664 /sys/module/lowmemorykiller/parameters/minfree - # Set init its forked children's oom_adj. + # Set init and its forked children's oom_adj. write /proc/1/oom_adj -16 # Tweak background writeout @@ -295,7 +254,8 @@ on boot # Define TCP buffer sizes for various networks # ReadMin, ReadInitial, ReadMax, WriteMin, WriteInitial, WriteMax, setprop net.tcp.buffersize.default 4096,87380,110208,4096,16384,110208 - setprop net.tcp.buffersize.wifi 4095,87380,110208,4096,16384,110208 + setprop net.tcp.buffersize.wifi 524288,1048576,2097152,262144,524288,1048576 + setprop net.tcp.buffersize.lte 524288,1048576,2097152,262144,524288,1048576 setprop net.tcp.buffersize.umts 4094,87380,110208,4096,16384,110208 setprop net.tcp.buffersize.edge 4093,26280,35040,4096,16384,35040 setprop net.tcp.buffersize.gprs 4092,8760,11680,4096,8760,11680 @@ -309,6 +269,9 @@ on boot on nonencrypted class_start late_start +on charger + class_start charger + on property:vold.decrypt=trigger_reset_main class_reset main @@ -329,6 +292,49 @@ on property:vold.decrypt=trigger_shutdown_framework class_reset late_start class_reset main +# Used to disable USB when switching states +on property:sys.usb.config=none + stop adbd + write /sys/class/android_usb/android0/enable 0 + write /sys/class/android_usb/android0/bDeviceClass 0 + setprop sys.usb.state $sys.usb.config + +# adb only USB configuration +# This should only be used during device bringup +# and as a fallback if the USB manager fails to set a standard configuration +on property:sys.usb.config=adb + write /sys/class/android_usb/android0/enable 0 + write /sys/class/android_usb/android0/idVendor 18d1 + write /sys/class/android_usb/android0/idProduct D002 + write /sys/class/android_usb/android0/functions $sys.usb.config + write /sys/class/android_usb/android0/enable 1 + start adbd + setprop sys.usb.state $sys.usb.config + +# USB accessory configuration +on property:sys.usb.config=accessory + write /sys/class/android_usb/android0/enable 0 + write /sys/class/android_usb/android0/idVendor 18d1 + write /sys/class/android_usb/android0/idProduct 2d00 + write /sys/class/android_usb/android0/functions $sys.usb.config + write /sys/class/android_usb/android0/enable 1 + setprop sys.usb.state $sys.usb.config + +# USB accessory configuration, with adb +on property:sys.usb.config=accessory,adb + write /sys/class/android_usb/android0/enable 0 + write /sys/class/android_usb/android0/idVendor 18d1 + write /sys/class/android_usb/android0/idProduct 2d01 + write /sys/class/android_usb/android0/functions $sys.usb.config + write /sys/class/android_usb/android0/enable 1 + start adbd + setprop sys.usb.state $sys.usb.config + +# Used to set USB configuration at boot and to switch the configuration +# when changing the default configuration +on property:persist.sys.usb.config=* + setprop sys.usb.config $persist.sys.usb.config + ## Daemon processes to be run by init. ## service ueventd /sbin/ueventd @@ -345,7 +351,7 @@ service console /system/bin/sh on property:ro.debuggable=1 start console -# adbd is controlled by the persist.service.adb.enable system property +# adbd is controlled via property triggers in init.<platform>.usb.rc service adbd /sbin/adbd class core disabled @@ -354,11 +360,17 @@ service adbd /sbin/adbd on property:ro.kernel.qemu=1 start adbd -on property:persist.service.adb.enable=1 - start adbd - -on property:persist.service.adb.enable=0 - stop adbd +# This property trigger has added to imitiate the previous behavior of "adb root". +# The adb gadget driver used to reset the USB bus when the adbd daemon exited, +# and the host side adb relied on this behavior to force it to reconnect with the +# new adbd instance after init relaunches it. So now we force the USB bus to reset +# here when adbd sets the service.adb.root property to 1. We also restart adbd here +# rather than waiting for init to notice its death and restarting it so the timing +# of USB resetting and adb restarting more closely matches the previous behavior. +on property:service.adb.root=1 + write /sys/class/android_usb/android0/enable 0 + restart adbd + write /sys/class/android_usb/android0/enable 1 service servicemanager /system/bin/servicemanager class core @@ -386,7 +398,7 @@ service ril-daemon /system/bin/rild socket rild stream 660 root radio socket rild-debug stream 660 radio system user root - group radio cache inet misc audio sdcard_rw + group radio cache inet misc audio sdcard_rw log service surfaceflinger /system/bin/surfaceflinger class main @@ -399,19 +411,18 @@ service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-sys socket zygote stream 666 onrestart write /sys/android_power/request_state wake onrestart write /sys/power/state on - onrestart restart surfaceflinger onrestart restart media onrestart restart netd service drm /system/bin/drmserver class main user drm - group system inet + group system inet drmrpc service media /system/bin/mediaserver class main user media - group audio camera inet net_bt net_bt_admin + group audio camera inet net_bt net_bt_admin net_bw_acct drmrpc ioprio rt 4 service bootanim /system/bin/bootanimation @@ -447,8 +458,8 @@ service flash_recovery /system/etc/install-recovery.sh service racoon /system/bin/racoon class main socket racoon stream 600 system system - # racoon will setuid to vpn after getting necessary resources. - group net_admin + # IKE uses UDP port 500. Racoon will setuid to vpn after binding the port. + group vpn net_admin inet disabled oneshot @@ -456,7 +467,7 @@ service mtpd /system/bin/mtpd class main socket mtpd stream 600 system system user vpn - group vpn net_admin net_raw + group vpn net_admin inet net_raw disabled oneshot diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc index 51a4337..438cf0a 100644 --- a/rootdir/ueventd.rc +++ b/rootdir/ueventd.rc @@ -70,11 +70,11 @@ /dev/bus/usb/* 0660 root usb /dev/mtp_usb 0660 root mtp /dev/usb_accessory 0660 root usb +/dev/tun 0660 system vpn # CDMA radio interface MUX /dev/ts0710mux* 0640 radio radio /dev/ppp 0660 radio vpn -/dev/tun 0640 vpn vpn # sysfs properties /sys/devices/virtual/input/input* enable 0660 root input diff --git a/sdcard/sdcard.c b/sdcard/sdcard.c index 7112ebf..689cd2a 100644 --- a/sdcard/sdcard.c +++ b/sdcard/sdcard.c @@ -326,15 +326,9 @@ void fuse_init(struct fuse *fuse, int fd, const char *path) fuse->all = &fuse->root; + memset(&fuse->root, 0, sizeof(fuse->root)); fuse->root.nid = FUSE_ROOT_ID; /* 1 */ - fuse->root.next = 0; - fuse->root.child = 0; - fuse->root.parent = 0; - - fuse->root.all = 0; fuse->root.refcount = 2; - - fuse->root.name = 0; rename_node(&fuse->root, path); } @@ -762,7 +756,7 @@ void handle_fuse_request(struct fuse *fuse, struct fuse_in_header *hdr, void *da h->fd = open(path, req->flags); if (h->fd < 0) { ERROR("ERROR\n"); - fuse_status(fuse, hdr->unique, errno); + fuse_status(fuse, hdr->unique, -errno); free(h); return; } @@ -784,7 +778,7 @@ void handle_fuse_request(struct fuse *fuse, struct fuse_in_header *hdr, void *da } res = pread64(h->fd, buffer, req->size, req->offset); if (res < 0) { - fuse_status(fuse, hdr->unique, errno); + fuse_status(fuse, hdr->unique, -errno); return; } fuse_reply(fuse, hdr->unique, buffer, res); @@ -798,7 +792,7 @@ void handle_fuse_request(struct fuse *fuse, struct fuse_in_header *hdr, void *da TRACE("WRITE %p(%d) %u@%llu\n", h, h->fd, req->size, req->offset); res = pwrite64(h->fd, ((char*) data) + sizeof(*req), req->size, req->offset); if (res < 0) { - fuse_status(fuse, hdr->unique, errno); + fuse_status(fuse, hdr->unique, -errno); return; } out.size = res; diff --git a/toolbox/Android.mk b/toolbox/Android.mk index ff01172..d7a675a 100644 --- a/toolbox/Android.mk +++ b/toolbox/Android.mk @@ -54,8 +54,13 @@ TOOLS := \ vmstat \ nandread \ ionice \ + touch \ lsof +ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT))) +TOOLS += r +endif + LOCAL_SRC_FILES:= \ dynarray.c \ toolbox.c \ diff --git a/toolbox/getevent.c b/toolbox/getevent.c index 256720d..352f6f9 100644 --- a/toolbox/getevent.c +++ b/toolbox/getevent.c @@ -8,9 +8,11 @@ #include <sys/inotify.h> #include <sys/limits.h> #include <sys/poll.h> -#include <linux/input.h> // this does not compile +#include <linux/input.h> #include <errno.h> +#include "getevent.h" + static struct pollfd *ufds; static char **device_names; static int nfds; @@ -22,18 +24,66 @@ enum { PRINT_DEVICE_INFO = 1U << 3, PRINT_VERSION = 1U << 4, PRINT_POSSIBLE_EVENTS = 1U << 5, + PRINT_INPUT_PROPS = 1U << 6, + PRINT_HID_DESCRIPTOR = 1U << 7, + + PRINT_ALL_INFO = (1U << 8) - 1, + + PRINT_LABELS = 1U << 16, }; -static int print_possible_events(int fd) +static const char *get_label(const struct label *labels, int value) +{ + while(labels->name && value != labels->value) { + labels++; + } + return labels->name; +} + +static int print_input_props(int fd) +{ + uint8_t bits[INPUT_PROP_CNT / 8]; + int i, j; + int res; + int count; + const char *bit_label; + + printf(" input props:\n"); + res = ioctl(fd, EVIOCGPROP(sizeof(bits)), bits); + if(res < 0) { + printf(" <not available\n"); + return 1; + } + count = 0; + for(i = 0; i < res; i++) { + for(j = 0; j < 8; j++) { + if (bits[i] & 1 << j) { + bit_label = get_label(input_prop_labels, i * 8 + j); + if(bit_label) + printf(" %s\n", bit_label); + else + printf(" %04x\n", i * 8 + j); + count++; + } + } + } + if (!count) + printf(" <none>\n"); + return 0; +} + +static int print_possible_events(int fd, int print_flags) { uint8_t *bits = NULL; ssize_t bits_size = 0; const char* label; int i, j, k; int res, res2; - + struct label* bit_labels; + const char *bit_label; + printf(" events:\n"); - for(i = 0; i <= EV_MAX; i++) { + for(i = EV_KEY; i <= EV_MAX; i++) { // skip EV_SYN since we cannot query its available codes int count = 0; while(1) { res = ioctl(fd, EVIOCGBIT(i, bits_size), bits); @@ -42,52 +92,64 @@ static int print_possible_events(int fd) bits_size = res + 16; bits = realloc(bits, bits_size * 2); if(bits == NULL) { - fprintf(stderr, "failed to allocate buffer of size %d\n", bits_size); + fprintf(stderr, "failed to allocate buffer of size %d\n", (int)bits_size); return 1; } } res2 = 0; switch(i) { - case EV_SYN: - label = "SYN"; - break; case EV_KEY: res2 = ioctl(fd, EVIOCGKEY(res), bits + bits_size); label = "KEY"; + bit_labels = key_labels; break; case EV_REL: label = "REL"; + bit_labels = rel_labels; break; case EV_ABS: label = "ABS"; + bit_labels = abs_labels; break; case EV_MSC: label = "MSC"; + bit_labels = msc_labels; break; case EV_LED: res2 = ioctl(fd, EVIOCGLED(res), bits + bits_size); label = "LED"; + bit_labels = led_labels; break; case EV_SND: res2 = ioctl(fd, EVIOCGSND(res), bits + bits_size); label = "SND"; + bit_labels = snd_labels; break; case EV_SW: res2 = ioctl(fd, EVIOCGSW(bits_size), bits + bits_size); label = "SW "; + bit_labels = sw_labels; break; case EV_REP: label = "REP"; + bit_labels = rep_labels; break; case EV_FF: label = "FF "; + bit_labels = ff_labels; break; case EV_PWR: label = "PWR"; + bit_labels = NULL; + break; + case EV_FF_STATUS: + label = "FFS"; + bit_labels = ff_status_labels; break; default: res2 = 0; label = "???"; + bit_labels = NULL; } for(j = 0; j < res; j++) { for(k = 0; k < 8; k++) @@ -99,13 +161,23 @@ static int print_possible_events(int fd) down = ' '; if(count == 0) printf(" %s (%04x):", label, i); - else if((count & 0x7) == 0 || i == EV_ABS) + else if((count & (print_flags & PRINT_LABELS ? 0x3 : 0x7)) == 0 || i == EV_ABS) printf("\n "); - printf(" %04x%c", j * 8 + k, down); + if(bit_labels && (print_flags & PRINT_LABELS)) { + bit_label = get_label(bit_labels, j * 8 + k); + if(bit_label) + printf(" %.20s%c%*s", bit_label, down, 20 - strlen(bit_label), ""); + else + printf(" %04x%c ", j * 8 + k, down); + } else { + printf(" %04x%c", j * 8 + k, down); + } if(i == EV_ABS) { struct input_absinfo abs; if(ioctl(fd, EVIOCGABS(j * 8 + k), &abs) == 0) { - printf(" value %d, min %d, max %d, fuzz %d flat %d", abs.value, abs.minimum, abs.maximum, abs.fuzz, abs.flat); + printf(" : value %d, min %d, max %d, fuzz %d, flat %d, resolution %d", + abs.value, abs.minimum, abs.maximum, abs.fuzz, abs.flat, + abs.resolution); } } count++; @@ -118,6 +190,107 @@ static int print_possible_events(int fd) return 0; } +static void print_event(int type, int code, int value, int print_flags) +{ + const char *type_label, *code_label, *value_label; + + if (print_flags & PRINT_LABELS) { + type_label = get_label(ev_labels, type); + code_label = NULL; + value_label = NULL; + + switch(type) { + case EV_SYN: + code_label = get_label(syn_labels, code); + break; + case EV_KEY: + code_label = get_label(key_labels, code); + value_label = get_label(key_value_labels, value); + break; + case EV_REL: + code_label = get_label(rel_labels, code); + break; + case EV_ABS: + code_label = get_label(abs_labels, code); + switch(code) { + case ABS_MT_TOOL_TYPE: + value_label = get_label(mt_tool_labels, value); + } + break; + case EV_MSC: + code_label = get_label(msc_labels, code); + break; + case EV_LED: + code_label = get_label(led_labels, code); + break; + case EV_SND: + code_label = get_label(snd_labels, code); + break; + case EV_SW: + code_label = get_label(sw_labels, code); + break; + case EV_REP: + code_label = get_label(rep_labels, code); + break; + case EV_FF: + code_label = get_label(ff_labels, code); + break; + case EV_FF_STATUS: + code_label = get_label(ff_status_labels, code); + break; + } + + if (type_label) + printf("%-12.12s", type_label); + else + printf("%04x ", type); + if (code_label) + printf(" %-20.20s", code_label); + else + printf(" %04x ", code); + if (value_label) + printf(" %-20.20s", value_label); + else + printf(" %08x ", value); + } else { + printf("%04x %04x %08x", type, code, value); + } +} + +static void print_hid_descriptor(int bus, int vendor, int product) +{ + const char *dirname = "/sys/kernel/debug/hid"; + char prefix[16]; + DIR *dir; + struct dirent *de; + char filename[PATH_MAX]; + FILE *file; + char line[2048]; + + snprintf(prefix, sizeof(prefix), "%04X:%04X:%04X.", bus, vendor, product); + + dir = opendir(dirname); + if(dir == NULL) + return; + while((de = readdir(dir))) { + if (strstr(de->d_name, prefix) == de->d_name) { + snprintf(filename, sizeof(filename), "%s/%s/rdesc", dirname, de->d_name); + + file = fopen(filename, "r"); + if (file) { + printf(" HID descriptor: %s\n\n", de->d_name); + while (fgets(line, sizeof(line), file)) { + fputs(" ", stdout); + fputs(line, stdout); + } + fclose(file); + puts(""); + } + } + } + closedir(dir); +} + static int open_device(const char *device, int print_flags) { int version; @@ -193,7 +366,14 @@ static int open_device(const char *device, int print_flags) version >> 16, (version >> 8) & 0xff, version & 0xff); if(print_flags & PRINT_POSSIBLE_EVENTS) { - print_possible_events(fd); + print_possible_events(fd, print_flags); + } + + if(print_flags & PRINT_INPUT_PROPS) { + print_input_props(fd); + } + if(print_flags & PRINT_HID_DESCRIPTOR) { + print_hid_descriptor(id.bustype, id.vendor, id.product); } ufds[nfds].fd = fd; @@ -292,13 +472,16 @@ static int scan_dir(const char *dirname, int print_flags) static void usage(int argc, char *argv[]) { - fprintf(stderr, "Usage: %s [-t] [-n] [-s switchmask] [-S] [-v [mask]] [-p] [-q] [-c count] [-r] [device]\n", argv[0]); + fprintf(stderr, "Usage: %s [-t] [-n] [-s switchmask] [-S] [-v [mask]] [-d] [-p] [-i] [-l] [-q] [-c count] [-r] [device]\n", argv[0]); fprintf(stderr, " -t: show time stamps\n"); fprintf(stderr, " -n: don't print newlines\n"); fprintf(stderr, " -s: print switch states for given bits\n"); fprintf(stderr, " -S: print all switch states\n"); - fprintf(stderr, " -v: verbosity mask (errs=1, dev=2, name=4, info=8, vers=16, pos. events=32)\n"); + fprintf(stderr, " -v: verbosity mask (errs=1, dev=2, name=4, info=8, vers=16, pos. events=32, props=64)\n"); + fprintf(stderr, " -d: show HID descriptor, if available\n"); fprintf(stderr, " -p: show possible events (errs, dev, name, pos. events)\n"); + fprintf(stderr, " -i: show all device info and possible events\n"); + fprintf(stderr, " -l: label event types and names in plain text\n"); fprintf(stderr, " -q: quiet (clear verbosity mask)\n"); fprintf(stderr, " -c: print given number of events then exit\n"); fprintf(stderr, " -r: print rate events are received\n"); @@ -316,7 +499,7 @@ int getevent_main(int argc, char *argv[]) uint16_t get_switch = 0; struct input_event event; int version; - int print_flags = PRINT_DEVICE_ERRORS | PRINT_DEVICE | PRINT_DEVICE_NAME; + int print_flags = 0; int print_flags_set = 0; int dont_block = -1; int event_count = 0; @@ -327,7 +510,7 @@ int getevent_main(int argc, char *argv[]) opterr = 0; do { - c = getopt(argc, argv, "tns:Sv::pqc:rh"); + c = getopt(argc, argv, "tns:Sv::dpilqc:rh"); if (c == EOF) break; switch (c) { @@ -349,19 +532,31 @@ int getevent_main(int argc, char *argv[]) break; case 'v': if(optarg) - print_flags = strtoul(optarg, NULL, 0); + print_flags |= strtoul(optarg, NULL, 0); else print_flags |= PRINT_DEVICE | PRINT_DEVICE_NAME | PRINT_DEVICE_INFO | PRINT_VERSION; print_flags_set = 1; break; + case 'd': + print_flags |= PRINT_HID_DESCRIPTOR; + break; case 'p': - print_flags = PRINT_DEVICE_ERRORS | PRINT_DEVICE | PRINT_DEVICE_NAME | PRINT_POSSIBLE_EVENTS; + print_flags |= PRINT_DEVICE_ERRORS | PRINT_DEVICE + | PRINT_DEVICE_NAME | PRINT_POSSIBLE_EVENTS | PRINT_INPUT_PROPS; + print_flags_set = 1; + if(dont_block == -1) + dont_block = 1; + break; + case 'i': + print_flags |= PRINT_ALL_INFO; print_flags_set = 1; if(dont_block == -1) dont_block = 1; break; + case 'l': + print_flags |= PRINT_LABELS; + break; case 'q': - print_flags = 0; print_flags_set = 1; break; case 'c': @@ -396,13 +591,14 @@ int getevent_main(int argc, char *argv[]) ufds[0].events = POLLIN; if(device) { if(!print_flags_set) - print_flags = PRINT_DEVICE_ERRORS; + print_flags |= PRINT_DEVICE_ERRORS; res = open_device(device, print_flags); if(res < 0) { return 1; } - } - else { + } else { + if(!print_flags_set) + print_flags |= PRINT_DEVICE_ERRORS | PRINT_DEVICE | PRINT_DEVICE_NAME; print_device = 1; res = inotify_add_watch(ufds[0].fd, device_path, IN_DELETE | IN_CREATE); if(res < 0) { @@ -451,7 +647,7 @@ int getevent_main(int argc, char *argv[]) } if(print_device) printf("%s: ", device_names[i]); - printf("%04x %04x %08x", event.type, event.code, event.value); + print_event(event.type, event.code, event.value, print_flags); if(sync_rate && event.type == 0 && event.code == 0) { int64_t now = event.time.tv_sec * 1000000LL + event.time.tv_usec; if(last_sync_time) diff --git a/toolbox/getevent.h b/toolbox/getevent.h new file mode 100644 index 0000000..2b76209 --- /dev/null +++ b/toolbox/getevent.h @@ -0,0 +1,725 @@ +#include <linux/input.h> + +struct label { + const char *name; + int value; +}; + +#define LABEL(constant) { #constant, constant } +#define LABEL_END { NULL, -1 } + +static struct label input_prop_labels[] = { + LABEL(INPUT_PROP_POINTER), + LABEL(INPUT_PROP_DIRECT), + LABEL(INPUT_PROP_BUTTONPAD), + LABEL(INPUT_PROP_SEMI_MT), + LABEL_END, +}; + +static struct label ev_labels[] = { + LABEL(EV_SYN), + LABEL(EV_KEY), + LABEL(EV_REL), + LABEL(EV_ABS), + LABEL(EV_MSC), + LABEL(EV_SW), + LABEL(EV_LED), + LABEL(EV_SND), + LABEL(EV_REP), + LABEL(EV_FF), + LABEL(EV_PWR), + LABEL(EV_FF_STATUS), + LABEL_END, +}; + +static struct label syn_labels[] = { + LABEL(SYN_REPORT), + LABEL(SYN_CONFIG), + LABEL(SYN_MT_REPORT), + LABEL(SYN_DROPPED), + LABEL_END, +}; + +static struct label key_labels[] = { + LABEL(KEY_RESERVED), + LABEL(KEY_ESC), + LABEL(KEY_1), + LABEL(KEY_2), + LABEL(KEY_3), + LABEL(KEY_4), + LABEL(KEY_5), + LABEL(KEY_6), + LABEL(KEY_7), + LABEL(KEY_8), + LABEL(KEY_9), + LABEL(KEY_0), + LABEL(KEY_MINUS), + LABEL(KEY_EQUAL), + LABEL(KEY_BACKSPACE), + LABEL(KEY_TAB), + LABEL(KEY_Q), + LABEL(KEY_W), + LABEL(KEY_E), + LABEL(KEY_R), + LABEL(KEY_T), + LABEL(KEY_Y), + LABEL(KEY_U), + LABEL(KEY_I), + LABEL(KEY_O), + LABEL(KEY_P), + LABEL(KEY_LEFTBRACE), + LABEL(KEY_RIGHTBRACE), + LABEL(KEY_ENTER), + LABEL(KEY_LEFTCTRL), + LABEL(KEY_A), + LABEL(KEY_S), + LABEL(KEY_D), + LABEL(KEY_F), + LABEL(KEY_G), + LABEL(KEY_H), + LABEL(KEY_J), + LABEL(KEY_K), + LABEL(KEY_L), + LABEL(KEY_SEMICOLON), + LABEL(KEY_APOSTROPHE), + LABEL(KEY_GRAVE), + LABEL(KEY_LEFTSHIFT), + LABEL(KEY_BACKSLASH), + LABEL(KEY_Z), + LABEL(KEY_X), + LABEL(KEY_C), + LABEL(KEY_V), + LABEL(KEY_B), + LABEL(KEY_N), + LABEL(KEY_M), + LABEL(KEY_COMMA), + LABEL(KEY_DOT), + LABEL(KEY_SLASH), + LABEL(KEY_RIGHTSHIFT), + LABEL(KEY_KPASTERISK), + LABEL(KEY_LEFTALT), + LABEL(KEY_SPACE), + LABEL(KEY_CAPSLOCK), + LABEL(KEY_F1), + LABEL(KEY_F2), + LABEL(KEY_F3), + LABEL(KEY_F4), + LABEL(KEY_F5), + LABEL(KEY_F6), + LABEL(KEY_F7), + LABEL(KEY_F8), + LABEL(KEY_F9), + LABEL(KEY_F10), + LABEL(KEY_NUMLOCK), + LABEL(KEY_SCROLLLOCK), + LABEL(KEY_KP7), + LABEL(KEY_KP8), + LABEL(KEY_KP9), + LABEL(KEY_KPMINUS), + LABEL(KEY_KP4), + LABEL(KEY_KP5), + LABEL(KEY_KP6), + LABEL(KEY_KPPLUS), + LABEL(KEY_KP1), + LABEL(KEY_KP2), + LABEL(KEY_KP3), + LABEL(KEY_KP0), + LABEL(KEY_KPDOT), + LABEL(KEY_ZENKAKUHANKAKU), + LABEL(KEY_102ND), + LABEL(KEY_F11), + LABEL(KEY_F12), + LABEL(KEY_RO), + LABEL(KEY_KATAKANA), + LABEL(KEY_HIRAGANA), + LABEL(KEY_HENKAN), + LABEL(KEY_KATAKANAHIRAGANA), + LABEL(KEY_MUHENKAN), + LABEL(KEY_KPJPCOMMA), + LABEL(KEY_KPENTER), + LABEL(KEY_RIGHTCTRL), + LABEL(KEY_KPSLASH), + LABEL(KEY_SYSRQ), + LABEL(KEY_RIGHTALT), + LABEL(KEY_LINEFEED), + LABEL(KEY_HOME), + LABEL(KEY_UP), + LABEL(KEY_PAGEUP), + LABEL(KEY_LEFT), + LABEL(KEY_RIGHT), + LABEL(KEY_END), + LABEL(KEY_DOWN), + LABEL(KEY_PAGEDOWN), + LABEL(KEY_INSERT), + LABEL(KEY_DELETE), + LABEL(KEY_MACRO), + LABEL(KEY_MUTE), + LABEL(KEY_VOLUMEDOWN), + LABEL(KEY_VOLUMEUP), + LABEL(KEY_POWER), + LABEL(KEY_KPEQUAL), + LABEL(KEY_KPPLUSMINUS), + LABEL(KEY_PAUSE), + LABEL(KEY_SCALE), + LABEL(KEY_KPCOMMA), + LABEL(KEY_HANGEUL), + LABEL(KEY_HANGUEL), + LABEL(KEY_HANJA), + LABEL(KEY_YEN), + LABEL(KEY_LEFTMETA), + LABEL(KEY_RIGHTMETA), + LABEL(KEY_COMPOSE), + LABEL(KEY_STOP), + LABEL(KEY_AGAIN), + LABEL(KEY_PROPS), + LABEL(KEY_UNDO), + LABEL(KEY_FRONT), + LABEL(KEY_COPY), + LABEL(KEY_OPEN), + LABEL(KEY_PASTE), + LABEL(KEY_FIND), + LABEL(KEY_CUT), + LABEL(KEY_HELP), + LABEL(KEY_MENU), + LABEL(KEY_CALC), + LABEL(KEY_SETUP), + LABEL(KEY_SLEEP), + LABEL(KEY_WAKEUP), + LABEL(KEY_FILE), + LABEL(KEY_SENDFILE), + LABEL(KEY_DELETEFILE), + LABEL(KEY_XFER), + LABEL(KEY_PROG1), + LABEL(KEY_PROG2), + LABEL(KEY_WWW), + LABEL(KEY_MSDOS), + LABEL(KEY_COFFEE), + LABEL(KEY_SCREENLOCK), + LABEL(KEY_DIRECTION), + LABEL(KEY_CYCLEWINDOWS), + LABEL(KEY_MAIL), + LABEL(KEY_BOOKMARKS), + LABEL(KEY_COMPUTER), + LABEL(KEY_BACK), + LABEL(KEY_FORWARD), + LABEL(KEY_CLOSECD), + LABEL(KEY_EJECTCD), + LABEL(KEY_EJECTCLOSECD), + LABEL(KEY_NEXTSONG), + LABEL(KEY_PLAYPAUSE), + LABEL(KEY_PREVIOUSSONG), + LABEL(KEY_STOPCD), + LABEL(KEY_RECORD), + LABEL(KEY_REWIND), + LABEL(KEY_PHONE), + LABEL(KEY_ISO), + LABEL(KEY_CONFIG), + LABEL(KEY_HOMEPAGE), + LABEL(KEY_REFRESH), + LABEL(KEY_EXIT), + LABEL(KEY_MOVE), + LABEL(KEY_EDIT), + LABEL(KEY_SCROLLUP), + LABEL(KEY_SCROLLDOWN), + LABEL(KEY_KPLEFTPAREN), + LABEL(KEY_KPRIGHTPAREN), + LABEL(KEY_NEW), + LABEL(KEY_REDO), + LABEL(KEY_F13), + LABEL(KEY_F14), + LABEL(KEY_F15), + LABEL(KEY_F16), + LABEL(KEY_F17), + LABEL(KEY_F18), + LABEL(KEY_F19), + LABEL(KEY_F20), + LABEL(KEY_F21), + LABEL(KEY_F22), + LABEL(KEY_F23), + LABEL(KEY_F24), + LABEL(KEY_PLAYCD), + LABEL(KEY_PAUSECD), + LABEL(KEY_PROG3), + LABEL(KEY_PROG4), + LABEL(KEY_DASHBOARD), + LABEL(KEY_SUSPEND), + LABEL(KEY_CLOSE), + LABEL(KEY_PLAY), + LABEL(KEY_FASTFORWARD), + LABEL(KEY_BASSBOOST), + LABEL(KEY_PRINT), + LABEL(KEY_HP), + LABEL(KEY_CAMERA), + LABEL(KEY_SOUND), + LABEL(KEY_QUESTION), + LABEL(KEY_EMAIL), + LABEL(KEY_CHAT), + LABEL(KEY_SEARCH), + LABEL(KEY_CONNECT), + LABEL(KEY_FINANCE), + LABEL(KEY_SPORT), + LABEL(KEY_SHOP), + LABEL(KEY_ALTERASE), + LABEL(KEY_CANCEL), + LABEL(KEY_BRIGHTNESSDOWN), + LABEL(KEY_BRIGHTNESSUP), + LABEL(KEY_MEDIA), + LABEL(KEY_SWITCHVIDEOMODE), + LABEL(KEY_KBDILLUMTOGGLE), + LABEL(KEY_KBDILLUMDOWN), + LABEL(KEY_KBDILLUMUP), + LABEL(KEY_SEND), + LABEL(KEY_REPLY), + LABEL(KEY_FORWARDMAIL), + LABEL(KEY_SAVE), + LABEL(KEY_DOCUMENTS), + LABEL(KEY_BATTERY), + LABEL(KEY_BLUETOOTH), + LABEL(KEY_WLAN), + LABEL(KEY_UWB), + LABEL(KEY_UNKNOWN), + LABEL(KEY_VIDEO_NEXT), + LABEL(KEY_VIDEO_PREV), + LABEL(KEY_BRIGHTNESS_CYCLE), + LABEL(KEY_BRIGHTNESS_ZERO), + LABEL(KEY_DISPLAY_OFF), + LABEL(KEY_WIMAX), + LABEL(KEY_RFKILL), + LABEL(BTN_0), + LABEL(BTN_1), + LABEL(BTN_2), + LABEL(BTN_3), + LABEL(BTN_4), + LABEL(BTN_5), + LABEL(BTN_6), + LABEL(BTN_7), + LABEL(BTN_8), + LABEL(BTN_9), + LABEL(BTN_LEFT), + LABEL(BTN_RIGHT), + LABEL(BTN_MIDDLE), + LABEL(BTN_SIDE), + LABEL(BTN_EXTRA), + LABEL(BTN_FORWARD), + LABEL(BTN_BACK), + LABEL(BTN_TASK), + LABEL(BTN_JOYSTICK), + LABEL(BTN_TRIGGER), + LABEL(BTN_THUMB), + LABEL(BTN_THUMB2), + LABEL(BTN_TOP), + LABEL(BTN_TOP2), + LABEL(BTN_PINKIE), + LABEL(BTN_BASE), + LABEL(BTN_BASE2), + LABEL(BTN_BASE3), + LABEL(BTN_BASE4), + LABEL(BTN_BASE5), + LABEL(BTN_BASE6), + LABEL(BTN_DEAD), + LABEL(BTN_A), + LABEL(BTN_B), + LABEL(BTN_C), + LABEL(BTN_X), + LABEL(BTN_Y), + LABEL(BTN_Z), + LABEL(BTN_TL), + LABEL(BTN_TR), + LABEL(BTN_TL2), + LABEL(BTN_TR2), + LABEL(BTN_SELECT), + LABEL(BTN_START), + LABEL(BTN_MODE), + LABEL(BTN_THUMBL), + LABEL(BTN_THUMBR), + LABEL(BTN_TOOL_PEN), + LABEL(BTN_TOOL_RUBBER), + LABEL(BTN_TOOL_BRUSH), + LABEL(BTN_TOOL_PENCIL), + LABEL(BTN_TOOL_AIRBRUSH), + LABEL(BTN_TOOL_FINGER), + LABEL(BTN_TOOL_MOUSE), + LABEL(BTN_TOOL_LENS), + LABEL(BTN_TOUCH), + LABEL(BTN_STYLUS), + LABEL(BTN_STYLUS2), + LABEL(BTN_TOOL_DOUBLETAP), + LABEL(BTN_TOOL_TRIPLETAP), + LABEL(BTN_TOOL_QUADTAP), + LABEL(BTN_GEAR_DOWN), + LABEL(BTN_GEAR_UP), + LABEL(KEY_OK), + LABEL(KEY_SELECT), + LABEL(KEY_GOTO), + LABEL(KEY_CLEAR), + LABEL(KEY_POWER2), + LABEL(KEY_OPTION), + LABEL(KEY_INFO), + LABEL(KEY_TIME), + LABEL(KEY_VENDOR), + LABEL(KEY_ARCHIVE), + LABEL(KEY_PROGRAM), + LABEL(KEY_CHANNEL), + LABEL(KEY_FAVORITES), + LABEL(KEY_EPG), + LABEL(KEY_PVR), + LABEL(KEY_MHP), + LABEL(KEY_LANGUAGE), + LABEL(KEY_TITLE), + LABEL(KEY_SUBTITLE), + LABEL(KEY_ANGLE), + LABEL(KEY_ZOOM), + LABEL(KEY_MODE), + LABEL(KEY_KEYBOARD), + LABEL(KEY_SCREEN), + LABEL(KEY_PC), + LABEL(KEY_TV), + LABEL(KEY_TV2), + LABEL(KEY_VCR), + LABEL(KEY_VCR2), + LABEL(KEY_SAT), + LABEL(KEY_SAT2), + LABEL(KEY_CD), + LABEL(KEY_TAPE), + LABEL(KEY_RADIO), + LABEL(KEY_TUNER), + LABEL(KEY_PLAYER), + LABEL(KEY_TEXT), + LABEL(KEY_DVD), + LABEL(KEY_AUX), + LABEL(KEY_MP3), + LABEL(KEY_AUDIO), + LABEL(KEY_VIDEO), + LABEL(KEY_DIRECTORY), + LABEL(KEY_LIST), + LABEL(KEY_MEMO), + LABEL(KEY_CALENDAR), + LABEL(KEY_RED), + LABEL(KEY_GREEN), + LABEL(KEY_YELLOW), + LABEL(KEY_BLUE), + LABEL(KEY_CHANNELUP), + LABEL(KEY_CHANNELDOWN), + LABEL(KEY_FIRST), + LABEL(KEY_LAST), + LABEL(KEY_AB), + LABEL(KEY_NEXT), + LABEL(KEY_RESTART), + LABEL(KEY_SLOW), + LABEL(KEY_SHUFFLE), + LABEL(KEY_BREAK), + LABEL(KEY_PREVIOUS), + LABEL(KEY_DIGITS), + LABEL(KEY_TEEN), + LABEL(KEY_TWEN), + LABEL(KEY_VIDEOPHONE), + LABEL(KEY_GAMES), + LABEL(KEY_ZOOMIN), + LABEL(KEY_ZOOMOUT), + LABEL(KEY_ZOOMRESET), + LABEL(KEY_WORDPROCESSOR), + LABEL(KEY_EDITOR), + LABEL(KEY_SPREADSHEET), + LABEL(KEY_GRAPHICSEDITOR), + LABEL(KEY_PRESENTATION), + LABEL(KEY_DATABASE), + LABEL(KEY_NEWS), + LABEL(KEY_VOICEMAIL), + LABEL(KEY_ADDRESSBOOK), + LABEL(KEY_MESSENGER), + LABEL(KEY_DISPLAYTOGGLE), + LABEL(KEY_SPELLCHECK), + LABEL(KEY_LOGOFF), + LABEL(KEY_DOLLAR), + LABEL(KEY_EURO), + LABEL(KEY_FRAMEBACK), + LABEL(KEY_FRAMEFORWARD), + LABEL(KEY_CONTEXT_MENU), + LABEL(KEY_MEDIA_REPEAT), + LABEL(KEY_10CHANNELSUP), + LABEL(KEY_10CHANNELSDOWN), + LABEL(KEY_IMAGES), + LABEL(KEY_DEL_EOL), + LABEL(KEY_DEL_EOS), + LABEL(KEY_INS_LINE), + LABEL(KEY_DEL_LINE), + LABEL(KEY_FN), + LABEL(KEY_FN_ESC), + LABEL(KEY_FN_F1), + LABEL(KEY_FN_F2), + LABEL(KEY_FN_F3), + LABEL(KEY_FN_F4), + LABEL(KEY_FN_F5), + LABEL(KEY_FN_F6), + LABEL(KEY_FN_F7), + LABEL(KEY_FN_F8), + LABEL(KEY_FN_F9), + LABEL(KEY_FN_F10), + LABEL(KEY_FN_F11), + LABEL(KEY_FN_F12), + LABEL(KEY_FN_1), + LABEL(KEY_FN_2), + LABEL(KEY_FN_D), + LABEL(KEY_FN_E), + LABEL(KEY_FN_F), + LABEL(KEY_FN_S), + LABEL(KEY_FN_B), + LABEL(KEY_BRL_DOT1), + LABEL(KEY_BRL_DOT2), + LABEL(KEY_BRL_DOT3), + LABEL(KEY_BRL_DOT4), + LABEL(KEY_BRL_DOT5), + LABEL(KEY_BRL_DOT6), + LABEL(KEY_BRL_DOT7), + LABEL(KEY_BRL_DOT8), + LABEL(KEY_BRL_DOT9), + LABEL(KEY_BRL_DOT10), + LABEL(KEY_NUMERIC_0), + LABEL(KEY_NUMERIC_1), + LABEL(KEY_NUMERIC_2), + LABEL(KEY_NUMERIC_3), + LABEL(KEY_NUMERIC_4), + LABEL(KEY_NUMERIC_5), + LABEL(KEY_NUMERIC_6), + LABEL(KEY_NUMERIC_7), + LABEL(KEY_NUMERIC_8), + LABEL(KEY_NUMERIC_9), + LABEL(KEY_NUMERIC_STAR), + LABEL(KEY_NUMERIC_POUND), + LABEL(KEY_CAMERA_FOCUS), + LABEL(KEY_WPS_BUTTON), + LABEL(KEY_TOUCHPAD_TOGGLE), + LABEL(KEY_TOUCHPAD_ON), + LABEL(KEY_TOUCHPAD_OFF), + LABEL(KEY_CAMERA_ZOOMIN), + LABEL(KEY_CAMERA_ZOOMOUT), + LABEL(KEY_CAMERA_UP), + LABEL(KEY_CAMERA_DOWN), + LABEL(KEY_CAMERA_LEFT), + LABEL(KEY_CAMERA_RIGHT), + LABEL(BTN_TRIGGER_HAPPY1), + LABEL(BTN_TRIGGER_HAPPY2), + LABEL(BTN_TRIGGER_HAPPY3), + LABEL(BTN_TRIGGER_HAPPY4), + LABEL(BTN_TRIGGER_HAPPY5), + LABEL(BTN_TRIGGER_HAPPY6), + LABEL(BTN_TRIGGER_HAPPY7), + LABEL(BTN_TRIGGER_HAPPY8), + LABEL(BTN_TRIGGER_HAPPY9), + LABEL(BTN_TRIGGER_HAPPY10), + LABEL(BTN_TRIGGER_HAPPY11), + LABEL(BTN_TRIGGER_HAPPY12), + LABEL(BTN_TRIGGER_HAPPY13), + LABEL(BTN_TRIGGER_HAPPY14), + LABEL(BTN_TRIGGER_HAPPY15), + LABEL(BTN_TRIGGER_HAPPY16), + LABEL(BTN_TRIGGER_HAPPY17), + LABEL(BTN_TRIGGER_HAPPY18), + LABEL(BTN_TRIGGER_HAPPY19), + LABEL(BTN_TRIGGER_HAPPY20), + LABEL(BTN_TRIGGER_HAPPY21), + LABEL(BTN_TRIGGER_HAPPY22), + LABEL(BTN_TRIGGER_HAPPY23), + LABEL(BTN_TRIGGER_HAPPY24), + LABEL(BTN_TRIGGER_HAPPY25), + LABEL(BTN_TRIGGER_HAPPY26), + LABEL(BTN_TRIGGER_HAPPY27), + LABEL(BTN_TRIGGER_HAPPY28), + LABEL(BTN_TRIGGER_HAPPY29), + LABEL(BTN_TRIGGER_HAPPY30), + LABEL(BTN_TRIGGER_HAPPY31), + LABEL(BTN_TRIGGER_HAPPY32), + LABEL(BTN_TRIGGER_HAPPY33), + LABEL(BTN_TRIGGER_HAPPY34), + LABEL(BTN_TRIGGER_HAPPY35), + LABEL(BTN_TRIGGER_HAPPY36), + LABEL(BTN_TRIGGER_HAPPY37), + LABEL(BTN_TRIGGER_HAPPY38), + LABEL(BTN_TRIGGER_HAPPY39), + LABEL(BTN_TRIGGER_HAPPY40), + LABEL_END, +}; + +static struct label rel_labels[] = { + LABEL(REL_X), + LABEL(REL_Y), + LABEL(REL_Z), + LABEL(REL_RX), + LABEL(REL_RY), + LABEL(REL_RZ), + LABEL(REL_HWHEEL), + LABEL(REL_DIAL), + LABEL(REL_WHEEL), + LABEL(REL_MISC), + LABEL_END, +}; + +static struct label abs_labels[] = { + LABEL(ABS_X), + LABEL(ABS_Y), + LABEL(ABS_Z), + LABEL(ABS_RX), + LABEL(ABS_RY), + LABEL(ABS_RZ), + LABEL(ABS_THROTTLE), + LABEL(ABS_RUDDER), + LABEL(ABS_WHEEL), + LABEL(ABS_GAS), + LABEL(ABS_BRAKE), + LABEL(ABS_HAT0X), + LABEL(ABS_HAT0Y), + LABEL(ABS_HAT1X), + LABEL(ABS_HAT1Y), + LABEL(ABS_HAT2X), + LABEL(ABS_HAT2Y), + LABEL(ABS_HAT3X), + LABEL(ABS_HAT3Y), + LABEL(ABS_PRESSURE), + LABEL(ABS_DISTANCE), + LABEL(ABS_TILT_X), + LABEL(ABS_TILT_Y), + LABEL(ABS_TOOL_WIDTH), + LABEL(ABS_VOLUME), + LABEL(ABS_MISC), + LABEL(ABS_MT_SLOT), + LABEL(ABS_MT_TOUCH_MAJOR), + LABEL(ABS_MT_TOUCH_MINOR), + LABEL(ABS_MT_WIDTH_MAJOR), + LABEL(ABS_MT_WIDTH_MINOR), + LABEL(ABS_MT_ORIENTATION), + LABEL(ABS_MT_POSITION_X), + LABEL(ABS_MT_POSITION_Y), + LABEL(ABS_MT_TOOL_TYPE), + LABEL(ABS_MT_BLOB_ID), + LABEL(ABS_MT_TRACKING_ID), + LABEL(ABS_MT_PRESSURE), + LABEL(ABS_MT_DISTANCE), + LABEL_END, +}; + +static struct label sw_labels[] = { + LABEL(SW_LID), + LABEL(SW_TABLET_MODE), + LABEL(SW_HEADPHONE_INSERT), + LABEL(SW_RFKILL_ALL), + LABEL(SW_RADIO), + LABEL(SW_MICROPHONE_INSERT), + LABEL(SW_DOCK), + LABEL(SW_LINEOUT_INSERT), + LABEL(SW_JACK_PHYSICAL_INSERT), + LABEL(SW_VIDEOOUT_INSERT), + LABEL(SW_CAMERA_LENS_COVER), + LABEL(SW_KEYPAD_SLIDE), + LABEL(SW_FRONT_PROXIMITY), + LABEL(SW_ROTATE_LOCK), + LABEL_END, +}; + +static struct label msc_labels[] = { + LABEL(MSC_SERIAL), + LABEL(MSC_PULSELED), + LABEL(MSC_GESTURE), + LABEL(MSC_RAW), + LABEL(MSC_SCAN), + LABEL_END, +}; + +static struct label led_labels[] = { + LABEL(LED_NUML), + LABEL(LED_CAPSL), + LABEL(LED_SCROLLL), + LABEL(LED_COMPOSE), + LABEL(LED_KANA), + LABEL(LED_SLEEP), + LABEL(LED_SUSPEND), + LABEL(LED_MUTE), + LABEL(LED_MISC), + LABEL(LED_MAIL), + LABEL(LED_CHARGING), + LABEL_END, +}; + +static struct label rep_labels[] = { + LABEL(REP_DELAY), + LABEL(REP_PERIOD), + LABEL_END, +}; + +static struct label snd_labels[] = { + LABEL(SND_CLICK), + LABEL(SND_BELL), + LABEL(SND_TONE), + LABEL_END, +}; + +static struct label id_labels[] = { + LABEL(ID_BUS), + LABEL(ID_VENDOR), + LABEL(ID_PRODUCT), + LABEL(ID_VERSION), + LABEL_END, +}; + +static struct label bus_labels[] = { + LABEL(BUS_PCI), + LABEL(BUS_ISAPNP), + LABEL(BUS_USB), + LABEL(BUS_HIL), + LABEL(BUS_BLUETOOTH), + LABEL(BUS_VIRTUAL), + LABEL(BUS_ISA), + LABEL(BUS_I8042), + LABEL(BUS_XTKBD), + LABEL(BUS_RS232), + LABEL(BUS_GAMEPORT), + LABEL(BUS_PARPORT), + LABEL(BUS_AMIGA), + LABEL(BUS_ADB), + LABEL(BUS_I2C), + LABEL(BUS_HOST), + LABEL(BUS_GSC), + LABEL(BUS_ATARI), + LABEL(BUS_SPI), + LABEL_END, +}; + +static struct label mt_tool_labels[] = { + LABEL(MT_TOOL_FINGER), + LABEL(MT_TOOL_PEN), + LABEL(MT_TOOL_MAX), + LABEL_END, +}; + +static struct label ff_status_labels[] = { + LABEL(FF_STATUS_STOPPED), + LABEL(FF_STATUS_PLAYING), + LABEL(FF_STATUS_MAX), + LABEL_END, +}; + +static struct label ff_labels[] = { + LABEL(FF_RUMBLE), + LABEL(FF_PERIODIC), + LABEL(FF_CONSTANT), + LABEL(FF_SPRING), + LABEL(FF_FRICTION), + LABEL(FF_DAMPER), + LABEL(FF_INERTIA), + LABEL(FF_RAMP), + LABEL(FF_SQUARE), + LABEL(FF_TRIANGLE), + LABEL(FF_SINE), + LABEL(FF_SAW_UP), + LABEL(FF_SAW_DOWN), + LABEL(FF_CUSTOM), + LABEL(FF_GAIN), + LABEL(FF_AUTOCENTER), + LABEL_END, +}; + +static struct label key_value_labels[] = { + { "UP", 0 }, + { "DOWN", 1 }, + { "REPEAT", 2 }, + LABEL_END, +}; diff --git a/toolbox/lsof.c b/toolbox/lsof.c index c55384b..4e2f77a 100644 --- a/toolbox/lsof.c +++ b/toolbox/lsof.c @@ -37,11 +37,16 @@ #include <stdlib.h> #include <unistd.h> +#include <pwd.h> +#include <sys/stat.h> + #define BUF_MAX 1024 -#define CMD_DISPLAY_MAX 10 +#define CMD_DISPLAY_MAX (9 + 1) +#define USER_DISPLAY_MAX (10 + 1) struct pid_info_t { pid_t pid; + char user[USER_DISPLAY_MAX]; char cmdline[CMD_DISPLAY_MAX]; @@ -82,7 +87,8 @@ void print_type(char *type, struct pid_info_t* info) if (!strcmp(link_dest, "/")) goto out; - printf("%-9s %5d %10s %4s %9s %18s %9s %10s %s\n", info->cmdline, info->pid, "???", type, + printf("%-9s %5d %10s %4s %9s %18s %9s %10s %s\n", + info->cmdline, info->pid, info->user, type, "???", "???", "???", "???", link_dest); out: @@ -113,7 +119,8 @@ void print_maps(struct pid_info_t* info) if (inode == 0 || !strcmp(device, "00:00")) continue; - printf("%-9s %5d %10s %4s %9s %18s %9zd %10ld %s\n", info->cmdline, info->pid, "???", "mem", + printf("%-9s %5d %10s %4s %9s %18s %9zd %10ld %s\n", + info->cmdline, info->pid, info->user, "mem", "???", device, offset, inode, file); } @@ -136,7 +143,8 @@ void print_fds(struct pid_info_t* info) if (dir == NULL) { char msg[BUF_MAX]; snprintf(msg, sizeof(msg), "%s (opendir: %s)", info->path, strerror(errno)); - printf("%-9s %5d %10s %4s %9s %18s %9s %10s %s\n", info->cmdline, info->pid, "???", "FDS", + printf("%-9s %5d %10s %4s %9s %18s %9s %10s %s\n", + info->cmdline, info->pid, info->user, "FDS", "", "", "", "", msg); goto out; } @@ -159,12 +167,26 @@ void lsof_dumpinfo(pid_t pid) { int fd; struct pid_info_t info; - info.pid = pid; + struct stat pidstat; + struct passwd *pw; + info.pid = pid; snprintf(info.path, sizeof(info.path), "/proc/%d/", pid); - info.parent_length = strlen(info.path); + // Get the UID by calling stat on the proc/pid directory. + if (!stat(info.path, &pidstat)) { + pw = getpwuid(pidstat.st_uid); + if (pw) { + strncpy(info.user, pw->pw_name, USER_DISPLAY_MAX - 1); + info.user[USER_DISPLAY_MAX - 1] = '\0'; + } else { + snprintf(info.user, USER_DISPLAY_MAX, "%d", (int)pidstat.st_uid); + } + } else { + strcpy(info.user, "???"); + } + // Read the command line information; each argument is terminated with NULL. strncat(info.path, "cmdline", sizeof(info.path)); fd = open(info.path, O_RDONLY); @@ -219,7 +241,7 @@ int lsof_main(int argc, char *argv[]) continue; // Only inspect directories that are PID numbers - pid = strtol(de->d_name, &endptr, 10); + pid = strtol(de->d_name, &endptr, 10); if (*endptr != '\0') continue; diff --git a/toolbox/mount.c b/toolbox/mount.c index 82ecc56..27cf3c9 100644 --- a/toolbox/mount.c +++ b/toolbox/mount.c @@ -15,8 +15,8 @@ #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) -// FIXME - only one loop mount is supported at a time -#define LOOP_DEVICE "/dev/block/loop0" +#define DEFAULT_LOOP_DEVICE "/dev/block/loop0" +#define LOOPDEV_MAXLEN 64 struct mount_opts { const char str[8]; @@ -87,7 +87,7 @@ static void add_extra_option(struct extra_opts *extra, char *s) } static unsigned long -parse_mount_options(char *arg, unsigned long rwflag, struct extra_opts *extra, int* loop) +parse_mount_options(char *arg, unsigned long rwflag, struct extra_opts *extra, int* loop, char *loopdev) { char *s; @@ -100,8 +100,15 @@ parse_mount_options(char *arg, unsigned long rwflag, struct extra_opts *extra, i if (no) s += 2; + if (strncmp(s, "loop=", 5) == 0) { + *loop = 1; + strlcpy(loopdev, s + 5, LOOPDEV_MAXLEN); + continue; + } + if (strcmp(s, "loop") == 0) { *loop = 1; + strlcpy(loopdev, DEFAULT_LOOP_DEVICE, LOOPDEV_MAXLEN); continue; } for (i = 0, res = 1; i < ARRAY_SIZE(options); i++) { @@ -131,7 +138,8 @@ static struct extra_opts extra; static unsigned long rwflag; static int -do_mount(char *dev, char *dir, char *type, unsigned long rwflag, void *data, int loop) +do_mount(char *dev, char *dir, char *type, unsigned long rwflag, void *data, int loop, + char *loopdev) { char *s; int error = 0; @@ -142,14 +150,13 @@ do_mount(char *dev, char *dir, char *type, unsigned long rwflag, void *data, int flags = (rwflag & MS_RDONLY) ? O_RDONLY : O_RDWR; - // FIXME - only one loop mount supported at a time file_fd = open(dev, flags); - if (file_fd < -1) { + if (file_fd < 0) { perror("open backing file failed"); return 1; } - device_fd = open(LOOP_DEVICE, flags); - if (device_fd < -1) { + device_fd = open(loopdev, flags); + if (device_fd < 0) { perror("open loop device failed"); close(file_fd); return 1; @@ -163,7 +170,7 @@ do_mount(char *dev, char *dir, char *type, unsigned long rwflag, void *data, int close(file_fd); close(device_fd); - dev = LOOP_DEVICE; + dev = loopdev; } while ((s = strsep(&type, ",")) != NULL) { @@ -268,6 +275,7 @@ int mount_main(int argc, char *argv[]) char *dir = NULL; int c; int loop = 0; + char loopdev[LOOPDEV_MAXLEN]; progname = argv[0]; rwflag = MS_VERBOSE; @@ -281,7 +289,7 @@ int mount_main(int argc, char *argv[]) break; switch (c) { case 'o': - rwflag = parse_mount_options(optarg, rwflag, &extra, &loop); + rwflag = parse_mount_options(optarg, rwflag, &extra, &loop, loopdev); break; case 'r': rwflag |= MS_RDONLY; @@ -319,6 +327,6 @@ int mount_main(int argc, char *argv[]) exit(1); } - return do_mount(dev, dir, type, rwflag, extra.str, loop); + return do_mount(dev, dir, type, rwflag, extra.str, loop, loopdev); /* We leak dev and dir in some cases, but we're about to exit */ } diff --git a/toolbox/start.c b/toolbox/start.c index 3bd9fbb..665a941 100644 --- a/toolbox/start.c +++ b/toolbox/start.c @@ -8,13 +8,14 @@ int start_main(int argc, char *argv[]) { char buf[1024]; + if(argc > 1) { property_set("ctl.start", argv[1]); } else { - /* default to "start zygote" "start runtime" */ + /* defaults to starting the common services stopped by stop.c */ + property_set("ctl.start", "surfaceflinger"); property_set("ctl.start", "zygote"); - property_set("ctl.start", "runtime"); } - + return 0; } diff --git a/toolbox/stop.c b/toolbox/stop.c index 05baffd..460f377 100644 --- a/toolbox/stop.c +++ b/toolbox/stop.c @@ -10,11 +10,10 @@ int stop_main(int argc, char *argv[]) if(argc > 1) { property_set("ctl.stop", argv[1]); } else{ - /* default to "stop runtime" "stop zygote" */ - property_set("ctl.stop", "runtime"); + /* defaults to stopping the common services */ property_set("ctl.stop", "zygote"); + property_set("ctl.stop", "surfaceflinger"); } return 0; } - diff --git a/toolbox/touch.c b/toolbox/touch.c new file mode 100644 index 0000000..b8ab310 --- /dev/null +++ b/toolbox/touch.c @@ -0,0 +1,87 @@ +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <stdlib.h> +#include <fcntl.h> + +static void usage(void) +{ + fprintf(stderr, "touch: usage: touch [-alm] [-t time_t] <file>\n"); + exit(1); +} + +int touch_main(int argc, char *argv[]) +{ + int i, fd, aflag = 0, mflag = 0, debug = 0, flags = 0; + struct timespec specified_time, times[2]; + char *file = 0; + + specified_time.tv_nsec = UTIME_NOW; + + for (i = 1; i < argc; i++) { + if (argv[i][0] == '-') { + /* an option */ + const char *arg = argv[i]+1; + while (arg[0]) { + switch (arg[0]) { + case 'a': aflag = 1; break; + case 'm': mflag = 1; break; + case 't': + if ((i+1) >= argc) + usage(); + specified_time.tv_sec = atol(argv[++i]); + if (specified_time.tv_sec == 0) { + fprintf(stderr, "touch: invalid time_t\n"); + exit(1); + } + specified_time.tv_nsec = 0; + break; + case 'l': flags |= AT_SYMLINK_NOFOLLOW; break; + case 'd': debug = 1; break; + default: + usage(); + } + arg++; + } + } else { + /* not an option, and only accept one filename */ + if (i+1 != argc) + usage(); + file = argv[i]; + } + } + + if (! file) { + fprintf(stderr, "touch: no file specified\n"); + exit(1); + } + + if (access(file, F_OK)) + if ((fd=creat(file, 0666)) != -1) + close(fd); + + if ((mflag == 0) && (aflag == 0)) + aflag = mflag = 1; + + if (aflag) + times[0] = specified_time; + else + times[0].tv_nsec = UTIME_OMIT; + + if (mflag) + times[1] = specified_time; + else + times[1].tv_nsec = UTIME_OMIT; + + if (debug) { + fprintf(stderr, "file = %s\n", file); + fprintf(stderr, "times[0].tv_sec = %ld, times[0].tv_nsec = %ld\n", times[0].tv_sec, times[0].tv_nsec); + fprintf(stderr, "times[1].tv_sec = %ld, times[1].tv_nsec = %ld\n", times[1].tv_sec, times[1].tv_nsec); + fprintf(stderr, "flags = 0x%8.8x\n", flags); + } + + return utimensat(AT_FDCWD, file, times, flags); +} + diff --git a/toolbox/umount.c b/toolbox/umount.c index 92c6076..890e870 100644 --- a/toolbox/umount.c +++ b/toolbox/umount.c @@ -6,11 +6,26 @@ #include <string.h> #include <unistd.h> #include <linux/loop.h> +#include <errno.h> -// FIXME - only one loop mount is supported at a time -#define LOOP_DEVICE "/dev/block/loop0" +#define LOOPDEV_MAXLEN 64 +#define LOOP_MAJOR 7 -static int is_loop_mount(const char* path) +static int is_loop(char *dev) +{ + struct stat st; + int ret = 0; + + if (stat(dev, &st) == 0) { + if (S_ISBLK(st.st_mode) && (major(st.st_rdev) == LOOP_MAJOR)) { + ret = 1; + } + } + + return ret; +} + +static int is_loop_mount(const char* path, char *loopdev) { FILE* f; int count; @@ -22,14 +37,15 @@ static int is_loop_mount(const char* path) f = fopen("/proc/mounts", "r"); if (!f) { - fprintf(stdout, "could not open /proc/mounts\n"); + fprintf(stdout, "could not open /proc/mounts: %s\n", strerror(errno)); return -1; } do { count = fscanf(f, "%255s %255s %255s\n", device, mount_path, rest); if (count == 3) { - if (strcmp(LOOP_DEVICE, device) == 0 && strcmp(path, mount_path) == 0) { + if (is_loop(device) && strcmp(path, mount_path) == 0) { + strlcpy(loopdev, device, LOOPDEV_MAXLEN); result = 1; break; } @@ -43,27 +59,28 @@ static int is_loop_mount(const char* path) int umount_main(int argc, char *argv[]) { int loop, loop_fd; - + char loopdev[LOOPDEV_MAXLEN]; + if(argc != 2) { fprintf(stderr,"umount <path>\n"); return 1; } - loop = is_loop_mount(argv[1]); - if(umount(argv[1])){ - fprintf(stderr,"failed.\n"); + loop = is_loop_mount(argv[1], loopdev); + if (umount(argv[1])) { + fprintf(stderr, "failed: %s\n", strerror(errno)); return 1; } if (loop) { // free the loop device - loop_fd = open(LOOP_DEVICE, O_RDONLY); - if (loop_fd < -1) { - perror("open loop device failed"); + loop_fd = open(loopdev, O_RDONLY); + if (loop_fd < 0) { + fprintf(stderr, "open loop device failed: %s\n", strerror(errno)); return 1; } if (ioctl(loop_fd, LOOP_CLR_FD, 0) < 0) { - perror("ioctl LOOP_CLR_FD failed"); + fprintf(stderr, "ioctl LOOP_CLR_FD failed: %s\n", strerror(errno)); return 1; } |