diff options
Diffstat (limited to 'cmds')
-rw-r--r-- | cmds/atrace/atrace.cpp | 67 | ||||
-rw-r--r-- | cmds/bugreport/bugreport.c | 2 | ||||
-rw-r--r-- | cmds/dumpstate/dumpstate.c | 133 | ||||
-rw-r--r-- | cmds/dumpstate/dumpstate.h | 14 | ||||
-rw-r--r-- | cmds/dumpstate/utils.c | 100 | ||||
-rw-r--r-- | cmds/flatland/Android.mk | 4 | ||||
-rw-r--r-- | cmds/flatland/GLHelper.cpp | 10 | ||||
-rw-r--r-- | cmds/flatland/Main.cpp | 6 | ||||
-rw-r--r-- | cmds/installd/commands.c | 271 | ||||
-rw-r--r-- | cmds/installd/installd.c | 47 | ||||
-rw-r--r-- | cmds/installd/installd.h | 8 | ||||
-rw-r--r-- | cmds/installd/tests/installd_utils_test.cpp | 58 | ||||
-rw-r--r-- | cmds/installd/utils.c | 78 | ||||
-rw-r--r-- | cmds/screenshot/Android.mk | 12 | ||||
-rw-r--r-- | cmds/screenshot/screenshot.c | 171 | ||||
-rw-r--r-- | cmds/servicemanager/service_manager.c | 146 |
16 files changed, 668 insertions, 459 deletions
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp index 3461e38..9e5c910 100644 --- a/cmds/atrace/atrace.cpp +++ b/cmds/atrace/atrace.cpp @@ -40,7 +40,7 @@ using namespace android; #define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0]))) -enum { MAX_SYS_FILES = 8 }; +enum { MAX_SYS_FILES = 10 }; const char* k_traceTagsProperty = "debug.atrace.tags.enableflags"; const char* k_traceAppCmdlineProperty = "debug.atrace.app_cmdlines"; @@ -77,18 +77,24 @@ static const TracingCategory k_categories[] = { { "webview", "WebView", ATRACE_TAG_WEBVIEW, { } }, { "wm", "Window Manager", ATRACE_TAG_WINDOW_MANAGER, { } }, { "am", "Activity Manager", ATRACE_TAG_ACTIVITY_MANAGER, { } }, + { "sync", "Sync Manager", ATRACE_TAG_SYNC_MANAGER, { } }, { "audio", "Audio", ATRACE_TAG_AUDIO, { } }, { "video", "Video", ATRACE_TAG_VIDEO, { } }, { "camera", "Camera", ATRACE_TAG_CAMERA, { } }, { "hal", "Hardware Modules", ATRACE_TAG_HAL, { } }, + { "app", "Application", ATRACE_TAG_APP, { } }, { "res", "Resource Loading", ATRACE_TAG_RESOURCES, { } }, { "dalvik", "Dalvik VM", ATRACE_TAG_DALVIK, { } }, { "rs", "RenderScript", ATRACE_TAG_RS, { } }, { "bionic", "Bionic C Library", ATRACE_TAG_BIONIC, { } }, + { "power", "Power Management", ATRACE_TAG_POWER, { } }, { "sched", "CPU Scheduling", 0, { { REQ, "/sys/kernel/debug/tracing/events/sched/sched_switch/enable" }, { REQ, "/sys/kernel/debug/tracing/events/sched/sched_wakeup/enable" }, } }, + { "irq", "IRQ Events", 0, { + { REQ, "/sys/kernel/debug/tracing/events/irq/enable" }, + } }, { "freq", "CPU Frequency", 0, { { REQ, "/sys/kernel/debug/tracing/events/power/cpu_frequency/enable" }, { OPT, "/sys/kernel/debug/tracing/events/power/clock_set_rate/enable" }, @@ -100,6 +106,12 @@ static const TracingCategory k_categories[] = { { REQ, "/sys/kernel/debug/tracing/events/power/cpu_idle/enable" }, } }, { "disk", "Disk I/O", 0, { + { REQ, "/sys/kernel/debug/tracing/events/f2fs/f2fs_sync_file_enter/enable" }, + { REQ, "/sys/kernel/debug/tracing/events/f2fs/f2fs_sync_file_exit/enable" }, + { REQ, "/sys/kernel/debug/tracing/events/f2fs/f2fs_write_begin/enable" }, + { REQ, "/sys/kernel/debug/tracing/events/f2fs/f2fs_write_end/enable" }, + { REQ, "/sys/kernel/debug/tracing/events/ext4/ext4_da_write_begin/enable" }, + { REQ, "/sys/kernel/debug/tracing/events/ext4/ext4_da_write_end/enable" }, { REQ, "/sys/kernel/debug/tracing/events/ext4/ext4_sync_file_enter/enable" }, { REQ, "/sys/kernel/debug/tracing/events/ext4/ext4_sync_file_exit/enable" }, { REQ, "/sys/kernel/debug/tracing/events/block/block_rq_issue/enable" }, @@ -117,6 +129,12 @@ static const TracingCategory k_categories[] = { { "workq", "Kernel Workqueues", 0, { { REQ, "/sys/kernel/debug/tracing/events/workqueue/enable" }, } }, + { "memreclaim", "Kernel Memory Reclaim", 0, { + { REQ, "/sys/kernel/debug/tracing/events/vmscan/mm_vmscan_direct_reclaim_begin/enable" }, + { REQ, "/sys/kernel/debug/tracing/events/vmscan/mm_vmscan_direct_reclaim_end/enable" }, + { REQ, "/sys/kernel/debug/tracing/events/vmscan/mm_vmscan_kswapd_wake/enable" }, + { REQ, "/sys/kernel/debug/tracing/events/vmscan/mm_vmscan_kswapd_sleep/enable" }, + } }, }; /* Command line options */ @@ -323,11 +341,56 @@ static bool setTraceBufferSizeKB(int size) return writeStr(k_traceBufferSizePath, str); } +// Read the trace_clock sysfs file and return true if it matches the requested +// value. The trace_clock file format is: +// local [global] counter uptime perf +static bool isTraceClock(const char *mode) +{ + int fd = open(k_traceClockPath, O_RDONLY); + if (fd == -1) { + fprintf(stderr, "error opening %s: %s (%d)\n", k_traceClockPath, + strerror(errno), errno); + return false; + } + + char buf[4097]; + ssize_t n = read(fd, buf, 4096); + close(fd); + if (n == -1) { + fprintf(stderr, "error reading %s: %s (%d)\n", k_traceClockPath, + strerror(errno), errno); + return false; + } + buf[n] = '\0'; + + char *start = strchr(buf, '['); + if (start == NULL) { + return false; + } + start++; + + char *end = strchr(start, ']'); + if (end == NULL) { + return false; + } + *end = '\0'; + + return strcmp(mode, start) == 0; +} + // Enable or disable the kernel's use of the global clock. Disabling the global // clock will result in the kernel using a per-CPU local clock. +// Any write to the trace_clock sysfs file will reset the buffer, so only +// update it if the requested value is not the current value. static bool setGlobalClockEnable(bool enable) { - return writeStr(k_traceClockPath, enable ? "global" : "local"); + const char *clock = enable ? "global" : "local"; + + if (isTraceClock(clock)) { + return true; + } + + return writeStr(k_traceClockPath, clock); } static bool setPrintTgidEnableIfPresent(bool enable) diff --git a/cmds/bugreport/bugreport.c b/cmds/bugreport/bugreport.c index 4a0b511..11e9057 100644 --- a/cmds/bugreport/bugreport.c +++ b/cmds/bugreport/bugreport.c @@ -29,7 +29,7 @@ int main(int argc, char *argv[]) { property_set("ctl.start", "dumpstate"); /* socket will not be available until service starts */ - for (i = 0; i < 10; i++) { + for (i = 0; i < 20; i++) { s = socket_local_client("dumpstate", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM); diff --git a/cmds/dumpstate/dumpstate.c b/cmds/dumpstate/dumpstate.c index 913cedf..f142095 100644 --- a/cmds/dumpstate/dumpstate.c +++ b/cmds/dumpstate/dumpstate.c @@ -20,13 +20,13 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sys/capability.h> +#include <sys/prctl.h> #include <sys/resource.h> #include <sys/stat.h> #include <sys/time.h> #include <sys/wait.h> #include <unistd.h> -#include <sys/capability.h> -#include <linux/prctl.h> #include <cutils/properties.h> @@ -45,6 +45,36 @@ static char screenshot_path[PATH_MAX] = ""; #define PSTORE_LAST_KMSG "/sys/fs/pstore/console-ramoops" +#define TOMBSTONE_DIR "/data/tombstones" +#define TOMBSTONE_FILE_PREFIX TOMBSTONE_DIR "/tombstone_" +/* Can accomodate a tombstone number up to 9999. */ +#define TOMBSTONE_MAX_LEN (sizeof(TOMBSTONE_FILE_PREFIX) + 4) +#define NUM_TOMBSTONES 10 + +typedef struct { + char name[TOMBSTONE_MAX_LEN]; + int fd; +} tombstone_data_t; + +static tombstone_data_t tombstone_data[NUM_TOMBSTONES]; + +/* Get the fds of any tombstone that was modified in the last half an hour. */ +static void get_tombstone_fds(tombstone_data_t data[NUM_TOMBSTONES]) { + time_t thirty_minutes_ago = time(NULL) - 60*30; + for (size_t i = 0; i < NUM_TOMBSTONES; i++) { + snprintf(data[i].name, sizeof(data[i].name), "%s%02zu", TOMBSTONE_FILE_PREFIX, i); + int fd = open(data[i].name, O_RDONLY | O_CLOEXEC | O_NOFOLLOW); + struct stat st; + if (fstat(fd, &st) == 0 && S_ISREG(st.st_mode) && + (time_t) st.st_mtime >= thirty_minutes_ago) { + data[i].fd = fd; + } else { + close(fd); + data[i].fd = -1; + } + } +} + /* dumps the current system state to stdout */ static void dumpstate() { time_t now = time(NULL); @@ -89,11 +119,11 @@ static void dumpstate() { dump_file("BUDDYINFO", "/proc/buddyinfo"); dump_file("FRAGMENTATION INFO", "/d/extfrag/unusable_index"); - dump_file("KERNEL WAKELOCKS", "/proc/wakelocks"); dump_file("KERNEL WAKE SOURCES", "/d/wakeup_sources"); dump_file("KERNEL CPUFREQ", "/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state"); dump_file("KERNEL SYNC", "/d/sync"); + dump_file("KERNEL BLUEDROID", "/d/bluedroid"); run_command("PROCESSES", 10, "ps", "-P", NULL); run_command("PROCESSES AND THREADS", 10, "ps", "-t", "-p", "-P", NULL); @@ -103,6 +133,8 @@ static void dumpstate() { do_dmesg(); run_command("LIST OF OPEN FILES", 10, SU_PATH, "root", "lsof", NULL); + for_each_pid(do_showmap, "SMAPS OF ALL PROCESSES"); + for_each_tid(show_wchan, "BLOCKED PROCESS WAIT-CHANNELS"); if (screenshot_path[0]) { ALOGI("taking screenshot\n"); @@ -110,15 +142,11 @@ static void dumpstate() { ALOGI("wrote screenshot: %s\n", screenshot_path); } - for_each_pid(do_showmap, "SMAPS OF ALL PROCESSES"); - for_each_tid(show_wchan, "BLOCKED PROCESS WAIT-CHANNELS"); - // dump_file("EVENT LOG TAGS", "/etc/event-log-tags"); run_command("SYSTEM LOG", 20, "logcat", "-v", "threadtime", "-d", "*:v", NULL); run_command("EVENT LOG", 20, "logcat", "-b", "events", "-v", "threadtime", "-d", "*:v", NULL); run_command("RADIO LOG", 20, "logcat", "-b", "radio", "-v", "threadtime", "-d", "*:v", NULL); - /* show the traces we collected in main(), if that was done */ if (dump_traces_path != NULL) { dump_file("VM TRACES JUST NOW", dump_traces_path); @@ -130,10 +158,13 @@ static void dumpstate() { property_get("dalvik.vm.stack-trace-file", anr_traces_path, ""); if (!anr_traces_path[0]) { printf("*** NO VM TRACES FILE DEFINED (dalvik.vm.stack-trace-file)\n\n"); - } else if (stat(anr_traces_path, &st)) { - printf("*** NO ANR VM TRACES FILE (%s): %s\n\n", anr_traces_path, strerror(errno)); } else { - dump_file("VM TRACES AT LAST ANR", anr_traces_path); + int fd = open(anr_traces_path, O_RDONLY | O_CLOEXEC | O_NOFOLLOW); + if (fd < 0) { + printf("*** NO ANR VM TRACES FILE (%s): %s\n\n", anr_traces_path, strerror(errno)); + } else { + dump_file_from_fd("VM TRACES AT LAST ANR", anr_traces_path, fd); + } } /* slow traces for slow operations */ @@ -154,15 +185,24 @@ static void dumpstate() { } } + int dumped = 0; + for (size_t i = 0; i < NUM_TOMBSTONES; i++) { + if (tombstone_data[i].fd != -1) { + dumped = 1; + dump_file_from_fd("TOMBSTONE", tombstone_data[i].name, tombstone_data[i].fd); + tombstone_data[i].fd = -1; + } + } + if (!dumped) { + printf("*** NO TOMBSTONES to dump in %s\n\n", TOMBSTONE_DIR); + } + dump_file("NETWORK DEV INFO", "/proc/net/dev"); dump_file("QTAGUID NETWORK INTERFACES INFO", "/proc/net/xt_qtaguid/iface_stat_all"); dump_file("QTAGUID NETWORK INTERFACES INFO (xt)", "/proc/net/xt_qtaguid/iface_stat_fmt"); dump_file("QTAGUID CTRL INFO", "/proc/net/xt_qtaguid/ctrl"); dump_file("QTAGUID STATS INFO", "/proc/net/xt_qtaguid/stats"); - dump_file("NETWORK ROUTES", "/proc/net/route"); - dump_file("NETWORK ROUTES IPV6", "/proc/net/ipv6_route"); - if (!stat(PSTORE_LAST_KMSG, &st)) { /* Also TODO: Make console-ramoops CAP_SYSLOG protected. */ dump_file("LAST KMSG", PSTORE_LAST_KMSG); @@ -174,18 +214,22 @@ static void dumpstate() { dump_file("LAST PANIC CONSOLE", "/data/dontpanic/apanic_console"); dump_file("LAST PANIC THREADS", "/data/dontpanic/apanic_threads"); - run_command("SYSTEM SETTINGS", 20, SU_PATH, "root", "sqlite3", - "/data/data/com.android.providers.settings/databases/settings.db", - "pragma user_version; select * from system; select * from secure; select * from global;", NULL); + for_each_userid(do_dump_settings, NULL); /* The following have a tendency to get wedged when wifi drivers/fw goes belly-up. */ run_command("NETWORK INTERFACES", 10, SU_PATH, "root", "netcfg", NULL); + + run_command("IPv4 ADDRESSES", 10, "ip", "-4", "addr", "show", NULL); + run_command("IPv6 ADDRESSES", 10, "ip", "-6", "addr", "show", NULL); + run_command("IP RULES", 10, "ip", "rule", "show", NULL); run_command("IP RULES v6", 10, "ip", "-6", "rule", "show", NULL); dump_route_tables(); - dump_file("ARP CACHE", "/proc/net/arp"); + run_command("ARP CACHE", 10, "ip", "-4", "neigh", "show", NULL); + run_command("IPv6 ND CACHE", 10, "ip", "-6", "neigh", "show", NULL); + run_command("IPTABLES", 10, SU_PATH, "root", "iptables", "-L", "-nvx", NULL); run_command("IP6TABLES", 10, SU_PATH, "root", "ip6tables", "-L", "-nvx", NULL); run_command("IPTABLE NAT", 10, SU_PATH, "root", "iptables", "-t", "nat", "-L", "-nvx", NULL); @@ -332,8 +376,13 @@ static void usage() { } static void sigpipe_handler(int n) { - (void)n; - exit(EXIT_FAILURE); + // don't complain to stderr or stdout + _exit(EXIT_FAILURE); +} + +static void vibrate(FILE* vibrator, int ms) { + fprintf(vibrator, "%d\n", ms); + fflush(vibrator); } int main(int argc, char *argv[]) { @@ -342,8 +391,6 @@ int main(int argc, char *argv[]) { int do_compress = 0; int do_vibrate = 1; char* use_outfile = 0; - char* begin_sound = 0; - char* end_sound = 0; int use_socket = 0; int do_fb = 0; int do_broadcast = 0; @@ -356,8 +403,10 @@ int main(int argc, char *argv[]) { // correct program. return execl("/system/bin/bugreport", "/system/bin/bugreport", NULL); } + ALOGI("begin\n"); + /* clear SIGPIPE handler */ memset(&sigact, 0, sizeof(sigact)); sigact.sa_handler = sigpipe_handler; sigaction(SIGPIPE, &sigact, NULL); @@ -370,15 +419,11 @@ int main(int argc, char *argv[]) { fclose(oom_adj); } - /* very first thing, collect stack traces from Dalvik and native processes (needs root) */ - dump_traces_path = dump_traces(); - + /* parse arguments */ int c; - while ((c = getopt(argc, argv, "b:de:ho:svqzpB")) != -1) { + while ((c = getopt(argc, argv, "dho:svqzpB")) != -1) { switch (c) { - case 'b': begin_sound = optarg; break; case 'd': do_add_date = 1; break; - case 'e': end_sound = optarg; break; case 'o': use_outfile = optarg; break; case 's': use_socket = 1; break; case 'v': break; // compatibility no-op @@ -393,11 +438,14 @@ int main(int argc, char *argv[]) { } } + /* open the vibrator before dropping root */ FILE *vibrator = 0; if (do_vibrate) { - /* open the vibrator before dropping root */ vibrator = fopen("/sys/class/timed_output/vibrator/enable", "w"); - if (vibrator) fcntl(fileno(vibrator), F_SETFD, FD_CLOEXEC); + if (vibrator) { + fcntl(fileno(vibrator), F_SETFD, FD_CLOEXEC); + vibrate(vibrator, 150); + } } /* read /proc/cmdline before dropping root */ @@ -407,6 +455,13 @@ int main(int argc, char *argv[]) { fclose(cmdline); } + /* collect stack traces from Dalvik and native processes (needs root) */ + dump_traces_path = dump_traces(); + + /* Get the tombstone fds here while we are running as root. */ + get_tombstone_fds(tombstone_data); + + /* ensure we will keep capabilities when we drop root */ if (prctl(PR_SET_KEEPCAPS, 1) < 0) { ALOGE("prctl(PR_SET_KEEPCAPS) failed: %s\n", strerror(errno)); return -1; @@ -445,6 +500,7 @@ int main(int argc, char *argv[]) { return -1; } + /* redirect output if needed */ char path[PATH_MAX], tmp_path[PATH_MAX]; pid_t gzip_pid = -1; @@ -469,22 +525,12 @@ int main(int argc, char *argv[]) { gzip_pid = redirect_to_file(stdout, tmp_path, do_compress); } - if (begin_sound) { - play_sound(begin_sound); - } else if (vibrator) { - fputs("150", vibrator); - fflush(vibrator); - } - dumpstate(); - if (end_sound) { - play_sound(end_sound); - } else if (vibrator) { - int i; - for (i = 0; i < 3; i++) { - fputs("75\n", vibrator); - fflush(vibrator); + /* done */ + if (vibrator) { + for (int i = 0; i < 3; i++) { + vibrate(vibrator, 75); usleep((75 + 50) * 1000); } fclose(vibrator); @@ -501,6 +547,7 @@ int main(int argc, char *argv[]) { fprintf(stderr, "rename(%s, %s): %s\n", tmp_path, path, strerror(errno)); } + /* tell activity manager we're done */ if (do_broadcast && use_outfile && do_fb) { run_command(NULL, 5, "/system/bin/am", "broadcast", "--user", "0", "-a", "android.intent.action.BUGREPORT_FINISHED", diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h index 6906dcf..53bfff6 100644 --- a/cmds/dumpstate/dumpstate.h +++ b/cmds/dumpstate/dumpstate.h @@ -26,9 +26,13 @@ typedef void (for_each_pid_func)(int, const char *); typedef void (for_each_tid_func)(int, int, const char *); +typedef void (for_each_userid_func)(int); /* prints the contents of a file */ -int dump_file(const char *title, const char* path); +int dump_file(const char *title, const char *path); + +/* prints the contents of the fd */ +int dump_file_from_fd(const char *title, const char *path, int fd); /* forks a command and waits for it to finish -- terminate args with NULL */ int run_command(const char *title, int timeout_seconds, const char *command, ...); @@ -51,6 +55,9 @@ void for_each_pid(for_each_pid_func func, const char *header); /* for each thread in the system, run the specified function */ void for_each_tid(for_each_tid_func func, const char *header); +/* for each user id in the system, run the specified function */ +void for_each_userid(for_each_userid_func func, const char *header); + /* Displays a blocked processes in-kernel wait channel */ void show_wchan(int pid, int tid, const char *name); @@ -60,11 +67,14 @@ void do_showmap(int pid, const char *name); /* Gets the dmesg output for the kernel */ void do_dmesg(); +/* Dumps settings for a given user id */ +void do_dump_settings(int userid); + /* Prints the contents of all the routing tables, both IPv4 and IPv6. */ void dump_route_tables(); /* Play a sound via Stagefright */ -void play_sound(const char* path); +void play_sound(const char *path); /* Implemented by libdumpstate_board to dump board-specific info */ void dumpstate_board(); diff --git a/cmds/dumpstate/utils.c b/cmds/dumpstate/utils.c index b6110c6..e3042eb 100644 --- a/cmds/dumpstate/utils.c +++ b/cmds/dumpstate/utils.c @@ -42,6 +42,8 @@ #include "dumpstate.h" +static const int64_t NANOS_PER_SEC = 1000000000; + /* list of native processes to include in the native dumps */ static const char* native_processes_to_dump[] = { "/system/bin/drmserver", @@ -51,6 +53,29 @@ static const char* native_processes_to_dump[] = { NULL, }; +void for_each_userid(void (*func)(int), const char *header) { + DIR *d; + struct dirent *de; + + if (header) printf("\n------ %s ------\n", header); + func(0); + + if (!(d = opendir("/data/system/users"))) { + printf("Failed to open /data/system/users (%s)\n", strerror(errno)); + return; + } + + while ((de = readdir(d))) { + int userid; + if (de->d_type != DT_DIR || !(userid = atoi(de->d_name))) { + continue; + } + func(userid); + } + + closedir(d); +} + static void __for_each_pid(void (*helper)(int, const char *, void *), const char *header, void *arg) { DIR *d; struct dirent *de; @@ -175,6 +200,22 @@ out_close: return; } +void do_dump_settings(int userid) { + char title[255]; + char dbpath[255]; + char sql[255]; + sprintf(title, "SYSTEM SETTINGS (user %d)", userid); + if (userid == 0) { + strcpy(dbpath, "/data/data/com.android.providers.settings/databases/settings.db"); + strcpy(sql, "pragma user_version; select * from system; select * from secure; select * from global;"); + } else { + sprintf(dbpath, "/data/system/users/%d/settings.db", userid); + strcpy(sql, "pragma user_version; select * from system; select * from secure;"); + } + run_command(title, 20, SU_PATH, "root", "sqlite3", dbpath, sql, NULL); + return; +} + void do_dmesg() { printf("------ KERNEL LOG (dmesg) ------\n"); /* Get size of kernel buffer */ @@ -210,8 +251,7 @@ void do_showmap(int pid, const char *name) { } /* prints the contents of a file */ -int dump_file(const char *title, const char* path) { - char buffer[32768]; +int dump_file(const char *title, const char *path) { int fd = open(path, O_RDONLY); if (fd < 0) { int err = errno; @@ -220,6 +260,11 @@ int dump_file(const char *title, const char* path) { if (title) printf("\n"); return -1; } + return dump_file_from_fd(title, path, fd); +} + +int dump_file_from_fd(const char *title, const char *path, int fd) { + char buffer[32768]; if (title) printf("------ %s (%s", title, path); @@ -243,17 +288,23 @@ int dump_file(const char *title, const char* path) { } if (ret <= 0) break; } - close(fd); + if (!newline) printf("\n"); if (title) printf("\n"); return 0; } +static int64_t nanotime() { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return (int64_t)ts.tv_sec * NANOS_PER_SEC + ts.tv_nsec; +} + /* forks a command and waits for it to finish */ int run_command(const char *title, int timeout_seconds, const char *command, ...) { fflush(stdout); - clock_t start = clock(); + int64_t start = nanotime(); pid_t pid = fork(); /* handle error case */ @@ -270,6 +321,12 @@ int run_command(const char *title, int timeout_seconds, const char *command, ... /* make sure the child dies when dumpstate dies */ prctl(PR_SET_PDEATHSIG, SIGKILL); + /* just ignore SIGPIPE, will go down with parent's */ + struct sigaction sigact; + memset(&sigact, 0, sizeof(sigact)); + sigact.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &sigact, NULL); + va_list ap; va_start(ap, command); if (title) printf("------ %s (%s", title, command); @@ -291,19 +348,19 @@ int run_command(const char *title, int timeout_seconds, const char *command, ... for (;;) { int status; pid_t p = waitpid(pid, &status, WNOHANG); - float elapsed = (float) (clock() - start) / CLOCKS_PER_SEC; + int64_t elapsed = nanotime() - start; if (p == pid) { if (WIFSIGNALED(status)) { printf("*** %s: Killed by signal %d\n", command, WTERMSIG(status)); } else if (WIFEXITED(status) && WEXITSTATUS(status) > 0) { printf("*** %s: Exit code %d\n", command, WEXITSTATUS(status)); } - if (title) printf("[%s: %.1fs elapsed]\n\n", command, elapsed); + if (title) printf("[%s: %.3fs elapsed]\n\n", command, (float)elapsed / NANOS_PER_SEC); return status; } - if (timeout_seconds && elapsed > timeout_seconds) { - printf("*** %s: Timed out after %.1fs (killing pid %d)\n", command, elapsed, pid); + if (timeout_seconds && elapsed / NANOS_PER_SEC > timeout_seconds) { + printf("*** %s: Timed out after %ds (killing pid %d)\n", command, (int) elapsed, pid); kill(pid, SIGTERM); return -1; } @@ -526,21 +583,22 @@ const char *dump_traces() { } data[len] = '\0'; - if (!strcmp(data, "/system/bin/app_process")) { + if (!strncmp(data, "/system/bin/app_process", strlen("/system/bin/app_process"))) { /* skip zygote -- it won't dump its stack anyway */ snprintf(path, sizeof(path), "/proc/%d/cmdline", pid); - int fd = open(path, O_RDONLY); - len = read(fd, data, sizeof(data) - 1); - close(fd); + int cfd = open(path, O_RDONLY); + len = read(cfd, data, sizeof(data) - 1); + close(cfd); if (len <= 0) { continue; } data[len] = '\0'; - if (!strcmp(data, "zygote")) { + if (!strncmp(data, "zygote", strlen("zygote"))) { continue; } ++dalvik_found; + int64_t start = nanotime(); if (kill(pid, SIGQUIT)) { fprintf(stderr, "kill(%d, SIGQUIT): %s\n", pid, strerror(errno)); continue; @@ -557,12 +615,24 @@ const char *dump_traces() { struct inotify_event ie; read(ifd, &ie, sizeof(ie)); } + + if (lseek(fd, 0, SEEK_END) < 0) { + fprintf(stderr, "lseek: %s\n", strerror(errno)); + } else { + snprintf(data, sizeof(data), "[dump dalvik stack %d: %.3fs elapsed]\n", + pid, (float)(nanotime() - start) / NANOS_PER_SEC); + write(fd, data, strlen(data)); + } } else if (should_dump_native_traces(data)) { /* dump native process if appropriate */ if (lseek(fd, 0, SEEK_END) < 0) { fprintf(stderr, "lseek: %s\n", strerror(errno)); } else { + int64_t start = nanotime(); dump_backtrace_to_file(pid, fd); + snprintf(data, sizeof(data), "[dump native stack %d: %.3fs elapsed]\n", + pid, (float)(nanotime() - start) / NANOS_PER_SEC); + write(fd, data, strlen(data)); } } } @@ -590,10 +660,6 @@ error_close_fd: return result; } -void play_sound(const char* path) { - run_command(NULL, 5, "/system/bin/stagefright", "-o", "-a", path, NULL); -} - void dump_route_tables() { const char* const RT_TABLES_PATH = "/data/misc/net/rt_tables"; dump_file("RT_TABLES", RT_TABLES_PATH); diff --git a/cmds/flatland/Android.mk b/cmds/flatland/Android.mk index d9478fe..c295167 100644 --- a/cmds/flatland/Android.mk +++ b/cmds/flatland/Android.mk @@ -13,7 +13,9 @@ LOCAL_MODULE:= flatland LOCAL_MODULE_TAGS := tests LOCAL_MODULE_PATH := $(local_target_dir) - +LOCAL_MULTILIB := both +LOCAL_MODULE_STEM_32 := flatland +LOCAL_MODULE_STEM_64 := flatland64 LOCAL_SHARED_LIBRARIES := \ libEGL \ libGLESv2 \ diff --git a/cmds/flatland/GLHelper.cpp b/cmds/flatland/GLHelper.cpp index 05d082b..3155766 100644 --- a/cmds/flatland/GLHelper.cpp +++ b/cmds/flatland/GLHelper.cpp @@ -201,14 +201,16 @@ bool GLHelper::getShaderProgram(const char* name, GLuint* outPgm) { bool GLHelper::createNamedSurfaceTexture(GLuint name, uint32_t w, uint32_t h, sp<GLConsumer>* glConsumer, EGLSurface* surface) { - sp<BufferQueue> bq = new BufferQueue(mGraphicBufferAlloc); - sp<GLConsumer> glc = new GLConsumer(bq, name, - GL_TEXTURE_EXTERNAL_OES, false); + sp<IGraphicBufferProducer> producer; + sp<IGraphicBufferConsumer> consumer; + BufferQueue::createBufferQueue(&producer, &consumer, mGraphicBufferAlloc); + sp<GLConsumer> glc = new GLConsumer(consumer, name, + GL_TEXTURE_EXTERNAL_OES, false, true); glc->setDefaultBufferSize(w, h); glc->setDefaultMaxBufferCount(3); glc->setConsumerUsageBits(GRALLOC_USAGE_HW_COMPOSER); - sp<ANativeWindow> anw = new Surface(bq); + sp<ANativeWindow> anw = new Surface(producer); EGLSurface s = eglCreateWindowSurface(mDisplay, mConfig, anw.get(), NULL); if (s == EGL_NO_SURFACE) { fprintf(stderr, "eglCreateWindowSurface error: %#x\n", eglGetError()); diff --git a/cmds/flatland/Main.cpp b/cmds/flatland/Main.cpp index c0e5b3d..866203f 100644 --- a/cmds/flatland/Main.cpp +++ b/cmds/flatland/Main.cpp @@ -73,7 +73,7 @@ static const BenchmarkDesc benchmarks[] = { }, }, - { "3:2 Single Static Window", + { "4:3 Single Static Window", 2048, 1536, { 1536 }, { { // Window @@ -117,7 +117,7 @@ static const BenchmarkDesc benchmarks[] = { }, }, - { "3:2 App -> Home Transition", + { "4:3 App -> Home Transition", 2048, 1536, { 1536 }, { { // Wallpaper @@ -173,7 +173,7 @@ static const BenchmarkDesc benchmarks[] = { }, }, - { "3:2 SurfaceView -> Home Transition", + { "4:3 SurfaceView -> Home Transition", 2048, 1536, { 1536 }, { { // Wallpaper diff --git a/cmds/installd/commands.c b/cmds/installd/commands.c index 41a8f30..fc3972e 100644 --- a/cmds/installd/commands.c +++ b/cmds/installd/commands.c @@ -17,6 +17,7 @@ #include <inttypes.h> #include <sys/capability.h> #include "installd.h" +#include <cutils/sched_policy.h> #include <diskusage/dirsize.h> #include <selinux/android.h> @@ -176,10 +177,6 @@ int fix_uid(const char *pkgname, uid_t uid, gid_t gid) return 0; } -static int lib_dir_matcher(const char* file_name, const int is_dir) { - return is_dir && !strcmp(file_name, "lib"); -} - int delete_user_data(const char *pkgname, userid_t userid) { char pkgdir[PKG_PATH_MAX]; @@ -187,8 +184,7 @@ int delete_user_data(const char *pkgname, userid_t userid) if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, userid)) return -1; - /* delete contents, excluding "lib", but not the directory itself */ - return delete_dir_contents(pkgdir, 0, &lib_dir_matcher); + return delete_dir_contents(pkgdir, 0, NULL); } int make_user_data(const char *pkgname, uid_t uid, userid_t userid, const char* seinfo) @@ -310,10 +306,27 @@ int delete_cache(const char *pkgname, userid_t userid) if (create_pkg_path(cachedir, pkgname, CACHE_DIR_POSTFIX, userid)) return -1; - /* delete contents, not the directory, no exceptions */ + /* delete contents, not the directory, no exceptions */ return delete_dir_contents(cachedir, 0, NULL); } +int delete_code_cache(const char *pkgname, userid_t userid) +{ + char codecachedir[PKG_PATH_MAX]; + struct stat s; + + if (create_pkg_path(codecachedir, pkgname, CODE_CACHE_DIR_POSTFIX, userid)) + return -1; + + /* it's okay if code cache is missing */ + if (lstat(codecachedir, &s) == -1 && errno == ENOENT) { + return 0; + } + + /* delete contents, not the directory, no exceptions */ + return delete_dir_contents(codecachedir, 0, NULL); +} + /* Try to ensure free_size bytes of storage are available. * Returns 0 on success. * This is rather simple-minded because doing a full LRU would @@ -409,8 +422,14 @@ int move_dex(const char *src, const char *dst, const char *instruction_set) char src_dex[PKG_PATH_MAX]; char dst_dex[PKG_PATH_MAX]; - if (validate_apk_path(src)) return -1; - if (validate_apk_path(dst)) return -1; + if (validate_apk_path(src)) { + ALOGE("invalid apk path '%s' (bad prefix)\n", src); + return -1; + } + if (validate_apk_path(dst)) { + ALOGE("invalid apk path '%s' (bad prefix)\n", dst); + return -1; + } if (create_cache_path(src_dex, src, instruction_set)) return -1; if (create_cache_path(dst_dex, dst, instruction_set)) return -1; @@ -428,12 +447,18 @@ int rm_dex(const char *path, const char *instruction_set) { char dex_path[PKG_PATH_MAX]; - if (validate_apk_path(path)) return -1; + if (validate_apk_path(path) && validate_system_app_path(path)) { + ALOGE("invalid apk path '%s' (bad prefix)\n", path); + return -1; + } + if (create_cache_path(dex_path, path, instruction_set)) return -1; ALOGV("unlink %s\n", dex_path); if (unlink(dex_path) < 0) { - ALOGE("Couldn't unlink %s: %s\n", dex_path, strerror(errno)); + if (errno != ENOENT) { + ALOGE("Couldn't unlink %s: %s\n", dex_path, strerror(errno)); + } return -1; } else { return 0; @@ -559,7 +584,6 @@ done: return 0; } - int create_cache_path(char path[PKG_PATH_MAX], const char *src, const char *instruction_set) { char *tmp; @@ -622,9 +646,58 @@ static void run_dexopt(int zip_fd, int odex_fd, const char* input_file_name, ALOGE("execl(%s) failed: %s\n", DEX_OPT_BIN, strerror(errno)); } -static void run_dex2oat(int zip_fd, int oat_fd, const char* input_file_name, +static void run_patchoat(int input_fd, int oat_fd, const char* input_file_name, const char* output_file_name, const char *pkgname, const char *instruction_set) { + static const int MAX_INT_LEN = 12; // '-'+10dig+'\0' -OR- 0x+8dig + static const unsigned int MAX_INSTRUCTION_SET_LEN = 7; + + static const char* PATCHOAT_BIN = "/system/bin/patchoat"; + if (strlen(instruction_set) >= MAX_INSTRUCTION_SET_LEN) { + ALOGE("Instruction set %s longer than max length of %d", + instruction_set, MAX_INSTRUCTION_SET_LEN); + return; + } + + /* input_file_name/input_fd should be the .odex/.oat file that is precompiled. I think*/ + char instruction_set_arg[strlen("--instruction-set=") + MAX_INSTRUCTION_SET_LEN]; + char output_oat_fd_arg[strlen("--output-oat-fd=") + MAX_INT_LEN]; + char input_oat_fd_arg[strlen("--input-oat-fd=") + MAX_INT_LEN]; + const char* patched_image_location_arg = "--patched-image-location=/system/framework/boot.art"; + // The caller has already gotten all the locks we need. + const char* no_lock_arg = "--no-lock-output"; + sprintf(instruction_set_arg, "--instruction-set=%s", instruction_set); + sprintf(output_oat_fd_arg, "--output-oat-fd=%d", oat_fd); + sprintf(input_oat_fd_arg, "--input-oat-fd=%d", input_fd); + ALOGE("Running %s isa=%s in-fd=%d (%s) out-fd=%d (%s)\n", + PATCHOAT_BIN, instruction_set, input_fd, input_file_name, oat_fd, output_file_name); + + /* patchoat, patched-image-location, no-lock, isa, input-fd, output-fd */ + char* argv[7]; + argv[0] = (char*) PATCHOAT_BIN; + argv[1] = (char*) patched_image_location_arg; + argv[2] = (char*) no_lock_arg; + argv[3] = instruction_set_arg; + argv[4] = output_oat_fd_arg; + argv[5] = input_oat_fd_arg; + argv[6] = NULL; + + execv(PATCHOAT_BIN, (char* const *)argv); + ALOGE("execv(%s) failed: %s\n", PATCHOAT_BIN, strerror(errno)); +} + +static void run_dex2oat(int zip_fd, int oat_fd, const char* input_file_name, + const char* output_file_name, const char *pkgname, const char *instruction_set, + bool vm_safe_mode) +{ + static const unsigned int MAX_INSTRUCTION_SET_LEN = 7; + + if (strlen(instruction_set) >= MAX_INSTRUCTION_SET_LEN) { + ALOGE("Instruction set %s longer than max length of %d", + instruction_set, MAX_INSTRUCTION_SET_LEN); + return; + } + char prop_buf[PROPERTY_VALUE_MAX]; bool profiler = (property_get("dalvik.vm.profiler", prop_buf, "0") > 0) && (prop_buf[0] == '1'); @@ -634,38 +707,51 @@ static void run_dex2oat(int zip_fd, int oat_fd, const char* input_file_name, char dex2oat_Xmx_flag[PROPERTY_VALUE_MAX]; bool have_dex2oat_Xmx_flag = property_get("dalvik.vm.dex2oat-Xmx", dex2oat_Xmx_flag, NULL) > 0; + char dex2oat_compiler_filter_flag[PROPERTY_VALUE_MAX]; + bool have_dex2oat_compiler_filter_flag = property_get("dalvik.vm.dex2oat-filter", + dex2oat_compiler_filter_flag, NULL) > 0; + + char dex2oat_isa_features_key[PROPERTY_KEY_MAX]; + sprintf(dex2oat_isa_features_key, "dalvik.vm.isa.%s.features", instruction_set); + char dex2oat_isa_features[PROPERTY_VALUE_MAX]; + bool have_dex2oat_isa_features = property_get(dex2oat_isa_features_key, + dex2oat_isa_features, NULL) > 0; + char dex2oat_flags[PROPERTY_VALUE_MAX]; bool have_dex2oat_flags = property_get("dalvik.vm.dex2oat-flags", dex2oat_flags, NULL) > 0; ALOGV("dalvik.vm.dex2oat-flags=%s\n", dex2oat_flags); + // If we booting without the real /data, don't spend time compiling. + char vold_decrypt[PROPERTY_VALUE_MAX]; + bool have_vold_decrypt = property_get("vold.decrypt", vold_decrypt, "") > 0; + bool skip_compilation = (have_vold_decrypt && + (strcmp(vold_decrypt, "trigger_restart_min_framework") == 0 || + (strcmp(vold_decrypt, "1") == 0))); + static const char* DEX2OAT_BIN = "/system/bin/dex2oat"; static const char* RUNTIME_ARG = "--runtime-arg"; static const int MAX_INT_LEN = 12; // '-'+10dig+'\0' -OR- 0x+8dig - static const unsigned int MAX_INSTRUCTION_SET_LEN = 32; - - if (strlen(instruction_set) >= MAX_INSTRUCTION_SET_LEN) { - ALOGE("Instruction set %s longer than max length of %d", - instruction_set, MAX_INSTRUCTION_SET_LEN); - return; - } char zip_fd_arg[strlen("--zip-fd=") + MAX_INT_LEN]; char zip_location_arg[strlen("--zip-location=") + PKG_PATH_MAX]; char oat_fd_arg[strlen("--oat-fd=") + MAX_INT_LEN]; char oat_location_arg[strlen("--oat-location=") + PKG_PATH_MAX]; char instruction_set_arg[strlen("--instruction-set=") + MAX_INSTRUCTION_SET_LEN]; + char instruction_set_features_arg[strlen("--instruction-set-features=") + PROPERTY_VALUE_MAX]; char profile_file_arg[strlen("--profile-file=") + PKG_PATH_MAX]; char top_k_profile_threshold_arg[strlen("--top-k-profile-threshold=") + PROPERTY_VALUE_MAX]; char dex2oat_Xms_arg[strlen("-Xms") + PROPERTY_VALUE_MAX]; char dex2oat_Xmx_arg[strlen("-Xmx") + PROPERTY_VALUE_MAX]; + char dex2oat_compiler_filter_arg[strlen("--compiler-filter=") + PROPERTY_VALUE_MAX]; sprintf(zip_fd_arg, "--zip-fd=%d", zip_fd); sprintf(zip_location_arg, "--zip-location=%s", input_file_name); sprintf(oat_fd_arg, "--oat-fd=%d", oat_fd); sprintf(oat_location_arg, "--oat-location=%s", output_file_name); sprintf(instruction_set_arg, "--instruction-set=%s", instruction_set); + sprintf(instruction_set_features_arg, "--instruction-set-features=%s", dex2oat_isa_features); bool have_profile_file = false; bool have_top_k_profile_threshold = false; @@ -691,14 +777,25 @@ static void run_dex2oat(int zip_fd, int oat_fd, const char* input_file_name, if (have_dex2oat_Xmx_flag) { sprintf(dex2oat_Xmx_arg, "-Xmx%s", dex2oat_Xmx_flag); } + if (skip_compilation) { + strcpy(dex2oat_compiler_filter_arg, "--compiler-filter=verify-none"); + have_dex2oat_compiler_filter_flag = true; + } else if (vm_safe_mode) { + strcpy(dex2oat_compiler_filter_arg, "--compiler-filter=interpret-only"); + have_dex2oat_compiler_filter_flag = true; + } else if (have_dex2oat_compiler_filter_flag) { + sprintf(dex2oat_compiler_filter_arg, "--compiler-filter=%s", dex2oat_compiler_filter_flag); + } ALOGV("Running %s in=%s out=%s\n", DEX2OAT_BIN, input_file_name, output_file_name); char* argv[7 // program name, mandatory arguments and the final NULL + + (have_dex2oat_isa_features ? 1 : 0) + (have_profile_file ? 1 : 0) + (have_top_k_profile_threshold ? 1 : 0) + (have_dex2oat_Xms_flag ? 2 : 0) + (have_dex2oat_Xmx_flag ? 2 : 0) + + (have_dex2oat_compiler_filter_flag ? 1 : 0) + (have_dex2oat_flags ? 1 : 0)]; int i = 0; argv[i++] = (char*)DEX2OAT_BIN; @@ -707,6 +804,9 @@ static void run_dex2oat(int zip_fd, int oat_fd, const char* input_file_name, argv[i++] = oat_fd_arg; argv[i++] = oat_location_arg; argv[i++] = instruction_set_arg; + if (have_dex2oat_isa_features) { + argv[i++] = instruction_set_features_arg; + } if (have_profile_file) { argv[i++] = profile_file_arg; } @@ -721,6 +821,9 @@ static void run_dex2oat(int zip_fd, int oat_fd, const char* input_file_name, argv[i++] = (char*)RUNTIME_ARG; argv[i++] = dex2oat_Xmx_arg; } + if (have_dex2oat_compiler_filter_flag) { + argv[i++] = dex2oat_compiler_filter_arg; + } if (have_dex2oat_flags) { argv[i++] = dex2oat_flags; } @@ -757,15 +860,18 @@ static int wait_child(pid_t pid) } } -int dexopt(const char *apk_path, uid_t uid, int is_public, - const char *pkgname, const char *instruction_set) +int dexopt(const char *apk_path, uid_t uid, bool is_public, + const char *pkgname, const char *instruction_set, + bool vm_safe_mode, bool is_patchoat) { struct utimbuf ut; - struct stat apk_stat, dex_stat; + struct stat input_stat, dex_stat; char out_path[PKG_PATH_MAX]; char persist_sys_dalvik_vm_lib[PROPERTY_VALUE_MAX]; char *end; - int res, zip_fd=-1, out_fd=-1; + const char *input_file; + char in_odex_path[PKG_PATH_MAX]; + int res, input_fd=-1, out_fd=-1; if (strlen(apk_path) >= (PKG_PATH_MAX - 8)) { return -1; @@ -774,12 +880,20 @@ int dexopt(const char *apk_path, uid_t uid, int is_public, /* The command to run depend on the value of persist.sys.dalvik.vm.lib */ property_get("persist.sys.dalvik.vm.lib.2", persist_sys_dalvik_vm_lib, "libart.so"); + if (is_patchoat && strncmp(persist_sys_dalvik_vm_lib, "libart", 6) != 0) { + /* We may only patch if we are libart */ + ALOGE("Patching is only supported in libart\n"); + return -1; + } + /* Before anything else: is there a .odex file? If so, we have * precompiled the apk and there is nothing to do here. + * + * We skip this if we are doing a patchoat. */ strcpy(out_path, apk_path); end = strrchr(out_path, '.'); - if (end != NULL) { + if (end != NULL && !is_patchoat) { strcpy(end, ".odex"); if (stat(out_path, &dex_stat) == 0) { return 0; @@ -790,12 +904,33 @@ int dexopt(const char *apk_path, uid_t uid, int is_public, return -1; } - memset(&apk_stat, 0, sizeof(apk_stat)); - stat(apk_path, &apk_stat); + if (is_patchoat) { + /* /system/framework/whatever.jar -> /system/framework/<isa>/whatever.odex */ + strcpy(in_odex_path, apk_path); + end = strrchr(in_odex_path, '/'); + if (end == NULL) { + ALOGE("apk_path '%s' has no '/'s in it?!\n", apk_path); + return -1; + } + const char *apk_end = apk_path + (end - in_odex_path); // strrchr(apk_path, '/'); + strcpy(end + 1, instruction_set); // in_odex_path now is /system/framework/<isa>\0 + strcat(in_odex_path, apk_end); + end = strrchr(in_odex_path, '.'); + if (end == NULL) { + return -1; + } + strcpy(end + 1, "odex"); + input_file = in_odex_path; + } else { + input_file = apk_path; + } + + memset(&input_stat, 0, sizeof(input_stat)); + stat(input_file, &input_stat); - zip_fd = open(apk_path, O_RDONLY, 0); - if (zip_fd < 0) { - ALOGE("installd cannot open '%s' for input during dexopt\n", apk_path); + input_fd = open(input_file, O_RDONLY, 0); + if (input_fd < 0) { + ALOGE("installd cannot open '%s' for input during dexopt\n", input_file); return -1; } @@ -822,7 +957,7 @@ int dexopt(const char *apk_path, uid_t uid, int is_public, } - ALOGV("DexInv: --- BEGIN '%s' ---\n", apk_path); + ALOGV("DexInv: --- BEGIN '%s' ---\n", input_file); pid_t pid; pid = fork(); @@ -846,15 +981,24 @@ int dexopt(const char *apk_path, uid_t uid, int is_public, ALOGE("capset failed: %s\n", strerror(errno)); exit(66); } + if (set_sched_policy(0, SP_BACKGROUND) < 0) { + ALOGE("set_sched_policy failed: %s\n", strerror(errno)); + exit(70); + } if (flock(out_fd, LOCK_EX | LOCK_NB) != 0) { ALOGE("flock(%s) failed: %s\n", out_path, strerror(errno)); exit(67); } if (strncmp(persist_sys_dalvik_vm_lib, "libdvm", 6) == 0) { - run_dexopt(zip_fd, out_fd, apk_path, out_path); + run_dexopt(input_fd, out_fd, input_file, out_path); } else if (strncmp(persist_sys_dalvik_vm_lib, "libart", 6) == 0) { - run_dex2oat(zip_fd, out_fd, apk_path, out_path, pkgname, instruction_set); + if (is_patchoat) { + run_patchoat(input_fd, out_fd, input_file, out_path, pkgname, instruction_set); + } else { + run_dex2oat(input_fd, out_fd, input_file, out_path, pkgname, instruction_set, + vm_safe_mode); + } } else { exit(69); /* Unexpected persist.sys.dalvik.vm.lib value */ } @@ -862,19 +1006,19 @@ int dexopt(const char *apk_path, uid_t uid, int is_public, } else { res = wait_child(pid); if (res == 0) { - ALOGV("DexInv: --- END '%s' (success) ---\n", apk_path); + ALOGV("DexInv: --- END '%s' (success) ---\n", input_file); } else { - ALOGE("DexInv: --- END '%s' --- status=0x%04x, process failed\n", apk_path, res); + ALOGE("DexInv: --- END '%s' --- status=0x%04x, process failed\n", input_file, res); goto fail; } } - ut.actime = apk_stat.st_atime; - ut.modtime = apk_stat.st_mtime; + ut.actime = input_stat.st_atime; + ut.modtime = input_stat.st_mtime; utime(out_path, &ut); close(out_fd); - close(zip_fd); + close(input_fd); return 0; fail: @@ -882,8 +1026,8 @@ fail: close(out_fd); unlink(out_path); } - if (zip_fd >= 0) { - close(zip_fd); + if (input_fd >= 0) { + close(input_fd); } return -1; } @@ -1411,50 +1555,3 @@ int restorecon_data(const char* pkgName, const char* seinfo, uid_t uid) return ret; } -static int prune_dex_exclusion_predicate(const char *file_name, const int is_dir) -{ - // Exclude all directories. The top level command will be - // given a list of ISA specific directories that are assumed - // to be flat. - if (is_dir) { - return 1; - } - - - // Don't exclude regular files that start with the list - // of prefixes. - static const char data_app_prefix[] = "data@app@"; - static const char data_priv_app_prefix[] = "data@priv-app@"; - if (!strncmp(file_name, data_app_prefix, sizeof(data_app_prefix) - 1) || - !strncmp(file_name, data_priv_app_prefix, sizeof(data_priv_app_prefix) - 1)) { - return 0; - } - - // Exclude all regular files that don't start with the prefix "data@app@" or - // "data@priv-app@". - return 1; -} - -int prune_dex_cache(const char* subdir) { - // "." is handled as a special case, and refers to - // DALVIK_CACHE_PREFIX (usually /data/dalvik-cache). - const bool is_dalvik_cache_root = !strcmp(subdir, "."); - - // Don't allow the path to contain "." or ".." except for the - // special case above. This is much stricter than we need to be, - // but there's no good reason to support them. - if (strchr(subdir, '.' ) != NULL && !is_dalvik_cache_root) { - return -1; - } - - if (!is_dalvik_cache_root) { - char full_path[PKG_PATH_MAX]; - snprintf(full_path, sizeof(full_path), "%s%s", DALVIK_CACHE_PREFIX, subdir); - return delete_dir_contents(full_path, 0, &prune_dex_exclusion_predicate); - } - - - // When subdir == ".", clean the contents of the top level - // dalvik-cache directory. - return delete_dir_contents(DALVIK_CACHE_PREFIX, 0, &prune_dex_exclusion_predicate); -} diff --git a/cmds/installd/installd.c b/cmds/installd/installd.c index e072d0d..3078f20 100644 --- a/cmds/installd/installd.c +++ b/cmds/installd/installd.c @@ -15,7 +15,7 @@ */ #include <sys/capability.h> -#include <linux/prctl.h> +#include <sys/prctl.h> #include <selinux/android.h> #include <selinux/avc.h> @@ -38,8 +38,8 @@ static int do_install(char **arg, char reply[REPLY_MAX]) static int do_dexopt(char **arg, char reply[REPLY_MAX]) { - /* apk_path, uid, is_public, pkgname, instruction_set */ - return dexopt(arg[0], atoi(arg[1]), atoi(arg[2]), arg[3], arg[4]); + /* apk_path, uid, is_public, pkgname, instruction_set, vm_safe_mode, should_relocate */ + return dexopt(arg[0], atoi(arg[1]), atoi(arg[2]), arg[3], arg[4], atoi(arg[5]), 0); } static int do_move_dex(char **arg, char reply[REPLY_MAX]) @@ -77,6 +77,11 @@ static int do_rm_cache(char **arg, char reply[REPLY_MAX]) return delete_cache(arg[0], atoi(arg[1])); /* pkgname, userid */ } +static int do_rm_code_cache(char **arg, char reply[REPLY_MAX]) +{ + return delete_code_cache(arg[0], atoi(arg[1])); /* pkgname, userid */ +} + static int do_get_size(char **arg, char reply[REPLY_MAX]) { int64_t codesize = 0; @@ -140,10 +145,9 @@ static int do_restorecon_data(char **arg, char reply[REPLY_MAX] __attribute__((u /* pkgName, seinfo, uid*/ } -static int do_prune_dex_cache(char **arg __attribute__((unused)), - char reply[REPLY_MAX] __attribute__((unused))) -{ - return prune_dex_cache(arg[0] /* subdirectory name */); +static int do_patchoat(char **arg, char reply[REPLY_MAX]) { + /* apk_path, uid, is_public, pkgname, instruction_set, vm_safe_mode, should_relocate */ + return dexopt(arg[0], atoi(arg[1]), atoi(arg[2]), arg[3], arg[4], 0, 1); } struct cmdinfo { @@ -155,7 +159,7 @@ struct cmdinfo { struct cmdinfo cmds[] = { { "ping", 0, do_ping }, { "install", 4, do_install }, - { "dexopt", 5, do_dexopt }, + { "dexopt", 6, do_dexopt }, { "movedex", 3, do_move_dex }, { "rmdex", 2, do_rm_dex }, { "remove", 2, do_remove }, @@ -163,6 +167,7 @@ struct cmdinfo cmds[] = { { "fixuid", 3, do_fixuid }, { "freecache", 1, do_free_cache }, { "rmcache", 2, do_rm_cache }, + { "rmcodecache", 2, do_rm_code_cache }, { "getsize", 7, do_get_size }, { "rmuserdata", 2, do_rm_user_data }, { "movefiles", 0, do_movefiles }, @@ -172,7 +177,7 @@ struct cmdinfo cmds[] = { { "rmuser", 1, do_rm_user }, { "idmap", 3, do_idmap }, { "restorecondata", 3, do_restorecon_data }, - { "prunedexcache", 1, do_prune_dex_cache }, + { "patchoat", 5, do_patchoat }, }; static int readx(int s, void *_buf, int count) @@ -323,7 +328,7 @@ int initialize_globals() { } // Take note of the system and vendor directories. - android_system_dirs.count = 2; + android_system_dirs.count = 4; android_system_dirs.dirs = calloc(android_system_dirs.count, sizeof(dir_rec_t)); if (android_system_dirs.dirs == NULL) { @@ -331,22 +336,24 @@ int initialize_globals() { return -1; } - // system - if (get_path_from_env(&android_system_dirs.dirs[0], "ANDROID_ROOT") < 0) { - free_globals(); + dir_rec_t android_root_dir; + if (get_path_from_env(&android_root_dir, "ANDROID_ROOT") < 0) { + ALOGE("Missing ANDROID_ROOT; aborting\n"); return -1; } - // append "app/" to dirs[0] - char *system_app_path = build_string2(android_system_dirs.dirs[0].path, APP_SUBDIR); - android_system_dirs.dirs[0].path = system_app_path; - android_system_dirs.dirs[0].len = strlen(system_app_path); + android_system_dirs.dirs[0].path = build_string2(android_root_dir.path, APP_SUBDIR); + android_system_dirs.dirs[0].len = strlen(android_system_dirs.dirs[0].path); - // vendor - // TODO replace this with an environment variable (doesn't exist yet) - android_system_dirs.dirs[1].path = "/vendor/app/"; + android_system_dirs.dirs[1].path = build_string2(android_root_dir.path, PRIV_APP_SUBDIR); android_system_dirs.dirs[1].len = strlen(android_system_dirs.dirs[1].path); + android_system_dirs.dirs[2].path = "/vendor/app/"; + android_system_dirs.dirs[2].len = strlen(android_system_dirs.dirs[2].path); + + android_system_dirs.dirs[3].path = "/oem/app/"; + android_system_dirs.dirs[3].len = strlen(android_system_dirs.dirs[3].path); + return 0; } diff --git a/cmds/installd/installd.h b/cmds/installd/installd.h index 258647a..a5cad45 100644 --- a/cmds/installd/installd.h +++ b/cmds/installd/installd.h @@ -59,8 +59,10 @@ #define PKG_LIB_POSTFIX "/lib" #define CACHE_DIR_POSTFIX "/cache" +#define CODE_CACHE_DIR_POSTFIX "/code_cache" #define APP_SUBDIR "app/" // sub-directory under ANDROID_DATA +#define PRIV_APP_SUBDIR "priv-app/" // sub-directory under ANDROID_DATA #define APP_LIB_SUBDIR "app-lib/" // sub-directory under ANDROID_DATA @@ -207,6 +209,7 @@ int make_user_data(const char *pkgname, uid_t uid, userid_t userid, const char* int make_user_config(userid_t userid); int delete_user(userid_t userid); int delete_cache(const char *pkgname, userid_t userid); +int delete_code_cache(const char *pkgname, userid_t userid); int move_dex(const char *src, const char *dst, const char *instruction_set); int rm_dex(const char *path, const char *instruction_set); int protect(char *pkgname, gid_t gid); @@ -214,10 +217,9 @@ int get_size(const char *pkgname, userid_t userid, const char *apkpath, const ch const char *fwdlock_apkpath, const char *asecpath, const char *instruction_set, int64_t *codesize, int64_t *datasize, int64_t *cachesize, int64_t *asecsize); int free_cache(int64_t free_size); -int dexopt(const char *apk_path, uid_t uid, int is_public, const char *pkgName, - const char *instruction_set); +int dexopt(const char *apk_path, uid_t uid, bool is_public, const char *pkgName, + const char *instruction_set, bool vm_safe_mode, bool should_relocate); int movefiles(); int linklib(const char* target, const char* source, int userId); int idmap(const char *target_path, const char *overlay_path, uid_t uid); int restorecon_data(); -int prune_dex_cache(const char* subdir); diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp index 0b182af..94e4792 100644 --- a/cmds/installd/tests/installd_utils_test.cpp +++ b/cmds/installd/tests/installd_utils_test.cpp @@ -101,6 +101,11 @@ TEST_F(UtilsTest, IsValidApkPath_Internal) { EXPECT_EQ(0, validate_apk_path(internal1)) << internal1 << " should be allowed as a valid path"; + // b/16888084 + const char *path2 = TEST_APP_DIR "example.com/example.apk"; + EXPECT_EQ(0, validate_apk_path(path2)) + << path2 << " should be allowed as a valid path"; + const char *badint1 = TEST_APP_DIR "../example.apk"; EXPECT_EQ(-1, validate_apk_path(badint1)) << badint1 << " should be rejected as a invalid path"; @@ -109,9 +114,18 @@ TEST_F(UtilsTest, IsValidApkPath_Internal) { EXPECT_EQ(-1, validate_apk_path(badint2)) << badint2 << " should be rejected as a invalid path"; - const char *badint3 = TEST_APP_DIR "example.com/pkg.apk"; - EXPECT_EQ(-1, validate_apk_path(badint3)) - << badint3 << " should be rejected as a invalid path"; + // Only one subdir should be allowed. + const char *bad_path3 = TEST_APP_DIR "example.com/subdir/pkg.apk"; + EXPECT_EQ(-1, validate_apk_path(bad_path3)) + << bad_path3 << " should be rejected as a invalid path"; + + const char *bad_path4 = TEST_APP_DIR "example.com/subdir/../pkg.apk"; + EXPECT_EQ(-1, validate_apk_path(bad_path4)) + << bad_path4 << " should be rejected as a invalid path"; + + const char *bad_path5 = TEST_APP_DIR "example.com1/../example.com2/pkg.apk"; + EXPECT_EQ(-1, validate_apk_path(bad_path5)) + << bad_path5 << " should be rejected as a invalid path"; } TEST_F(UtilsTest, IsValidApkPath_Private) { @@ -120,6 +134,11 @@ TEST_F(UtilsTest, IsValidApkPath_Private) { EXPECT_EQ(0, validate_apk_path(private1)) << private1 << " should be allowed as a valid path"; + // b/16888084 + const char *path2 = TEST_APP_DIR "example.com/example.apk"; + EXPECT_EQ(0, validate_apk_path(path2)) + << path2 << " should be allowed as a valid path"; + const char *badpriv1 = TEST_APP_PRIVATE_DIR "../example.apk"; EXPECT_EQ(-1, validate_apk_path(badpriv1)) << badpriv1 << " should be rejected as a invalid path"; @@ -128,9 +147,18 @@ TEST_F(UtilsTest, IsValidApkPath_Private) { EXPECT_EQ(-1, validate_apk_path(badpriv2)) << badpriv2 << " should be rejected as a invalid path"; - const char *badpriv3 = TEST_APP_PRIVATE_DIR "example.com/pkg.apk"; - EXPECT_EQ(-1, validate_apk_path(badpriv3)) - << badpriv3 << " should be rejected as a invalid path"; + // Only one subdir should be allowed. + const char *bad_path3 = TEST_APP_PRIVATE_DIR "example.com/subdir/pkg.apk"; + EXPECT_EQ(-1, validate_apk_path(bad_path3)) + << bad_path3 << " should be rejected as a invalid path"; + + const char *bad_path4 = TEST_APP_PRIVATE_DIR "example.com/subdir/../pkg.apk"; + EXPECT_EQ(-1, validate_apk_path(bad_path4)) + << bad_path4 << " should be rejected as a invalid path"; + + const char *bad_path5 = TEST_APP_PRIVATE_DIR "example.com1/../example.com2/pkg.apk"; + EXPECT_EQ(-1, validate_apk_path(bad_path5)) + << bad_path5 << " should be rejected as a invalid path"; } @@ -218,6 +246,24 @@ TEST_F(UtilsTest, CheckSystemApp_BadPathEscapeFail) { << badapp3 << " should be rejected not a system path"; } +TEST_F(UtilsTest, CheckSystemApp_Subdir) { + const char *sysapp = TEST_SYSTEM_DIR1 "com.example/com.example.apk"; + EXPECT_EQ(0, validate_system_app_path(sysapp)) + << sysapp << " should be allowed as a system path"; + + const char *badapp = TEST_SYSTEM_DIR1 "com.example/subdir/com.example.apk"; + EXPECT_EQ(-1, validate_system_app_path(badapp)) + << badapp << " should be rejected not a system path"; + + const char *badapp1 = TEST_SYSTEM_DIR1 "com.example/subdir/../com.example.apk"; + EXPECT_EQ(-1, validate_system_app_path(badapp1)) + << badapp1 << " should be rejected not a system path"; + + const char *badapp2 = TEST_SYSTEM_DIR1 "com.example1/../com.example2/com.example.apk"; + EXPECT_EQ(-1, validate_system_app_path(badapp2)) + << badapp2 << " should be rejected not a system path"; +} + TEST_F(UtilsTest, GetPathFromString_NullPathFail) { dir_rec_t test1; EXPECT_EQ(-1, get_path_from_string(&test1, (const char *) NULL)) diff --git a/cmds/installd/utils.c b/cmds/installd/utils.c index 35172de..60d20de 100644 --- a/cmds/installd/utils.c +++ b/cmds/installd/utils.c @@ -808,6 +808,33 @@ void finish_cache_collection(cache_t* cache) } /** + * Validate that the path is valid in the context of the provided directory. + * The path is allowed to have at most one subdirectory and no indirections + * to top level directories (i.e. have ".."). + */ +static int validate_path(const dir_rec_t* dir, const char* path) { + size_t dir_len = dir->len; + const char* subdir = strchr(path + dir_len, '/'); + + // Only allow the path to have at most one subdirectory. + if (subdir != NULL) { + ++subdir; + if (strchr(subdir, '/') != NULL) { + ALOGE("invalid apk path '%s' (subdir?)\n", path); + return -1; + } + } + + // Directories can't have a period directly after the directory markers to prevent "..". + if ((path[dir_len] == '.') || ((subdir != NULL) && (*subdir == '.'))) { + ALOGE("invalid apk path '%s' (trickery)\n", path); + return -1; + } + + return 0; +} + +/** * Checks whether a path points to a system app (.apk file). Returns 0 * if it is a system app or -1 if it is not. */ @@ -817,11 +844,7 @@ int validate_system_app_path(const char* path) { for (i = 0; i < android_system_dirs.count; i++) { const size_t dir_len = android_system_dirs.dirs[i].len; if (!strncmp(path, android_system_dirs.dirs[i].path, dir_len)) { - if (path[dir_len] == '.' || strchr(path + dir_len, '/') != NULL) { - ALOGE("invalid system apk path '%s' (trickery)\n", path); - return -1; - } - return 0; + return validate_path(android_system_dirs.dirs + i, path); } } @@ -914,54 +937,25 @@ int copy_and_append(dir_rec_t* dst, const dir_rec_t* src, const char* suffix) { } /** - * Check whether path points to a valid path for an APK file. An ASEC - * directory is allowed to have one level of subdirectory names. Returns -1 - * when an invalid path is encountered and 0 when a valid path is encountered. + * Check whether path points to a valid path for an APK file. Only one level of + * subdirectory names is allowed. Returns -1 when an invalid path is encountered + * and 0 when a valid path is encountered. */ int validate_apk_path(const char *path) { - int allowsubdir = 0; - char *subdir = NULL; - size_t dir_len; - size_t path_len; + const dir_rec_t* dir = NULL; if (!strncmp(path, android_app_dir.path, android_app_dir.len)) { - dir_len = android_app_dir.len; + dir = &android_app_dir; } else if (!strncmp(path, android_app_private_dir.path, android_app_private_dir.len)) { - dir_len = android_app_private_dir.len; + dir = &android_app_private_dir; } else if (!strncmp(path, android_asec_dir.path, android_asec_dir.len)) { - dir_len = android_asec_dir.len; - allowsubdir = 1; + dir = &android_asec_dir; } else { - ALOGE("invalid apk path '%s' (bad prefix)\n", path); return -1; } - path_len = strlen(path); - - /* - * Only allow the path to have a subdirectory if it's been marked as being allowed. - */ - if ((subdir = strchr(path + dir_len, '/')) != NULL) { - ++subdir; - if (!allowsubdir - || (path_len > (size_t) (subdir - path) && (strchr(subdir, '/') != NULL))) { - ALOGE("invalid apk path '%s' (subdir?)\n", path); - return -1; - } - } - - /* - * Directories can't have a period directly after the directory markers - * to prevent ".." - */ - if (path[dir_len] == '.' - || (subdir != NULL && ((*subdir == '.') || (strchr(subdir, '/') != NULL)))) { - ALOGE("invalid apk path '%s' (trickery)\n", path); - return -1; - } - - return 0; + return validate_path(dir, path); } int append_and_increment(char** dst, const char* src, size_t* dst_size) { diff --git a/cmds/screenshot/Android.mk b/cmds/screenshot/Android.mk deleted file mode 100644 index 1ee7807..0000000 --- a/cmds/screenshot/Android.mk +++ /dev/null @@ -1,12 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := screenshot.c - -LOCAL_MODULE := screenshot - -LOCAL_SHARED_LIBRARIES := libcutils libz liblog -LOCAL_STATIC_LIBRARIES := libpng -LOCAL_C_INCLUDES += external/zlib external/libpng - -include $(BUILD_EXECUTABLE) diff --git a/cmds/screenshot/screenshot.c b/cmds/screenshot/screenshot.c deleted file mode 100644 index be1ecd4..0000000 --- a/cmds/screenshot/screenshot.c +++ /dev/null @@ -1,171 +0,0 @@ -#include <stdlib.h> -#include <stdio.h> -#include <unistd.h> -#include <string.h> -#include <fcntl.h> -#include <errno.h> - -#include <linux/fb.h> - -#include <zlib.h> -#include <png.h> - -#include "private/android_filesystem_config.h" - -#define LOG_TAG "screenshot" -#include <utils/Log.h> - -void take_screenshot(FILE *fb_in, FILE *fb_out) { - int fb; - char imgbuf[0x10000]; - struct fb_var_screeninfo vinfo; - png_structp png; - png_infop info; - unsigned int r,c,rowlen; - unsigned int bytespp,offset; - - fb = fileno(fb_in); - if(fb < 0) { - ALOGE("failed to open framebuffer\n"); - return; - } - fb_in = fdopen(fb, "r"); - - if(ioctl(fb, FBIOGET_VSCREENINFO, &vinfo) < 0) { - ALOGE("failed to get framebuffer info\n"); - return; - } - fcntl(fb, F_SETFD, FD_CLOEXEC); - - png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - if (png == NULL) { - ALOGE("failed png_create_write_struct\n"); - fclose(fb_in); - return; - } - - png_init_io(png, fb_out); - info = png_create_info_struct(png); - if (info == NULL) { - ALOGE("failed png_create_info_struct\n"); - png_destroy_write_struct(&png, NULL); - fclose(fb_in); - return; - } - if (setjmp(png_jmpbuf(png))) { - ALOGE("failed png setjmp\n"); - png_destroy_write_struct(&png, NULL); - fclose(fb_in); - return; - } - - bytespp = vinfo.bits_per_pixel / 8; - png_set_IHDR(png, info, - vinfo.xres, vinfo.yres, vinfo.bits_per_pixel / 4, - PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, - PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); - png_write_info(png, info); - - rowlen=vinfo.xres * bytespp; - if (rowlen > sizeof(imgbuf)) { - ALOGE("crazy rowlen: %d\n", rowlen); - png_destroy_write_struct(&png, NULL); - fclose(fb_in); - return; - } - - offset = vinfo.xoffset * bytespp + vinfo.xres * vinfo.yoffset * bytespp; - fseek(fb_in, offset, SEEK_SET); - - for(r=0; r<vinfo.yres; r++) { - int len = fread(imgbuf, 1, rowlen, fb_in); - if (len <= 0) break; - png_write_row(png, (png_bytep)imgbuf); - } - - png_write_end(png, info); - fclose(fb_in); - png_destroy_write_struct(&png, NULL); -} - -void fork_sound(const char* path) { - pid_t pid = fork(); - if (pid == 0) { - execl("/system/bin/stagefright", "stagefright", "-o", "-a", path, NULL); - } -} - -void usage() { - fprintf(stderr, - "usage: screenshot [-s soundfile] filename.png\n" - " -s: play a sound effect to signal success\n" - " -i: autoincrement to avoid overwriting filename.png\n" - ); -} - -int main(int argc, char**argv) { - FILE *png = NULL; - FILE *fb_in = NULL; - char outfile[PATH_MAX] = ""; - - char * soundfile = NULL; - int do_increment = 0; - - int c; - while ((c = getopt(argc, argv, "s:i")) != -1) { - switch (c) { - case 's': soundfile = optarg; break; - case 'i': do_increment = 1; break; - case '?': - case 'h': - usage(); exit(1); - } - } - argc -= optind; - argv += optind; - - if (argc < 1) { - usage(); exit(1); - } - - strlcpy(outfile, argv[0], PATH_MAX); - if (do_increment) { - struct stat st; - char base[PATH_MAX] = ""; - int i = 0; - while (stat(outfile, &st) == 0) { - if (!base[0]) { - char *p = strrchr(outfile, '.'); - if (p) *p = '\0'; - strcpy(base, outfile); - } - snprintf(outfile, PATH_MAX, "%s-%d.png", base, ++i); - } - } - - fb_in = fopen("/dev/graphics/fb0", "r"); - if (!fb_in) { - fprintf(stderr, "error: could not read framebuffer\n"); - exit(1); - } - - /* switch to non-root user and group */ - gid_t groups[] = { AID_LOG, AID_SDCARD_RW }; - setgroups(sizeof(groups)/sizeof(groups[0]), groups); - setuid(AID_SHELL); - - png = fopen(outfile, "w"); - if (!png) { - fprintf(stderr, "error: writing file %s: %s\n", - outfile, strerror(errno)); - exit(1); - } - - take_screenshot(fb_in, png); - - if (soundfile) { - fork_sound(soundfile); - } - - exit(0); -} diff --git a/cmds/servicemanager/service_manager.c b/cmds/servicemanager/service_manager.c index f142093..f37427a 100644 --- a/cmds/servicemanager/service_manager.c +++ b/cmds/servicemanager/service_manager.c @@ -9,6 +9,7 @@ #include <private/android_filesystem_config.h> #include <selinux/android.h> +#include <selinux/avc.h> #include "binder.h" @@ -22,15 +23,20 @@ uint32_t svcmgr_handle; -const char *str8(const uint16_t *x) +const char *str8(const uint16_t *x, size_t x_len) { static char buf[128]; - unsigned max = 127; + size_t max = 127; char *p = buf; + if (x_len < max) { + max = x_len; + } + if (x) { - while (*x && max--) { + while ((max > 0) && (*x != '\0')) { *p++ = *x++; + max--; } } *p++ = 0; @@ -46,60 +52,77 @@ int str16eq(const uint16_t *a, const char *b) return 1; } +static int selinux_enabled; +static char *service_manager_context; static struct selabel_handle* sehandle; -static bool check_mac_perms(const char *name, pid_t spid) +static bool check_mac_perms(pid_t spid, const char *tctx, const char *perm, const char *name) { - if (is_selinux_enabled() <= 0) { - return true; - } - - bool allowed = false; - - const char *class = "service_manager"; - const char *perm = "add"; - - char *tctx = NULL; char *sctx = NULL; + const char *class = "service_manager"; + bool allowed; - if (!sehandle) { - ALOGE("SELinux: Failed to find sehandle %s.\n", name); + if (getpidcon(spid, &sctx) < 0) { + ALOGE("SELinux: getpidcon(pid=%d) failed to retrieve pid context.\n", spid); return false; } - if (getpidcon(spid, &sctx) < 0) { - ALOGE("SELinux: getpidcon failed to retrieve pid context.\n"); - return false; + int result = selinux_check_access(sctx, tctx, class, perm, (void *) name); + allowed = (result == 0); + + freecon(sctx); + return allowed; +} + +static bool check_mac_perms_from_getcon(pid_t spid, const char *perm) +{ + if (selinux_enabled <= 0) { + return true; } - if (!sctx) { - ALOGE("SELinux: Failed to find sctx for %s.\n", name); - return false; + return check_mac_perms(spid, service_manager_context, perm, NULL); +} + +static bool check_mac_perms_from_lookup(pid_t spid, const char *perm, const char *name) +{ + bool allowed; + char *tctx = NULL; + + if (selinux_enabled <= 0) { + return true; } - if (selabel_lookup(sehandle, &tctx, name, 1) != 0) { - ALOGE("SELinux: selabel_lookup failed to set tctx for %s.\n", name); - freecon(sctx); - return false; + if (!sehandle) { + ALOGE("SELinux: Failed to find sehandle. Aborting service_manager.\n"); + abort(); } - if (!tctx) { - ALOGE("SELinux: Failed to find tctx for %s.\n", name); - freecon(sctx); + if (selabel_lookup(sehandle, &tctx, name, 0) != 0) { + ALOGE("SELinux: No match for %s in service_contexts.\n", name); return false; } - int result = selinux_check_access(sctx, tctx, class, perm, (void *) name); - allowed = (result == 0); - - freecon(sctx); + allowed = check_mac_perms(spid, tctx, perm, name); freecon(tctx); return allowed; } -static int svc_can_register(uid_t uid, const uint16_t *name, pid_t spid) +static int svc_can_register(const uint16_t *name, size_t name_len, pid_t spid) { - return check_mac_perms(str8(name), spid) ? 1 : 0; + const char *perm = "add"; + return check_mac_perms_from_lookup(spid, perm, str8(name, name_len)) ? 1 : 0; +} + +static int svc_can_list(pid_t spid) +{ + const char *perm = "list"; + return check_mac_perms_from_getcon(spid, perm) ? 1 : 0; +} + +static int svc_can_find(const uint16_t *name, size_t name_len, pid_t spid) +{ + const char *perm = "find"; + return check_mac_perms_from_lookup(spid, perm, str8(name, name_len)) ? 1 : 0; } struct svcinfo @@ -131,7 +154,7 @@ void svcinfo_death(struct binder_state *bs, void *ptr) { struct svcinfo *si = (struct svcinfo* ) ptr; - ALOGI("service '%s' died\n", str8(si->name)); + ALOGI("service '%s' died\n", str8(si->name, si->len)); if (si->handle) { binder_release(bs, si->handle); si->handle = 0; @@ -144,12 +167,17 @@ uint16_t svcmgr_id[] = { }; -uint32_t do_find_service(struct binder_state *bs, const uint16_t *s, size_t len, uid_t uid) +uint32_t do_find_service(struct binder_state *bs, const uint16_t *s, size_t len, uid_t uid, pid_t spid) { struct svcinfo *si; + if (!svc_can_find(s, len, spid)) { + ALOGE("find_service('%s') uid=%d - PERMISSION DENIED\n", + str8(s, len), uid); + return 0; + } si = find_svc(s, len); - //ALOGI("check_service('%s') handle = %x\n", str8(s), si ? si->handle : 0); + //ALOGI("check_service('%s') handle = %x\n", str8(s, len), si ? si->handle : 0); if (si && si->handle) { if (!si->allow_isolated) { // If this service doesn't allow access from isolated processes, @@ -172,15 +200,15 @@ int do_add_service(struct binder_state *bs, { struct svcinfo *si; - //ALOGI("add_service('%s',%x,%s) uid=%d\n", str8(s), handle, + //ALOGI("add_service('%s',%x,%s) uid=%d\n", str8(s, len), handle, // allow_isolated ? "allow_isolated" : "!allow_isolated", uid); if (!handle || (len == 0) || (len > 127)) return -1; - if (!svc_can_register(uid, s, spid)) { + if (!svc_can_register(s, len, spid)) { ALOGE("add_service('%s',%x) uid=%d - PERMISSION DENIED\n", - str8(s), handle, uid); + str8(s, len), handle, uid); return -1; } @@ -188,7 +216,7 @@ int do_add_service(struct binder_state *bs, if (si) { if (si->handle) { ALOGE("add_service('%s',%x) uid=%d - ALREADY REGISTERED, OVERRIDE\n", - str8(s), handle, uid); + str8(s, len), handle, uid); svcinfo_death(bs, si); } si->handle = handle; @@ -196,7 +224,7 @@ int do_add_service(struct binder_state *bs, si = malloc(sizeof(*si) + (len + 1) * sizeof(uint16_t)); if (!si) { ALOGE("add_service('%s',%x) uid=%d - OUT OF MEMORY\n", - str8(s), handle, uid); + str8(s, len), handle, uid); return -1; } si->handle = handle; @@ -242,9 +270,13 @@ int svcmgr_handler(struct binder_state *bs, // further (since we do no outbound RPCs anyway). strict_policy = bio_get_uint32(msg); s = bio_get_string16(msg, &len); + if (s == NULL) { + return -1; + } + if ((len != (sizeof(svcmgr_id) / 2)) || memcmp(svcmgr_id, s, sizeof(svcmgr_id))) { - fprintf(stderr,"invalid id %s\n", str8(s)); + fprintf(stderr,"invalid id %s\n", str8(s, len)); return -1; } @@ -260,7 +292,10 @@ int svcmgr_handler(struct binder_state *bs, case SVC_MGR_GET_SERVICE: case SVC_MGR_CHECK_SERVICE: s = bio_get_string16(msg, &len); - handle = do_find_service(bs, s, len, txn->sender_euid); + if (s == NULL) { + return -1; + } + handle = do_find_service(bs, s, len, txn->sender_euid, txn->sender_pid); if (!handle) break; bio_put_ref(reply, handle); @@ -268,6 +303,9 @@ int svcmgr_handler(struct binder_state *bs, case SVC_MGR_ADD_SERVICE: s = bio_get_string16(msg, &len); + if (s == NULL) { + return -1; + } handle = bio_get_ref(msg); allow_isolated = bio_get_uint32(msg) ? 1 : 0; if (do_add_service(bs, s, len, handle, txn->sender_euid, @@ -278,6 +316,11 @@ int svcmgr_handler(struct binder_state *bs, case SVC_MGR_LIST_SERVICES: { uint32_t n = bio_get_uint32(msg); + if (!svc_can_list(txn->sender_pid)) { + ALOGE("list_service() uid=%d - PERMISSION DENIED\n", + txn->sender_euid); + return -1; + } si = svclist; while ((n-- > 0) && si) si = si->next; @@ -318,8 +361,21 @@ int main(int argc, char **argv) return -1; } + selinux_enabled = is_selinux_enabled(); sehandle = selinux_android_service_context_handle(); + if (selinux_enabled > 0) { + if (sehandle == NULL) { + ALOGE("SELinux: Failed to acquire sehandle. Aborting.\n"); + abort(); + } + + if (getcon(&service_manager_context) != 0) { + ALOGE("SELinux: Failed to acquire service_manager context. Aborting.\n"); + abort(); + } + } + union selinux_callback cb; cb.func_audit = audit_callback; selinux_set_callback(SELINUX_CB_AUDIT, cb); |