diff options
356 files changed, 20993 insertions, 24509 deletions
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp index 1911839..b500a6b 100644 --- a/cmds/atrace/atrace.cpp +++ b/cmds/atrace/atrace.cpp @@ -82,6 +82,7 @@ static const TracingCategory k_categories[] = { { "hal", "Hardware Modules", ATRACE_TAG_HAL, { } }, { "res", "Resource Loading", ATRACE_TAG_RESOURCES, { } }, { "dalvik", "Dalvik VM", ATRACE_TAG_DALVIK, { } }, + { "rs", "RenderScript", ATRACE_TAG_RS, { } }, { "sched", "CPU Scheduling", 0, { { REQ, "/sys/kernel/debug/tracing/events/sched/sched_switch/enable" }, { REQ, "/sys/kernel/debug/tracing/events/sched/sched_wakeup/enable" }, @@ -102,6 +103,9 @@ static const TracingCategory k_categories[] = { { REQ, "/sys/kernel/debug/tracing/events/block/block_rq_issue/enable" }, { REQ, "/sys/kernel/debug/tracing/events/block/block_rq_complete/enable" }, } }, + { "mmc", "eMMC commands", 0, { + { REQ, "/sys/kernel/debug/tracing/events/mmc/enable" }, + } }, { "load", "CPU Load", 0, { { REQ, "/sys/kernel/debug/tracing/events/cpufreq_interactive/enable" }, } }, diff --git a/cmds/dumpstate/Android.mk b/cmds/dumpstate/Android.mk index 3194dbf..9065ee1 100644 --- a/cmds/dumpstate/Android.mk +++ b/cmds/dumpstate/Android.mk @@ -1,5 +1,10 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) +LOCAL_SRC_FILES := libdumpstate_default.c +LOCAL_MODULE := libdumpstate.default +include $(BUILD_STATIC_LIBRARY) + +include $(CLEAR_VARS) ifdef BOARD_WLAN_DEVICE LOCAL_CFLAGS := -DFWDUMP_$(BOARD_WLAN_DEVICE) @@ -10,12 +15,7 @@ LOCAL_SRC_FILES := dumpstate.c utils.c LOCAL_MODULE := dumpstate LOCAL_SHARED_LIBRARIES := libcutils liblog libselinux - -ifdef BOARD_LIB_DUMPSTATE -LOCAL_STATIC_LIBRARIES := $(BOARD_LIB_DUMPSTATE) -LOCAL_CFLAGS += -DBOARD_HAS_DUMPSTATE -endif - +LOCAL_HAL_STATIC_LIBRARIES := libdumpstate LOCAL_CFLAGS += -Wall -Wno-unused-parameter -std=gnu99 include $(BUILD_EXECUTABLE) diff --git a/cmds/dumpstate/dumpstate.c b/cmds/dumpstate/dumpstate.c index 89bea91..3c79ae9 100644 --- a/cmds/dumpstate/dumpstate.c +++ b/cmds/dumpstate/dumpstate.c @@ -33,7 +33,7 @@ #include "private/android_filesystem_config.h" #define LOG_TAG "dumpstate" -#include <utils/Log.h> +#include <cutils/log.h> #include "dumpstate.h" @@ -95,6 +95,7 @@ static void dumpstate() { run_command("PROCESSES", 10, "ps", "-P", NULL); run_command("PROCESSES AND THREADS", 10, "ps", "-t", "-p", "-P", NULL); + run_command("PROCESSES (SELINUX LABELS)", 10, "ps", "-Z", NULL); run_command("LIBRANK", 10, "librank", NULL); do_dmesg(); @@ -186,7 +187,7 @@ static void dumpstate() { run_command("IP6TABLE RAW", 10, SU_PATH, "root", "ip6tables", "-t", "raw", "-L", "-nvx", NULL); run_command("WIFI NETWORKS", 20, - SU_PATH, "root", "wpa_cli", "list_networks", NULL); + SU_PATH, "root", "wpa_cli", "IFNAME=wlan0", "list_networks", NULL); #ifdef FWDUMP_bcmdhd run_command("DUMP WIFI INTERNAL COUNTERS", 20, @@ -243,14 +244,12 @@ static void dumpstate() { dump_file("BINDER STATS", "/sys/kernel/debug/binder/stats"); dump_file("BINDER STATE", "/sys/kernel/debug/binder/state"); -#ifdef BOARD_HAS_DUMPSTATE printf("========================================================\n"); printf("== Board\n"); printf("========================================================\n"); dumpstate_board(); printf("\n"); -#endif /* Migrate the ril_dumpstate to a dumpstate_board()? */ char ril_dumpstate_timeout[PROPERTY_VALUE_MAX] = {0}; @@ -278,6 +277,16 @@ static void dumpstate() { run_command("DUMPSYS", 60, "dumpsys", NULL); printf("========================================================\n"); + printf("== Checkins\n"); + printf("========================================================\n"); + + run_command("CHECKIN BATTERYSTATS", 30, "dumpsys", "batterystats", "-c", NULL); + run_command("CHECKIN MEMINFO", 30, "dumpsys", "meminfo", "--checkin", NULL); + run_command("CHECKIN NETSTATS", 30, "dumpsys", "netstats", "--checkin", NULL); + run_command("CHECKIN PROCSTATS", 30, "dumpsys", "procstats", "-c", NULL); + run_command("CHECKIN USAGESTATS", 30, "dumpsys", "usagestats", "-c", NULL); + + printf("========================================================\n"); printf("== Running Application Activities\n"); printf("========================================================\n"); diff --git a/libs/utils/Flattenable.cpp b/cmds/dumpstate/libdumpstate_default.c index 1f2ffaa..fd840df 100644 --- a/libs/utils/Flattenable.cpp +++ b/cmds/dumpstate/libdumpstate_default.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 The Android Open Source Project + * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,11 +14,9 @@ * limitations under the License. */ -#include <utils/Flattenable.h> +#include "dumpstate.h" -namespace android { - -Flattenable::~Flattenable() { +void dumpstate_board(void) +{ } -}; // namespace android diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp index c9fcc00..ce8993d 100644 --- a/cmds/dumpsys/dumpsys.cpp +++ b/cmds/dumpsys/dumpsys.cpp @@ -9,7 +9,7 @@ #include <binder/Parcel.h> #include <binder/ProcessState.h> #include <binder/IServiceManager.h> -#include <utils/TextOutput.h> +#include <binder/TextOutput.h> #include <utils/Vector.h> #include <getopt.h> @@ -39,7 +39,11 @@ int main(int argc, char* const argv[]) Vector<String16> services; Vector<String16> args; - if (argc == 1) { + bool showListOnly = false; + if ((argc == 2) && (strcmp(argv[1], "-l") == 0)) { + showListOnly = true; + } + if ((argc == 1) || showListOnly) { services = sm->listServices(); services.sort(sort_func); args.add(String16("-a")); @@ -64,6 +68,10 @@ int main(int argc, char* const argv[]) } } + if (showListOnly) { + return 0; + } + for (size_t i=0; i<N; i++) { sp<IBinder> service = sm->checkService(services[i]); if (service != NULL) { diff --git a/cmds/flatland/Composers.cpp b/cmds/flatland/Composers.cpp index 8365a31..15cdb29 100644 --- a/cmds/flatland/Composers.cpp +++ b/cmds/flatland/Composers.cpp @@ -14,6 +14,9 @@ * limitations under the License. */ +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> + #include "Flatland.h" #include "GLHelper.h" diff --git a/cmds/flatland/GLHelper.cpp b/cmds/flatland/GLHelper.cpp index 4f7697f..42694b3 100644 --- a/cmds/flatland/GLHelper.cpp +++ b/cmds/flatland/GLHelper.cpp @@ -14,6 +14,9 @@ * limitations under the License. */ +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> + #include <ui/DisplayInfo.h> #include <gui/SurfaceComposerClient.h> @@ -198,9 +201,9 @@ 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(true, mGraphicBufferAlloc); - sp<GLConsumer> glc = new GLConsumer(name, true, - GL_TEXTURE_EXTERNAL_OES, false, bq); + sp<BufferQueue> bq = new BufferQueue(mGraphicBufferAlloc); + sp<GLConsumer> glc = new GLConsumer(bq, name, + GL_TEXTURE_EXTERNAL_OES, false); glc->setDefaultBufferSize(w, h); glc->setDefaultMaxBufferCount(3); glc->setConsumerUsageBits(GRALLOC_USAGE_HW_COMPOSER); diff --git a/cmds/flatland/Main.cpp b/cmds/flatland/Main.cpp index 99715d3..d6ac3d2 100644 --- a/cmds/flatland/Main.cpp +++ b/cmds/flatland/Main.cpp @@ -56,7 +56,7 @@ struct BenchmarkDesc { static const BenchmarkDesc benchmarks[] = { { "16:10 Single Static Window", - 2560, 1600, { 800, 1600, 2400 }, + 2560, 1600, { 800, 1200, 1600, 2400 }, { { // Window 0, staticGradient, opaque, @@ -73,8 +73,26 @@ static const BenchmarkDesc benchmarks[] = { }, }, + { "3:2 Single Static Window", + 2048, 1536, { 1536 }, + { + { // Window + 0, staticGradient, opaque, + 0, 50, 2048, 1440, + }, + { // Status bar + 0, staticGradient, opaque, + 0, 0, 2048, 50, + }, + { // Navigation bar + 0, staticGradient, opaque, + 0, 1440, 2048, 96, + }, + }, + }, + { "16:10 App -> Home Transition", - 2560, 1600, { 800, 1600, 2400 }, + 2560, 1600, { 800, 1200, 1600, 2400 }, { { // Wallpaper 0, staticGradient, opaque, @@ -99,8 +117,34 @@ static const BenchmarkDesc benchmarks[] = { }, }, + { "3:2 App -> Home Transition", + 2048, 1536, { 1536 }, + { + { // Wallpaper + 0, staticGradient, opaque, + 0, 50, 2048, 1440, + }, + { // Launcher + 0, staticGradient, blend, + 0, 50, 2048, 1440, + }, + { // Outgoing activity + 0, staticGradient, blendShrink, + 20, 70, 2048, 1400, + }, + { // Status bar + 0, staticGradient, opaque, + 0, 0, 2048, 50, + }, + { // Navigation bar + 0, staticGradient, opaque, + 0, 1440, 2048, 96, + }, + }, + }, + { "16:10 SurfaceView -> Home Transition", - 2560, 1600, { 800, 1600, 2400 }, + 2560, 1600, { 800, 1200, 1600, 2400 }, { { // Wallpaper 0, staticGradient, opaque, @@ -128,6 +172,36 @@ static const BenchmarkDesc benchmarks[] = { }, }, }, + + { "3:2 SurfaceView -> Home Transition", + 2048, 1536, { 1536 }, + { + { // Wallpaper + 0, staticGradient, opaque, + 0, 50, 2048, 1440, + }, + { // Launcher + 0, staticGradient, blend, + 0, 50, 2048, 1440, + }, + { // Outgoing SurfaceView + 0, staticGradient, blendShrink, + 20, 70, 2048, 1400, + }, + { // Outgoing activity + 0, staticGradient, blendShrink, + 20, 70, 2048, 1400, + }, + { // Status bar + 0, staticGradient, opaque, + 0, 0, 2048, 50, + }, + { // Navigation bar + 0, staticGradient, opaque, + 0, 1440, 2048, 96, + }, + }, + }, }; static const ShaderDesc shaders[] = { diff --git a/cmds/installd/commands.c b/cmds/installd/commands.c index 8e14a2c..e9d6b15 100644 --- a/cmds/installd/commands.c +++ b/cmds/installd/commands.c @@ -108,11 +108,11 @@ int install(const char *pkgname, uid_t uid, gid_t gid, const char *seinfo) return 0; } -int uninstall(const char *pkgname, uid_t persona) +int uninstall(const char *pkgname, userid_t userid) { char pkgdir[PKG_PATH_MAX]; - if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, persona)) + if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, userid)) return -1; /* delete contents AND directory, no exceptions */ @@ -173,18 +173,18 @@ int fix_uid(const char *pkgname, uid_t uid, gid_t gid) return 0; } -int delete_user_data(const char *pkgname, uid_t persona) +int delete_user_data(const char *pkgname, userid_t userid) { char pkgdir[PKG_PATH_MAX]; - if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, persona)) + 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"); } -int make_user_data(const char *pkgname, uid_t uid, uid_t persona) +int make_user_data(const char *pkgname, uid_t uid, userid_t userid, const char* seinfo) { char pkgdir[PKG_PATH_MAX]; char applibdir[PKG_PATH_MAX]; @@ -192,10 +192,10 @@ int make_user_data(const char *pkgname, uid_t uid, uid_t persona) struct stat libStat; // Create the data dir for the package - if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, persona)) { + if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, userid)) { return -1; } - if (create_pkg_path(libsymlink, pkgname, PKG_LIB_POSTFIX, persona)) { + if (create_pkg_path(libsymlink, pkgname, PKG_LIB_POSTFIX, userid)) { ALOGE("cannot create package lib symlink origin path\n"); return -1; } @@ -245,7 +245,7 @@ int make_user_data(const char *pkgname, uid_t uid, uid_t persona) return -1; } - if (selinux_android_setfilecon(pkgdir, pkgname, uid) < 0) { + if (selinux_android_setfilecon2(pkgdir, pkgname, seinfo, uid) < 0) { ALOGE("cannot setfilecon dir '%s': %s\n", pkgdir, strerror(errno)); unlink(libsymlink); unlink(pkgdir); @@ -262,10 +262,10 @@ int make_user_data(const char *pkgname, uid_t uid, uid_t persona) return 0; } -int delete_persona(uid_t persona) +int delete_user(userid_t userid) { char data_path[PKG_PATH_MAX]; - if (create_persona_path(data_path, persona)) { + if (create_user_path(data_path, userid)) { return -1; } if (delete_dir_contents(data_path, 1, NULL)) { @@ -273,7 +273,7 @@ int delete_persona(uid_t persona) } char media_path[PATH_MAX]; - if (create_persona_media_path(media_path, (userid_t) persona) == -1) { + if (create_user_media_path(media_path, userid) == -1) { return -1; } if (delete_dir_contents(media_path, 1, NULL) == -1) { @@ -283,11 +283,11 @@ int delete_persona(uid_t persona) return 0; } -int delete_cache(const char *pkgname, uid_t persona) +int delete_cache(const char *pkgname, userid_t userid) { char cachedir[PKG_PATH_MAX]; - if (create_pkg_path(cachedir, pkgname, CACHE_DIR_POSTFIX, persona)) + if (create_pkg_path(cachedir, pkgname, CACHE_DIR_POSTFIX, userid)) return -1; /* delete contents, not the directory, no exceptions */ @@ -319,7 +319,7 @@ int free_cache(int64_t free_size) cache = start_cache_collection(); // Collect cache files for primary user. - if (create_persona_path(tmpdir, 0) == 0) { + if (create_user_path(tmpdir, 0) == 0) { //ALOGI("adding cache files from %s\n", tmpdir); add_cache_files(cache, tmpdir, "cache"); } @@ -420,7 +420,7 @@ int rm_dex(const char *path) } } -int get_size(const char *pkgname, int persona, const char *apkpath, +int get_size(const char *pkgname, userid_t userid, const char *apkpath, const char *libdirpath, const char *fwdlock_apkpath, const char *asecpath, int64_t *_codesize, int64_t *_datasize, int64_t *_cachesize, int64_t* _asecsize) @@ -477,7 +477,7 @@ int get_size(const char *pkgname, int persona, const char *apkpath, } } - if (create_pkg_path(path, pkgname, PKG_DIR_POSTFIX, persona)) { + if (create_pkg_path(path, pkgname, PKG_DIR_POSTFIX, userid)) { goto done; } @@ -540,7 +540,6 @@ done: } -/* a simpler version of dexOptGenerateCacheFileName() */ int create_cache_path(char path[PKG_PATH_MAX], const char *src) { char *tmp; @@ -580,7 +579,7 @@ int create_cache_path(char path[PKG_PATH_MAX], const char *src) } static void run_dexopt(int zip_fd, int odex_fd, const char* input_file_name, - const char* dexopt_flags) + const char* output_file_name, const char* dexopt_flags) { static const char* DEX_OPT_BIN = "/system/bin/dexopt"; static const int MAX_INT_LEN = 12; // '-'+10dig+'\0' -OR- 0x+8dig @@ -590,11 +589,35 @@ static void run_dexopt(int zip_fd, int odex_fd, const char* input_file_name, sprintf(zip_num, "%d", zip_fd); sprintf(odex_num, "%d", odex_fd); + ALOGV("Running %s in=%s out=%s\n", DEX_OPT_BIN, input_file_name, output_file_name); execl(DEX_OPT_BIN, DEX_OPT_BIN, "--zip", zip_num, odex_num, input_file_name, dexopt_flags, (char*) NULL); 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, + const char* output_file_name, const char* dexopt_flags) +{ + static const char* DEX2OAT_BIN = "/system/bin/dex2oat"; + static const int MAX_INT_LEN = 12; // '-'+10dig+'\0' -OR- 0x+8dig + 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-name=") + PKG_PATH_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); + + ALOGV("Running %s in=%s out=%s\n", DEX2OAT_BIN, input_file_name, output_file_name); + execl(DEX2OAT_BIN, DEX2OAT_BIN, + zip_fd_arg, zip_location_arg, + oat_fd_arg, oat_location_arg, + (char*) NULL); + ALOGE("execl(%s) failed: %s\n", DEX2OAT_BIN, strerror(errno)); +} + static int wait_dexopt(pid_t pid, const char* apk_path) { int status; @@ -631,31 +654,32 @@ int dexopt(const char *apk_path, uid_t uid, int is_public) { struct utimbuf ut; struct stat apk_stat, dex_stat; - char dex_path[PKG_PATH_MAX]; + char out_path[PKG_PATH_MAX]; char dexopt_flags[PROPERTY_VALUE_MAX]; + char persist_sys_dalvik_vm_lib[PROPERTY_VALUE_MAX]; char *end; - int res, zip_fd=-1, odex_fd=-1; + int res, zip_fd=-1, out_fd=-1; - /* Before anything else: is there a .odex file? If so, we have - * pre-optimized the apk and there is nothing to do here. - */ if (strlen(apk_path) >= (PKG_PATH_MAX - 8)) { return -1; } /* platform-specific flags affecting optimization and verification */ property_get("dalvik.vm.dexopt-flags", dexopt_flags, ""); + ALOGV("dalvik.vm.dexopt_flags=%s\n", dexopt_flags); - strcpy(dex_path, apk_path); - end = strrchr(dex_path, '.'); - if (end != NULL) { - strcpy(end, ".odex"); - if (stat(dex_path, &dex_stat) == 0) { - return 0; - } + /* The command to run depend ones the value of persist.sys.dalvik.vm.lib */ + property_get("persist.sys.dalvik.vm.lib", persist_sys_dalvik_vm_lib, "libdvm.so"); + + /* Before anything else: is there a .odex file? If so, we have + * precompiled the apk and there is nothing to do here. + */ + sprintf(out_path, "%s%s", apk_path, ".odex"); + if (stat(out_path, &dex_stat) == 0) { + return 0; } - if (create_cache_path(dex_path, apk_path)) { + if (create_cache_path(out_path, apk_path)) { return -1; } @@ -664,24 +688,24 @@ int dexopt(const char *apk_path, uid_t uid, int is_public) zip_fd = open(apk_path, O_RDONLY, 0); if (zip_fd < 0) { - ALOGE("dexopt cannot open '%s' for input\n", apk_path); + ALOGE("installd cannot open '%s' for input during dexopt\n", apk_path); return -1; } - unlink(dex_path); - odex_fd = open(dex_path, O_RDWR | O_CREAT | O_EXCL, 0644); - if (odex_fd < 0) { - ALOGE("dexopt cannot open '%s' for output\n", dex_path); + unlink(out_path); + out_fd = open(out_path, O_RDWR | O_CREAT | O_EXCL, 0644); + if (out_fd < 0) { + ALOGE("installd cannot open '%s' for output during dexopt\n", out_path); goto fail; } - if (fchmod(odex_fd, + if (fchmod(out_fd, S_IRUSR|S_IWUSR|S_IRGRP | (is_public ? S_IROTH : 0)) < 0) { - ALOGE("dexopt cannot chmod '%s'\n", dex_path); + ALOGE("installd cannot chmod '%s' during dexopt\n", out_path); goto fail; } - if (fchown(odex_fd, AID_SYSTEM, uid) < 0) { - ALOGE("dexopt cannot chown '%s'\n", dex_path); + if (fchown(out_fd, AID_SYSTEM, uid) < 0) { + ALOGE("installd cannot chown '%s' during dexopt\n", out_path); goto fail; } @@ -692,11 +716,11 @@ int dexopt(const char *apk_path, uid_t uid, int is_public) if (pid == 0) { /* child -- drop privileges before continuing */ if (setgid(uid) != 0) { - ALOGE("setgid(%d) failed during dexopt\n", uid); + ALOGE("setgid(%d) failed in installd during dexopt\n", uid); exit(64); } if (setuid(uid) != 0) { - ALOGE("setuid(%d) during dexopt\n", uid); + ALOGE("setuid(%d) failed in installd during dexopt\n", uid); exit(65); } // drop capabilities @@ -709,33 +733,39 @@ int dexopt(const char *apk_path, uid_t uid, int is_public) ALOGE("capset failed: %s\n", strerror(errno)); exit(66); } - if (flock(odex_fd, LOCK_EX | LOCK_NB) != 0) { - ALOGE("flock(%s) failed: %s\n", dex_path, strerror(errno)); + if (flock(out_fd, LOCK_EX | LOCK_NB) != 0) { + ALOGE("flock(%s) failed: %s\n", out_path, strerror(errno)); exit(67); } - run_dexopt(zip_fd, odex_fd, apk_path, dexopt_flags); + if (strncmp(persist_sys_dalvik_vm_lib, "libdvm", 6) == 0) { + run_dexopt(zip_fd, out_fd, apk_path, out_path, dexopt_flags); + } else if (strncmp(persist_sys_dalvik_vm_lib, "libart", 6) == 0) { + run_dex2oat(zip_fd, out_fd, apk_path, out_path, dexopt_flags); + } else { + exit(69); /* Unexpected persist.sys.dalvik.vm.lib value */ + } exit(68); /* only get here on exec failure */ } else { res = wait_dexopt(pid, apk_path); if (res != 0) { - ALOGE("dexopt failed on '%s' res = %d\n", dex_path, res); + ALOGE("dexopt in='%s' out='%s' res=%d\n", apk_path, out_path, res); goto fail; } } ut.actime = apk_stat.st_atime; ut.modtime = apk_stat.st_mtime; - utime(dex_path, &ut); - - close(odex_fd); + utime(out_path, &ut); + + close(out_fd); close(zip_fd); return 0; fail: - if (odex_fd >= 0) { - close(odex_fd); - unlink(dex_path); + if (out_fd >= 0) { + close(out_fd); + unlink(out_path); } if (zip_fd >= 0) { close(zip_fd); diff --git a/cmds/installd/installd.c b/cmds/installd/installd.c index 83b2b4e..f52cee0 100644 --- a/cmds/installd/installd.c +++ b/cmds/installd/installd.c @@ -85,7 +85,7 @@ static int do_get_size(char **arg, char reply[REPLY_MAX]) int64_t asecsize = 0; int res = 0; - /* pkgdir, persona, apkpath */ + /* pkgdir, userid, apkpath */ res = get_size(arg[0], atoi(arg[1]), arg[2], arg[3], arg[4], arg[5], &codesize, &datasize, &cachesize, &asecsize); @@ -105,12 +105,13 @@ static int do_rm_user_data(char **arg, char reply[REPLY_MAX]) static int do_mk_user_data(char **arg, char reply[REPLY_MAX]) { - return make_user_data(arg[0], atoi(arg[1]), atoi(arg[2])); /* pkgname, uid, userid */ + return make_user_data(arg[0], atoi(arg[1]), atoi(arg[2]), arg[3]); + /* pkgname, uid, userid, seinfo */ } static int do_rm_user(char **arg, char reply[REPLY_MAX]) { - return delete_persona(atoi(arg[0])); /* userid */ + return delete_user(atoi(arg[0])); /* userid */ } static int do_movefiles(char **arg, char reply[REPLY_MAX]) @@ -144,7 +145,7 @@ struct cmdinfo cmds[] = { { "rmuserdata", 2, do_rm_user_data }, { "movefiles", 0, do_movefiles }, { "linklib", 3, do_linklib }, - { "mkuserdata", 3, do_mk_user_data }, + { "mkuserdata", 4, do_mk_user_data }, { "rmuser", 1, do_rm_user }, }; @@ -200,7 +201,7 @@ static int execute(int s, char cmd[BUFFER_MAX]) unsigned short count; int ret = -1; -// ALOGI("execute('%s')\n", cmd); + // ALOGI("execute('%s')\n", cmd); /* default reply is "" */ reply[0] = 0; @@ -242,7 +243,7 @@ done: if (n > BUFFER_MAX) n = BUFFER_MAX; count = n; -// ALOGI("reply: '%s'\n", cmd); + // ALOGI("reply: '%s'\n", cmd); if (writex(s, &count, sizeof(count))) return -1; if (writex(s, cmd, count)) return -1; return 0; diff --git a/cmds/installd/installd.h b/cmds/installd/installd.h index fbfc876..9ca2f86 100644 --- a/cmds/installd/installd.h +++ b/cmds/installd/installd.h @@ -78,9 +78,6 @@ #define PKG_NAME_MAX 128 /* largest allowed package name */ #define PKG_PATH_MAX 256 /* max size of any path we use */ -#define PER_USER_RANGE ((uid_t)100000) /* range of uids per user - uid = persona * PER_USER_RANGE + appid */ - /* data structures */ typedef struct { @@ -138,17 +135,17 @@ int create_pkg_path_in_dir(char path[PKG_PATH_MAX], int create_pkg_path(char path[PKG_PATH_MAX], const char *pkgname, const char *postfix, - uid_t persona); + userid_t userid); -int create_persona_path(char path[PKG_PATH_MAX], - uid_t persona); +int create_user_path(char path[PKG_PATH_MAX], + userid_t userid); -int create_persona_media_path(char path[PKG_PATH_MAX], userid_t userid); +int create_user_media_path(char path[PKG_PATH_MAX], userid_t userid); int create_move_path(char path[PKG_PATH_MAX], const char* pkgname, const char* leaf, - uid_t persona); + userid_t userid); int is_valid_package_name(const char* pkgname); @@ -193,17 +190,17 @@ int ensure_media_user_dirs(userid_t userid); /* commands.c */ int install(const char *pkgname, uid_t uid, gid_t gid, const char *seinfo); -int uninstall(const char *pkgname, uid_t persona); +int uninstall(const char *pkgname, userid_t userid); int renamepkg(const char *oldpkgname, const char *newpkgname); int fix_uid(const char *pkgname, uid_t uid, gid_t gid); -int delete_user_data(const char *pkgname, uid_t persona); -int make_user_data(const char *pkgname, uid_t uid, uid_t persona); -int delete_persona(uid_t persona); -int delete_cache(const char *pkgname, uid_t persona); +int delete_user_data(const char *pkgname, userid_t userid); +int make_user_data(const char *pkgname, uid_t uid, userid_t userid, const char* seinfo); +int delete_user(userid_t userid); +int delete_cache(const char *pkgname, userid_t userid); int move_dex(const char *src, const char *dst); int rm_dex(const char *path); int protect(char *pkgname, gid_t gid); -int get_size(const char *pkgname, int persona, const char *apkpath, const char *libdirpath, +int get_size(const char *pkgname, userid_t userid, const char *apkpath, const char *libdirpath, const char *fwdlock_apkpath, const char *asecpath, int64_t *codesize, int64_t *datasize, int64_t *cachesize, int64_t *asecsize); int free_cache(int64_t free_size); diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp index 7cb9b37..0b182af 100644 --- a/cmds/installd/tests/installd_utils_test.cpp +++ b/cmds/installd/tests/installd_utils_test.cpp @@ -340,7 +340,7 @@ TEST_F(UtilsTest, CreatePkgPathInDir_ProtectedDir) { TEST_F(UtilsTest, CreatePersonaPath_Primary) { char path[PKG_PATH_MAX]; - EXPECT_EQ(0, create_persona_path(path, 0)) + EXPECT_EQ(0, create_user_path(path, 0)) << "Should successfully build primary user path."; EXPECT_STREQ("/data/data/", path) @@ -350,7 +350,7 @@ TEST_F(UtilsTest, CreatePersonaPath_Primary) { TEST_F(UtilsTest, CreatePersonaPath_Secondary) { char path[PKG_PATH_MAX]; - EXPECT_EQ(0, create_persona_path(path, 1)) + EXPECT_EQ(0, create_user_path(path, 1)) << "Should successfully build primary user path."; EXPECT_STREQ("/data/user/1/", path) diff --git a/cmds/installd/utils.c b/cmds/installd/utils.c index 625a35e..ef634c6 100644 --- a/cmds/installd/utils.c +++ b/cmds/installd/utils.c @@ -53,38 +53,39 @@ int create_pkg_path_in_dir(char path[PKG_PATH_MAX], /** * Create the package path name for a given package name with a postfix for - * a certain persona. Returns 0 on success, and -1 on failure. + * a certain userid. Returns 0 on success, and -1 on failure. */ int create_pkg_path(char path[PKG_PATH_MAX], const char *pkgname, const char *postfix, - uid_t persona) + userid_t userid) { - size_t uid_len; - char* persona_prefix; - if (persona == 0) { - persona_prefix = PRIMARY_USER_PREFIX; - uid_len = 0; + size_t userid_len; + char* userid_prefix; + if (userid == 0) { + userid_prefix = PRIMARY_USER_PREFIX; + userid_len = 0; } else { - persona_prefix = SECONDARY_USER_PREFIX; - uid_len = snprintf(NULL, 0, "%d", persona); + userid_prefix = SECONDARY_USER_PREFIX; + userid_len = snprintf(NULL, 0, "%d", userid); } - const size_t prefix_len = android_data_dir.len + strlen(persona_prefix) + uid_len + 1 /*slash*/; + const size_t prefix_len = android_data_dir.len + strlen(userid_prefix) + + userid_len + 1 /*slash*/; char prefix[prefix_len + 1]; char *dst = prefix; size_t dst_size = sizeof(prefix); if (append_and_increment(&dst, android_data_dir.path, &dst_size) < 0 - || append_and_increment(&dst, persona_prefix, &dst_size) < 0) { + || append_and_increment(&dst, userid_prefix, &dst_size) < 0) { ALOGE("Error building prefix for APK path"); return -1; } - if (persona != 0) { - int ret = snprintf(dst, dst_size, "%d/", persona); - if (ret < 0 || (size_t) ret != uid_len + 1) { + if (userid != 0) { + int ret = snprintf(dst, dst_size, "%d/", userid); + if (ret < 0 || (size_t) ret != userid_len + 1) { ALOGW("Error appending UID to APK path"); return -1; } @@ -98,39 +99,39 @@ int create_pkg_path(char path[PKG_PATH_MAX], } /** - * Create the path name for user data for a certain persona. + * Create the path name for user data for a certain userid. * Returns 0 on success, and -1 on failure. */ -int create_persona_path(char path[PKG_PATH_MAX], - uid_t persona) +int create_user_path(char path[PKG_PATH_MAX], + userid_t userid) { - size_t uid_len; - char* persona_prefix; - if (persona == 0) { - persona_prefix = PRIMARY_USER_PREFIX; - uid_len = 0; + size_t userid_len; + char* userid_prefix; + if (userid == 0) { + userid_prefix = PRIMARY_USER_PREFIX; + userid_len = 0; } else { - persona_prefix = SECONDARY_USER_PREFIX; - uid_len = snprintf(NULL, 0, "%d/", persona); + userid_prefix = SECONDARY_USER_PREFIX; + userid_len = snprintf(NULL, 0, "%d/", userid); } char *dst = path; size_t dst_size = PKG_PATH_MAX; if (append_and_increment(&dst, android_data_dir.path, &dst_size) < 0 - || append_and_increment(&dst, persona_prefix, &dst_size) < 0) { + || append_and_increment(&dst, userid_prefix, &dst_size) < 0) { ALOGE("Error building prefix for user path"); return -1; } - if (persona != 0) { - if (dst_size < uid_len + 1) { + if (userid != 0) { + if (dst_size < userid_len + 1) { ALOGE("Error building user path"); return -1; } - int ret = snprintf(dst, dst_size, "%d/", persona); - if (ret < 0 || (size_t) ret != uid_len) { - ALOGE("Error appending persona id to path"); + int ret = snprintf(dst, dst_size, "%d/", userid); + if (ret < 0 || (size_t) ret != userid_len) { + ALOGE("Error appending userid to path"); return -1; } } @@ -138,10 +139,10 @@ int create_persona_path(char path[PKG_PATH_MAX], } /** - * Create the path name for media for a certain persona. + * Create the path name for media for a certain userid. * Returns 0 on success, and -1 on failure. */ -int create_persona_media_path(char path[PATH_MAX], userid_t userid) { +int create_user_media_path(char path[PATH_MAX], userid_t userid) { if (snprintf(path, PATH_MAX, "%s%d", android_media_dir.path, userid) > PATH_MAX) { return -1; } @@ -151,7 +152,7 @@ int create_persona_media_path(char path[PATH_MAX], userid_t userid) { int create_move_path(char path[PKG_PATH_MAX], const char* pkgname, const char* leaf, - uid_t persona) + userid_t userid) { if ((android_data_dir.len + strlen(PRIMARY_USER_PREFIX) + strlen(pkgname) + strlen(leaf) + 1) >= PKG_PATH_MAX) { @@ -997,7 +998,7 @@ int ensure_media_user_dirs(userid_t userid) { char path[PATH_MAX]; // Ensure /data/media/<userid> exists - create_persona_media_path(media_user_path, userid); + create_user_media_path(media_user_path, userid); if (fs_prepare_dir(media_user_path, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) { return -1; } diff --git a/cmds/rawbu/backup.cpp b/cmds/rawbu/backup.cpp index 70e7b57..ff6719f 100644 --- a/cmds/rawbu/backup.cpp +++ b/cmds/rawbu/backup.cpp @@ -639,6 +639,12 @@ static void show_help(const char *cmd) fprintf(stderr, "options include:\n" " -h Show this help text.\n" " -a Backup all files.\n"); + fprintf(stderr, "\n backup-file-path Defaults to /sdcard/backup.dat .\n" + " On devices that emulate the sdcard, you will need to\n" + " explicitly specify the directory it is mapped to,\n" + " to avoid recursive backup or deletion of the backup file\n" + " during restore.\n\n" + " Eg. /data/media/0/backup.dat\n"); fprintf(stderr, "\nThe %s command allows you to perform low-level\n" "backup and restore of the /data partition. This is\n" "where all user data is kept, allowing for a fairly\n" diff --git a/cmds/screenshot/Android.mk b/cmds/screenshot/Android.mk index 0afb2c5..1ee7807 100644 --- a/cmds/screenshot/Android.mk +++ b/cmds/screenshot/Android.mk @@ -7,6 +7,6 @@ LOCAL_MODULE := screenshot LOCAL_SHARED_LIBRARIES := libcutils libz liblog LOCAL_STATIC_LIBRARIES := libpng -LOCAL_C_INCLUDES += external/zlib +LOCAL_C_INCLUDES += external/zlib external/libpng include $(BUILD_EXECUTABLE) diff --git a/cmds/screenshot/screenshot.c b/cmds/screenshot/screenshot.c index cca80c3..be1ecd4 100644 --- a/cmds/screenshot/screenshot.c +++ b/cmds/screenshot/screenshot.c @@ -8,7 +8,7 @@ #include <linux/fb.h> #include <zlib.h> -#include <libpng/png.h> +#include <png.h> #include "private/android_filesystem_config.h" diff --git a/cmds/sensorservice/Android.mk b/cmds/sensorservice/Android.mk deleted file mode 100644 index 0811be5..0000000 --- a/cmds/sensorservice/Android.mk +++ /dev/null @@ -1,19 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_SRC_FILES:= \ - main_sensorservice.cpp - -LOCAL_SHARED_LIBRARIES := \ - libsensorservice \ - libbinder \ - libutils - -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/../../services/sensorservice - -LOCAL_MODULE_TAGS := optional - -LOCAL_MODULE:= sensorservice - -include $(BUILD_EXECUTABLE) diff --git a/cmds/service/service.cpp b/cmds/service/service.cpp index 32db83b..97fc47c 100644 --- a/cmds/service/service.cpp +++ b/cmds/service/service.cpp @@ -1,12 +1,23 @@ /* - * Command line access to services. + * Copyright 2013 The Android Open Source Project * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - + #include <binder/Parcel.h> #include <binder/ProcessState.h> #include <binder/IServiceManager.h> -#include <utils/TextOutput.h> +#include <binder/TextOutput.h> #include <getopt.h> #include <stdlib.h> diff --git a/cmds/surfaceflinger/Android.mk b/cmds/surfaceflinger/Android.mk deleted file mode 100644 index 1df32bb..0000000 --- a/cmds/surfaceflinger/Android.mk +++ /dev/null @@ -1,17 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_SRC_FILES:= \ - main_surfaceflinger.cpp - -LOCAL_SHARED_LIBRARIES := \ - libsurfaceflinger \ - libbinder \ - libutils - -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/../../services/surfaceflinger - -LOCAL_MODULE:= surfaceflinger - -include $(BUILD_EXECUTABLE) diff --git a/cmds/surfaceflinger/main_surfaceflinger.cpp b/cmds/surfaceflinger/main_surfaceflinger.cpp deleted file mode 100644 index ce7fde0..0000000 --- a/cmds/surfaceflinger/main_surfaceflinger.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <binder/BinderService.h> -#include <SurfaceFlinger.h> - -using namespace android; - -int main(int argc, char** argv) { - // When SF is launched in its own process, limit the number of - // binder threads to 4. - ProcessState::self()->setThreadPoolMaxThreadCount(4); - SurfaceFlinger::publishAndJoinThreadPool(true); - return 0; -} diff --git a/data/etc/android.hardware.consumerir.xml b/data/etc/android.hardware.consumerir.xml new file mode 100644 index 0000000..adba2f9 --- /dev/null +++ b/data/etc/android.hardware.consumerir.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2013 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!-- This is the standard feature indicating that the device can communicate + using Near-Field Communications (NFC). --> +<permissions> + <feature name="android.hardware.consumerir" /> +</permissions> diff --git a/data/etc/android.hardware.nfc.hce.xml b/data/etc/android.hardware.nfc.hce.xml new file mode 100644 index 0000000..10b96b1 --- /dev/null +++ b/data/etc/android.hardware.nfc.hce.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2013 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!-- This feature indicates that the device supports host-based + NFC card emulation --> +<permissions> + <feature name="android.hardware.nfc.hce" /> +</permissions> diff --git a/data/etc/android.hardware.sensor.stepcounter.xml b/data/etc/android.hardware.sensor.stepcounter.xml new file mode 100644 index 0000000..401ae92 --- /dev/null +++ b/data/etc/android.hardware.sensor.stepcounter.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2013 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!-- Feature for devices with a hardware step counter. --> +<permissions> + <feature name="android.hardware.sensor.stepcounter" /> +</permissions> diff --git a/data/etc/android.hardware.sensor.stepdetector.xml b/data/etc/android.hardware.sensor.stepdetector.xml new file mode 100644 index 0000000..7037b9a --- /dev/null +++ b/data/etc/android.hardware.sensor.stepdetector.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2013 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!-- Feature for devices with a step detector. --> +<permissions> + <feature name="android.hardware.sensor.stepdetector" /> +</permissions> diff --git a/data/etc/handheld_core_hardware.xml b/data/etc/handheld_core_hardware.xml index 290afc2..4a9f2dd 100644 --- a/data/etc/handheld_core_hardware.xml +++ b/data/etc/handheld_core_hardware.xml @@ -36,6 +36,8 @@ <feature name="android.software.app_widgets" /> <feature name="android.software.home_screen" /> <feature name="android.software.input_methods" /> + <!-- Feature to specify if the device supports adding device admins. --> + <feature name="android.software.device_admin" /> <!-- devices with GPS must include android.hardware.location.gps.xml --> <!-- devices with an autofocus camera and/or flash must include either android.hardware.camera.autofocus.xml or diff --git a/data/etc/tablet_core_hardware.xml b/data/etc/tablet_core_hardware.xml index 207fc9a..78b9736 100644 --- a/data/etc/tablet_core_hardware.xml +++ b/data/etc/tablet_core_hardware.xml @@ -37,6 +37,8 @@ <feature name="android.software.app_widgets" /> <feature name="android.software.home_screen" /> <feature name="android.software.input_methods" /> + <!-- Feature to specify if the device supports adding device admins. --> + <feature name="android.software.device_admin" /> <!-- devices with GPS must include android.hardware.location.gps.xml --> <!-- devices with a rear-facing camera must include one of these as appropriate: android.hardware.camera.xml or diff --git a/include/android/configuration.h b/include/android/configuration.h index d5cddb3..6d8784d 100644 --- a/include/android/configuration.h +++ b/include/android/configuration.h @@ -113,6 +113,8 @@ enum { ACONFIGURATION_UI_MODE = 0x1000, ACONFIGURATION_SMALLEST_SCREEN_SIZE = 0x2000, ACONFIGURATION_LAYOUTDIR = 0x4000, + + ACONFIGURATION_MNC_ZERO = 0xffff, }; /** diff --git a/include/android/keycodes.h b/include/android/keycodes.h index cf38d1a..1ca1332 100644 --- a/include/android/keycodes.h +++ b/include/android/keycodes.h @@ -265,6 +265,7 @@ enum { AKEYCODE_ASSIST = 219, AKEYCODE_BRIGHTNESS_DOWN = 220, AKEYCODE_BRIGHTNESS_UP = 221, + AKEYCODE_MEDIA_AUDIO_TRACK = 222, // NOTE: If you add a new keycode here you must also add it to several other files. // Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list. diff --git a/include/android/sensor.h b/include/android/sensor.h index f163f18..129ea3e 100644 --- a/include/android/sensor.h +++ b/include/android/sensor.h @@ -106,6 +106,30 @@ typedef struct ASensorVector { uint8_t reserved[3]; } ASensorVector; +typedef struct AMetaDataEvent { + int32_t what; + int32_t sensor; +} AMetaDataEvent; + +typedef struct AUncalibratedEvent { + union { + float uncalib[3]; + struct { + float x_uncalib; + float y_uncalib; + float z_uncalib; + }; + }; + union { + float bias[3]; + struct { + float x_bias; + float y_bias; + float z_bias; + }; + }; +} AUncalibratedEvent; + /* NOTE: Must match hardware/sensors.h */ typedef struct ASensorEvent { int32_t version; /* sizeof(struct ASensorEvent) */ @@ -114,19 +138,28 @@ typedef struct ASensorEvent { int32_t reserved0; int64_t timestamp; union { - float data[16]; - ASensorVector vector; - ASensorVector acceleration; - ASensorVector magnetic; - float temperature; - float distance; - float light; - float pressure; + union { + float data[16]; + ASensorVector vector; + ASensorVector acceleration; + ASensorVector magnetic; + float temperature; + float distance; + float light; + float pressure; + float relative_humidity; + AUncalibratedEvent uncalibrated_gyro; + AUncalibratedEvent uncalibrated_magnetic; + AMetaDataEvent meta_data; + }; + union { + uint64_t data[8]; + uint64_t step_counter; + } u64; }; int32_t reserved1[4]; } ASensorEvent; - struct ASensorManager; typedef struct ASensorManager ASensorManager; diff --git a/include/batteryservice/BatteryService.h b/include/batteryservice/BatteryService.h new file mode 100644 index 0000000..829061a --- /dev/null +++ b/include/batteryservice/BatteryService.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_BATTERYSERVICE_H +#define ANDROID_BATTERYSERVICE_H + +#include <binder/Parcel.h> +#include <utils/Errors.h> +#include <utils/String8.h> + +namespace android { + +// must be kept in sync with definitions in BatteryManager.java +enum { + BATTERY_STATUS_UNKNOWN = 1, // equals BatteryManager.BATTERY_STATUS_UNKNOWN constant + BATTERY_STATUS_CHARGING = 2, // equals BatteryManager.BATTERY_STATUS_CHARGING constant + BATTERY_STATUS_DISCHARGING = 3, // equals BatteryManager.BATTERY_STATUS_DISCHARGING constant + BATTERY_STATUS_NOT_CHARGING = 4, // equals BatteryManager.BATTERY_STATUS_NOT_CHARGING constant + BATTERY_STATUS_FULL = 5, // equals BatteryManager.BATTERY_STATUS_FULL constant +}; + +// must be kept in sync with definitions in BatteryManager.java +enum { + BATTERY_HEALTH_UNKNOWN = 1, // equals BatteryManager.BATTERY_HEALTH_UNKNOWN constant + BATTERY_HEALTH_GOOD = 2, // equals BatteryManager.BATTERY_HEALTH_GOOD constant + BATTERY_HEALTH_OVERHEAT = 3, // equals BatteryManager.BATTERY_HEALTH_OVERHEAT constant + BATTERY_HEALTH_DEAD = 4, // equals BatteryManager.BATTERY_HEALTH_DEAD constant + BATTERY_HEALTH_OVER_VOLTAGE = 5, // equals BatteryManager.BATTERY_HEALTH_OVER_VOLTAGE constant + BATTERY_HEALTH_UNSPECIFIED_FAILURE = 6, // equals BatteryManager.BATTERY_HEALTH_UNSPECIFIED_FAILURE constant + BATTERY_HEALTH_COLD = 7, // equals BatteryManager.BATTERY_HEALTH_COLD constant +}; + +struct BatteryProperties { + bool chargerAcOnline; + bool chargerUsbOnline; + bool chargerWirelessOnline; + int batteryStatus; + int batteryHealth; + bool batteryPresent; + int batteryLevel; + int batteryVoltage; + int batteryCurrentNow; + int batteryChargeCounter; + int batteryTemperature; + String8 batteryTechnology; + + status_t writeToParcel(Parcel* parcel) const; + status_t readFromParcel(Parcel* parcel); +}; + +}; // namespace android + +#endif // ANDROID_BATTERYSERVICE_H diff --git a/include/gui/DummyConsumer.h b/include/batteryservice/IBatteryPropertiesListener.h index 08e8ec8..b02d8e9 100644 --- a/include/gui/DummyConsumer.h +++ b/include/batteryservice/IBatteryPropertiesListener.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Android Open Source Project + * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,33 +14,32 @@ * limitations under the License. */ -#ifndef ANDROID_GUI_DUMMYCONSUMER_H -#define ANDROID_GUI_DUMMYCONSUMER_H +#ifndef ANDROID_IBATTERYPROPERTIESLISTENER_H +#define ANDROID_IBATTERYPROPERTIESLISTENER_H -#include <gui/BufferQueue.h> +#include <binder/IBinder.h> +#include <binder/IInterface.h> + +#include <batteryservice/BatteryService.h> namespace android { -// ---------------------------------------------------------------------------- +// must be kept in sync with interface defined in IBatteryPropertiesListener.aidl +enum { + TRANSACT_BATTERYPROPERTIESCHANGED = IBinder::FIRST_CALL_TRANSACTION, +}; -// The DummyConsumer does not keep a reference to BufferQueue -// unlike GLConsumer. This prevents a circular reference from -// forming without having to use a ProxyConsumerListener -class DummyConsumer : public BufferQueue::ConsumerListener { -public: - DummyConsumer(); - virtual ~DummyConsumer(); -protected: +// ---------------------------------------------------------------------------- - // Implementation of the BufferQueue::ConsumerListener interface. These - // calls are used to notify the GLConsumer of asynchronous events in the - // BufferQueue. - virtual void onFrameAvailable(); - virtual void onBuffersReleased(); +class IBatteryPropertiesListener : public IInterface { +public: + DECLARE_META_INTERFACE(BatteryPropertiesListener); + virtual void batteryPropertiesChanged(struct BatteryProperties props) = 0; }; // ---------------------------------------------------------------------------- + }; // namespace android -#endif // ANDROID_GUI_DUMMYCONSUMER_H +#endif // ANDROID_IBATTERYPROPERTIESLISTENER_H diff --git a/include/batteryservice/IBatteryPropertiesRegistrar.h b/include/batteryservice/IBatteryPropertiesRegistrar.h new file mode 100644 index 0000000..8d28b1d --- /dev/null +++ b/include/batteryservice/IBatteryPropertiesRegistrar.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_IBATTERYPROPERTIESREGISTRAR_H +#define ANDROID_IBATTERYPROPERTIESREGISTRAR_H + +#include <binder/IInterface.h> +#include <batteryservice/IBatteryPropertiesListener.h> + +namespace android { + +// must be kept in sync with interface defined in IBatteryPropertiesRegistrar.aidl +enum { + REGISTER_LISTENER = IBinder::FIRST_CALL_TRANSACTION, + UNREGISTER_LISTENER, +}; + +class IBatteryPropertiesRegistrar : public IInterface { +public: + DECLARE_META_INTERFACE(BatteryPropertiesRegistrar); + + virtual void registerListener(const sp<IBatteryPropertiesListener>& listener) = 0; + virtual void unregisterListener(const sp<IBatteryPropertiesListener>& listener) = 0; +}; + +class BnBatteryPropertiesRegistrar : public BnInterface<IBatteryPropertiesRegistrar> { +public: + virtual status_t onTransact(uint32_t code, const Parcel& data, + Parcel* reply, uint32_t flags = 0); +}; + +}; // namespace android + +#endif // ANDROID_IBATTERYPROPERTIESREGISTRAR_H diff --git a/include/binder/BinderService.h b/include/binder/BinderService.h index 5ac36d9..ef703bd 100644 --- a/include/binder/BinderService.h +++ b/include/binder/BinderService.h @@ -42,19 +42,20 @@ public: } static void publishAndJoinThreadPool(bool allowIsolated = false) { - sp<IServiceManager> sm(defaultServiceManager()); - sm->addService( - String16(SERVICE::getServiceName()), - new SERVICE(), allowIsolated); - ProcessState::self()->startThreadPool(); - ProcessState::self()->giveThreadPoolName(); - IPCThreadState::self()->joinThreadPool(); + publish(allowIsolated); + joinThreadPool(); } static void instantiate() { publish(); } - static status_t shutdown() { - return NO_ERROR; + static status_t shutdown() { return NO_ERROR; } + +private: + static void joinThreadPool() { + sp<ProcessState> ps(ProcessState::self()); + ps->startThreadPool(); + ps->giveThreadPoolName(); + IPCThreadState::self()->joinThreadPool(); } }; diff --git a/include/utils/BufferedTextOutput.h b/include/binder/BufferedTextOutput.h index 69c6240..9a7c43b 100644 --- a/include/utils/BufferedTextOutput.h +++ b/include/binder/BufferedTextOutput.h @@ -17,9 +17,9 @@ #ifndef ANDROID_BUFFEREDTEXTOUTPUT_H #define ANDROID_BUFFEREDTEXTOUTPUT_H -#include <utils/TextOutput.h> +#include <binder/TextOutput.h> #include <utils/threads.h> -#include <cutils/uio.h> +#include <sys/uio.h> // --------------------------------------------------------------------------- namespace android { diff --git a/include/utils/Debug.h b/include/binder/Debug.h index d9ed32d..f6a3355 100644 --- a/include/utils/Debug.h +++ b/include/binder/Debug.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_DEBUG_H -#define ANDROID_DEBUG_H +#ifndef ANDROID_BINDER_DEBUG_H +#define ANDROID_BINDER_DEBUG_H #include <stdint.h> #include <sys/types.h> @@ -24,27 +24,6 @@ namespace android { // --------------------------------------------------------------------------- #ifdef __cplusplus -template<bool> struct CompileTimeAssert; -template<> struct CompileTimeAssert<true> {}; -#define COMPILE_TIME_ASSERT(_exp) \ - template class CompileTimeAssert< (_exp) >; -#endif -#define COMPILE_TIME_ASSERT_FUNCTION_SCOPE(_exp) \ - CompileTimeAssert<( _exp )>(); - -// --------------------------------------------------------------------------- - -#ifdef __cplusplus -template<bool C, typename LSH, typename RHS> struct CompileTimeIfElse; -template<typename LHS, typename RHS> -struct CompileTimeIfElse<true, LHS, RHS> { typedef LHS TYPE; }; -template<typename LHS, typename RHS> -struct CompileTimeIfElse<false, LHS, RHS> { typedef RHS TYPE; }; -#endif - -// --------------------------------------------------------------------------- - -#ifdef __cplusplus extern "C" { #endif @@ -67,4 +46,4 @@ void printHexData(int32_t indent, const void *buf, size_t length, // --------------------------------------------------------------------------- }; // namespace android -#endif // ANDROID_DEBUG_H +#endif // ANDROID_BINDER_DEBUG_H diff --git a/include/binder/IAppOpsService.h b/include/binder/IAppOpsService.h index 7cb55e5..193e9cc 100644 --- a/include/binder/IAppOpsService.h +++ b/include/binder/IAppOpsService.h @@ -32,11 +32,14 @@ public: virtual int32_t checkOperation(int32_t code, int32_t uid, const String16& packageName) = 0; virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName) = 0; - virtual int32_t startOperation(int32_t code, int32_t uid, const String16& packageName) = 0; - virtual void finishOperation(int32_t code, int32_t uid, const String16& packageName) = 0; + virtual int32_t startOperation(const sp<IBinder>& token, int32_t code, int32_t uid, + const String16& packageName) = 0; + virtual void finishOperation(const sp<IBinder>& token, int32_t code, int32_t uid, + const String16& packageName) = 0; virtual void startWatchingMode(int32_t op, const String16& packageName, const sp<IAppOpsCallback>& callback) = 0; virtual void stopWatchingMode(const sp<IAppOpsCallback>& callback) = 0; + virtual sp<IBinder> getToken(const sp<IBinder>& clientToken) = 0; enum { CHECK_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION, @@ -44,7 +47,8 @@ public: START_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+2, FINISH_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+3, START_WATCHING_MODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+4, - STOP_WATCHING_MODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+5 + STOP_WATCHING_MODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+5, + GET_TOKEN_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+6, }; enum { diff --git a/include/binder/IPCThreadState.h b/include/binder/IPCThreadState.h index 3378d97..5bc123e 100644 --- a/include/binder/IPCThreadState.h +++ b/include/binder/IPCThreadState.h @@ -39,8 +39,8 @@ public: status_t clearLastError(); - int getCallingPid(); - int getCallingUid(); + int getCallingPid() const; + int getCallingUid() const; void setStrictModePolicy(int32_t policy); int32_t getStrictModePolicy() const; @@ -51,6 +51,8 @@ public: int64_t clearCallingIdentity(); void restoreCallingIdentity(int64_t token); + int setupPolling(int* fd); + status_t handlePolledCommands(); void flushCommands(); void joinThreadPool(bool isMain = true); @@ -96,7 +98,9 @@ private: uint32_t code, const Parcel& data, status_t* statusBuffer); + status_t getAndExecuteCommand(); status_t executeCommand(int32_t command); + void processPendingDerefs(); void clearCaller(); diff --git a/include/binder/Parcel.h b/include/binder/Parcel.h index 3ff95d2..7a782f5 100644 --- a/include/binder/Parcel.h +++ b/include/binder/Parcel.h @@ -27,8 +27,8 @@ // --------------------------------------------------------------------------- namespace android { +template <typename T> class Flattenable; template <typename T> class LightFlattenable; -class Flattenable; class IBinder; class IPCThreadState; class ProcessState; @@ -37,8 +37,7 @@ class TextOutput; struct flat_binder_object; // defined in support_p/binder_module.h -class Parcel -{ +class Parcel { public: class ReadableBlob; class WritableBlob; @@ -102,7 +101,9 @@ public: status_t writeString16(const char16_t* str, size_t len); status_t writeStrongBinder(const sp<IBinder>& val); status_t writeWeakBinder(const wp<IBinder>& val); - status_t write(const Flattenable& val); + + template<typename T> + status_t write(const Flattenable<T>& val); template<typename T> status_t write(const LightFlattenable<T>& val); @@ -157,7 +158,9 @@ public: const char16_t* readString16Inplace(size_t* outLen) const; sp<IBinder> readStrongBinder() const; wp<IBinder> readWeakBinder() const; - status_t read(Flattenable& val) const; + + template<typename T> + status_t read(Flattenable<T>& val) const; template<typename T> status_t read(LightFlattenable<T>& val) const; @@ -260,6 +263,39 @@ private: size_t mSize; }; + class FlattenableHelperInterface { + protected: + ~FlattenableHelperInterface() { } + public: + virtual size_t getFlattenedSize() const = 0; + virtual size_t getFdCount() const = 0; + virtual status_t flatten(void* buffer, size_t size, int* fds, size_t count) const = 0; + virtual status_t unflatten(void const* buffer, size_t size, int const* fds, size_t count) = 0; + }; + + template<typename T> + class FlattenableHelper : public FlattenableHelperInterface { + friend class Parcel; + const Flattenable<T>& val; + explicit FlattenableHelper(const Flattenable<T>& val) : val(val) { } + + public: + virtual size_t getFlattenedSize() const { + return val.getFlattenedSize(); + } + virtual size_t getFdCount() const { + return val.getFdCount(); + } + virtual status_t flatten(void* buffer, size_t size, int* fds, size_t count) const { + return val.flatten(buffer, size, fds, count); + } + virtual status_t unflatten(void const* buffer, size_t size, int const* fds, size_t count) { + return const_cast<Flattenable<T>&>(val).unflatten(buffer, size, fds, count); + } + }; + status_t write(const FlattenableHelperInterface& val); + status_t read(FlattenableHelperInterface& val) const; + public: class ReadableBlob : public Blob { friend class Parcel; @@ -277,8 +313,14 @@ public: // --------------------------------------------------------------------------- template<typename T> +status_t Parcel::write(const Flattenable<T>& val) { + const FlattenableHelper<T> helper(val); + return write(helper); +} + +template<typename T> status_t Parcel::write(const LightFlattenable<T>& val) { - size_t size(val.getSize()); + size_t size(val.getFlattenedSize()); if (!val.isFixedSize()) { status_t err = writeInt32(size); if (err != NO_ERROR) { @@ -287,17 +329,24 @@ status_t Parcel::write(const LightFlattenable<T>& val) { } if (size) { void* buffer = writeInplace(size); - return buffer == NULL ? NO_MEMORY : - val.flatten(buffer); + if (buffer == NULL) + return NO_MEMORY; + return val.flatten(buffer, size); } return NO_ERROR; } template<typename T> +status_t Parcel::read(Flattenable<T>& val) const { + FlattenableHelper<T> helper(val); + return read(helper); +} + +template<typename T> status_t Parcel::read(LightFlattenable<T>& val) const { size_t size; if (val.isFixedSize()) { - size = val.getSize(); + size = val.getFlattenedSize(); } else { int32_t s; status_t err = readInt32(&s); diff --git a/include/utils/TextOutput.h b/include/binder/TextOutput.h index de2fbbe..974a194 100644 --- a/include/utils/TextOutput.h +++ b/include/binder/TextOutput.h @@ -25,6 +25,9 @@ // --------------------------------------------------------------------------- namespace android { +class String8; +class String16; + class TextOutput { public: @@ -76,6 +79,8 @@ TextOutput& operator<<(TextOutput& to, float); TextOutput& operator<<(TextOutput& to, double); TextOutput& operator<<(TextOutput& to, TextOutputManipFunc func); TextOutput& operator<<(TextOutput& to, const void*); +TextOutput& operator<<(TextOutput& to, const String8& val); +TextOutput& operator<<(TextOutput& to, const String16& val); class TypeCode { diff --git a/include/cpustats/CentralTendencyStatistics.h b/include/cpustats/CentralTendencyStatistics.h deleted file mode 100644 index 21b6981..0000000 --- a/include/cpustats/CentralTendencyStatistics.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _CENTRAL_TENDENCY_STATISTICS_H -#define _CENTRAL_TENDENCY_STATISTICS_H - -#include <math.h> - -// Not multithread safe -class CentralTendencyStatistics { - -public: - - CentralTendencyStatistics() : - mMean(NAN), mMedian(NAN), mMinimum(INFINITY), mMaximum(-INFINITY), mN(0), mM2(0), - mVariance(NAN), mVarianceKnownForN(0), mStddev(NAN), mStddevKnownForN(0) { } - - ~CentralTendencyStatistics() { } - - // add x to the set of samples - void sample(double x); - - // return the arithmetic mean of all samples so far - double mean() const { return mMean; } - - // return the minimum of all samples so far - double minimum() const { return mMinimum; } - - // return the maximum of all samples so far - double maximum() const { return mMaximum; } - - // return the variance of all samples so far - double variance() const; - - // return the standard deviation of all samples so far - double stddev() const; - - // return the number of samples added so far - unsigned n() const { return mN; } - - // reset the set of samples to be empty - void reset(); - -private: - double mMean; - double mMedian; - double mMinimum; - double mMaximum; - unsigned mN; // number of samples so far - double mM2; - - // cached variance, and n at time of caching - mutable double mVariance; - mutable unsigned mVarianceKnownForN; - - // cached standard deviation, and n at time of caching - mutable double mStddev; - mutable unsigned mStddevKnownForN; - -}; - -#endif // _CENTRAL_TENDENCY_STATISTICS_H diff --git a/include/cpustats/README.txt b/include/cpustats/README.txt deleted file mode 100644 index 14439f0..0000000 --- a/include/cpustats/README.txt +++ /dev/null @@ -1,6 +0,0 @@ -This is a static library of CPU usage statistics, originally written -for audio but most are not actually specific to audio. - -Requirements to be here: - * should be related to CPU usage statistics - * should be portable to host; avoid Android OS dependencies without a conditional diff --git a/include/cpustats/ThreadCpuUsage.h b/include/cpustats/ThreadCpuUsage.h deleted file mode 100644 index 9756844..0000000 --- a/include/cpustats/ThreadCpuUsage.h +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _THREAD_CPU_USAGE_H -#define _THREAD_CPU_USAGE_H - -#include <fcntl.h> -#include <pthread.h> - -namespace android { - -// Track CPU usage for the current thread. -// Units are in per-thread CPU ns, as reported by -// clock_gettime(CLOCK_THREAD_CPUTIME_ID). Simple usage: for cyclic -// threads where you want to measure the execution time of the whole -// cycle, just call sampleAndEnable() at the start of each cycle. -// For acyclic threads, or for cyclic threads where you want to measure/track -// only part of each cycle, call enable(), disable(), and/or setEnabled() -// to demarcate the region(s) of interest, and then call sample() periodically. -// This class is not thread-safe for concurrent calls from multiple threads; -// the methods of this class may only be called by the current thread -// which constructed the object. - -class ThreadCpuUsage -{ - -public: - ThreadCpuUsage() : - mIsEnabled(false), - mWasEverEnabled(false), - mAccumulator(0), - // mPreviousTs - // mMonotonicTs - mMonotonicKnown(false) - { - (void) pthread_once(&sOnceControl, &init); - for (int i = 0; i < sKernelMax; ++i) { - mCurrentkHz[i] = (uint32_t) ~0; // unknown - } - } - - ~ThreadCpuUsage() { } - - // Return whether currently tracking CPU usage by current thread - bool isEnabled() const { return mIsEnabled; } - - // Enable tracking of CPU usage by current thread; - // any CPU used from this point forward will be tracked. - // Returns the previous enabled status. - bool enable() { return setEnabled(true); } - - // Disable tracking of CPU usage by current thread; - // any CPU used from this point forward will be ignored. - // Returns the previous enabled status. - bool disable() { return setEnabled(false); } - - // Set the enabled status and return the previous enabled status. - // This method is intended to be used for safe nested enable/disabling. - bool setEnabled(bool isEnabled); - - // Add a sample point, and also enable tracking if needed. - // If tracking has never been enabled, then this call enables tracking but - // does _not_ add a sample -- it is not possible to add a sample the - // first time because there is no previous point to subtract from. - // Otherwise, if tracking is enabled, - // then adds a sample for tracked CPU ns since the previous - // sample, or since the first call to sampleAndEnable(), enable(), or - // setEnabled(true). If there was a previous sample but tracking is - // now disabled, then adds a sample for the tracked CPU ns accumulated - // up until the most recent disable(), resets this accumulator, and then - // enables tracking. Calling this method rather than enable() followed - // by sample() avoids a race condition for the first sample. - // Returns true if the sample 'ns' is valid, or false if invalid. - // Note that 'ns' is an output parameter passed by reference. - // The caller does not need to initialize this variable. - // The units are CPU nanoseconds consumed by current thread. - bool sampleAndEnable(double& ns); - - // Add a sample point, but do not - // change the tracking enabled status. If tracking has either never been - // enabled, or has never been enabled since the last sample, then log a warning - // and don't add sample. Otherwise, adds a sample for tracked CPU ns since - // the previous sample or since the first call to sampleAndEnable(), - // enable(), or setEnabled(true) if no previous sample. - // Returns true if the sample is valid, or false if invalid. - // Note that 'ns' is an output parameter passed by reference. - // The caller does not need to initialize this variable. - // The units are CPU nanoseconds consumed by current thread. - bool sample(double& ns); - - // Return the elapsed delta wall clock ns since initial enable or reset, - // as reported by clock_gettime(CLOCK_MONOTONIC). - long long elapsed() const; - - // Reset elapsed wall clock. Has no effect on tracking or accumulator. - void resetElapsed(); - - // Return current clock frequency for specified CPU, in kHz. - // You can get your CPU number using sched_getcpu(2). Note that, unless CPU affinity - // has been configured appropriately, the CPU number can change. - // Also note that, unless the CPU governor has been configured appropriately, - // the CPU frequency can change. And even if the CPU frequency is locked down - // to a particular value, that the frequency might still be adjusted - // to prevent thermal overload. Therefore you should poll for your thread's - // current CPU number and clock frequency periodically. - uint32_t getCpukHz(int cpuNum); - -private: - bool mIsEnabled; // whether tracking is currently enabled - bool mWasEverEnabled; // whether tracking was ever enabled - long long mAccumulator; // accumulated thread CPU time since last sample, in ns - struct timespec mPreviousTs; // most recent thread CPU time, valid only if mIsEnabled is true - struct timespec mMonotonicTs; // most recent monotonic time - bool mMonotonicKnown; // whether mMonotonicTs has been set - - static const int MAX_CPU = 8; - static int sScalingFds[MAX_CPU];// file descriptor per CPU for reading scaling_cur_freq - uint32_t mCurrentkHz[MAX_CPU]; // current CPU frequency in kHz, not static to avoid a race - static pthread_once_t sOnceControl; - static int sKernelMax; // like MAX_CPU, but determined at runtime == cpu/kernel_max + 1 - static void init(); // called once at first ThreadCpuUsage construction - static pthread_mutex_t sMutex; // protects sScalingFds[] after initialization -}; - -} // namespace android - -#endif // _THREAD_CPU_USAGE_H diff --git a/include/gui/BitTube.h b/include/gui/BitTube.h index 3022d05..d32df84 100644 --- a/include/gui/BitTube.h +++ b/include/gui/BitTube.h @@ -33,30 +33,49 @@ class BitTube : public RefBase { public: - BitTube(); - BitTube(const Parcel& data); + // creates a BitTube with a default (4KB) send buffer + BitTube(); + + // creates a BitTube with a a specified send and receive buffer size + explicit BitTube(size_t bufsize); + + explicit BitTube(const Parcel& data); virtual ~BitTube(); + // check state after construction status_t initCheck() const; - int getFd() const; - ssize_t write(void const* vaddr, size_t size); - ssize_t read(void* vaddr, size_t size); - status_t writeToParcel(Parcel* reply) const; + // get receive file-descriptor + int getFd() const; + // send objects (sized blobs). All objects are guaranteed to be written or the call fails. template <typename T> static ssize_t sendObjects(const sp<BitTube>& tube, T const* events, size_t count) { return sendObjects(tube, events, count, sizeof(T)); } + // receive objects (sized blobs). If the receiving buffer isn't large enough, + // excess messages are silently discarded. template <typename T> static ssize_t recvObjects(const sp<BitTube>& tube, T* events, size_t count) { return recvObjects(tube, events, count, sizeof(T)); } + // parcels this BitTube + status_t writeToParcel(Parcel* reply) const; + private: + void init(size_t rcvbuf, size_t sndbuf); + + // send a message. The write is guaranteed to send the whole message or fail. + ssize_t write(void const* vaddr, size_t size); + + // receive a message. the passed buffer must be at least as large as the + // write call used to send the message, excess data is silently discarded. + ssize_t read(void* vaddr, size_t size); + int mSendFd; mutable int mReceiveFd; diff --git a/include/gui/BufferItemConsumer.h b/include/gui/BufferItemConsumer.h index 98b450c..52edf17 100644 --- a/include/gui/BufferItemConsumer.h +++ b/include/gui/BufferItemConsumer.h @@ -29,6 +29,8 @@ namespace android { +class BufferQueue; + /** * BufferItemConsumer is a BufferQueue consumer endpoint that allows clients * access to the whole BufferItem entry from BufferQueue. Multiple buffers may @@ -49,9 +51,11 @@ class BufferItemConsumer: public ConsumerBase // the consumer usage flags passed to the graphics allocator. The // bufferCount parameter specifies how many buffers can be locked for user // access at the same time. - BufferItemConsumer(uint32_t consumerUsage, + // controlledByApp tells whether this consumer is controlled by the + // application. + BufferItemConsumer(const sp<BufferQueue>& bq, uint32_t consumerUsage, int bufferCount = BufferQueue::MIN_UNDEQUEUED_BUFFERS, - bool synchronousMode = false); + bool controlledByApp = false); virtual ~BufferItemConsumer(); @@ -71,7 +75,8 @@ class BufferItemConsumer: public ConsumerBase // // If waitForFence is true, and the acquired BufferItem has a valid fence object, // acquireBuffer will wait on the fence with no timeout before returning. - status_t acquireBuffer(BufferItem *item, bool waitForFence = true); + status_t acquireBuffer(BufferItem *item, nsecs_t presentWhen, + bool waitForFence = true); // Returns an acquired buffer to the queue, allowing it to be reused. Since // only a fixed number of buffers may be acquired at a time, old buffers @@ -82,8 +87,6 @@ class BufferItemConsumer: public ConsumerBase status_t releaseBuffer(const BufferItem &item, const sp<Fence>& releaseFence = Fence::NO_FENCE); - sp<IGraphicBufferProducer> getProducerInterface() const { return getBufferQueue(); } - // setDefaultBufferSize is used to set the size of buffers returned by // requestBuffers when a with and height of zero is requested. status_t setDefaultBufferSize(uint32_t w, uint32_t h); diff --git a/include/gui/BufferQueue.h b/include/gui/BufferQueue.h index 6c1b691..408956b 100644 --- a/include/gui/BufferQueue.h +++ b/include/gui/BufferQueue.h @@ -20,8 +20,12 @@ #include <EGL/egl.h> #include <EGL/eglext.h> +#include <binder/IBinder.h> + +#include <gui/IConsumerListener.h> #include <gui/IGraphicBufferAlloc.h> #include <gui/IGraphicBufferProducer.h> +#include <gui/IGraphicBufferConsumer.h> #include <ui/Fence.h> #include <ui/GraphicBuffer.h> @@ -33,43 +37,22 @@ namespace android { // ---------------------------------------------------------------------------- -class BufferQueue : public BnGraphicBufferProducer { +class BufferQueue : public BnGraphicBufferProducer, + public BnGraphicBufferConsumer, + private IBinder::DeathRecipient { public: enum { MIN_UNDEQUEUED_BUFFERS = 2 }; enum { NUM_BUFFER_SLOTS = 32 }; enum { NO_CONNECTED_API = 0 }; enum { INVALID_BUFFER_SLOT = -1 }; - enum { STALE_BUFFER_SLOT = 1, NO_BUFFER_AVAILABLE }; + enum { STALE_BUFFER_SLOT = 1, NO_BUFFER_AVAILABLE, PRESENT_LATER }; // When in async mode we reserve two slots in order to guarantee that the // producer and consumer can run asynchronously. enum { MAX_MAX_ACQUIRED_BUFFERS = NUM_BUFFER_SLOTS - 2 }; - // ConsumerListener is the interface through which the BufferQueue notifies - // the consumer of events that the consumer may wish to react to. Because - // the consumer will generally have a mutex that is locked during calls from - // the consumer to the BufferQueue, these calls from the BufferQueue to the - // consumer *MUST* be called only when the BufferQueue mutex is NOT locked. - struct ConsumerListener : public virtual RefBase { - // onFrameAvailable is called from queueBuffer each time an additional - // frame becomes available for consumption. This means that frames that - // are queued while in asynchronous mode only trigger the callback if no - // previous frames are pending. Frames queued while in synchronous mode - // always trigger the callback. - // - // This is called without any lock held and can be called concurrently - // by multiple threads. - virtual void onFrameAvailable() = 0; - - // onBuffersReleased is called to notify the buffer consumer that the - // BufferQueue has released its references to one or more GraphicBuffers - // contained in its slots. The buffer consumer should then call - // BufferQueue::getReleasedBuffers to retrieve the list of buffers - // - // This is called without any lock held and can be called concurrently - // by multiple threads. - virtual void onBuffersReleased() = 0; - }; + // for backward source compatibility + typedef ::android::ConsumerListener ConsumerListener; // ProxyConsumerListener is a ConsumerListener implementation that keeps a weak // reference to the actual consumer object. It forwards all calls to that @@ -80,30 +63,35 @@ public: // reference in the BufferQueue class is because we're planning to expose the // consumer side of a BufferQueue as a binder interface, which doesn't support // weak references. - class ProxyConsumerListener : public BufferQueue::ConsumerListener { + class ProxyConsumerListener : public BnConsumerListener { public: - - ProxyConsumerListener(const wp<BufferQueue::ConsumerListener>& consumerListener); + ProxyConsumerListener(const wp<ConsumerListener>& consumerListener); virtual ~ProxyConsumerListener(); virtual void onFrameAvailable(); virtual void onBuffersReleased(); - private: - - // mConsumerListener is a weak reference to the ConsumerListener. This is + // mConsumerListener is a weak reference to the IConsumerListener. This is // the raison d'etre of ProxyConsumerListener. - wp<BufferQueue::ConsumerListener> mConsumerListener; + wp<ConsumerListener> mConsumerListener; }; // BufferQueue manages a pool of gralloc memory slots to be used by - // producers and consumers. allowSynchronousMode specifies whether or not - // synchronous mode can be enabled by the producer. allocator is used to - // allocate all the needed gralloc buffers. - BufferQueue(bool allowSynchronousMode = true, - const sp<IGraphicBufferAlloc>& allocator = NULL); + // producers and consumers. allocator is used to allocate all the + // needed gralloc buffers. + BufferQueue(const sp<IGraphicBufferAlloc>& allocator = NULL); virtual ~BufferQueue(); + /* + * IBinder::DeathRecipient interface + */ + + virtual void binderDied(const wp<IBinder>& who); + + /* + * IGraphicBufferProducer interface + */ + // Query native window attributes. The "what" values are enumerated in // window.h (e.g. NATIVE_WINDOW_FORMAT). virtual int query(int what, int* value); @@ -169,7 +157,7 @@ public: // // In both cases, the producer will need to call requestBuffer to get a // GraphicBuffer handle for the returned slot. - virtual status_t dequeueBuffer(int *buf, sp<Fence>* fence, + virtual status_t dequeueBuffer(int *buf, sp<Fence>* fence, bool async, uint32_t width, uint32_t height, uint32_t format, uint32_t usage); // queueBuffer returns a filled buffer to the BufferQueue. @@ -197,15 +185,6 @@ public: // will usually be the one obtained from dequeueBuffer. virtual void cancelBuffer(int buf, const sp<Fence>& fence); - // setSynchronousMode sets whether dequeueBuffer is synchronous or - // asynchronous. In synchronous mode, dequeueBuffer blocks until - // a buffer is available, the currently bound buffer can be dequeued and - // queued buffers will be acquired in order. In asynchronous mode, - // a queued buffer may be replaced by a subsequently queued buffer. - // - // The default mode is asynchronous. - virtual status_t setSynchronousMode(bool enabled); - // connect attempts to connect a producer API to the BufferQueue. This // must be called before any other IGraphicBufferProducer methods are // called except for getAllocator. A consumer must already be connected. @@ -215,7 +194,8 @@ public: // it's still connected to a producer). // // APIs are enumerated in window.h (e.g. NATIVE_WINDOW_API_CPU). - virtual status_t connect(int api, QueueBufferOutput* output); + virtual status_t connect(const sp<IBinder>& token, + int api, bool producerControlledByApp, QueueBufferOutput* output); // disconnect attempts to disconnect a producer API from the BufferQueue. // Calling this method will cause any subsequent calls to other @@ -227,51 +207,9 @@ public: // connected to the specified producer API. virtual status_t disconnect(int api); - // dump our state in a String - virtual void dump(String8& result) const; - virtual void dump(String8& result, const char* prefix, char* buffer, size_t SIZE) const; - - // public facing structure for BufferSlot - struct BufferItem { - - BufferItem() - : - mTransform(0), - mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), - mTimestamp(0), - mFrameNumber(0), - mBuf(INVALID_BUFFER_SLOT) { - mCrop.makeInvalid(); - } - // mGraphicBuffer points to the buffer allocated for this slot, or is NULL - // if the buffer in this slot has been acquired in the past (see - // BufferSlot.mAcquireCalled). - sp<GraphicBuffer> mGraphicBuffer; - - // mCrop is the current crop rectangle for this buffer slot. - Rect mCrop; - - // mTransform is the current transform flags for this buffer slot. - uint32_t mTransform; - - // mScalingMode is the current scaling mode for this buffer slot. - uint32_t mScalingMode; - - // mTimestamp is the current timestamp for this buffer slot. This gets - // to set by queueBuffer each time this slot is queued. - int64_t mTimestamp; - - // mFrameNumber is the number of the queued frame for this slot. - uint64_t mFrameNumber; - - // mBuf is the slot index of this buffer - int mBuf; - - // mFence is a fence that will signal when the buffer is idle. - sp<Fence> mFence; - }; - - // The following public functions are the consumer-facing interface + /* + * IGraphicBufferConsumer interface + */ // acquireBuffer attempts to acquire ownership of the next pending buffer in // the BufferQueue. If no buffer is pending then it returns -EINVAL. If a @@ -280,12 +218,18 @@ public: // acquired then the BufferItem::mGraphicBuffer field of buffer is set to // NULL and it is assumed that the consumer still holds a reference to the // buffer. - status_t acquireBuffer(BufferItem *buffer); + // + // If presentWhen is nonzero, it indicates the time when the buffer will + // be displayed on screen. If the buffer's timestamp is farther in the + // future, the buffer won't be acquired, and PRESENT_LATER will be + // returned. The presentation time is in nanoseconds, and the time base + // is CLOCK_MONOTONIC. + virtual status_t acquireBuffer(BufferItem *buffer, nsecs_t presentWhen); // releaseBuffer releases a buffer slot from the consumer back to the // BufferQueue. This may be done while the buffer's contents are still // being accessed. The fence will signal when the buffer is no longer - // in use. + // in use. frameNumber is used to indentify the exact buffer returned. // // If releaseBuffer returns STALE_BUFFER_SLOT, then the consumer must free // any references to the just-released buffer that it might have, as if it @@ -294,34 +238,37 @@ public: // // Note that the dependencies on EGL will be removed once we switch to using // the Android HW Sync HAL. - status_t releaseBuffer(int buf, EGLDisplay display, EGLSyncKHR fence, + virtual status_t releaseBuffer(int buf, uint64_t frameNumber, + EGLDisplay display, EGLSyncKHR fence, const sp<Fence>& releaseFence); // consumerConnect connects a consumer to the BufferQueue. Only one // consumer may be connected, and when that consumer disconnects the // BufferQueue is placed into the "abandoned" state, causing most // interactions with the BufferQueue by the producer to fail. + // controlledByApp indicates whether the consumer is controlled by + // the application. // // consumer may not be NULL. - status_t consumerConnect(const sp<ConsumerListener>& consumer); + virtual status_t consumerConnect(const sp<IConsumerListener>& consumer, bool controlledByApp); // consumerDisconnect disconnects a consumer from the BufferQueue. All // buffers will be freed and the BufferQueue is placed in the "abandoned" // state, causing most interactions with the BufferQueue by the producer to // fail. - status_t consumerDisconnect(); + virtual status_t consumerDisconnect(); // getReleasedBuffers sets the value pointed to by slotMask to a bit mask // indicating which buffer slots have been released by the BufferQueue // but have not yet been released by the consumer. // // This should be called from the onBuffersReleased() callback. - status_t getReleasedBuffers(uint32_t* slotMask); + virtual status_t getReleasedBuffers(uint32_t* slotMask); // setDefaultBufferSize is used to set the size of buffers returned by // dequeueBuffer when a width and height of zero is requested. Default // is 1x1. - status_t setDefaultBufferSize(uint32_t w, uint32_t h); + virtual status_t setDefaultBufferSize(uint32_t w, uint32_t h); // setDefaultMaxBufferCount sets the default value for the maximum buffer // count (the initial default is 2). If the producer has requested a @@ -329,35 +276,42 @@ public: // take effect if the producer sets the count back to zero. // // The count must be between 2 and NUM_BUFFER_SLOTS, inclusive. - status_t setDefaultMaxBufferCount(int bufferCount); + virtual status_t setDefaultMaxBufferCount(int bufferCount); + + // disableAsyncBuffer disables the extra buffer used in async mode + // (when both producer and consumer have set their "isControlledByApp" + // flag) and has dequeueBuffer() return WOULD_BLOCK instead. + // + // This can only be called before consumerConnect(). + virtual status_t disableAsyncBuffer(); // setMaxAcquiredBufferCount sets the maximum number of buffers that can // be acquired by the consumer at one time (default 1). This call will // fail if a producer is connected to the BufferQueue. - status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers); - - // isSynchronousMode returns whether the BufferQueue is currently in - // synchronous mode. - bool isSynchronousMode() const; + virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers); // setConsumerName sets the name used in logging - void setConsumerName(const String8& name); + virtual void setConsumerName(const String8& name); // setDefaultBufferFormat allows the BufferQueue to create // GraphicBuffers of a defaultFormat if no format is specified // in dequeueBuffer. Formats are enumerated in graphics.h; the // initial default is HAL_PIXEL_FORMAT_RGBA_8888. - status_t setDefaultBufferFormat(uint32_t defaultFormat); + virtual status_t setDefaultBufferFormat(uint32_t defaultFormat); // setConsumerUsageBits will turn on additional usage bits for dequeueBuffer. // These are merged with the bits passed to dequeueBuffer. The values are // enumerated in gralloc.h, e.g. GRALLOC_USAGE_HW_RENDER; the default is 0. - status_t setConsumerUsageBits(uint32_t usage); + virtual status_t setConsumerUsageBits(uint32_t usage); // setTransformHint bakes in rotation to buffers so overlays can be used. // The values are enumerated in window.h, e.g. // NATIVE_WINDOW_TRANSFORM_ROT_90. The default is 0 (no transform). - status_t setTransformHint(uint32_t hint); + virtual status_t setTransformHint(uint32_t hint); + + // dump our state in a String + virtual void dump(String8& result, const char* prefix) const; + private: // freeBufferLocked frees the GraphicBuffer and sync resources for the @@ -368,47 +322,39 @@ private: // all slots. void freeAllBuffersLocked(); - // freeAllBuffersExceptHeadLocked frees the GraphicBuffer and sync - // resources for all slots except the head of mQueue. - void freeAllBuffersExceptHeadLocked(); - - // drainQueueLocked waits for the buffer queue to empty if we're in - // synchronous mode, or returns immediately otherwise. It returns NO_INIT - // if the BufferQueue is abandoned (consumer disconnected) or disconnected - // (producer disconnected) during the call. - status_t drainQueueLocked(); - - // drainQueueAndFreeBuffersLocked drains the buffer queue if we're in - // synchronous mode and free all buffers. In asynchronous mode, all buffers - // are freed except the currently queued buffer (if it exists). - status_t drainQueueAndFreeBuffersLocked(); - // setDefaultMaxBufferCountLocked sets the maximum number of buffer slots // that will be used if the producer does not override the buffer slot // count. The count must be between 2 and NUM_BUFFER_SLOTS, inclusive. // The initial default is 2. status_t setDefaultMaxBufferCountLocked(int count); + // getMinUndequeuedBufferCount returns the minimum number of buffers + // that must remain in a state other than DEQUEUED. + // The async parameter tells whether we're in asynchronous mode. + int getMinUndequeuedBufferCount(bool async) const; + // getMinBufferCountLocked returns the minimum number of buffers allowed // given the current BufferQueue state. - int getMinMaxBufferCountLocked() const; - - // getMinUndequeuedBufferCountLocked returns the minimum number of buffers - // that must remain in a state other than DEQUEUED. - int getMinUndequeuedBufferCountLocked() const; + // The async parameter tells whether we're in asynchronous mode. + int getMinMaxBufferCountLocked(bool async) const; // getMaxBufferCountLocked returns the maximum number of buffers that can // be allocated at once. This value depends upon the following member // variables: // - // mSynchronousMode + // mDequeueBufferCannotBlock // mMaxAcquiredBufferCount // mDefaultMaxBufferCount // mOverrideMaxBufferCount + // async parameter // // Any time one of these member variables is changed while a producer is // connected, mDequeueCondition must be broadcast. - int getMaxBufferCountLocked() const; + int getMaxBufferCountLocked(bool async) const; + + // stillTracking returns true iff the buffer item is still being tracked + // in one of the slots. + bool stillTracking(const BufferItem *item) const; struct BufferSlot { @@ -416,14 +362,10 @@ private: : mEglDisplay(EGL_NO_DISPLAY), mBufferState(BufferSlot::FREE), mRequestBufferCalled(false), - mTransform(0), - mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), - mTimestamp(0), mFrameNumber(0), mEglFence(EGL_NO_SYNC_KHR), mAcquireCalled(false), mNeedsCleanupOnRelease(false) { - mCrop.makeInvalid(); } // mGraphicBuffer points to the buffer allocated for this slot or is NULL @@ -482,21 +424,6 @@ private: // needed but useful for debugging and catching producer bugs. bool mRequestBufferCalled; - // mCrop is the current crop rectangle for this buffer slot. - Rect mCrop; - - // mTransform is the current transform flags for this buffer slot. - // (example: NATIVE_WINDOW_TRANSFORM_ROT_90) - uint32_t mTransform; - - // mScalingMode is the current scaling mode for this buffer slot. - // (example: NATIVE_WINDOW_SCALING_MODE_FREEZE) - uint32_t mScalingMode; - - // mTimestamp is the current timestamp for this buffer slot. This gets - // to set by queueBuffer each time this slot is queued. - int64_t mTimestamp; - // mFrameNumber is the number of the queued frame for this slot. This // is used to dequeue buffers in LRU order (useful because buffers // may be released before their release fence is signaled). @@ -574,14 +501,20 @@ private: // mConsumerListener is used to notify the connected consumer of // asynchronous events that it may wish to react to. It is initially set // to NULL and is written by consumerConnect and consumerDisconnect. - sp<ConsumerListener> mConsumerListener; + sp<IConsumerListener> mConsumerListener; - // mSynchronousMode whether we're in synchronous mode or not - bool mSynchronousMode; + // mConsumerControlledByApp whether the connected consumer is controlled by the + // application. + bool mConsumerControlledByApp; - // mAllowSynchronousMode whether we allow synchronous mode or not. Set - // when the BufferQueue is created (by the consumer). - const bool mAllowSynchronousMode; + // mDequeueBufferCannotBlock whether dequeueBuffer() isn't allowed to block. + // this flag is set during connect() when both consumer and producer are controlled + // by the application. + bool mDequeueBufferCannotBlock; + + // mUseAsyncBuffer whether an extra buffer is used in async mode to prevent + // dequeueBuffer() from ever blocking. + bool mUseAsyncBuffer; // mConnectedApi indicates the producer API that is currently connected // to this BufferQueue. It defaults to NO_CONNECTED_API (= 0), and gets @@ -592,7 +525,7 @@ private: mutable Condition mDequeueCondition; // mQueue is a FIFO of queued buffers used in synchronous mode - typedef Vector<int> Fifo; + typedef Vector<BufferItem> Fifo; Fifo mQueue; // mAbandoned indicates that the BufferQueue will no longer be used to @@ -613,7 +546,7 @@ private: mutable Mutex mMutex; // mFrameCounter is the free running counter, incremented on every - // successful queueBuffer call. + // successful queueBuffer call, and buffer allocation. uint64_t mFrameCounter; // mBufferHasBeenQueued is true once a buffer has been queued. It is @@ -630,6 +563,9 @@ private: // mTransformHint is used to optimize for screen rotations uint32_t mTransformHint; + + // mConnectedProducerToken is used to set a binder death notification on the producer + sp<IBinder> mConnectedProducerToken; }; // ---------------------------------------------------------------------------- diff --git a/include/gui/ConsumerBase.h b/include/gui/ConsumerBase.h index 8a7545d..fb21185 100644 --- a/include/gui/ConsumerBase.h +++ b/include/gui/ConsumerBase.h @@ -24,6 +24,7 @@ #include <utils/String8.h> #include <utils/Vector.h> #include <utils/threads.h> +#include <gui/IConsumerListener.h> namespace android { // ---------------------------------------------------------------------------- @@ -34,7 +35,7 @@ class String8; // handles common tasks like management of the connection to the BufferQueue // and the buffer pool. class ConsumerBase : public virtual RefBase, - protected BufferQueue::ConsumerListener { + protected ConsumerListener { public: struct FrameAvailableListener : public virtual RefBase { // onFrameAvailable() is called each time an additional frame becomes @@ -65,15 +66,11 @@ public: // log messages. void setName(const String8& name); - // getBufferQueue returns the BufferQueue object to which this - // ConsumerBase is connected. - sp<BufferQueue> getBufferQueue() const; - // dump writes the current state to a string. Child classes should add // their state to the dump by overriding the dumpLocked method, which is // called by these methods after locking the mutex. void dump(String8& result) const; - void dump(String8& result, const char* prefix, char* buffer, size_t SIZE) const; + void dump(String8& result, const char* prefix) const; // setFrameAvailableListener sets the listener object that will be notified // when a new frame becomes available. @@ -84,10 +81,11 @@ private: void operator=(const ConsumerBase&); protected: - // ConsumerBase constructs a new ConsumerBase object to consume image - // buffers from the given BufferQueue. - ConsumerBase(const sp<BufferQueue> &bufferQueue); + // buffers from the given IGraphicBufferConsumer. + // The controlledByApp flag indicates that this consumer is under the application's + // control. + ConsumerBase(const sp<IGraphicBufferConsumer>& consumer, bool controlledByApp = false); // onLastStrongRef gets called by RefBase just before the dtor of the most // derived class. It is used to clean up the buffers so that ConsumerBase @@ -101,7 +99,7 @@ protected: // from the derived class. virtual void onLastStrongRef(const void* id); - // Implementation of the BufferQueue::ConsumerListener interface. These + // Implementation of the IConsumerListener interface. These // calls are used to notify the ConsumerBase of asynchronous events in the // BufferQueue. These methods should not need to be overridden by derived // classes, but if they are overridden the ConsumerBase implementation @@ -143,8 +141,7 @@ protected: // should call ConsumerBase::dumpLocked. // // This method must be called with mMutex locked. - virtual void dumpLocked(String8& result, const char* prefix, char* buffer, - size_t size) const; + virtual void dumpLocked(String8& result, const char* prefix) const; // acquireBufferLocked fetches the next buffer from the BufferQueue and // updates the buffer slot for the buffer returned. @@ -153,7 +150,8 @@ protected: // initialization that must take place the first time a buffer is assigned // to a slot. If it is overridden the derived class's implementation must // call ConsumerBase::acquireBufferLocked. - virtual status_t acquireBufferLocked(BufferQueue::BufferItem *item); + virtual status_t acquireBufferLocked(IGraphicBufferConsumer::BufferItem *item, + nsecs_t presentWhen); // releaseBufferLocked relinquishes control over a buffer, returning that // control to the BufferQueue. @@ -161,17 +159,23 @@ protected: // Derived classes should override this method to perform any cleanup that // must take place when a buffer is released back to the BufferQueue. If // it is overridden the derived class's implementation must call - // ConsumerBase::releaseBufferLocked. - virtual status_t releaseBufferLocked(int buf, EGLDisplay display, - EGLSyncKHR eglFence); + // ConsumerBase::releaseBufferLocked.e + virtual status_t releaseBufferLocked(int slot, + const sp<GraphicBuffer> graphicBuffer, + EGLDisplay display, EGLSyncKHR eglFence); + + // returns true iff the slot still has the graphicBuffer in it. + bool stillTracking(int slot, const sp<GraphicBuffer> graphicBuffer); // addReleaseFence* adds the sync points associated with a fence to the set // of sync points that must be reached before the buffer in the given slot // may be used after the slot has been released. This should be called by // derived classes each time some asynchronous work is kicked off that // references the buffer. - status_t addReleaseFence(int slot, const sp<Fence>& fence); - status_t addReleaseFenceLocked(int slot, const sp<Fence>& fence); + status_t addReleaseFence(int slot, + const sp<GraphicBuffer> graphicBuffer, const sp<Fence>& fence); + status_t addReleaseFenceLocked(int slot, + const sp<GraphicBuffer> graphicBuffer, const sp<Fence>& fence); // Slot contains the information and object references that // ConsumerBase maintains about a BufferQueue buffer slot. @@ -185,6 +189,9 @@ protected: // overwritten. The buffer can be dequeued before the fence signals; // the producer is responsible for delaying writes until it signals. sp<Fence> mFence; + + // the frame number of the last acquired frame for this slot + uint64_t mFrameNumber; }; // mSlots stores the buffers that have been allocated by the BufferQueue @@ -214,7 +221,7 @@ protected: // The ConsumerBase has-a BufferQueue and is responsible for creating this object // if none is supplied - sp<BufferQueue> mBufferQueue; + sp<IGraphicBufferConsumer> mConsumer; // mMutex is the mutex used to prevent concurrent access to the member // variables of ConsumerBase objects. It must be locked whenever the diff --git a/include/gui/CpuConsumer.h b/include/gui/CpuConsumer.h index bf9918e..6f4c2ec 100644 --- a/include/gui/CpuConsumer.h +++ b/include/gui/CpuConsumer.h @@ -25,10 +25,11 @@ #include <utils/Vector.h> #include <utils/threads.h> -#define ANDROID_GRAPHICS_CPUCONSUMER_JNI_ID "mCpuConsumer" namespace android { +class BufferQueue; + /** * CpuConsumer is a BufferQueue consumer endpoint that allows direct CPU * access to the underlying gralloc buffers provided by BufferQueue. Multiple @@ -65,7 +66,8 @@ class CpuConsumer : public ConsumerBase // Create a new CPU consumer. The maxLockedBuffers parameter specifies // how many buffers can be locked for user access at the same time. - CpuConsumer(uint32_t maxLockedBuffers, bool synchronousMode = true); + CpuConsumer(const sp<IGraphicBufferConsumer>& bq, + uint32_t maxLockedBuffers, bool controlledByApp = false); virtual ~CpuConsumer(); @@ -73,10 +75,22 @@ class CpuConsumer : public ConsumerBase // log messages. void setName(const String8& name); + // setDefaultBufferSize is used to set the size of buffers returned by + // requestBuffers when a width and height of zero is requested. + // A call to setDefaultBufferSize() may trigger requestBuffers() to + // be called from the client. Default size is 1x1. + status_t setDefaultBufferSize(uint32_t width, uint32_t height); + + // setDefaultBufferFormat allows CpuConsumer's BufferQueue to create buffers + // of a defaultFormat if no format is specified by producer. Formats are + // enumerated in graphics.h; the initial default is + // HAL_PIXEL_FORMAT_RGBA_8888. + status_t setDefaultBufferFormat(uint32_t defaultFormat); + // Gets the next graphics buffer from the producer and locks it for CPU use, // filling out the passed-in locked buffer structure with the native pointer // and metadata. Returns BAD_VALUE if no new buffer is available, and - // INVALID_OPERATION if the maximum number of buffers is already locked. + // NOT_ENOUGH_DATA if the maximum number of buffers is already locked. // // Only a fixed number of buffers can be locked at a time, determined by the // construction-time maxLockedBuffers parameter. If INVALID_OPERATION is @@ -90,8 +104,6 @@ class CpuConsumer : public ConsumerBase // lockNextBuffer. status_t unlockBuffer(const LockedBuffer &nativeBuffer); - sp<IGraphicBufferProducer> getProducerInterface() const { return getBufferQueue(); } - private: // Maximum number of buffers that can be locked at a time uint32_t mMaxLockedBuffers; diff --git a/include/gui/GLConsumer.h b/include/gui/GLConsumer.h index 1ef54f5..a5fdfb9 100644 --- a/include/gui/GLConsumer.h +++ b/include/gui/GLConsumer.h @@ -19,8 +19,6 @@ #include <EGL/egl.h> #include <EGL/eglext.h> -#include <GLES2/gl2.h> -#include <GLES2/gl2ext.h> #include <gui/IGraphicBufferProducer.h> #include <gui/BufferQueue.h> @@ -32,10 +30,6 @@ #include <utils/Vector.h> #include <utils/threads.h> -#define ANDROID_GRAPHICS_SURFACETEXTURE_JNI_ID "mSurfaceTexture" -#define ANDROID_GRAPHICS_FRAMEAVAILABLELISTENER_JNI_ID \ - "mFrameAvailableListener" - namespace android { // ---------------------------------------------------------------------------- @@ -58,6 +52,8 @@ class String8; * This class was previously called SurfaceTexture. */ class GLConsumer : public ConsumerBase { +protected: + enum { TEXTURE_EXTERNAL = 0x8D65 }; // GL_TEXTURE_EXTERNAL_OES public: typedef ConsumerBase::FrameAvailableListener FrameAvailableListener; @@ -85,12 +81,9 @@ public: // purely to allow a GLConsumer to be transferred from one consumer // context to another. If such a transfer is not needed there is no // requirement that either of these methods be called. - GLConsumer(const sp<BufferQueue>& bq, - GLuint tex, GLenum texTarget = GL_TEXTURE_EXTERNAL_OES, - bool useFenceSync = true); - GLConsumer(GLuint tex, bool allowSynchronousMode = true, - GLenum texTarget = GL_TEXTURE_EXTERNAL_OES, bool useFenceSync = true, - const sp<BufferQueue> &bufferQueue = 0); + GLConsumer(const sp<IGraphicBufferConsumer>& bq, + uint32_t tex, uint32_t texureTarget = TEXTURE_EXTERNAL, + bool useFenceSync = true, bool isControlledByApp = false); // updateTexImage acquires the most recently queued buffer, and sets the // image contents of the target texture to it. @@ -101,6 +94,13 @@ public: // This calls doGLFenceWait to ensure proper synchronization. status_t updateTexImage(); + // releaseTexImage releases the texture acquired in updateTexImage(). + // This is intended to be used in single buffer mode. + // + // This call may only be made while the OpenGL ES context to which the + // target texture belongs is bound to the calling thread. + status_t releaseTexImage(); + // setReleaseFence stores a fence that will signal when the current buffer // is no longer being read. This fence will be returned to the producer // when the current buffer is released by updateTexImage(). Multiple @@ -141,6 +141,13 @@ public: // documented by the source. int64_t getTimestamp(); + // getFrameNumber retrieves the frame number associated with the texture + // image set by the most recent call to updateTexImage. + // + // The frame number is an incrementing counter set to 0 at the creation of + // the BufferQueue associated with this consumer. + int64_t getFrameNumber(); + // setDefaultBufferSize is used to set the size of buffers returned by // requestBuffers when a with and height of zero is requested. // A call to setDefaultBufferSize() may trigger requestBuffers() to @@ -160,7 +167,7 @@ public: // getCurrentTextureTarget returns the texture target of the current // texture as returned by updateTexImage(). - GLenum getCurrentTextureTarget() const; + uint32_t getCurrentTextureTarget() const; // getCurrentCrop returns the cropping rectangle of the current buffer. Rect getCurrentCrop() const; @@ -180,10 +187,6 @@ public: // current texture buffer. status_t doGLFenceWait() const; - // isSynchronousMode returns whether the GLConsumer is currently in - // synchronous mode. - bool isSynchronousMode() const; - // set the name of the GLConsumer that will be used to identify it in // log messages. void setName(const String8& name); @@ -193,13 +196,6 @@ public: status_t setDefaultBufferFormat(uint32_t defaultFormat); status_t setConsumerUsageBits(uint32_t usage); status_t setTransformHint(uint32_t hint); - virtual status_t setSynchronousMode(bool enabled); - - // getBufferQueue returns the BufferQueue object to which this - // GLConsumer is connected. - sp<BufferQueue> getBufferQueue() const { - return mBufferQueue; - } // detachFromContext detaches the GLConsumer from the calling thread's // current OpenGL ES context. This context must be the same as the context @@ -226,7 +222,7 @@ public: // call to attachToContext will result in this texture object being bound to // the texture target and populated with the image contents that were // current at the time of the last call to detachFromContext. - status_t attachToContext(GLuint tex); + status_t attachToContext(uint32_t tex); protected: @@ -236,20 +232,22 @@ protected: // dumpLocked overrides the ConsumerBase method to dump GLConsumer- // specific info in addition to the ConsumerBase behavior. - virtual void dumpLocked(String8& result, const char* prefix, char* buffer, - size_t size) const; + virtual void dumpLocked(String8& result, const char* prefix) const; // acquireBufferLocked overrides the ConsumerBase method to update the // mEglSlots array in addition to the ConsumerBase behavior. - virtual status_t acquireBufferLocked(BufferQueue::BufferItem *item); + virtual status_t acquireBufferLocked(BufferQueue::BufferItem *item, + nsecs_t presentWhen); // releaseBufferLocked overrides the ConsumerBase method to update the // mEglSlots array in addition to the ConsumerBase. - virtual status_t releaseBufferLocked(int buf, EGLDisplay display, - EGLSyncKHR eglFence); + virtual status_t releaseBufferLocked(int slot, + const sp<GraphicBuffer> graphicBuffer, + EGLDisplay display, EGLSyncKHR eglFence); - status_t releaseBufferLocked(int buf, EGLSyncKHR eglFence) { - return releaseBufferLocked(buf, mEglDisplay, eglFence); + status_t releaseBufferLocked(int slot, + const sp<GraphicBuffer> graphicBuffer, EGLSyncKHR eglFence) { + return releaseBufferLocked(slot, graphicBuffer, mEglDisplay, eglFence); } static bool isExternalFormat(uint32_t format); @@ -257,7 +255,7 @@ protected: // This releases the buffer in the slot referenced by mCurrentTexture, // then updates state to refer to the BufferItem, which must be a // newly-acquired buffer. - status_t releaseAndUpdateLocked(const BufferQueue::BufferItem& item); + status_t updateAndReleaseLocked(const BufferQueue::BufferItem& item); // Binds mTexName and the current buffer to mTexTarget. Uses // mCurrentTexture if it's set, mCurrentTextureBuf if not. If the @@ -268,12 +266,14 @@ protected: // to mEglDisplay and mEglContext. If the fields have been previously // set, the values must match; if not, the fields are set to the current // values. - status_t checkAndUpdateEglStateLocked(); + // The contextCheck argument is used to ensure that a GL context is + // properly set; when set to false, the check is not performed. + status_t checkAndUpdateEglStateLocked(bool contextCheck = false); private: // createImage creates a new EGLImage from a GraphicBuffer. EGLImageKHR createImage(EGLDisplay dpy, - const sp<GraphicBuffer>& graphicBuffer); + const sp<GraphicBuffer>& graphicBuffer, const Rect& crop); // freeBufferLocked frees up the given buffer slot. If the slot has been // initialized this will release the reference to the GraphicBuffer in that @@ -306,6 +306,9 @@ private: // binding the buffer without touching the EglSlots. status_t bindUnslottedBufferLocked(EGLDisplay dpy); + // returns a graphic buffer used when the texture image has been released + static sp<GraphicBuffer> getDebugTexImageBuffer(); + // The default consumer usage flags that GLConsumer always sets on its // BufferQueue instance; these will be OR:d with any additional flags passed // from the GLConsumer user. In particular, GLConsumer will always @@ -341,6 +344,10 @@ private: // gets set each time updateTexImage is called. int64_t mCurrentTimestamp; + // mCurrentFrameNumber is the frame counter for the current texture. + // It gets set each time updateTexImage is called. + int64_t mCurrentFrameNumber; + uint32_t mDefaultWidth, mDefaultHeight; // mFilteringEnabled indicates whether the transform matrix is computed for @@ -351,7 +358,7 @@ private: // mTexName is the name of the OpenGL texture to which streamed images will // be bound when updateTexImage is called. It is set at construction time // and can be changed with a call to attachToContext. - GLuint mTexName; + uint32_t mTexName; // mUseFenceSync indicates whether creation of the EGL_KHR_fence_sync // extension should be used to prevent buffers from being dequeued before @@ -366,7 +373,7 @@ private: // glCopyTexSubImage to read from the texture. This is a hack to work // around a GL driver limitation on the number of FBO attachments, which the // browser's tile cache exceeds. - const GLenum mTexTarget; + const uint32_t mTexTarget; // EGLSlot contains the information and object references that // GLConsumer maintains about a BufferQueue buffer slot. @@ -379,6 +386,10 @@ private: // mEglImage is the EGLImage created from mGraphicBuffer. EGLImageKHR mEglImage; + // mCropRect is the crop rectangle passed to EGL when mEglImage was + // created. + Rect mCropRect; + // mFence is the EGL sync object that must signal before the buffer // associated with this buffer slot may be dequeued. It is initialized // to EGL_NO_SYNC_KHR when the buffer is created and (optionally, based @@ -422,6 +433,13 @@ private: // It is set to false by detachFromContext, and then set to true again by // attachToContext. bool mAttached; + + // protects static initialization + static Mutex sStaticInitLock; + + // mReleasedTexImageBuffer is a dummy buffer used when in single buffer + // mode and releaseTexImage() has been called + static sp<GraphicBuffer> sReleasedTexImageBuffer; }; // ---------------------------------------------------------------------------- diff --git a/include/gui/IConsumerListener.h b/include/gui/IConsumerListener.h new file mode 100644 index 0000000..ac2f9bb --- /dev/null +++ b/include/gui/IConsumerListener.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_GUI_ICONSUMERLISTENER_H +#define ANDROID_GUI_ICONSUMERLISTENER_H + +#include <stdint.h> +#include <sys/types.h> + +#include <utils/Errors.h> +#include <utils/RefBase.h> + +#include <binder/IInterface.h> + +namespace android { +// ---------------------------------------------------------------------------- + +// ConsumerListener is the interface through which the BufferQueue notifies +// the consumer of events that the consumer may wish to react to. Because +// the consumer will generally have a mutex that is locked during calls from +// the consumer to the BufferQueue, these calls from the BufferQueue to the +// consumer *MUST* be called only when the BufferQueue mutex is NOT locked. + +class ConsumerListener : public virtual RefBase { +public: + ConsumerListener() { } + virtual ~ConsumerListener() { } + + // onFrameAvailable is called from queueBuffer each time an additional + // frame becomes available for consumption. This means that frames that + // are queued while in asynchronous mode only trigger the callback if no + // previous frames are pending. Frames queued while in synchronous mode + // always trigger the callback. + // + // This is called without any lock held and can be called concurrently + // by multiple threads. + virtual void onFrameAvailable() = 0; /* Asynchronous */ + + // onBuffersReleased is called to notify the buffer consumer that the + // BufferQueue has released its references to one or more GraphicBuffers + // contained in its slots. The buffer consumer should then call + // BufferQueue::getReleasedBuffers to retrieve the list of buffers + // + // This is called without any lock held and can be called concurrently + // by multiple threads. + virtual void onBuffersReleased() = 0; /* Asynchronous */ +}; + + +class IConsumerListener : public ConsumerListener, public IInterface +{ +public: + DECLARE_META_INTERFACE(ConsumerListener); +}; + +// ---------------------------------------------------------------------------- + +class BnConsumerListener : public BnInterface<IConsumerListener> +{ +public: + virtual status_t onTransact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +// ---------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_GUI_ICONSUMERLISTENER_H diff --git a/include/gui/IGraphicBufferConsumer.h b/include/gui/IGraphicBufferConsumer.h new file mode 100644 index 0000000..0e35f13 --- /dev/null +++ b/include/gui/IGraphicBufferConsumer.h @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_GUI_IGRAPHICBUFFERCONSUMER_H +#define ANDROID_GUI_IGRAPHICBUFFERCONSUMER_H + +#include <stdint.h> +#include <sys/types.h> + +#include <utils/Errors.h> +#include <utils/RefBase.h> +#include <utils/Timers.h> + +#include <binder/IInterface.h> +#include <ui/Rect.h> + +namespace android { +// ---------------------------------------------------------------------------- + +class IConsumerListener; +class GraphicBuffer; +class Fence; + +class IGraphicBufferConsumer : public IInterface { + +public: + + // public facing structure for BufferSlot + class BufferItem : public Flattenable<BufferItem> { + friend class Flattenable<BufferItem>; + size_t getPodSize() const; + size_t getFlattenedSize() const; + size_t getFdCount() const; + status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const; + status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count); + + public: + enum { INVALID_BUFFER_SLOT = -1 }; + BufferItem(); + + // mGraphicBuffer points to the buffer allocated for this slot, or is NULL + // if the buffer in this slot has been acquired in the past (see + // BufferSlot.mAcquireCalled). + sp<GraphicBuffer> mGraphicBuffer; + + // mFence is a fence that will signal when the buffer is idle. + sp<Fence> mFence; + + // mCrop is the current crop rectangle for this buffer slot. + Rect mCrop; + + // mTransform is the current transform flags for this buffer slot. + uint32_t mTransform; + + // mScalingMode is the current scaling mode for this buffer slot. + uint32_t mScalingMode; + + // mTimestamp is the current timestamp for this buffer slot. This gets + // to set by queueBuffer each time this slot is queued. + int64_t mTimestamp; + + // mIsAutoTimestamp indicates whether mTimestamp was generated + // automatically when the buffer was queued. + bool mIsAutoTimestamp; + + // mFrameNumber is the number of the queued frame for this slot. + uint64_t mFrameNumber; + + // mBuf is the slot index of this buffer + int mBuf; + + // mIsDroppable whether this buffer was queued with the + // property that it can be replaced by a new buffer for the purpose of + // making sure dequeueBuffer() won't block. + // i.e.: was the BufferQueue in "mDequeueBufferCannotBlock" when this buffer + // was queued. + bool mIsDroppable; + + // Indicates whether this buffer has been seen by a consumer yet + bool mAcquireCalled; + + // Indicates this buffer must be transformed by the inverse transform of the screen + // it is displayed onto. This is applied after mTransform. + bool mTransformToDisplayInverse; + }; + + + // acquireBuffer attempts to acquire ownership of the next pending buffer in + // the BufferQueue. If no buffer is pending then it returns -EINVAL. If a + // buffer is successfully acquired, the information about the buffer is + // returned in BufferItem. If the buffer returned had previously been + // acquired then the BufferItem::mGraphicBuffer field of buffer is set to + // NULL and it is assumed that the consumer still holds a reference to the + // buffer. + // + // If presentWhen is nonzero, it indicates the time when the buffer will + // be displayed on screen. If the buffer's timestamp is farther in the + // future, the buffer won't be acquired, and PRESENT_LATER will be + // returned. The presentation time is in nanoseconds, and the time base + // is CLOCK_MONOTONIC. + virtual status_t acquireBuffer(BufferItem *buffer, nsecs_t presentWhen) = 0; + + // releaseBuffer releases a buffer slot from the consumer back to the + // BufferQueue. This may be done while the buffer's contents are still + // being accessed. The fence will signal when the buffer is no longer + // in use. frameNumber is used to indentify the exact buffer returned. + // + // If releaseBuffer returns STALE_BUFFER_SLOT, then the consumer must free + // any references to the just-released buffer that it might have, as if it + // had received a onBuffersReleased() call with a mask set for the released + // buffer. + // + // Note that the dependencies on EGL will be removed once we switch to using + // the Android HW Sync HAL. + virtual status_t releaseBuffer(int buf, uint64_t frameNumber, + EGLDisplay display, EGLSyncKHR fence, + const sp<Fence>& releaseFence) = 0; + + // consumerConnect connects a consumer to the BufferQueue. Only one + // consumer may be connected, and when that consumer disconnects the + // BufferQueue is placed into the "abandoned" state, causing most + // interactions with the BufferQueue by the producer to fail. + // controlledByApp indicates whether the consumer is controlled by + // the application. + // + // consumer may not be NULL. + virtual status_t consumerConnect(const sp<IConsumerListener>& consumer, bool controlledByApp) = 0; + + // consumerDisconnect disconnects a consumer from the BufferQueue. All + // buffers will be freed and the BufferQueue is placed in the "abandoned" + // state, causing most interactions with the BufferQueue by the producer to + // fail. + virtual status_t consumerDisconnect() = 0; + + // getReleasedBuffers sets the value pointed to by slotMask to a bit mask + // indicating which buffer slots have been released by the BufferQueue + // but have not yet been released by the consumer. + // + // This should be called from the onBuffersReleased() callback. + virtual status_t getReleasedBuffers(uint32_t* slotMask) = 0; + + // setDefaultBufferSize is used to set the size of buffers returned by + // dequeueBuffer when a width and height of zero is requested. Default + // is 1x1. + virtual status_t setDefaultBufferSize(uint32_t w, uint32_t h) = 0; + + // setDefaultMaxBufferCount sets the default value for the maximum buffer + // count (the initial default is 2). If the producer has requested a + // buffer count using setBufferCount, the default buffer count will only + // take effect if the producer sets the count back to zero. + // + // The count must be between 2 and NUM_BUFFER_SLOTS, inclusive. + virtual status_t setDefaultMaxBufferCount(int bufferCount) = 0; + + // disableAsyncBuffer disables the extra buffer used in async mode + // (when both producer and consumer have set their "isControlledByApp" + // flag) and has dequeueBuffer() return WOULD_BLOCK instead. + // + // This can only be called before consumerConnect(). + virtual status_t disableAsyncBuffer() = 0; + + // setMaxAcquiredBufferCount sets the maximum number of buffers that can + // be acquired by the consumer at one time (default 1). This call will + // fail if a producer is connected to the BufferQueue. + virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) = 0; + + // setConsumerName sets the name used in logging + virtual void setConsumerName(const String8& name) = 0; + + // setDefaultBufferFormat allows the BufferQueue to create + // GraphicBuffers of a defaultFormat if no format is specified + // in dequeueBuffer. Formats are enumerated in graphics.h; the + // initial default is HAL_PIXEL_FORMAT_RGBA_8888. + virtual status_t setDefaultBufferFormat(uint32_t defaultFormat) = 0; + + // setConsumerUsageBits will turn on additional usage bits for dequeueBuffer. + // These are merged with the bits passed to dequeueBuffer. The values are + // enumerated in gralloc.h, e.g. GRALLOC_USAGE_HW_RENDER; the default is 0. + virtual status_t setConsumerUsageBits(uint32_t usage) = 0; + + // setTransformHint bakes in rotation to buffers so overlays can be used. + // The values are enumerated in window.h, e.g. + // NATIVE_WINDOW_TRANSFORM_ROT_90. The default is 0 (no transform). + virtual status_t setTransformHint(uint32_t hint) = 0; + + // dump state into a string + virtual void dump(String8& result, const char* prefix) const = 0; + +public: + DECLARE_META_INTERFACE(GraphicBufferConsumer); +}; + +// ---------------------------------------------------------------------------- + +class BnGraphicBufferConsumer : public BnInterface<IGraphicBufferConsumer> +{ +public: + virtual status_t onTransact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +// ---------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_GUI_IGRAPHICBUFFERCONSUMER_H diff --git a/include/gui/IGraphicBufferProducer.h b/include/gui/IGraphicBufferProducer.h index 29c7ff3..342ba08 100644 --- a/include/gui/IGraphicBufferProducer.h +++ b/include/gui/IGraphicBufferProducer.h @@ -84,7 +84,10 @@ public: // the buffer. The contents of the buffer must not be overwritten until the // fence signals. If the fence is NULL, the buffer may be written // immediately. - virtual status_t dequeueBuffer(int *slot, sp<Fence>* fence, + // + // The async parameter sets whether we're in asynchrnous mode for this + // deququeBuffer() call. + virtual status_t dequeueBuffer(int *slot, sp<Fence>* fence, bool async, uint32_t w, uint32_t h, uint32_t format, uint32_t usage) = 0; // queueBuffer indicates that the client has finished filling in the @@ -96,40 +99,46 @@ public: // must be monotonically increasing. Its other properties (zero point, etc) // are client-dependent, and should be documented by the client. // + // The async parameter sets whether we're queuing a buffer in asynchronous mode. + // // outWidth, outHeight and outTransform are filled with the default width // and height of the window and current transform applied to buffers, // respectively. - struct QueueBufferInput : public Flattenable { + struct QueueBufferInput : public Flattenable<QueueBufferInput> { + friend class Flattenable<QueueBufferInput>; inline QueueBufferInput(const Parcel& parcel); - inline QueueBufferInput(int64_t timestamp, - const Rect& crop, int scalingMode, uint32_t transform, - sp<Fence> fence) - : timestamp(timestamp), crop(crop), scalingMode(scalingMode), - transform(transform), fence(fence) { } - inline void deflate(int64_t* outTimestamp, Rect* outCrop, - int* outScalingMode, uint32_t* outTransform, - sp<Fence>* outFence) const { + inline QueueBufferInput(int64_t timestamp, bool isAutoTimestamp, + const Rect& crop, int scalingMode, uint32_t transform, bool async, + const sp<Fence>& fence) + : timestamp(timestamp), isAutoTimestamp(isAutoTimestamp), crop(crop), + scalingMode(scalingMode), transform(transform), async(async), + fence(fence) { } + inline void deflate(int64_t* outTimestamp, bool* outIsAutoTimestamp, + Rect* outCrop, int* outScalingMode, uint32_t* outTransform, + bool* outAsync, sp<Fence>* outFence) const { *outTimestamp = timestamp; + *outIsAutoTimestamp = bool(isAutoTimestamp); *outCrop = crop; *outScalingMode = scalingMode; *outTransform = transform; + *outAsync = bool(async); *outFence = fence; } - // Flattenable interface - virtual size_t getFlattenedSize() const; - virtual size_t getFdCount() const; - virtual status_t flatten(void* buffer, size_t size, - int fds[], size_t count) const; - virtual status_t unflatten(void const* buffer, size_t size, - int fds[], size_t count); + // Flattenable protocol + size_t getFlattenedSize() const; + size_t getFdCount() const; + status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const; + status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count); private: int64_t timestamp; + int isAutoTimestamp; Rect crop; int scalingMode; uint32_t transform; + int async; sp<Fence> fence; }; @@ -171,13 +180,6 @@ public: // 'what' tokens allowed are that of android_natives.h virtual int query(int what, int* value) = 0; - // setSynchronousMode set whether dequeueBuffer is synchronous or - // asynchronous. In synchronous mode, dequeueBuffer blocks until - // a buffer is available, the currently bound buffer can be dequeued and - // queued buffers will be retired in order. - // The default mode is asynchronous. - virtual status_t setSynchronousMode(bool enabled) = 0; - // connect attempts to connect a client API to the IGraphicBufferProducer. // This must be called before any other IGraphicBufferProducer methods are // called except for getAllocator. @@ -187,8 +189,11 @@ public: // // outWidth, outHeight and outTransform are filled with the default width // and height of the window and current transform applied to buffers, - // respectively. - virtual status_t connect(int api, QueueBufferOutput* output) = 0; + // respectively. The token needs to be any binder object that lives in the + // producer process -- it is solely used for obtaining a death notification + // when the producer is killed. + virtual status_t connect(const sp<IBinder>& token, + int api, bool producerControlledByApp, QueueBufferOutput* output) = 0; // disconnect attempts to disconnect a client API from the // IGraphicBufferProducer. Calling this method will cause any subsequent diff --git a/include/gui/ISensorEventConnection.h b/include/gui/ISensorEventConnection.h index 749065e..f64c6b8 100644 --- a/include/gui/ISensorEventConnection.h +++ b/include/gui/ISensorEventConnection.h @@ -36,8 +36,10 @@ public: DECLARE_META_INTERFACE(SensorEventConnection); virtual sp<BitTube> getSensorChannel() const = 0; - virtual status_t enableDisable(int handle, bool enabled) = 0; + virtual status_t enableDisable(int handle, bool enabled, nsecs_t samplingPeriodNs, + nsecs_t maxBatchReportLatencyNs, int reservedFlags) = 0; virtual status_t setEventRate(int handle, nsecs_t ns) = 0; + virtual status_t flush() = 0; }; // ---------------------------------------------------------------------------- diff --git a/include/gui/ISurfaceComposer.h b/include/gui/ISurfaceComposer.h index 9018b87..5c3c99c 100644 --- a/include/gui/ISurfaceComposer.h +++ b/include/gui/ISurfaceComposer.h @@ -70,12 +70,17 @@ public: /* return an IDisplayEventConnection */ virtual sp<IDisplayEventConnection> createDisplayEventConnection() = 0; - /* create a display + /* create a virtual display * requires ACCESS_SURFACE_FLINGER permission. */ virtual sp<IBinder> createDisplay(const String8& displayName, bool secure) = 0; + /* destroy a virtual display + * requires ACCESS_SURFACE_FLINGER permission. + */ + virtual void destroyDisplay(const sp<IBinder>& display) = 0; + /* get the token for the existing default displays. possible values * for id are eDisplayIdMain and eDisplayIdHdmi. */ @@ -115,8 +120,7 @@ public: virtual status_t captureScreen(const sp<IBinder>& display, const sp<IGraphicBufferProducer>& producer, uint32_t reqWidth, uint32_t reqHeight, - uint32_t minLayerZ, uint32_t maxLayerZ, - bool isCpuConsumer) = 0; + uint32_t minLayerZ, uint32_t maxLayerZ) = 0; }; // ---------------------------------------------------------------------------- @@ -131,6 +135,7 @@ public: CREATE_GRAPHIC_BUFFER_ALLOC, CREATE_DISPLAY_EVENT_CONNECTION, CREATE_DISPLAY, + DESTROY_DISPLAY, GET_BUILT_IN_DISPLAY, SET_TRANSACTION_STATE, AUTHENTICATE_SURFACE, diff --git a/include/gui/Sensor.h b/include/gui/Sensor.h index 2af2307..0c81426 100644 --- a/include/gui/Sensor.h +++ b/include/gui/Sensor.h @@ -53,7 +53,7 @@ public: }; Sensor(); - Sensor(struct sensor_t const* hwSensor); + Sensor(struct sensor_t const* hwSensor, int halVersion = 0); ~Sensor(); const String8& getName() const; @@ -67,11 +67,13 @@ public: int32_t getMinDelay() const; nsecs_t getMinDelayNs() const; int32_t getVersion() const; + int32_t getFifoReservedEventCount() const; + int32_t getFifoMaxEventCount() const; // LightFlattenable protocol inline bool isFixedSize() const { return false; } - size_t getSize() const; - status_t flatten(void* buffer) const; + size_t getFlattenedSize() const; + status_t flatten(void* buffer, size_t size) const; status_t unflatten(void const* buffer, size_t size); private: @@ -85,6 +87,8 @@ private: float mPower; int32_t mMinDelay; int32_t mVersion; + int32_t mFifoReservedEventCount; + int32_t mFifoMaxEventCount; }; // ---------------------------------------------------------------------------- diff --git a/include/gui/SensorEventQueue.h b/include/gui/SensorEventQueue.h index 759b5cb..0bfc7a0 100644 --- a/include/gui/SensorEventQueue.h +++ b/include/gui/SensorEventQueue.h @@ -49,6 +49,9 @@ class Looper; class SensorEventQueue : public ASensorEventQueue, public RefBase { public: + + enum { MAX_RECEIVE_BUFFER_EVENT_COUNT = 256 }; + SensorEventQueue(const sp<ISensorEventConnection>& connection); virtual ~SensorEventQueue(); virtual void onFirstRef(); @@ -68,8 +71,10 @@ public: status_t setEventRate(Sensor const* sensor, nsecs_t ns) const; // these are here only to support SensorManager.java - status_t enableSensor(int32_t handle, int32_t us) const; + status_t enableSensor(int32_t handle, int32_t samplingPeriodUs, int maxBatchReportLatencyUs, + int reservedFlags) const; status_t disableSensor(int32_t handle) const; + status_t flush() const; private: sp<Looper> getLooper() const; @@ -77,6 +82,9 @@ private: sp<BitTube> mSensorChannel; mutable Mutex mLock; mutable sp<Looper> mLooper; + ASensorEvent* mRecBuffer; + size_t mAvailable; + size_t mConsumed; }; // ---------------------------------------------------------------------------- diff --git a/include/gui/Surface.h b/include/gui/Surface.h index c25847c..6f8a97c 100644 --- a/include/gui/Surface.h +++ b/include/gui/Surface.h @@ -18,7 +18,6 @@ #define ANDROID_GUI_SURFACE_H #include <gui/IGraphicBufferProducer.h> -#include <gui/GLConsumer.h> #include <gui/BufferQueue.h> #include <ui/ANativeObjectBase.h> @@ -61,8 +60,11 @@ public: * However, once a Surface is connected, it'll prevent other Surfaces * referring to the same IGraphicBufferProducer to become connected and * therefore prevent them to be used as actual producers of buffers. + * + * the controlledByApp flag indicates that this Surface (producer) is + * controlled by the application. This flag is used at connect time. */ - Surface(const sp<IGraphicBufferProducer>& bufferProducer); + Surface(const sp<IGraphicBufferProducer>& bufferProducer, bool controlledByApp = false); /* getIGraphicBufferProducer() returns the IGraphicBufferProducer this * Surface was created with. Usually it's an error to use the @@ -228,6 +230,14 @@ private: // window. this is only a hint, actual transform may differ. uint32_t mTransformHint; + // mProducerControlledByApp whether this buffer producer is controlled + // by the application + bool mProducerControlledByApp; + + // mSwapIntervalZero set if we should drop buffers at queue() time to + // achieve an asynchronous swap interval + bool mSwapIntervalZero; + // mConsumerRunningBehind whether the consumer is running more than // one buffer behind the producer. mutable bool mConsumerRunningBehind; diff --git a/include/gui/SurfaceComposerClient.h b/include/gui/SurfaceComposerClient.h index 6bf5b47..e982bcd 100644 --- a/include/gui/SurfaceComposerClient.h +++ b/include/gui/SurfaceComposerClient.h @@ -48,7 +48,7 @@ class Region; class SurfaceComposerClient : public RefBase { friend class Composer; -public: +public: SurfaceComposerClient(); virtual ~SurfaceComposerClient(); @@ -57,7 +57,7 @@ public: // Return the connection of this client sp<IBinder> connection() const; - + // Forcibly remove connection before all references have gone away. void dispose(); @@ -86,9 +86,12 @@ public: uint32_t flags = 0 // usage flags ); - //! Create a display + //! Create a virtual display static sp<IBinder> createDisplay(const String8& displayName, bool secure); + //! Destroy a virtual display + static void destroyDisplay(const sp<IBinder>& display); + //! Get the token for the existing default displays. //! Possible values for id are eDisplayIdMain and eDisplayIdHdmi. static sp<IBinder> getBuiltInDisplay(int32_t id); @@ -165,6 +168,7 @@ public: private: mutable sp<CpuConsumer> mCpuConsumer; + mutable sp<BufferQueue> mBufferQueue; CpuConsumer::LockedBuffer mBuffer; bool mHaveBuffer; diff --git a/include/input/Input.h b/include/input/Input.h new file mode 100644 index 0000000..e778076 --- /dev/null +++ b/include/input/Input.h @@ -0,0 +1,620 @@ +/* + * 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 _LIBINPUT_INPUT_H +#define _LIBINPUT_INPUT_H + +/** + * Native input event structures. + */ + +#include <android/input.h> +#include <utils/Vector.h> +#include <utils/KeyedVector.h> +#include <utils/Timers.h> +#include <utils/RefBase.h> +#include <utils/String8.h> + +/* + * Additional private constants not defined in ndk/ui/input.h. + */ +enum { + /* Signifies that the key is being predispatched */ + AKEY_EVENT_FLAG_PREDISPATCH = 0x20000000, + + /* Private control to determine when an app is tracking a key sequence. */ + AKEY_EVENT_FLAG_START_TRACKING = 0x40000000, + + /* Key event is inconsistent with previously sent key events. */ + AKEY_EVENT_FLAG_TAINTED = 0x80000000, +}; + +enum { + /* Motion event is inconsistent with previously sent motion events. */ + AMOTION_EVENT_FLAG_TAINTED = 0x80000000, +}; + +enum { + /* Used when a motion event is not associated with any display. + * Typically used for non-pointer events. */ + ADISPLAY_ID_NONE = -1, + + /* The default display id. */ + ADISPLAY_ID_DEFAULT = 0, +}; + +enum { + /* + * Indicates that an input device has switches. + * This input source flag is hidden from the API because switches are only used by the system + * and applications have no way to interact with them. + */ + AINPUT_SOURCE_SWITCH = 0x80000000, +}; + +/* + * SystemUiVisibility constants from View. + */ +enum { + ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE = 0, + ASYSTEM_UI_VISIBILITY_STATUS_BAR_HIDDEN = 0x00000001, +}; + +/* + * Maximum number of pointers supported per motion event. + * Smallest number of pointers is 1. + * (We want at least 10 but some touch controllers obstensibly configured for 10 pointers + * will occasionally emit 11. There is not much harm making this constant bigger.) + */ +#define MAX_POINTERS 16 + +/* + * Maximum pointer id value supported in a motion event. + * Smallest pointer id is 0. + * (This is limited by our use of BitSet32 to track pointer assignments.) + */ +#define MAX_POINTER_ID 31 + +/* + * Declare a concrete type for the NDK's input event forward declaration. + */ +struct AInputEvent { + virtual ~AInputEvent() { } +}; + +/* + * Declare a concrete type for the NDK's input device forward declaration. + */ +struct AInputDevice { + virtual ~AInputDevice() { } +}; + + +namespace android { + +#ifdef HAVE_ANDROID_OS +class Parcel; +#endif + +/* + * Flags that flow alongside events in the input dispatch system to help with certain + * policy decisions such as waking from device sleep. + * + * These flags are also defined in frameworks/base/core/java/android/view/WindowManagerPolicy.java. + */ +enum { + /* These flags originate in RawEvents and are generally set in the key map. + * NOTE: If you edit these flags, also edit labels in KeycodeLabels.h. */ + + POLICY_FLAG_WAKE = 0x00000001, + POLICY_FLAG_WAKE_DROPPED = 0x00000002, + POLICY_FLAG_SHIFT = 0x00000004, + POLICY_FLAG_CAPS_LOCK = 0x00000008, + POLICY_FLAG_ALT = 0x00000010, + POLICY_FLAG_ALT_GR = 0x00000020, + POLICY_FLAG_MENU = 0x00000040, + POLICY_FLAG_LAUNCHER = 0x00000080, + POLICY_FLAG_VIRTUAL = 0x00000100, + POLICY_FLAG_FUNCTION = 0x00000200, + + POLICY_FLAG_RAW_MASK = 0x0000ffff, + + /* These flags are set by the input dispatcher. */ + + // Indicates that the input event was injected. + POLICY_FLAG_INJECTED = 0x01000000, + + // Indicates that the input event is from a trusted source such as a directly attached + // input device or an application with system-wide event injection permission. + POLICY_FLAG_TRUSTED = 0x02000000, + + // Indicates that the input event has passed through an input filter. + POLICY_FLAG_FILTERED = 0x04000000, + + // Disables automatic key repeating behavior. + POLICY_FLAG_DISABLE_KEY_REPEAT = 0x08000000, + + /* These flags are set by the input reader policy as it intercepts each event. */ + + // Indicates that the screen was off when the event was received and the event + // should wake the device. + POLICY_FLAG_WOKE_HERE = 0x10000000, + + // Indicates that the screen was dim when the event was received and the event + // should brighten the device. + POLICY_FLAG_BRIGHT_HERE = 0x20000000, + + // Indicates that the event should be dispatched to applications. + // The input event should still be sent to the InputDispatcher so that it can see all + // input events received include those that it will not deliver. + POLICY_FLAG_PASS_TO_USER = 0x40000000, +}; + +/* + * Pointer coordinate data. + */ +struct PointerCoords { + enum { MAX_AXES = 14 }; // 14 so that sizeof(PointerCoords) == 64 + + // Bitfield of axes that are present in this structure. + uint64_t bits; + + // Values of axes that are stored in this structure packed in order by axis id + // for each axis that is present in the structure according to 'bits'. + float values[MAX_AXES]; + + inline void clear() { + bits = 0; + } + + float getAxisValue(int32_t axis) const; + status_t setAxisValue(int32_t axis, float value); + + void scale(float scale); + + inline float getX() const { + return getAxisValue(AMOTION_EVENT_AXIS_X); + } + + inline float getY() const { + return getAxisValue(AMOTION_EVENT_AXIS_Y); + } + +#ifdef HAVE_ANDROID_OS + status_t readFromParcel(Parcel* parcel); + status_t writeToParcel(Parcel* parcel) const; +#endif + + bool operator==(const PointerCoords& other) const; + inline bool operator!=(const PointerCoords& other) const { + return !(*this == other); + } + + void copyFrom(const PointerCoords& other); + +private: + void tooManyAxes(int axis); +}; + +/* + * Pointer property data. + */ +struct PointerProperties { + // The id of the pointer. + int32_t id; + + // The pointer tool type. + int32_t toolType; + + inline void clear() { + id = -1; + toolType = 0; + } + + bool operator==(const PointerProperties& other) const; + inline bool operator!=(const PointerProperties& other) const { + return !(*this == other); + } + + void copyFrom(const PointerProperties& other); +}; + +/* + * Input events. + */ +class InputEvent : public AInputEvent { +public: + virtual ~InputEvent() { } + + virtual int32_t getType() const = 0; + + inline int32_t getDeviceId() const { return mDeviceId; } + + inline int32_t getSource() const { return mSource; } + + inline void setSource(int32_t source) { mSource = source; } + +protected: + void initialize(int32_t deviceId, int32_t source); + void initialize(const InputEvent& from); + + int32_t mDeviceId; + int32_t mSource; +}; + +/* + * Key events. + */ +class KeyEvent : public InputEvent { +public: + virtual ~KeyEvent() { } + + virtual int32_t getType() const { return AINPUT_EVENT_TYPE_KEY; } + + inline int32_t getAction() const { return mAction; } + + inline int32_t getFlags() const { return mFlags; } + + inline void setFlags(int32_t flags) { mFlags = flags; } + + inline int32_t getKeyCode() const { return mKeyCode; } + + inline int32_t getScanCode() const { return mScanCode; } + + inline int32_t getMetaState() const { return mMetaState; } + + inline int32_t getRepeatCount() const { return mRepeatCount; } + + inline nsecs_t getDownTime() const { return mDownTime; } + + inline nsecs_t getEventTime() const { return mEventTime; } + + // Return true if this event may have a default action implementation. + static bool hasDefaultAction(int32_t keyCode); + bool hasDefaultAction() const; + + // Return true if this event represents a system key. + static bool isSystemKey(int32_t keyCode); + bool isSystemKey() const; + + void initialize( + int32_t deviceId, + int32_t source, + int32_t action, + int32_t flags, + int32_t keyCode, + int32_t scanCode, + int32_t metaState, + int32_t repeatCount, + nsecs_t downTime, + nsecs_t eventTime); + void initialize(const KeyEvent& from); + +protected: + int32_t mAction; + int32_t mFlags; + int32_t mKeyCode; + int32_t mScanCode; + int32_t mMetaState; + int32_t mRepeatCount; + nsecs_t mDownTime; + nsecs_t mEventTime; +}; + +/* + * Motion events. + */ +class MotionEvent : public InputEvent { +public: + virtual ~MotionEvent() { } + + virtual int32_t getType() const { return AINPUT_EVENT_TYPE_MOTION; } + + inline int32_t getAction() const { return mAction; } + + inline int32_t getActionMasked() const { return mAction & AMOTION_EVENT_ACTION_MASK; } + + inline int32_t getActionIndex() const { + return (mAction & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) + >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; + } + + inline void setAction(int32_t action) { mAction = action; } + + inline int32_t getFlags() const { return mFlags; } + + inline void setFlags(int32_t flags) { mFlags = flags; } + + inline int32_t getEdgeFlags() const { return mEdgeFlags; } + + inline void setEdgeFlags(int32_t edgeFlags) { mEdgeFlags = edgeFlags; } + + inline int32_t getMetaState() const { return mMetaState; } + + inline void setMetaState(int32_t metaState) { mMetaState = metaState; } + + inline int32_t getButtonState() const { return mButtonState; } + + inline float getXOffset() const { return mXOffset; } + + inline float getYOffset() const { return mYOffset; } + + inline float getXPrecision() const { return mXPrecision; } + + inline float getYPrecision() const { return mYPrecision; } + + inline nsecs_t getDownTime() const { return mDownTime; } + + inline void setDownTime(nsecs_t downTime) { mDownTime = downTime; } + + inline size_t getPointerCount() const { return mPointerProperties.size(); } + + inline const PointerProperties* getPointerProperties(size_t pointerIndex) const { + return &mPointerProperties[pointerIndex]; + } + + inline int32_t getPointerId(size_t pointerIndex) const { + return mPointerProperties[pointerIndex].id; + } + + inline int32_t getToolType(size_t pointerIndex) const { + return mPointerProperties[pointerIndex].toolType; + } + + inline nsecs_t getEventTime() const { return mSampleEventTimes[getHistorySize()]; } + + const PointerCoords* getRawPointerCoords(size_t pointerIndex) const; + + float getRawAxisValue(int32_t axis, size_t pointerIndex) const; + + inline float getRawX(size_t pointerIndex) const { + return getRawAxisValue(AMOTION_EVENT_AXIS_X, pointerIndex); + } + + inline float getRawY(size_t pointerIndex) const { + return getRawAxisValue(AMOTION_EVENT_AXIS_Y, pointerIndex); + } + + float getAxisValue(int32_t axis, size_t pointerIndex) const; + + inline float getX(size_t pointerIndex) const { + return getAxisValue(AMOTION_EVENT_AXIS_X, pointerIndex); + } + + inline float getY(size_t pointerIndex) const { + return getAxisValue(AMOTION_EVENT_AXIS_Y, pointerIndex); + } + + inline float getPressure(size_t pointerIndex) const { + return getAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pointerIndex); + } + + inline float getSize(size_t pointerIndex) const { + return getAxisValue(AMOTION_EVENT_AXIS_SIZE, pointerIndex); + } + + inline float getTouchMajor(size_t pointerIndex) const { + return getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, pointerIndex); + } + + inline float getTouchMinor(size_t pointerIndex) const { + return getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, pointerIndex); + } + + inline float getToolMajor(size_t pointerIndex) const { + return getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, pointerIndex); + } + + inline float getToolMinor(size_t pointerIndex) const { + return getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, pointerIndex); + } + + inline float getOrientation(size_t pointerIndex) const { + return getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, pointerIndex); + } + + inline size_t getHistorySize() const { return mSampleEventTimes.size() - 1; } + + inline nsecs_t getHistoricalEventTime(size_t historicalIndex) const { + return mSampleEventTimes[historicalIndex]; + } + + const PointerCoords* getHistoricalRawPointerCoords( + size_t pointerIndex, size_t historicalIndex) const; + + float getHistoricalRawAxisValue(int32_t axis, size_t pointerIndex, + size_t historicalIndex) const; + + inline float getHistoricalRawX(size_t pointerIndex, size_t historicalIndex) const { + return getHistoricalRawAxisValue( + AMOTION_EVENT_AXIS_X, pointerIndex, historicalIndex); + } + + inline float getHistoricalRawY(size_t pointerIndex, size_t historicalIndex) const { + return getHistoricalRawAxisValue( + AMOTION_EVENT_AXIS_Y, pointerIndex, historicalIndex); + } + + float getHistoricalAxisValue(int32_t axis, size_t pointerIndex, size_t historicalIndex) const; + + inline float getHistoricalX(size_t pointerIndex, size_t historicalIndex) const { + return getHistoricalAxisValue( + AMOTION_EVENT_AXIS_X, pointerIndex, historicalIndex); + } + + inline float getHistoricalY(size_t pointerIndex, size_t historicalIndex) const { + return getHistoricalAxisValue( + AMOTION_EVENT_AXIS_Y, pointerIndex, historicalIndex); + } + + inline float getHistoricalPressure(size_t pointerIndex, size_t historicalIndex) const { + return getHistoricalAxisValue( + AMOTION_EVENT_AXIS_PRESSURE, pointerIndex, historicalIndex); + } + + inline float getHistoricalSize(size_t pointerIndex, size_t historicalIndex) const { + return getHistoricalAxisValue( + AMOTION_EVENT_AXIS_SIZE, pointerIndex, historicalIndex); + } + + inline float getHistoricalTouchMajor(size_t pointerIndex, size_t historicalIndex) const { + return getHistoricalAxisValue( + AMOTION_EVENT_AXIS_TOUCH_MAJOR, pointerIndex, historicalIndex); + } + + inline float getHistoricalTouchMinor(size_t pointerIndex, size_t historicalIndex) const { + return getHistoricalAxisValue( + AMOTION_EVENT_AXIS_TOUCH_MINOR, pointerIndex, historicalIndex); + } + + inline float getHistoricalToolMajor(size_t pointerIndex, size_t historicalIndex) const { + return getHistoricalAxisValue( + AMOTION_EVENT_AXIS_TOOL_MAJOR, pointerIndex, historicalIndex); + } + + inline float getHistoricalToolMinor(size_t pointerIndex, size_t historicalIndex) const { + return getHistoricalAxisValue( + AMOTION_EVENT_AXIS_TOOL_MINOR, pointerIndex, historicalIndex); + } + + inline float getHistoricalOrientation(size_t pointerIndex, size_t historicalIndex) const { + return getHistoricalAxisValue( + AMOTION_EVENT_AXIS_ORIENTATION, pointerIndex, historicalIndex); + } + + ssize_t findPointerIndex(int32_t pointerId) const; + + void initialize( + int32_t deviceId, + int32_t source, + int32_t action, + int32_t flags, + int32_t edgeFlags, + int32_t metaState, + int32_t buttonState, + float xOffset, + float yOffset, + float xPrecision, + float yPrecision, + nsecs_t downTime, + nsecs_t eventTime, + size_t pointerCount, + const PointerProperties* pointerProperties, + const PointerCoords* pointerCoords); + + void copyFrom(const MotionEvent* other, bool keepHistory); + + void addSample( + nsecs_t eventTime, + const PointerCoords* pointerCoords); + + void offsetLocation(float xOffset, float yOffset); + + void scale(float scaleFactor); + + // Apply 3x3 perspective matrix transformation. + // Matrix is in row-major form and compatible with SkMatrix. + void transform(const float matrix[9]); + +#ifdef HAVE_ANDROID_OS + status_t readFromParcel(Parcel* parcel); + status_t writeToParcel(Parcel* parcel) const; +#endif + + static bool isTouchEvent(int32_t source, int32_t action); + inline bool isTouchEvent() const { + return isTouchEvent(mSource, mAction); + } + + // Low-level accessors. + inline const PointerProperties* getPointerProperties() const { + return mPointerProperties.array(); + } + inline const nsecs_t* getSampleEventTimes() const { return mSampleEventTimes.array(); } + inline const PointerCoords* getSamplePointerCoords() const { + return mSamplePointerCoords.array(); + } + +protected: + int32_t mAction; + int32_t mFlags; + int32_t mEdgeFlags; + int32_t mMetaState; + int32_t mButtonState; + float mXOffset; + float mYOffset; + float mXPrecision; + float mYPrecision; + nsecs_t mDownTime; + Vector<PointerProperties> mPointerProperties; + Vector<nsecs_t> mSampleEventTimes; + Vector<PointerCoords> mSamplePointerCoords; +}; + +/* + * Input event factory. + */ +class InputEventFactoryInterface { +protected: + virtual ~InputEventFactoryInterface() { } + +public: + InputEventFactoryInterface() { } + + virtual KeyEvent* createKeyEvent() = 0; + virtual MotionEvent* createMotionEvent() = 0; +}; + +/* + * A simple input event factory implementation that uses a single preallocated instance + * of each type of input event that are reused for each request. + */ +class PreallocatedInputEventFactory : public InputEventFactoryInterface { +public: + PreallocatedInputEventFactory() { } + virtual ~PreallocatedInputEventFactory() { } + + virtual KeyEvent* createKeyEvent() { return & mKeyEvent; } + virtual MotionEvent* createMotionEvent() { return & mMotionEvent; } + +private: + KeyEvent mKeyEvent; + MotionEvent mMotionEvent; +}; + +/* + * An input event factory implementation that maintains a pool of input events. + */ +class PooledInputEventFactory : public InputEventFactoryInterface { +public: + PooledInputEventFactory(size_t maxPoolSize = 20); + virtual ~PooledInputEventFactory(); + + virtual KeyEvent* createKeyEvent(); + virtual MotionEvent* createMotionEvent(); + + void recycle(InputEvent* event); + +private: + const size_t mMaxPoolSize; + + Vector<KeyEvent*> mKeyEventPool; + Vector<MotionEvent*> mMotionEventPool; +}; + +} // namespace android + +#endif // _LIBINPUT_INPUT_H diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h new file mode 100644 index 0000000..1419b45 --- /dev/null +++ b/include/input/InputDevice.h @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2012 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 _LIBINPUT_INPUT_DEVICE_H +#define _LIBINPUT_INPUT_DEVICE_H + +#include <input/Input.h> +#include <input/KeyCharacterMap.h> + +namespace android { + +/* + * Identifies a device. + */ +struct InputDeviceIdentifier { + inline InputDeviceIdentifier() : + bus(0), vendor(0), product(0), version(0) { + } + + // Information provided by the kernel. + String8 name; + String8 location; + String8 uniqueId; + uint16_t bus; + uint16_t vendor; + uint16_t product; + uint16_t version; + + // A composite input device descriptor string that uniquely identifies the device + // even across reboots or reconnections. The value of this field is used by + // upper layers of the input system to associate settings with individual devices. + // It is hashed from whatever kernel provided information is available. + // Ideally, the way this value is computed should not change between Android releases + // because that would invalidate persistent settings that rely on it. + String8 descriptor; +}; + +/* + * Describes the characteristics and capabilities of an input device. + */ +class InputDeviceInfo { +public: + InputDeviceInfo(); + InputDeviceInfo(const InputDeviceInfo& other); + ~InputDeviceInfo(); + + struct MotionRange { + int32_t axis; + uint32_t source; + float min; + float max; + float flat; + float fuzz; + float resolution; + }; + + void initialize(int32_t id, int32_t generation, int32_t controllerNumber, + const InputDeviceIdentifier& identifier, const String8& alias, bool isExternal); + + inline int32_t getId() const { return mId; } + inline int32_t getControllerNumber() const { return mControllerNumber; } + inline int32_t getGeneration() const { return mGeneration; } + inline const InputDeviceIdentifier& getIdentifier() const { return mIdentifier; } + inline const String8& getAlias() const { return mAlias; } + inline const String8& getDisplayName() const { + return mAlias.isEmpty() ? mIdentifier.name : mAlias; + } + inline bool isExternal() const { return mIsExternal; } + inline uint32_t getSources() const { return mSources; } + + const MotionRange* getMotionRange(int32_t axis, uint32_t source) const; + + void addSource(uint32_t source); + void addMotionRange(int32_t axis, uint32_t source, + float min, float max, float flat, float fuzz, float resolution); + void addMotionRange(const MotionRange& range); + + inline void setKeyboardType(int32_t keyboardType) { mKeyboardType = keyboardType; } + inline int32_t getKeyboardType() const { return mKeyboardType; } + + inline void setKeyCharacterMap(const sp<KeyCharacterMap>& value) { + mKeyCharacterMap = value; + } + + inline sp<KeyCharacterMap> getKeyCharacterMap() const { + return mKeyCharacterMap; + } + + inline void setVibrator(bool hasVibrator) { mHasVibrator = hasVibrator; } + inline bool hasVibrator() const { return mHasVibrator; } + + inline void setButtonUnderPad(bool hasButton) { mHasButtonUnderPad = hasButton; } + inline bool hasButtonUnderPad() const { return mHasButtonUnderPad; } + + inline const Vector<MotionRange>& getMotionRanges() const { + return mMotionRanges; + } + +private: + int32_t mId; + int32_t mGeneration; + int32_t mControllerNumber; + InputDeviceIdentifier mIdentifier; + String8 mAlias; + bool mIsExternal; + uint32_t mSources; + int32_t mKeyboardType; + sp<KeyCharacterMap> mKeyCharacterMap; + bool mHasVibrator; + bool mHasButtonUnderPad; + + Vector<MotionRange> mMotionRanges; +}; + +/* Types of input device configuration files. */ +enum InputDeviceConfigurationFileType { + INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION = 0, /* .idc file */ + INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT = 1, /* .kl file */ + INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP = 2, /* .kcm file */ +}; + +/* + * Gets the path of an input device configuration file, if one is available. + * Considers both system provided and user installed configuration files. + * + * The device identifier is used to construct several default configuration file + * names to try based on the device name, vendor, product, and version. + * + * Returns an empty string if not found. + */ +extern String8 getInputDeviceConfigurationFilePathByDeviceIdentifier( + const InputDeviceIdentifier& deviceIdentifier, + InputDeviceConfigurationFileType type); + +/* + * Gets the path of an input device configuration file, if one is available. + * Considers both system provided and user installed configuration files. + * + * The name is case-sensitive and is used to construct the filename to resolve. + * All characters except 'a'-'z', 'A'-'Z', '0'-'9', '-', and '_' are replaced by underscores. + * + * Returns an empty string if not found. + */ +extern String8 getInputDeviceConfigurationFilePathByName( + const String8& name, InputDeviceConfigurationFileType type); + +} // namespace android + +#endif // _LIBINPUT_INPUT_DEVICE_H diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h new file mode 100644 index 0000000..609b679 --- /dev/null +++ b/include/input/InputTransport.h @@ -0,0 +1,443 @@ +/* + * 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 _LIBINPUT_INPUT_TRANSPORT_H +#define _LIBINPUT_INPUT_TRANSPORT_H + +/** + * Native input transport. + * + * The InputChannel provides a mechanism for exchanging InputMessage structures across processes. + * + * The InputPublisher and InputConsumer each handle one end-point of an input channel. + * The InputPublisher is used by the input dispatcher to send events to the application. + * The InputConsumer is used by the application to receive events from the input dispatcher. + */ + +#include <input/Input.h> +#include <utils/Errors.h> +#include <utils/Timers.h> +#include <utils/RefBase.h> +#include <utils/String8.h> +#include <utils/Vector.h> +#include <utils/BitSet.h> + +namespace android { + +/* + * Intermediate representation used to send input events and related signals. + */ +struct InputMessage { + enum { + TYPE_KEY = 1, + TYPE_MOTION = 2, + TYPE_FINISHED = 3, + }; + + struct Header { + uint32_t type; + uint32_t padding; // 8 byte alignment for the body that follows + } header; + + union Body { + struct Key { + uint32_t seq; + nsecs_t eventTime; + int32_t deviceId; + int32_t source; + int32_t action; + int32_t flags; + int32_t keyCode; + int32_t scanCode; + int32_t metaState; + int32_t repeatCount; + nsecs_t downTime; + + inline size_t size() const { + return sizeof(Key); + } + } key; + + struct Motion { + uint32_t seq; + nsecs_t eventTime; + int32_t deviceId; + int32_t source; + int32_t action; + int32_t flags; + int32_t metaState; + int32_t buttonState; + int32_t edgeFlags; + nsecs_t downTime; + float xOffset; + float yOffset; + float xPrecision; + float yPrecision; + size_t pointerCount; + struct Pointer { + PointerProperties properties; + PointerCoords coords; + } pointers[MAX_POINTERS]; + + int32_t getActionId() const { + uint32_t index = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) + >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; + return pointers[index].properties.id; + } + + inline size_t size() const { + return sizeof(Motion) - sizeof(Pointer) * MAX_POINTERS + + sizeof(Pointer) * pointerCount; + } + } motion; + + struct Finished { + uint32_t seq; + bool handled; + + inline size_t size() const { + return sizeof(Finished); + } + } finished; + } body; + + bool isValid(size_t actualSize) const; + size_t size() const; +}; + +/* + * An input channel consists of a local unix domain socket used to send and receive + * input messages across processes. Each channel has a descriptive name for debugging purposes. + * + * Each endpoint has its own InputChannel object that specifies its file descriptor. + * + * The input channel is closed when all references to it are released. + */ +class InputChannel : public RefBase { +protected: + virtual ~InputChannel(); + +public: + InputChannel(const String8& name, int fd); + + /* Creates a pair of input channels. + * + * Returns OK on success. + */ + static status_t openInputChannelPair(const String8& name, + sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel); + + inline String8 getName() const { return mName; } + inline int getFd() const { return mFd; } + + /* Sends a message to the other endpoint. + * + * If the channel is full then the message is guaranteed not to have been sent at all. + * Try again after the consumer has sent a finished signal indicating that it has + * consumed some of the pending messages from the channel. + * + * Returns OK on success. + * Returns WOULD_BLOCK if the channel is full. + * Returns DEAD_OBJECT if the channel's peer has been closed. + * Other errors probably indicate that the channel is broken. + */ + status_t sendMessage(const InputMessage* msg); + + /* Receives a message sent by the other endpoint. + * + * If there is no message present, try again after poll() indicates that the fd + * is readable. + * + * Returns OK on success. + * Returns WOULD_BLOCK if there is no message present. + * Returns DEAD_OBJECT if the channel's peer has been closed. + * Other errors probably indicate that the channel is broken. + */ + status_t receiveMessage(InputMessage* msg); + + /* Returns a new object that has a duplicate of this channel's fd. */ + sp<InputChannel> dup() const; + +private: + String8 mName; + int mFd; +}; + +/* + * Publishes input events to an input channel. + */ +class InputPublisher { +public: + /* Creates a publisher associated with an input channel. */ + explicit InputPublisher(const sp<InputChannel>& channel); + + /* Destroys the publisher and releases its input channel. */ + ~InputPublisher(); + + /* Gets the underlying input channel. */ + inline sp<InputChannel> getChannel() { return mChannel; } + + /* Publishes a key event to the input channel. + * + * Returns OK on success. + * Returns WOULD_BLOCK if the channel is full. + * Returns DEAD_OBJECT if the channel's peer has been closed. + * Returns BAD_VALUE if seq is 0. + * Other errors probably indicate that the channel is broken. + */ + status_t publishKeyEvent( + uint32_t seq, + int32_t deviceId, + int32_t source, + int32_t action, + int32_t flags, + int32_t keyCode, + int32_t scanCode, + int32_t metaState, + int32_t repeatCount, + nsecs_t downTime, + nsecs_t eventTime); + + /* Publishes a motion event to the input channel. + * + * Returns OK on success. + * Returns WOULD_BLOCK if the channel is full. + * Returns DEAD_OBJECT if the channel's peer has been closed. + * Returns BAD_VALUE if seq is 0 or if pointerCount is less than 1 or greater than MAX_POINTERS. + * Other errors probably indicate that the channel is broken. + */ + status_t publishMotionEvent( + uint32_t seq, + int32_t deviceId, + int32_t source, + int32_t action, + int32_t flags, + int32_t edgeFlags, + int32_t metaState, + int32_t buttonState, + float xOffset, + float yOffset, + float xPrecision, + float yPrecision, + nsecs_t downTime, + nsecs_t eventTime, + size_t pointerCount, + const PointerProperties* pointerProperties, + const PointerCoords* pointerCoords); + + /* Receives the finished signal from the consumer in reply to the original dispatch signal. + * If a signal was received, returns the message sequence number, + * and whether the consumer handled the message. + * + * The returned sequence number is never 0 unless the operation failed. + * + * Returns OK on success. + * Returns WOULD_BLOCK if there is no signal present. + * Returns DEAD_OBJECT if the channel's peer has been closed. + * Other errors probably indicate that the channel is broken. + */ + status_t receiveFinishedSignal(uint32_t* outSeq, bool* outHandled); + +private: + sp<InputChannel> mChannel; +}; + +/* + * Consumes input events from an input channel. + */ +class InputConsumer { +public: + /* Creates a consumer associated with an input channel. */ + explicit InputConsumer(const sp<InputChannel>& channel); + + /* Destroys the consumer and releases its input channel. */ + ~InputConsumer(); + + /* Gets the underlying input channel. */ + inline sp<InputChannel> getChannel() { return mChannel; } + + /* Consumes an input event from the input channel and copies its contents into + * an InputEvent object created using the specified factory. + * + * Tries to combine a series of move events into larger batches whenever possible. + * + * If consumeBatches is false, then defers consuming pending batched events if it + * is possible for additional samples to be added to them later. Call hasPendingBatch() + * to determine whether a pending batch is available to be consumed. + * + * If consumeBatches is true, then events are still batched but they are consumed + * immediately as soon as the input channel is exhausted. + * + * The frameTime parameter specifies the time when the current display frame started + * rendering in the CLOCK_MONOTONIC time base, or -1 if unknown. + * + * The returned sequence number is never 0 unless the operation failed. + * + * Returns OK on success. + * Returns WOULD_BLOCK if there is no event present. + * Returns DEAD_OBJECT if the channel's peer has been closed. + * Returns NO_MEMORY if the event could not be created. + * Other errors probably indicate that the channel is broken. + */ + status_t consume(InputEventFactoryInterface* factory, bool consumeBatches, + nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent); + + /* Sends a finished signal to the publisher to inform it that the message + * with the specified sequence number has finished being process and whether + * the message was handled by the consumer. + * + * Returns OK on success. + * Returns BAD_VALUE if seq is 0. + * Other errors probably indicate that the channel is broken. + */ + status_t sendFinishedSignal(uint32_t seq, bool handled); + + /* Returns true if there is a deferred event waiting. + * + * Should be called after calling consume() to determine whether the consumer + * has a deferred event to be processed. Deferred events are somewhat special in + * that they have already been removed from the input channel. If the input channel + * becomes empty, the client may need to do extra work to ensure that it processes + * the deferred event despite the fact that the input channel's file descriptor + * is not readable. + * + * One option is simply to call consume() in a loop until it returns WOULD_BLOCK. + * This guarantees that all deferred events will be processed. + * + * Alternately, the caller can call hasDeferredEvent() to determine whether there is + * a deferred event waiting and then ensure that its event loop wakes up at least + * one more time to consume the deferred event. + */ + bool hasDeferredEvent() const; + + /* Returns true if there is a pending batch. + * + * Should be called after calling consume() with consumeBatches == false to determine + * whether consume() should be called again later on with consumeBatches == true. + */ + bool hasPendingBatch() const; + +private: + // True if touch resampling is enabled. + const bool mResampleTouch; + + // The input channel. + sp<InputChannel> mChannel; + + // The current input message. + InputMessage mMsg; + + // True if mMsg contains a valid input message that was deferred from the previous + // call to consume and that still needs to be handled. + bool mMsgDeferred; + + // Batched motion events per device and source. + struct Batch { + Vector<InputMessage> samples; + }; + Vector<Batch> mBatches; + + // Touch state per device and source, only for sources of class pointer. + struct History { + nsecs_t eventTime; + BitSet32 idBits; + int32_t idToIndex[MAX_POINTER_ID + 1]; + PointerCoords pointers[MAX_POINTERS]; + + void initializeFrom(const InputMessage* msg) { + eventTime = msg->body.motion.eventTime; + idBits.clear(); + for (size_t i = 0; i < msg->body.motion.pointerCount; i++) { + uint32_t id = msg->body.motion.pointers[i].properties.id; + idBits.markBit(id); + idToIndex[id] = i; + pointers[i].copyFrom(msg->body.motion.pointers[i].coords); + } + } + + const PointerCoords& getPointerById(uint32_t id) const { + return pointers[idToIndex[id]]; + } + }; + struct TouchState { + int32_t deviceId; + int32_t source; + size_t historyCurrent; + size_t historySize; + History history[2]; + History lastResample; + + void initialize(int32_t deviceId, int32_t source) { + this->deviceId = deviceId; + this->source = source; + historyCurrent = 0; + historySize = 0; + lastResample.eventTime = 0; + lastResample.idBits.clear(); + } + + void addHistory(const InputMessage* msg) { + historyCurrent ^= 1; + if (historySize < 2) { + historySize += 1; + } + history[historyCurrent].initializeFrom(msg); + } + + const History* getHistory(size_t index) const { + return &history[(historyCurrent + index) & 1]; + } + }; + Vector<TouchState> mTouchStates; + + // Chain of batched sequence numbers. When multiple input messages are combined into + // a batch, we append a record here that associates the last sequence number in the + // batch with the previous one. When the finished signal is sent, we traverse the + // chain to individually finish all input messages that were part of the batch. + struct SeqChain { + uint32_t seq; // sequence number of batched input message + uint32_t chain; // sequence number of previous batched input message + }; + Vector<SeqChain> mSeqChains; + + status_t consumeBatch(InputEventFactoryInterface* factory, + nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent); + status_t consumeSamples(InputEventFactoryInterface* factory, + Batch& batch, size_t count, uint32_t* outSeq, InputEvent** outEvent); + + void updateTouchState(InputMessage* msg); + void rewriteMessage(const TouchState& state, InputMessage* msg); + void resampleTouchState(nsecs_t frameTime, MotionEvent* event, + const InputMessage *next); + + ssize_t findBatch(int32_t deviceId, int32_t source) const; + ssize_t findTouchState(int32_t deviceId, int32_t source) const; + + status_t sendUnchainedFinishedSignal(uint32_t seq, bool handled); + + static void initializeKeyEvent(KeyEvent* event, const InputMessage* msg); + static void initializeMotionEvent(MotionEvent* event, const InputMessage* msg); + static void addSample(MotionEvent* event, const InputMessage* msg); + static bool canAddSample(const Batch& batch, const InputMessage* msg); + static ssize_t findSampleNoLaterThan(const Batch& batch, nsecs_t time); + static bool shouldResampleTool(int32_t toolType); + + static bool isTouchResamplingEnabled(); +}; + +} // namespace android + +#endif // _LIBINPUT_INPUT_TRANSPORT_H diff --git a/include/input/KeyCharacterMap.h b/include/input/KeyCharacterMap.h new file mode 100644 index 0000000..e70666a --- /dev/null +++ b/include/input/KeyCharacterMap.h @@ -0,0 +1,257 @@ +/* + * 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. + */ + +#ifndef _LIBINPUT_KEY_CHARACTER_MAP_H +#define _LIBINPUT_KEY_CHARACTER_MAP_H + +#include <stdint.h> + +#if HAVE_ANDROID_OS +#include <binder/IBinder.h> +#endif + +#include <input/Input.h> +#include <utils/Errors.h> +#include <utils/KeyedVector.h> +#include <utils/Tokenizer.h> +#include <utils/String8.h> +#include <utils/Unicode.h> +#include <utils/RefBase.h> + +namespace android { + +/** + * Describes a mapping from Android key codes to characters. + * Also specifies other functions of the keyboard such as the keyboard type + * and key modifier semantics. + * + * This object is immutable after it has been loaded. + */ +class KeyCharacterMap : public RefBase { +public: + enum KeyboardType { + KEYBOARD_TYPE_UNKNOWN = 0, + KEYBOARD_TYPE_NUMERIC = 1, + KEYBOARD_TYPE_PREDICTIVE = 2, + KEYBOARD_TYPE_ALPHA = 3, + KEYBOARD_TYPE_FULL = 4, + KEYBOARD_TYPE_SPECIAL_FUNCTION = 5, + KEYBOARD_TYPE_OVERLAY = 6, + }; + + enum Format { + // Base keyboard layout, may contain device-specific options, such as "type" declaration. + FORMAT_BASE = 0, + // Overlay keyboard layout, more restrictive, may be published by applications, + // cannot override device-specific options. + FORMAT_OVERLAY = 1, + // Either base or overlay layout ok. + FORMAT_ANY = 2, + }; + + // Substitute key code and meta state for fallback action. + struct FallbackAction { + int32_t keyCode; + int32_t metaState; + }; + + /* Loads a key character map from a file. */ + static status_t load(const String8& filename, Format format, sp<KeyCharacterMap>* outMap); + + /* Loads a key character map from its string contents. */ + static status_t loadContents(const String8& filename, + const char* contents, Format format, sp<KeyCharacterMap>* outMap); + + /* Combines a base key character map and an overlay. */ + static sp<KeyCharacterMap> combine(const sp<KeyCharacterMap>& base, + const sp<KeyCharacterMap>& overlay); + + /* Returns an empty key character map. */ + static sp<KeyCharacterMap> empty(); + + /* Gets the keyboard type. */ + int32_t getKeyboardType() const; + + /* Gets the primary character for this key as in the label physically printed on it. + * Returns 0 if none (eg. for non-printing keys). */ + char16_t getDisplayLabel(int32_t keyCode) const; + + /* Gets the Unicode character for the number or symbol generated by the key + * when the keyboard is used as a dialing pad. + * Returns 0 if no number or symbol is generated. + */ + char16_t getNumber(int32_t keyCode) const; + + /* Gets the Unicode character generated by the key and meta key modifiers. + * Returns 0 if no character is generated. + */ + char16_t getCharacter(int32_t keyCode, int32_t metaState) const; + + /* Gets the fallback action to use by default if the application does not + * handle the specified key. + * Returns true if an action was available, false if none. + */ + bool getFallbackAction(int32_t keyCode, int32_t metaState, + FallbackAction* outFallbackAction) const; + + /* Gets the first matching Unicode character that can be generated by the key, + * preferring the one with the specified meta key modifiers. + * Returns 0 if no matching character is generated. + */ + char16_t getMatch(int32_t keyCode, const char16_t* chars, + size_t numChars, int32_t metaState) const; + + /* Gets a sequence of key events that could plausibly generate the specified + * character sequence. Returns false if some of the characters cannot be generated. + */ + bool getEvents(int32_t deviceId, const char16_t* chars, size_t numChars, + Vector<KeyEvent>& outEvents) const; + + /* Maps a scan code and usage code to a key code, in case this key map overrides + * the mapping in some way. */ + status_t mapKey(int32_t scanCode, int32_t usageCode, int32_t* outKeyCode) const; + +#if HAVE_ANDROID_OS + /* Reads a key map from a parcel. */ + static sp<KeyCharacterMap> readFromParcel(Parcel* parcel); + + /* Writes a key map to a parcel. */ + void writeToParcel(Parcel* parcel) const; +#endif + +protected: + virtual ~KeyCharacterMap(); + +private: + struct Behavior { + Behavior(); + Behavior(const Behavior& other); + + /* The next behavior in the list, or NULL if none. */ + Behavior* next; + + /* The meta key modifiers for this behavior. */ + int32_t metaState; + + /* The character to insert. */ + char16_t character; + + /* The fallback keycode if the key is not handled. */ + int32_t fallbackKeyCode; + }; + + struct Key { + Key(); + Key(const Key& other); + ~Key(); + + /* The single character label printed on the key, or 0 if none. */ + char16_t label; + + /* The number or symbol character generated by the key, or 0 if none. */ + char16_t number; + + /* The list of key behaviors sorted from most specific to least specific + * meta key binding. */ + Behavior* firstBehavior; + }; + + class Parser { + enum State { + STATE_TOP = 0, + STATE_KEY = 1, + }; + + enum { + PROPERTY_LABEL = 1, + PROPERTY_NUMBER = 2, + PROPERTY_META = 3, + }; + + struct Property { + inline Property(int32_t property = 0, int32_t metaState = 0) : + property(property), metaState(metaState) { } + + int32_t property; + int32_t metaState; + }; + + KeyCharacterMap* mMap; + Tokenizer* mTokenizer; + Format mFormat; + State mState; + int32_t mKeyCode; + + public: + Parser(KeyCharacterMap* map, Tokenizer* tokenizer, Format format); + ~Parser(); + status_t parse(); + + private: + status_t parseType(); + status_t parseMap(); + status_t parseMapKey(); + status_t parseKey(); + status_t parseKeyProperty(); + status_t finishKey(Key* key); + status_t parseModifier(const String8& token, int32_t* outMetaState); + status_t parseCharacterLiteral(char16_t* outCharacter); + }; + + static sp<KeyCharacterMap> sEmpty; + + KeyedVector<int32_t, Key*> mKeys; + int mType; + + KeyedVector<int32_t, int32_t> mKeysByScanCode; + KeyedVector<int32_t, int32_t> mKeysByUsageCode; + + KeyCharacterMap(); + KeyCharacterMap(const KeyCharacterMap& other); + + bool getKey(int32_t keyCode, const Key** outKey) const; + bool getKeyBehavior(int32_t keyCode, int32_t metaState, + const Key** outKey, const Behavior** outBehavior) const; + static bool matchesMetaState(int32_t eventMetaState, int32_t behaviorMetaState); + + bool findKey(char16_t ch, int32_t* outKeyCode, int32_t* outMetaState) const; + + static status_t load(Tokenizer* tokenizer, Format format, sp<KeyCharacterMap>* outMap); + + static void addKey(Vector<KeyEvent>& outEvents, + int32_t deviceId, int32_t keyCode, int32_t metaState, bool down, nsecs_t time); + static void addMetaKeys(Vector<KeyEvent>& outEvents, + int32_t deviceId, int32_t metaState, bool down, nsecs_t time, + int32_t* currentMetaState); + static bool addSingleEphemeralMetaKey(Vector<KeyEvent>& outEvents, + int32_t deviceId, int32_t metaState, bool down, nsecs_t time, + int32_t keyCode, int32_t keyMetaState, + int32_t* currentMetaState); + static void addDoubleEphemeralMetaKey(Vector<KeyEvent>& outEvents, + int32_t deviceId, int32_t metaState, bool down, nsecs_t time, + int32_t leftKeyCode, int32_t leftKeyMetaState, + int32_t rightKeyCode, int32_t rightKeyMetaState, + int32_t eitherKeyMetaState, + int32_t* currentMetaState); + static void addLockedMetaKey(Vector<KeyEvent>& outEvents, + int32_t deviceId, int32_t metaState, nsecs_t time, + int32_t keyCode, int32_t keyMetaState, + int32_t* currentMetaState); +}; + +} // namespace android + +#endif // _LIBINPUT_KEY_CHARACTER_MAP_H diff --git a/include/input/KeyLayoutMap.h b/include/input/KeyLayoutMap.h new file mode 100644 index 0000000..eec11cf --- /dev/null +++ b/include/input/KeyLayoutMap.h @@ -0,0 +1,107 @@ +/* + * 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. + */ + +#ifndef _LIBINPUT_KEY_LAYOUT_MAP_H +#define _LIBINPUT_KEY_LAYOUT_MAP_H + +#include <stdint.h> +#include <utils/Errors.h> +#include <utils/KeyedVector.h> +#include <utils/Tokenizer.h> +#include <utils/RefBase.h> + +namespace android { + +struct AxisInfo { + enum Mode { + // Axis value is reported directly. + MODE_NORMAL = 0, + // Axis value should be inverted before reporting. + MODE_INVERT = 1, + // Axis value should be split into two axes + MODE_SPLIT = 2, + }; + + // Axis mode. + Mode mode; + + // Axis id. + // When split, this is the axis used for values smaller than the split position. + int32_t axis; + + // When split, this is the axis used for values after higher than the split position. + int32_t highAxis; + + // The split value, or 0 if not split. + int32_t splitValue; + + // The flat value, or -1 if none. + int32_t flatOverride; + + AxisInfo() : mode(MODE_NORMAL), axis(-1), highAxis(-1), splitValue(0), flatOverride(-1) { + } +}; + +/** + * Describes a mapping from keyboard scan codes and joystick axes to Android key codes and axes. + * + * This object is immutable after it has been loaded. + */ +class KeyLayoutMap : public RefBase { +public: + static status_t load(const String8& filename, sp<KeyLayoutMap>* outMap); + + status_t mapKey(int32_t scanCode, int32_t usageCode, + int32_t* outKeyCode, uint32_t* outFlags) const; + status_t findScanCodesForKey(int32_t keyCode, Vector<int32_t>* outScanCodes) const; + + status_t mapAxis(int32_t scanCode, AxisInfo* outAxisInfo) const; + +protected: + virtual ~KeyLayoutMap(); + +private: + struct Key { + int32_t keyCode; + uint32_t flags; + }; + + KeyedVector<int32_t, Key> mKeysByScanCode; + KeyedVector<int32_t, Key> mKeysByUsageCode; + KeyedVector<int32_t, AxisInfo> mAxes; + + KeyLayoutMap(); + + const Key* getKey(int32_t scanCode, int32_t usageCode) const; + + class Parser { + KeyLayoutMap* mMap; + Tokenizer* mTokenizer; + + public: + Parser(KeyLayoutMap* map, Tokenizer* tokenizer); + ~Parser(); + status_t parse(); + + private: + status_t parseKey(); + status_t parseAxis(); + }; +}; + +} // namespace android + +#endif // _LIBINPUT_KEY_LAYOUT_MAP_H diff --git a/include/input/Keyboard.h b/include/input/Keyboard.h new file mode 100644 index 0000000..846cb0c --- /dev/null +++ b/include/input/Keyboard.h @@ -0,0 +1,120 @@ +/* + * 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 _LIBINPUT_KEYBOARD_H +#define _LIBINPUT_KEYBOARD_H + +#include <input/Input.h> +#include <input/InputDevice.h> +#include <utils/Errors.h> +#include <utils/String8.h> +#include <utils/PropertyMap.h> + +namespace android { + +enum { + /* Device id of the built in keyboard. */ + DEVICE_ID_BUILT_IN_KEYBOARD = 0, + + /* Device id of a generic virtual keyboard with a full layout that can be used + * to synthesize key events. */ + DEVICE_ID_VIRTUAL_KEYBOARD = -1, +}; + +class KeyLayoutMap; +class KeyCharacterMap; + +/** + * Loads the key layout map and key character map for a keyboard device. + */ +class KeyMap { +public: + String8 keyLayoutFile; + sp<KeyLayoutMap> keyLayoutMap; + + String8 keyCharacterMapFile; + sp<KeyCharacterMap> keyCharacterMap; + + KeyMap(); + ~KeyMap(); + + status_t load(const InputDeviceIdentifier& deviceIdenfier, + const PropertyMap* deviceConfiguration); + + inline bool haveKeyLayout() const { + return !keyLayoutFile.isEmpty(); + } + + inline bool haveKeyCharacterMap() const { + return !keyCharacterMapFile.isEmpty(); + } + + inline bool isComplete() const { + return haveKeyLayout() && haveKeyCharacterMap(); + } + +private: + bool probeKeyMap(const InputDeviceIdentifier& deviceIdentifier, const String8& name); + status_t loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier, const String8& name); + status_t loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifier, + const String8& name); + String8 getPath(const InputDeviceIdentifier& deviceIdentifier, + const String8& name, InputDeviceConfigurationFileType type); +}; + +/** + * Returns true if the keyboard is eligible for use as a built-in keyboard. + */ +extern bool isEligibleBuiltInKeyboard(const InputDeviceIdentifier& deviceIdentifier, + const PropertyMap* deviceConfiguration, const KeyMap* keyMap); + +/** + * Gets a key code by its short form label, eg. "HOME". + * Returns 0 if unknown. + */ +extern int32_t getKeyCodeByLabel(const char* label); + +/** + * Gets a key flag by its short form label, eg. "WAKE". + * Returns 0 if unknown. + */ +extern uint32_t getKeyFlagByLabel(const char* label); + +/** + * Gets a axis by its short form label, eg. "X". + * Returns -1 if unknown. + */ +extern int32_t getAxisByLabel(const char* label); + +/** + * Gets a axis label by its id. + * Returns NULL if unknown. + */ +extern const char* getAxisLabel(int32_t axisId); + +/** + * Updates a meta state field when a key is pressed or released. + */ +extern int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState); + +/** + * Returns true if a key is a meta key like ALT or CAPS_LOCK. + */ +extern bool isMetaKey(int32_t keyCode); + +} // namespace android + +#endif // _LIBINPUT_KEYBOARD_H diff --git a/include/input/KeycodeLabels.h b/include/input/KeycodeLabels.h index 1e91ea8..c64c5d8 100644 --- a/include/input/KeycodeLabels.h +++ b/include/input/KeycodeLabels.h @@ -1,6 +1,322 @@ +/* + * 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. + */ + #ifndef _LIBINPUT_KEYCODE_LABELS_H #define _LIBINPUT_KEYCODE_LABELS_H -#include <androidfw/KeycodeLabels.h> +#include <android/keycodes.h> + +struct KeycodeLabel { + const char *literal; + int value; +}; + +static const KeycodeLabel KEYCODES[] = { + { "SOFT_LEFT", 1 }, + { "SOFT_RIGHT", 2 }, + { "HOME", 3 }, + { "BACK", 4 }, + { "CALL", 5 }, + { "ENDCALL", 6 }, + { "0", 7 }, + { "1", 8 }, + { "2", 9 }, + { "3", 10 }, + { "4", 11 }, + { "5", 12 }, + { "6", 13 }, + { "7", 14 }, + { "8", 15 }, + { "9", 16 }, + { "STAR", 17 }, + { "POUND", 18 }, + { "DPAD_UP", 19 }, + { "DPAD_DOWN", 20 }, + { "DPAD_LEFT", 21 }, + { "DPAD_RIGHT", 22 }, + { "DPAD_CENTER", 23 }, + { "VOLUME_UP", 24 }, + { "VOLUME_DOWN", 25 }, + { "POWER", 26 }, + { "CAMERA", 27 }, + { "CLEAR", 28 }, + { "A", 29 }, + { "B", 30 }, + { "C", 31 }, + { "D", 32 }, + { "E", 33 }, + { "F", 34 }, + { "G", 35 }, + { "H", 36 }, + { "I", 37 }, + { "J", 38 }, + { "K", 39 }, + { "L", 40 }, + { "M", 41 }, + { "N", 42 }, + { "O", 43 }, + { "P", 44 }, + { "Q", 45 }, + { "R", 46 }, + { "S", 47 }, + { "T", 48 }, + { "U", 49 }, + { "V", 50 }, + { "W", 51 }, + { "X", 52 }, + { "Y", 53 }, + { "Z", 54 }, + { "COMMA", 55 }, + { "PERIOD", 56 }, + { "ALT_LEFT", 57 }, + { "ALT_RIGHT", 58 }, + { "SHIFT_LEFT", 59 }, + { "SHIFT_RIGHT", 60 }, + { "TAB", 61 }, + { "SPACE", 62 }, + { "SYM", 63 }, + { "EXPLORER", 64 }, + { "ENVELOPE", 65 }, + { "ENTER", 66 }, + { "DEL", 67 }, + { "GRAVE", 68 }, + { "MINUS", 69 }, + { "EQUALS", 70 }, + { "LEFT_BRACKET", 71 }, + { "RIGHT_BRACKET", 72 }, + { "BACKSLASH", 73 }, + { "SEMICOLON", 74 }, + { "APOSTROPHE", 75 }, + { "SLASH", 76 }, + { "AT", 77 }, + { "NUM", 78 }, + { "HEADSETHOOK", 79 }, + { "FOCUS", 80 }, + { "PLUS", 81 }, + { "MENU", 82 }, + { "NOTIFICATION", 83 }, + { "SEARCH", 84 }, + { "MEDIA_PLAY_PAUSE", 85 }, + { "MEDIA_STOP", 86 }, + { "MEDIA_NEXT", 87 }, + { "MEDIA_PREVIOUS", 88 }, + { "MEDIA_REWIND", 89 }, + { "MEDIA_FAST_FORWARD", 90 }, + { "MUTE", 91 }, + { "PAGE_UP", 92 }, + { "PAGE_DOWN", 93 }, + { "PICTSYMBOLS", 94 }, + { "SWITCH_CHARSET", 95 }, + { "BUTTON_A", 96 }, + { "BUTTON_B", 97 }, + { "BUTTON_C", 98 }, + { "BUTTON_X", 99 }, + { "BUTTON_Y", 100 }, + { "BUTTON_Z", 101 }, + { "BUTTON_L1", 102 }, + { "BUTTON_R1", 103 }, + { "BUTTON_L2", 104 }, + { "BUTTON_R2", 105 }, + { "BUTTON_THUMBL", 106 }, + { "BUTTON_THUMBR", 107 }, + { "BUTTON_START", 108 }, + { "BUTTON_SELECT", 109 }, + { "BUTTON_MODE", 110 }, + { "ESCAPE", 111 }, + { "FORWARD_DEL", 112 }, + { "CTRL_LEFT", 113 }, + { "CTRL_RIGHT", 114 }, + { "CAPS_LOCK", 115 }, + { "SCROLL_LOCK", 116 }, + { "META_LEFT", 117 }, + { "META_RIGHT", 118 }, + { "FUNCTION", 119 }, + { "SYSRQ", 120 }, + { "BREAK", 121 }, + { "MOVE_HOME", 122 }, + { "MOVE_END", 123 }, + { "INSERT", 124 }, + { "FORWARD", 125 }, + { "MEDIA_PLAY", 126 }, + { "MEDIA_PAUSE", 127 }, + { "MEDIA_CLOSE", 128 }, + { "MEDIA_EJECT", 129 }, + { "MEDIA_RECORD", 130 }, + { "F1", 131 }, + { "F2", 132 }, + { "F3", 133 }, + { "F4", 134 }, + { "F5", 135 }, + { "F6", 136 }, + { "F7", 137 }, + { "F8", 138 }, + { "F9", 139 }, + { "F10", 140 }, + { "F11", 141 }, + { "F12", 142 }, + { "NUM_LOCK", 143 }, + { "NUMPAD_0", 144 }, + { "NUMPAD_1", 145 }, + { "NUMPAD_2", 146 }, + { "NUMPAD_3", 147 }, + { "NUMPAD_4", 148 }, + { "NUMPAD_5", 149 }, + { "NUMPAD_6", 150 }, + { "NUMPAD_7", 151 }, + { "NUMPAD_8", 152 }, + { "NUMPAD_9", 153 }, + { "NUMPAD_DIVIDE", 154 }, + { "NUMPAD_MULTIPLY", 155 }, + { "NUMPAD_SUBTRACT", 156 }, + { "NUMPAD_ADD", 157 }, + { "NUMPAD_DOT", 158 }, + { "NUMPAD_COMMA", 159 }, + { "NUMPAD_ENTER", 160 }, + { "NUMPAD_EQUALS", 161 }, + { "NUMPAD_LEFT_PAREN", 162 }, + { "NUMPAD_RIGHT_PAREN", 163 }, + { "VOLUME_MUTE", 164 }, + { "INFO", 165 }, + { "CHANNEL_UP", 166 }, + { "CHANNEL_DOWN", 167 }, + { "ZOOM_IN", 168 }, + { "ZOOM_OUT", 169 }, + { "TV", 170 }, + { "WINDOW", 171 }, + { "GUIDE", 172 }, + { "DVR", 173 }, + { "BOOKMARK", 174 }, + { "CAPTIONS", 175 }, + { "SETTINGS", 176 }, + { "TV_POWER", 177 }, + { "TV_INPUT", 178 }, + { "STB_POWER", 179 }, + { "STB_INPUT", 180 }, + { "AVR_POWER", 181 }, + { "AVR_INPUT", 182 }, + { "PROG_RED", 183 }, + { "PROG_GREEN", 184 }, + { "PROG_YELLOW", 185 }, + { "PROG_BLUE", 186 }, + { "APP_SWITCH", 187 }, + { "BUTTON_1", 188 }, + { "BUTTON_2", 189 }, + { "BUTTON_3", 190 }, + { "BUTTON_4", 191 }, + { "BUTTON_5", 192 }, + { "BUTTON_6", 193 }, + { "BUTTON_7", 194 }, + { "BUTTON_8", 195 }, + { "BUTTON_9", 196 }, + { "BUTTON_10", 197 }, + { "BUTTON_11", 198 }, + { "BUTTON_12", 199 }, + { "BUTTON_13", 200 }, + { "BUTTON_14", 201 }, + { "BUTTON_15", 202 }, + { "BUTTON_16", 203 }, + { "LANGUAGE_SWITCH", 204 }, + { "MANNER_MODE", 205 }, + { "3D_MODE", 206 }, + { "CONTACTS", 207 }, + { "CALENDAR", 208 }, + { "MUSIC", 209 }, + { "CALCULATOR", 210 }, + { "ZENKAKU_HANKAKU", 211 }, + { "EISU", 212 }, + { "MUHENKAN", 213 }, + { "HENKAN", 214 }, + { "KATAKANA_HIRAGANA", 215 }, + { "YEN", 216 }, + { "RO", 217 }, + { "KANA", 218 }, + { "ASSIST", 219 }, + { "BRIGHTNESS_DOWN", 220 }, + { "BRIGHTNESS_UP", 221 }, + { "MEDIA_AUDIO_TRACK", 222 }, + + // NOTE: If you add a new keycode here you must also add it to several other files. + // Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list. + + { NULL, 0 } +}; + +// NOTE: If you edit these flags, also edit policy flags in Input.h. +static const KeycodeLabel FLAGS[] = { + { "WAKE", 0x00000001 }, + { "WAKE_DROPPED", 0x00000002 }, + { "SHIFT", 0x00000004 }, + { "CAPS_LOCK", 0x00000008 }, + { "ALT", 0x00000010 }, + { "ALT_GR", 0x00000020 }, + { "MENU", 0x00000040 }, + { "LAUNCHER", 0x00000080 }, + { "VIRTUAL", 0x00000100 }, + { "FUNCTION", 0x00000200 }, + { NULL, 0 } +}; + +static const KeycodeLabel AXES[] = { + { "X", 0 }, + { "Y", 1 }, + { "PRESSURE", 2 }, + { "SIZE", 3 }, + { "TOUCH_MAJOR", 4 }, + { "TOUCH_MINOR", 5 }, + { "TOOL_MAJOR", 6 }, + { "TOOL_MINOR", 7 }, + { "ORIENTATION", 8 }, + { "VSCROLL", 9 }, + { "HSCROLL", 10 }, + { "Z", 11 }, + { "RX", 12 }, + { "RY", 13 }, + { "RZ", 14 }, + { "HAT_X", 15 }, + { "HAT_Y", 16 }, + { "LTRIGGER", 17 }, + { "RTRIGGER", 18 }, + { "THROTTLE", 19 }, + { "RUDDER", 20 }, + { "WHEEL", 21 }, + { "GAS", 22 }, + { "BRAKE", 23 }, + { "DISTANCE", 24 }, + { "TILT", 25 }, + { "GENERIC_1", 32 }, + { "GENERIC_2", 33 }, + { "GENERIC_3", 34 }, + { "GENERIC_4", 35 }, + { "GENERIC_5", 36 }, + { "GENERIC_6", 37 }, + { "GENERIC_7", 38 }, + { "GENERIC_8", 39 }, + { "GENERIC_9", 40 }, + { "GENERIC_10", 41 }, + { "GENERIC_11", 42 }, + { "GENERIC_12", 43 }, + { "GENERIC_13", 44 }, + { "GENERIC_14", 45 }, + { "GENERIC_15", 46 }, + { "GENERIC_16", 47 }, + + // NOTE: If you add a new axis here you must also add it to several other files. + // Refer to frameworks/base/core/java/android/view/MotionEvent.java for the full list. + + { NULL, -1 } +}; #endif // _LIBINPUT_KEYCODE_LABELS_H diff --git a/include/input/VelocityControl.h b/include/input/VelocityControl.h new file mode 100644 index 0000000..1acc2ae --- /dev/null +++ b/include/input/VelocityControl.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2012 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 _LIBINPUT_VELOCITY_CONTROL_H +#define _LIBINPUT_VELOCITY_CONTROL_H + +#include <input/Input.h> +#include <input/VelocityTracker.h> +#include <utils/Timers.h> + +namespace android { + +/* + * Specifies parameters that govern pointer or wheel acceleration. + */ +struct VelocityControlParameters { + // A scale factor that is multiplied with the raw velocity deltas + // prior to applying any other velocity control factors. The scale + // factor should be used to adapt the input device resolution + // (eg. counts per inch) to the output device resolution (eg. pixels per inch). + // + // Must be a positive value. + // Default is 1.0 (no scaling). + float scale; + + // The scaled speed at which acceleration begins to be applied. + // This value establishes the upper bound of a low speed regime for + // small precise motions that are performed without any acceleration. + // + // Must be a non-negative value. + // Default is 0.0 (no low threshold). + float lowThreshold; + + // The scaled speed at which maximum acceleration is applied. + // The difference between highThreshold and lowThreshold controls + // the range of speeds over which the acceleration factor is interpolated. + // The wider the range, the smoother the acceleration. + // + // Must be a non-negative value greater than or equal to lowThreshold. + // Default is 0.0 (no high threshold). + float highThreshold; + + // The acceleration factor. + // When the speed is above the low speed threshold, the velocity will scaled + // by an interpolated value between 1.0 and this amount. + // + // Must be a positive greater than or equal to 1.0. + // Default is 1.0 (no acceleration). + float acceleration; + + VelocityControlParameters() : + scale(1.0f), lowThreshold(0.0f), highThreshold(0.0f), acceleration(1.0f) { + } + + VelocityControlParameters(float scale, float lowThreshold, + float highThreshold, float acceleration) : + scale(scale), lowThreshold(lowThreshold), + highThreshold(highThreshold), acceleration(acceleration) { + } +}; + +/* + * Implements mouse pointer and wheel speed control and acceleration. + */ +class VelocityControl { +public: + VelocityControl(); + + /* Sets the various parameters. */ + void setParameters(const VelocityControlParameters& parameters); + + /* Resets the current movement counters to zero. + * This has the effect of nullifying any acceleration. */ + void reset(); + + /* Translates a raw movement delta into an appropriately + * scaled / accelerated delta based on the current velocity. */ + void move(nsecs_t eventTime, float* deltaX, float* deltaY); + +private: + // If no movements are received within this amount of time, + // we assume the movement has stopped and reset the movement counters. + static const nsecs_t STOP_TIME = 500 * 1000000; // 500 ms + + VelocityControlParameters mParameters; + + nsecs_t mLastMovementTime; + VelocityTracker::Position mRawPosition; + VelocityTracker mVelocityTracker; +}; + +} // namespace android + +#endif // _LIBINPUT_VELOCITY_CONTROL_H diff --git a/include/input/VelocityTracker.h b/include/input/VelocityTracker.h new file mode 100644 index 0000000..795f575 --- /dev/null +++ b/include/input/VelocityTracker.h @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2012 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 _LIBINPUT_VELOCITY_TRACKER_H +#define _LIBINPUT_VELOCITY_TRACKER_H + +#include <input/Input.h> +#include <utils/Timers.h> +#include <utils/BitSet.h> + +namespace android { + +class VelocityTrackerStrategy; + +/* + * Calculates the velocity of pointer movements over time. + */ +class VelocityTracker { +public: + struct Position { + float x, y; + }; + + struct Estimator { + static const size_t MAX_DEGREE = 4; + + // Estimator time base. + nsecs_t time; + + // Polynomial coefficients describing motion in X and Y. + float xCoeff[MAX_DEGREE + 1], yCoeff[MAX_DEGREE + 1]; + + // Polynomial degree (number of coefficients), or zero if no information is + // available. + uint32_t degree; + + // Confidence (coefficient of determination), between 0 (no fit) and 1 (perfect fit). + float confidence; + + inline void clear() { + time = 0; + degree = 0; + confidence = 0; + for (size_t i = 0; i <= MAX_DEGREE; i++) { + xCoeff[i] = 0; + yCoeff[i] = 0; + } + } + }; + + // Creates a velocity tracker using the specified strategy. + // If strategy is NULL, uses the default strategy for the platform. + VelocityTracker(const char* strategy = NULL); + + ~VelocityTracker(); + + // Resets the velocity tracker state. + void clear(); + + // Resets the velocity tracker state for specific pointers. + // Call this method when some pointers have changed and may be reusing + // an id that was assigned to a different pointer earlier. + void clearPointers(BitSet32 idBits); + + // Adds movement information for a set of pointers. + // The idBits bitfield specifies the pointer ids of the pointers whose positions + // are included in the movement. + // The positions array contains position information for each pointer in order by + // increasing id. Its size should be equal to the number of one bits in idBits. + void addMovement(nsecs_t eventTime, BitSet32 idBits, const Position* positions); + + // Adds movement information for all pointers in a MotionEvent, including historical samples. + void addMovement(const MotionEvent* event); + + // Gets the velocity of the specified pointer id in position units per second. + // Returns false and sets the velocity components to zero if there is + // insufficient movement information for the pointer. + bool getVelocity(uint32_t id, float* outVx, float* outVy) const; + + // Gets an estimator for the recent movements of the specified pointer id. + // Returns false and clears the estimator if there is no information available + // about the pointer. + bool getEstimator(uint32_t id, Estimator* outEstimator) const; + + // Gets the active pointer id, or -1 if none. + inline int32_t getActivePointerId() const { return mActivePointerId; } + + // Gets a bitset containing all pointer ids from the most recent movement. + inline BitSet32 getCurrentPointerIdBits() const { return mCurrentPointerIdBits; } + +private: + static const char* DEFAULT_STRATEGY; + + nsecs_t mLastEventTime; + BitSet32 mCurrentPointerIdBits; + int32_t mActivePointerId; + VelocityTrackerStrategy* mStrategy; + + bool configureStrategy(const char* strategy); + + static VelocityTrackerStrategy* createStrategy(const char* strategy); +}; + + +/* + * Implements a particular velocity tracker algorithm. + */ +class VelocityTrackerStrategy { +protected: + VelocityTrackerStrategy() { } + +public: + virtual ~VelocityTrackerStrategy() { } + + virtual void clear() = 0; + virtual void clearPointers(BitSet32 idBits) = 0; + virtual void addMovement(nsecs_t eventTime, BitSet32 idBits, + const VelocityTracker::Position* positions) = 0; + virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const = 0; +}; + + +/* + * Velocity tracker algorithm based on least-squares linear regression. + */ +class LeastSquaresVelocityTrackerStrategy : public VelocityTrackerStrategy { +public: + enum Weighting { + // No weights applied. All data points are equally reliable. + WEIGHTING_NONE, + + // Weight by time delta. Data points clustered together are weighted less. + WEIGHTING_DELTA, + + // Weight such that points within a certain horizon are weighed more than those + // outside of that horizon. + WEIGHTING_CENTRAL, + + // Weight such that points older than a certain amount are weighed less. + WEIGHTING_RECENT, + }; + + // Degree must be no greater than Estimator::MAX_DEGREE. + LeastSquaresVelocityTrackerStrategy(uint32_t degree, Weighting weighting = WEIGHTING_NONE); + virtual ~LeastSquaresVelocityTrackerStrategy(); + + virtual void clear(); + virtual void clearPointers(BitSet32 idBits); + virtual void addMovement(nsecs_t eventTime, BitSet32 idBits, + const VelocityTracker::Position* positions); + virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const; + +private: + // Sample horizon. + // We don't use too much history by default since we want to react to quick + // changes in direction. + static const nsecs_t HORIZON = 100 * 1000000; // 100 ms + + // Number of samples to keep. + static const uint32_t HISTORY_SIZE = 20; + + struct Movement { + nsecs_t eventTime; + BitSet32 idBits; + VelocityTracker::Position positions[MAX_POINTERS]; + + inline const VelocityTracker::Position& getPosition(uint32_t id) const { + return positions[idBits.getIndexOfBit(id)]; + } + }; + + float chooseWeight(uint32_t index) const; + + const uint32_t mDegree; + const Weighting mWeighting; + uint32_t mIndex; + Movement mMovements[HISTORY_SIZE]; +}; + + +/* + * Velocity tracker algorithm that uses an IIR filter. + */ +class IntegratingVelocityTrackerStrategy : public VelocityTrackerStrategy { +public: + // Degree must be 1 or 2. + IntegratingVelocityTrackerStrategy(uint32_t degree); + ~IntegratingVelocityTrackerStrategy(); + + virtual void clear(); + virtual void clearPointers(BitSet32 idBits); + virtual void addMovement(nsecs_t eventTime, BitSet32 idBits, + const VelocityTracker::Position* positions); + virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const; + +private: + // Current state estimate for a particular pointer. + struct State { + nsecs_t updateTime; + uint32_t degree; + + float xpos, xvel, xaccel; + float ypos, yvel, yaccel; + }; + + const uint32_t mDegree; + BitSet32 mPointerIdBits; + State mPointerState[MAX_POINTER_ID + 1]; + + void initState(State& state, nsecs_t eventTime, float xpos, float ypos) const; + void updateState(State& state, nsecs_t eventTime, float xpos, float ypos) const; + void populateEstimator(const State& state, VelocityTracker::Estimator* outEstimator) const; +}; + + +/* + * Velocity tracker strategy used prior to ICS. + */ +class LegacyVelocityTrackerStrategy : public VelocityTrackerStrategy { +public: + LegacyVelocityTrackerStrategy(); + virtual ~LegacyVelocityTrackerStrategy(); + + virtual void clear(); + virtual void clearPointers(BitSet32 idBits); + virtual void addMovement(nsecs_t eventTime, BitSet32 idBits, + const VelocityTracker::Position* positions); + virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const; + +private: + // Oldest sample to consider when calculating the velocity. + static const nsecs_t HORIZON = 200 * 1000000; // 100 ms + + // Number of samples to keep. + static const uint32_t HISTORY_SIZE = 20; + + // The minimum duration between samples when estimating velocity. + static const nsecs_t MIN_DURATION = 10 * 1000000; // 10 ms + + struct Movement { + nsecs_t eventTime; + BitSet32 idBits; + VelocityTracker::Position positions[MAX_POINTERS]; + + inline const VelocityTracker::Position& getPosition(uint32_t id) const { + return positions[idBits.getIndexOfBit(id)]; + } + }; + + uint32_t mIndex; + Movement mMovements[HISTORY_SIZE]; +}; + +} // namespace android + +#endif // _LIBINPUT_VELOCITY_TRACKER_H diff --git a/include/input/VirtualKeyMap.h b/include/input/VirtualKeyMap.h new file mode 100644 index 0000000..e245ead --- /dev/null +++ b/include/input/VirtualKeyMap.h @@ -0,0 +1,81 @@ +/* + * 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 _LIBINPUT_VIRTUAL_KEY_MAP_H +#define _LIBINPUT_VIRTUAL_KEY_MAP_H + +#include <stdint.h> + +#include <input/Input.h> +#include <utils/Errors.h> +#include <utils/KeyedVector.h> +#include <utils/Tokenizer.h> +#include <utils/String8.h> +#include <utils/Unicode.h> + +namespace android { + +/* Describes a virtual key. */ +struct VirtualKeyDefinition { + int32_t scanCode; + + // configured position data, specified in display coords + int32_t centerX; + int32_t centerY; + int32_t width; + int32_t height; +}; + + +/** + * Describes a collection of virtual keys on a touch screen in terms of + * virtual scan codes and hit rectangles. + * + * This object is immutable after it has been loaded. + */ +class VirtualKeyMap { +public: + ~VirtualKeyMap(); + + static status_t load(const String8& filename, VirtualKeyMap** outMap); + + inline const Vector<VirtualKeyDefinition>& getVirtualKeys() const { + return mVirtualKeys; + } + +private: + class Parser { + VirtualKeyMap* mMap; + Tokenizer* mTokenizer; + + public: + Parser(VirtualKeyMap* map, Tokenizer* tokenizer); + ~Parser(); + status_t parse(); + + private: + bool consumeFieldDelimiterAndSkipWhitespace(); + bool parseNextIntField(int32_t* outValue); + }; + + Vector<VirtualKeyDefinition> mVirtualKeys; + + VirtualKeyMap(); +}; + +} // namespace android + +#endif // _LIBINPUT_KEY_CHARACTER_MAP_H diff --git a/include/media/drm/DrmAPI.h b/include/media/drm/DrmAPI.h index fbf93bc..95bdf77 100644 --- a/include/media/drm/DrmAPI.h +++ b/include/media/drm/DrmAPI.h @@ -60,6 +60,11 @@ namespace android { // given crypto scheme, which is specified by a UUID. virtual bool isCryptoSchemeSupported(const uint8_t uuid[16]) = 0; + // DrmFactory::isContentTypeSupported can be called to determine + // if the plugin factory is able to construct plugins that support a + // given media container format specified by mimeType + virtual bool isContentTypeSupported(const String8 &mimeType) = 0; + // Construct a DrmPlugin for the crypto scheme specified by UUID. virtual status_t createDrmPlugin( const uint8_t uuid[16], DrmPlugin **plugin) = 0; diff --git a/include/media/hardware/HDCPAPI.h b/include/media/hardware/HDCPAPI.h index 147448e..d4abb3f 100644 --- a/include/media/hardware/HDCPAPI.h +++ b/include/media/hardware/HDCPAPI.h @@ -19,6 +19,7 @@ #define HDCP_API_H_ #include <utils/Errors.h> +#include <system/window.h> namespace android { @@ -56,6 +57,19 @@ struct HDCPModule { HDCP_SESSION_ESTABLISHED, }; + // HDCPModule capability bit masks + enum { + // HDCP_CAPS_ENCRYPT: mandatory, meaning the HDCP module can encrypt + // from an input byte-array buffer to an output byte-array buffer + HDCP_CAPS_ENCRYPT = (1 << 0), + // HDCP_CAPS_ENCRYPT_NATIVE: the HDCP module supports encryption from + // a native buffer to an output byte-array buffer. The format of the + // input native buffer is specific to vendor's encoder implementation. + // It is the same format as that used by the encoder when + // "storeMetaDataInBuffers" extension is enabled on its output port. + HDCP_CAPS_ENCRYPT_NATIVE = (1 << 1), + }; + // Module can call the notification function to signal completion/failure // of asynchronous operations (such as initialization) or out of band // events. @@ -90,6 +104,20 @@ struct HDCPModule { return INVALID_OPERATION; } + // Encrypt data according to the HDCP spec. "size" bytes of data starting + // at location "offset" are available in "buffer" (buffer handle). "size" + // may not be a multiple of 128 bits (16 bytes). An equal number of + // encrypted bytes should be written to the buffer at "outData" (virtual + // address). This operation is to be synchronous, i.e. this call does not + // return until outData contains size bytes of encrypted data. + // streamCTR will be assigned by the caller (to 0 for the first PES stream, + // 1 for the second and so on) + // inputCTR _will_be_maintained_by_the_callee_ for each PES stream. + virtual status_t encryptNative( + buffer_handle_t buffer, size_t offset, size_t size, + uint32_t streamCTR, uint64_t *outInputCTR, void *outData) { + return INVALID_OPERATION; + } // DECRYPTION only: // Decrypt data according to the HDCP spec. // "size" bytes of encrypted data are available at "inData" diff --git a/include/media/hardware/HardwareAPI.h b/include/media/hardware/HardwareAPI.h index cc43bf6..de3aeb1 100644 --- a/include/media/hardware/HardwareAPI.h +++ b/include/media/hardware/HardwareAPI.h @@ -18,7 +18,8 @@ #define HARDWARE_API_H_ -#include <OMXPluginBase.h> +#include <media/hardware/OMXPluginBase.h> +#include <media/hardware/MetadataBufferType.h> #include <system/window.h> #include <utils/RefBase.h> @@ -37,9 +38,13 @@ namespace android { // // When Android native buffer use has been enabled for a given port, the video // color format for the port is to be interpreted as an Android pixel format -// rather than an OMX color format. The node should then expect to receive -// UseAndroidNativeBuffer calls (via OMX_SetParameter) rather than UseBuffer -// calls for that port. +// rather than an OMX color format. Enabling Android native buffers may also +// change how the component receives the native buffers. If store-metadata-mode +// is enabled on the port, the component will receive the buffers as specified +// in the section below. Otherwise, unless the node supports the +// 'OMX.google.android.index.useAndroidNativeBuffer2' extension, it should +// expect to receive UseAndroidNativeBuffer calls (via OMX_SetParameter) rather +// than UseBuffer calls for that port. struct EnableAndroidNativeBuffersParams { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; @@ -61,11 +66,15 @@ struct EnableAndroidNativeBuffersParams { // // Currently, this is specifically used to pass meta data from video source // (camera component, for instance) to video encoder to avoid memcpying of -// input video frame data. To do this, bStoreMetaDta is set to OMX_TRUE. +// input video frame data. To do this, bStoreMetaData is set to OMX_TRUE. // If bStoreMetaData is set to false, real YUV frame data will be stored // in the buffers. In addition, if no OMX_SetParameter() call is made // with the corresponding extension index, real YUV data is stored // in the buffers. +// +// For video decoder output port, the metadata buffer layout is defined below. +// +// Metadata buffers are registered with the component using UseBuffer calls. struct StoreMetaDataInBuffersParams { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; @@ -73,6 +82,38 @@ struct StoreMetaDataInBuffersParams { OMX_BOOL bStoreMetaData; }; +// Meta data buffer layout used to transport output frames to the decoder for +// dynamic buffer handling. +struct VideoDecoderOutputMetaData { + MetadataBufferType eType; + buffer_handle_t pHandle; +}; + +// A pointer to this struct is passed to OMX_SetParameter() when the extension +// index "OMX.google.android.index.prepareForAdaptivePlayback" is given. +// +// This method is used to signal a video decoder, that the user has requested +// seamless resolution change support (if bEnable is set to OMX_TRUE). +// nMaxFrameWidth and nMaxFrameHeight are the dimensions of the largest +// anticipated frames in the video. If bEnable is OMX_FALSE, no resolution +// change is expected, and the nMaxFrameWidth/Height fields are unused. +// +// If the decoder supports dynamic output buffers, it may ignore this +// request. Otherwise, it shall request resources in such a way so that it +// avoids full port-reconfiguration (due to output port-definition change) +// during resolution changes. +// +// DO NOT USE THIS STRUCTURE AS IT WILL BE REMOVED. INSTEAD, IMPLEMENT +// METADATA SUPPORT FOR VIDEO DECODERS. +struct PrepareForAdaptivePlaybackParams { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_BOOL bEnable; + OMX_U32 nMaxFrameWidth; + OMX_U32 nMaxFrameHeight; +}; + // A pointer to this struct is passed to OMX_SetParameter when the extension // index for the 'OMX.google.android.index.useAndroidNativeBuffer' extension is // given. This call will only be performed if a prior call was made with the diff --git a/include/media/openmax/OMX_IVCommon.h b/include/media/openmax/OMX_IVCommon.h index 85bf00d..96a4396 100644 --- a/include/media/openmax/OMX_IVCommon.h +++ b/include/media/openmax/OMX_IVCommon.h @@ -161,6 +161,7 @@ typedef enum OMX_COLOR_FORMATTYPE { OMX_QCOM_COLOR_FormatYVU420SemiPlanar = 0x7FA30C00, OMX_QCOM_COLOR_FormatYUV420PackedSemiPlanar64x32Tile2m8ka = 0x7FA30C03, OMX_SEC_COLOR_FormatNV12Tiled = 0x7FC00002, + OMX_QCOM_COLOR_FormatYUV420PackedSemiPlanar32m = 0x7FA30C04, OMX_COLOR_FormatMax = 0x7FFFFFFF } OMX_COLOR_FORMATTYPE; diff --git a/include/media/openmax/OMX_Video.h b/include/media/openmax/OMX_Video.h index 4f8485d..4441a7a 100644 --- a/include/media/openmax/OMX_Video.h +++ b/include/media/openmax/OMX_Video.h @@ -85,7 +85,8 @@ typedef enum OMX_VIDEO_CODINGTYPE { OMX_VIDEO_CodingRV, /**< all versions of Real Video */ OMX_VIDEO_CodingAVC, /**< H.264/AVC */ OMX_VIDEO_CodingMJPEG, /**< Motion JPEG */ - OMX_VIDEO_CodingVPX, /**< Google VPX, formerly known as On2 VP8 */ + OMX_VIDEO_CodingVP8, /**< Google VP8, formerly known as On2 VP8 */ + OMX_VIDEO_CodingVP9, /**< Google VP9 */ OMX_VIDEO_CodingKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_VIDEO_CodingVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_VIDEO_CodingMax = 0x7FFFFFFF diff --git a/include/media/openmax/OMX_VideoExt.h b/include/media/openmax/OMX_VideoExt.h index 5e79b47..fa24168 100644 --- a/include/media/openmax/OMX_VideoExt.h +++ b/include/media/openmax/OMX_VideoExt.h @@ -58,12 +58,6 @@ typedef struct OMX_NALSTREAMFORMATTYPE{ OMX_NALUFORMATSTYPE eNaluFormat; } OMX_NALSTREAMFORMATTYPE; -/** Enum for standard video codingtype extensions */ -typedef enum OMX_VIDEO_CODINGEXTTYPE { - OMX_VIDEO_ExtCodingUnused = OMX_VIDEO_CodingKhronosExtensions, - OMX_VIDEO_CodingVP8, /**< VP8/WebM */ -} OMX_VIDEO_CODINGEXTTYPE; - /** VP8 profiles */ typedef enum OMX_VIDEO_VP8PROFILETYPE { OMX_VIDEO_VP8ProfileMain = 0x01, diff --git a/include/powermanager/IPowerManager.h b/include/powermanager/IPowerManager.h index 1723f04..2f4c3c4 100644 --- a/include/powermanager/IPowerManager.h +++ b/include/powermanager/IPowerManager.h @@ -30,7 +30,10 @@ class IPowerManager : public IInterface public: DECLARE_META_INTERFACE(PowerManager); - virtual status_t acquireWakeLock(int flags, const sp<IBinder>& lock, const String16& tag) = 0; + virtual status_t acquireWakeLock(int flags, const sp<IBinder>& lock, const String16& tag, + const String16& packageName) = 0; + virtual status_t acquireWakeLockWithUid(int flags, const sp<IBinder>& lock, const String16& tag, + const String16& packageName, int uid) = 0; virtual status_t releaseWakeLock(const sp<IBinder>& lock, int flags) = 0; }; diff --git a/include/private/binder/Static.h b/include/private/binder/Static.h index 5b0f9fc..6a03594 100644 --- a/include/private/binder/Static.h +++ b/include/private/binder/Static.h @@ -27,6 +27,9 @@ namespace android { +// For TextStream.cpp +extern Vector<int32_t> gTextBuffers; + // For ProcessState.cpp extern Mutex gProcessMutex; extern sp<ProcessState> gProcess; diff --git a/include/private/utils/Static.h b/include/private/utils/Static.h deleted file mode 100644 index d95ae0d..0000000 --- a/include/private/utils/Static.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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. - */ - -// All static variables go here, to control initialization and -// destruction order in the library. - -#include <utils/threads.h> -#include <utils/KeyedVector.h> - -namespace android { -// For TextStream.cpp -extern Vector<int32_t> gTextBuffers; - -// For String8.cpp -extern void initialize_string8(); -extern void terminate_string8(); - -// For String16.cpp -extern void initialize_string16(); -extern void terminate_string16(); - -} // namespace android diff --git a/include/ui/DisplayInfo.h b/include/ui/DisplayInfo.h index c3a4d6b..2853e06 100644 --- a/include/ui/DisplayInfo.h +++ b/include/ui/DisplayInfo.h @@ -34,8 +34,6 @@ struct DisplayInfo { uint8_t orientation; bool secure; uint8_t reserved[2]; - // TODO: this needs to go away (currently needed only by webkit) - PixelFormatInfo pixelFormatInfo; }; /* Display orientations as defined in Surface.java and ISurfaceComposer.h. */ diff --git a/include/ui/Fence.h b/include/ui/Fence.h index 60156e7..20466b6 100644 --- a/include/ui/Fence.h +++ b/include/ui/Fence.h @@ -36,7 +36,7 @@ namespace android { // =========================================================================== class Fence - : public LightRefBase<Fence>, public Flattenable + : public LightRefBase<Fence>, public Flattenable<Fence> { public: static const sp<Fence> NO_FENCE; @@ -96,15 +96,13 @@ public: // Flattenable interface size_t getFlattenedSize() const; size_t getFdCount() const; - status_t flatten(void* buffer, size_t size, - int fds[], size_t count) const; - status_t unflatten(void const* buffer, size_t size, - int fds[], size_t count); + status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const; + status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count); private: // Only allow instantiation using ref counting. friend class LightRefBase<Fence>; - virtual ~Fence(); + ~Fence(); // Disallow copying Fence(const Fence& rhs); diff --git a/include/ui/GraphicBuffer.h b/include/ui/GraphicBuffer.h index e5ad1e0..3cf628c 100644 --- a/include/ui/GraphicBuffer.h +++ b/include/ui/GraphicBuffer.h @@ -39,8 +39,9 @@ class GraphicBufferMapper; class GraphicBuffer : public ANativeObjectBase< ANativeWindowBuffer, GraphicBuffer, RefBase >, - public Flattenable + public Flattenable<GraphicBuffer> { + friend class Flattenable<GraphicBuffer>; public: enum { @@ -98,15 +99,18 @@ public: status_t unlock(); ANativeWindowBuffer* getNativeBuffer() const; - - void setIndex(int index); - int getIndex() const; // for debugging static void dumpAllocationsToSystemLog(); + // Flattenable protocol + size_t getFlattenedSize() const; + size_t getFdCount() const; + status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const; + status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count); + private: - virtual ~GraphicBuffer(); + ~GraphicBuffer(); enum { ownNone = 0, @@ -136,18 +140,8 @@ private: void free_handle(); - // Flattenable interface - size_t getFlattenedSize() const; - size_t getFdCount() const; - status_t flatten(void* buffer, size_t size, - int fds[], size_t count) const; - status_t unflatten(void const* buffer, size_t size, - int fds[], size_t count); - - GraphicBufferMapper& mBufferMapper; ssize_t mInitCheck; - int mIndex; // If we're wrapping another buffer then this reference will make sure it // doesn't get freed. diff --git a/include/ui/PixelFormat.h b/include/ui/PixelFormat.h index 9f3e267..627cfb6 100644 --- a/include/ui/PixelFormat.h +++ b/include/ui/PixelFormat.h @@ -61,67 +61,14 @@ enum { PIXEL_FORMAT_RGB_888 = HAL_PIXEL_FORMAT_RGB_888, // 3x8-bit RGB PIXEL_FORMAT_RGB_565 = HAL_PIXEL_FORMAT_RGB_565, // 16-bit RGB PIXEL_FORMAT_BGRA_8888 = HAL_PIXEL_FORMAT_BGRA_8888, // 4x8-bit BGRA - PIXEL_FORMAT_RGBA_5551 = HAL_PIXEL_FORMAT_RGBA_5551, // 16-bit ARGB - PIXEL_FORMAT_RGBA_4444 = HAL_PIXEL_FORMAT_RGBA_4444, // 16-bit ARGB - PIXEL_FORMAT_A_8 = 8, // 8-bit A + PIXEL_FORMAT_RGBA_5551 = 6, // 16-bit ARGB + PIXEL_FORMAT_RGBA_4444 = 7, // 16-bit ARGB }; typedef int32_t PixelFormat; -struct PixelFormatInfo { - enum { - INDEX_ALPHA = 0, - INDEX_RED = 1, - INDEX_GREEN = 2, - INDEX_BLUE = 3 - }; - - enum { // components - ALPHA = 1, - RGB = 2, - RGBA = 3, - L = 4, - LA = 5, - OTHER = 0xFF - }; - - struct szinfo { - uint8_t h; - uint8_t l; - }; - - inline PixelFormatInfo() : version(sizeof(PixelFormatInfo)) { } - size_t getScanlineSize(unsigned int width) const; - size_t getSize(size_t ci) const { - return (ci <= 3) ? (cinfo[ci].h - cinfo[ci].l) : 0; - } - size_t version; - PixelFormat format; - size_t bytesPerPixel; - size_t bitsPerPixel; - union { - szinfo cinfo[4]; - struct { - uint8_t h_alpha; - uint8_t l_alpha; - uint8_t h_red; - uint8_t l_red; - uint8_t h_green; - uint8_t l_green; - uint8_t h_blue; - uint8_t l_blue; - }; - }; - uint8_t components; - uint8_t reserved0[3]; - uint32_t reserved1; -}; - -// Consider caching the results of these functions are they're not -// guaranteed to be fast. -ssize_t bytesPerPixel(PixelFormat format); -ssize_t bitsPerPixel(PixelFormat format); -status_t getPixelFormatInfo(PixelFormat format, PixelFormatInfo* info); +ssize_t bytesPerPixel(PixelFormat format); +ssize_t bitsPerPixel(PixelFormat format); }; // namespace android diff --git a/include/ui/Rect.h b/include/ui/Rect.h index 47d37b6..6cf64eb 100644 --- a/include/ui/Rect.h +++ b/include/ui/Rect.h @@ -35,14 +35,25 @@ public: inline Rect() { } + inline Rect(int32_t w, int32_t h) { - left = top = 0; right = w; bottom = h; + left = top = 0; + right = w; + bottom = h; } + inline Rect(int32_t l, int32_t t, int32_t r, int32_t b) { - left = l; top = t; right = r; bottom = b; + left = l; + top = t; + right = r; + bottom = b; } + inline Rect(const Point& lt, const Point& rb) { - left = lt.x; top = lt.y; right = rb.x; bottom = rb.y; + left = lt.x; + top = lt.y; + right = rb.x; + bottom = rb.y; } void makeInvalid(); @@ -53,43 +64,36 @@ public: // a valid rectangle has a non negative width and height inline bool isValid() const { - return (width()>=0) && (height()>=0); + return (getWidth() >= 0) && (getHeight() >= 0); } // an empty rect has a zero width or height, or is invalid inline bool isEmpty() const { - return (width()<=0) || (height()<=0); - } - - inline void set(const Rect& rhs) { - operator = (rhs); + return (getWidth() <= 0) || (getHeight() <= 0); } // rectangle's width inline int32_t getWidth() const { - return right-left; + return right - left; } - + // rectangle's height inline int32_t getHeight() const { - return bottom-top; + return bottom - top; } inline Rect getBounds() const { - return Rect(right-left, bottom-top); + return Rect(right - left, bottom - top); } - inline int32_t width() const { return getWidth(); } - inline int32_t height() const { return getHeight(); } - void setLeftTop(const Point& lt) { left = lt.x; - top = lt.y; + top = lt.y; } void setRightBottom(const Point& rb) { right = rb.x; - bottom = rb.y; + bottom = rb.y; } // the following 4 functions return the 4 corners of the rect as Point @@ -120,6 +124,16 @@ public: // vectors. bool operator < (const Rect& rhs) const; + const Rect operator + (const Point& rhs) const; + const Rect operator - (const Point& rhs) const; + + Rect& operator += (const Point& rhs) { + return offsetBy(rhs.x, rhs.y); + } + Rect& operator -= (const Point& rhs) { + return offsetBy(-rhs.x, -rhs.y); + } + Rect& offsetToOrigin() { right -= left; bottom -= top; @@ -132,22 +146,11 @@ public: Rect& offsetBy(const Point& dp) { return offsetBy(dp.x, dp.y); } - Rect& operator += (const Point& rhs) { - return offsetBy(rhs.x, rhs.y); - } - Rect& operator -= (const Point& rhs) { - return offsetBy(-rhs.x, -rhs.y); - } - const Rect operator + (const Point& rhs) const; - const Rect operator - (const Point& rhs) const; - void translate(int32_t dx, int32_t dy) { // legacy, don't use. - offsetBy(dx, dy); - } + Rect& offsetTo(int32_t x, int32_t y); + Rect& offsetBy(int32_t x, int32_t y); - Rect& offsetTo(int32_t x, int32_t y); - Rect& offsetBy(int32_t x, int32_t y); - bool intersect(const Rect& with, Rect* result) const; + bool intersect(const Rect& with, Rect* result) const; // Create a new Rect by transforming this one using a graphics HAL // transform. This rectangle is defined in a coordinate space starting at @@ -156,6 +159,15 @@ public: // (height, width). Otherwise the output rectangle is in the same space as // the input. Rect transform(uint32_t xform, int32_t width, int32_t height) const; + + // this calculates (Region(*this) - exclude).bounds() efficiently + Rect reduce(const Rect& exclude) const; + + + // for backward compatibility + inline int32_t width() const { return getWidth(); } + inline int32_t height() const { return getHeight(); } + inline void set(const Rect& rhs) { operator = (rhs); } }; ANDROID_BASIC_TYPES_TRAITS(Rect) diff --git a/include/ui/Region.h b/include/ui/Region.h index ce91f3b..d906dbb 100644 --- a/include/ui/Region.h +++ b/include/ui/Region.h @@ -136,8 +136,8 @@ public: void addRectUnchecked(int l, int t, int r, int b); inline bool isFixedSize() const { return false; } - size_t getSize() const; - status_t flatten(void* buffer) const; + size_t getFlattenedSize() const; + status_t flatten(void* buffer, size_t size) const; status_t unflatten(void const* buffer, size_t size); void dump(String8& out, const char* what, uint32_t flags=0) const; diff --git a/include/ui/TMatHelpers.h b/include/ui/TMatHelpers.h new file mode 100644 index 0000000..a6aadca --- /dev/null +++ b/include/ui/TMatHelpers.h @@ -0,0 +1,257 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TMAT_IMPLEMENTATION +#error "Don't include TMatHelpers.h directly. use ui/mat*.h instead" +#else +#undef TMAT_IMPLEMENTATION +#endif + + +#ifndef UI_TMAT_HELPERS_H +#define UI_TMAT_HELPERS_H + +#include <stdint.h> +#include <sys/types.h> +#include <math.h> +#include <utils/Debug.h> +#include <utils/String8.h> + +#define PURE __attribute__((pure)) + +namespace android { +// ------------------------------------------------------------------------------------- + +/* + * No user serviceable parts here. + * + * Don't use this file directly, instead include ui/mat*.h + */ + + +/* + * Matrix utilities + */ + +namespace matrix { + +inline int PURE transpose(int v) { return v; } +inline float PURE transpose(float v) { return v; } +inline double PURE transpose(double v) { return v; } + +inline int PURE trace(int v) { return v; } +inline float PURE trace(float v) { return v; } +inline double PURE trace(double v) { return v; } + +template<typename MATRIX> +MATRIX PURE inverse(const MATRIX& src) { + + COMPILE_TIME_ASSERT_FUNCTION_SCOPE( MATRIX::COL_SIZE == MATRIX::ROW_SIZE ); + + typename MATRIX::value_type t; + const size_t N = MATRIX::col_size(); + size_t swap; + MATRIX tmp(src); + MATRIX inverse(1); + + for (size_t i=0 ; i<N ; i++) { + // look for largest element in column + swap = i; + for (size_t j=i+1 ; j<N ; j++) { + if (fabs(tmp[j][i]) > fabs(tmp[i][i])) { + swap = j; + } + } + + if (swap != i) { + /* swap rows. */ + for (size_t k=0 ; k<N ; k++) { + t = tmp[i][k]; + tmp[i][k] = tmp[swap][k]; + tmp[swap][k] = t; + + t = inverse[i][k]; + inverse[i][k] = inverse[swap][k]; + inverse[swap][k] = t; + } + } + + t = 1 / tmp[i][i]; + for (size_t k=0 ; k<N ; k++) { + tmp[i][k] *= t; + inverse[i][k] *= t; + } + for (size_t j=0 ; j<N ; j++) { + if (j != i) { + t = tmp[j][i]; + for (size_t k=0 ; k<N ; k++) { + tmp[j][k] -= tmp[i][k] * t; + inverse[j][k] -= inverse[i][k] * t; + } + } + } + } + return inverse; +} + +template<typename MATRIX_R, typename MATRIX_A, typename MATRIX_B> +MATRIX_R PURE multiply(const MATRIX_A& lhs, const MATRIX_B& rhs) { + // pre-requisite: + // lhs : D columns, R rows + // rhs : C columns, D rows + // res : C columns, R rows + + COMPILE_TIME_ASSERT_FUNCTION_SCOPE( MATRIX_A::ROW_SIZE == MATRIX_B::COL_SIZE ); + COMPILE_TIME_ASSERT_FUNCTION_SCOPE( MATRIX_R::ROW_SIZE == MATRIX_B::ROW_SIZE ); + COMPILE_TIME_ASSERT_FUNCTION_SCOPE( MATRIX_R::COL_SIZE == MATRIX_A::COL_SIZE ); + + MATRIX_R res(MATRIX_R::NO_INIT); + for (size_t r=0 ; r<MATRIX_R::row_size() ; r++) { + res[r] = lhs * rhs[r]; + } + return res; +} + +// transpose. this handles matrices of matrices +template <typename MATRIX> +MATRIX PURE transpose(const MATRIX& m) { + // for now we only handle square matrix transpose + COMPILE_TIME_ASSERT_FUNCTION_SCOPE( MATRIX::ROW_SIZE == MATRIX::COL_SIZE ); + MATRIX result(MATRIX::NO_INIT); + for (size_t r=0 ; r<MATRIX::row_size() ; r++) + for (size_t c=0 ; c<MATRIX::col_size() ; c++) + result[c][r] = transpose(m[r][c]); + return result; +} + +// trace. this handles matrices of matrices +template <typename MATRIX> +typename MATRIX::value_type PURE trace(const MATRIX& m) { + COMPILE_TIME_ASSERT_FUNCTION_SCOPE( MATRIX::ROW_SIZE == MATRIX::COL_SIZE ); + typename MATRIX::value_type result(0); + for (size_t r=0 ; r<MATRIX::row_size() ; r++) + result += trace(m[r][r]); + return result; +} + +// trace. this handles matrices of matrices +template <typename MATRIX> +typename MATRIX::col_type PURE diag(const MATRIX& m) { + COMPILE_TIME_ASSERT_FUNCTION_SCOPE( MATRIX::ROW_SIZE == MATRIX::COL_SIZE ); + typename MATRIX::col_type result(MATRIX::col_type::NO_INIT); + for (size_t r=0 ; r<MATRIX::row_size() ; r++) + result[r] = m[r][r]; + return result; +} + +template <typename MATRIX> +String8 asString(const MATRIX& m) { + String8 s; + for (size_t c=0 ; c<MATRIX::col_size() ; c++) { + s.append("| "); + for (size_t r=0 ; r<MATRIX::row_size() ; r++) { + s.appendFormat("%7.2f ", m[r][c]); + } + s.append("|\n"); + } + return s; +} + +}; // namespace matrix + +// ------------------------------------------------------------------------------------- + +/* + * TMatProductOperators implements basic arithmetic and basic compound assignments + * operators on a vector of type BASE<T>. + * + * BASE only needs to implement operator[] and size(). + * By simply inheriting from TMatProductOperators<BASE, T> BASE will automatically + * get all the functionality here. + */ + +template <template<typename T> class BASE, typename T> +class TMatProductOperators { +public: + // multiply by a scalar + BASE<T>& operator *= (T v) { + BASE<T>& lhs(static_cast< BASE<T>& >(*this)); + for (size_t r=0 ; r<lhs.row_size() ; r++) { + lhs[r] *= v; + } + return lhs; + } + + // divide by a scalar + BASE<T>& operator /= (T v) { + BASE<T>& lhs(static_cast< BASE<T>& >(*this)); + for (size_t r=0 ; r<lhs.row_size() ; r++) { + lhs[r] /= v; + } + return lhs; + } + + // matrix * matrix, result is a matrix of the same type than the lhs matrix + template<typename U> + friend BASE<T> PURE operator *(const BASE<T>& lhs, const BASE<U>& rhs) { + return matrix::multiply<BASE<T> >(lhs, rhs); + } +}; + + +/* + * TMatSquareFunctions implements functions on a matrix of type BASE<T>. + * + * BASE only needs to implement: + * - operator[] + * - col_type + * - row_type + * - COL_SIZE + * - ROW_SIZE + * + * By simply inheriting from TMatSquareFunctions<BASE, T> BASE will automatically + * get all the functionality here. + */ + +template<template<typename U> class BASE, typename T> +class TMatSquareFunctions { +public: + /* + * NOTE: the functions below ARE NOT member methods. They are friend functions + * with they definition inlined with their declaration. This makes these + * template functions available to the compiler when (and only when) this class + * is instantiated, at which point they're only templated on the 2nd parameter + * (the first one, BASE<T> being known). + */ + friend BASE<T> PURE inverse(const BASE<T>& m) { return matrix::inverse(m); } + friend BASE<T> PURE transpose(const BASE<T>& m) { return matrix::transpose(m); } + friend T PURE trace(const BASE<T>& m) { return matrix::trace(m); } +}; + +template <template<typename T> class BASE, typename T> +class TMatDebug { +public: + String8 asString() const { + return matrix::asString( static_cast< const BASE<T>& >(*this) ); + } +}; + +// ------------------------------------------------------------------------------------- +}; // namespace android + +#undef PURE + +#endif /* UI_TMAT_HELPERS_H */ diff --git a/include/ui/TVecHelpers.h b/include/ui/TVecHelpers.h new file mode 100644 index 0000000..bb7dbfc --- /dev/null +++ b/include/ui/TVecHelpers.h @@ -0,0 +1,381 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TVEC_IMPLEMENTATION +#error "Don't include TVecHelpers.h directly. use ui/vec*.h instead" +#else +#undef TVEC_IMPLEMENTATION +#endif + + +#ifndef UI_TVEC_HELPERS_H +#define UI_TVEC_HELPERS_H + +#include <stdint.h> +#include <sys/types.h> + +#define PURE __attribute__((pure)) + +namespace android { +// ------------------------------------------------------------------------------------- + +/* + * No user serviceable parts here. + * + * Don't use this file directly, instead include ui/vec{2|3|4}.h + */ + +/* + * This class casts itself into anything and assign itself from anything! + * Use with caution! + */ +template <typename TYPE> +struct Impersonator { + Impersonator& operator = (const TYPE& rhs) { + reinterpret_cast<TYPE&>(*this) = rhs; + return *this; + } + operator TYPE& () { + return reinterpret_cast<TYPE&>(*this); + } + operator TYPE const& () const { + return reinterpret_cast<TYPE const&>(*this); + } +}; + +/* + * TVec{Add|Product}Operators implements basic arithmetic and basic compound assignments + * operators on a vector of type BASE<T>. + * + * BASE only needs to implement operator[] and size(). + * By simply inheriting from TVec{Add|Product}Operators<BASE, T> BASE will automatically + * get all the functionality here. + */ + +template <template<typename T> class BASE, typename T> +class TVecAddOperators { +public: + /* compound assignment from a another vector of the same size but different + * element type. + */ + template <typename OTHER> + BASE<T>& operator += (const BASE<OTHER>& v) { + BASE<T>& rhs = static_cast<BASE<T>&>(*this); + for (size_t i=0 ; i<BASE<T>::size() ; i++) { + rhs[i] += v[i]; + } + return rhs; + } + template <typename OTHER> + BASE<T>& operator -= (const BASE<OTHER>& v) { + BASE<T>& rhs = static_cast<BASE<T>&>(*this); + for (size_t i=0 ; i<BASE<T>::size() ; i++) { + rhs[i] -= v[i]; + } + return rhs; + } + + /* compound assignment from a another vector of the same type. + * These operators can be used for implicit conversion and handle operations + * like "vector *= scalar" by letting the compiler implicitly convert a scalar + * to a vector (assuming the BASE<T> allows it). + */ + BASE<T>& operator += (const BASE<T>& v) { + BASE<T>& rhs = static_cast<BASE<T>&>(*this); + for (size_t i=0 ; i<BASE<T>::size() ; i++) { + rhs[i] += v[i]; + } + return rhs; + } + BASE<T>& operator -= (const BASE<T>& v) { + BASE<T>& rhs = static_cast<BASE<T>&>(*this); + for (size_t i=0 ; i<BASE<T>::size() ; i++) { + rhs[i] -= v[i]; + } + return rhs; + } + + /* + * NOTE: the functions below ARE NOT member methods. They are friend functions + * with they definition inlined with their declaration. This makes these + * template functions available to the compiler when (and only when) this class + * is instantiated, at which point they're only templated on the 2nd parameter + * (the first one, BASE<T> being known). + */ + + /* The operators below handle operation between vectors of the same side + * but of a different element type. + */ + template<typename RT> + friend inline + BASE<T> PURE operator +(const BASE<T>& lv, const BASE<RT>& rv) { + return BASE<T>(lv) += rv; + } + template<typename RT> + friend inline + BASE<T> PURE operator -(const BASE<T>& lv, const BASE<RT>& rv) { + return BASE<T>(lv) -= rv; + } + + /* The operators below (which are not templates once this class is instanced, + * i.e.: BASE<T> is known) can be used for implicit conversion on both sides. + * These handle operations like "vector * scalar" and "scalar * vector" by + * letting the compiler implicitly convert a scalar to a vector (assuming + * the BASE<T> allows it). + */ + friend inline + BASE<T> PURE operator +(const BASE<T>& lv, const BASE<T>& rv) { + return BASE<T>(lv) += rv; + } + friend inline + BASE<T> PURE operator -(const BASE<T>& lv, const BASE<T>& rv) { + return BASE<T>(lv) -= rv; + } +}; + +template <template<typename T> class BASE, typename T> +class TVecProductOperators { +public: + /* compound assignment from a another vector of the same size but different + * element type. + */ + template <typename OTHER> + BASE<T>& operator *= (const BASE<OTHER>& v) { + BASE<T>& rhs = static_cast<BASE<T>&>(*this); + for (size_t i=0 ; i<BASE<T>::size() ; i++) { + rhs[i] *= v[i]; + } + return rhs; + } + template <typename OTHER> + BASE<T>& operator /= (const BASE<OTHER>& v) { + BASE<T>& rhs = static_cast<BASE<T>&>(*this); + for (size_t i=0 ; i<BASE<T>::size() ; i++) { + rhs[i] /= v[i]; + } + return rhs; + } + + /* compound assignment from a another vector of the same type. + * These operators can be used for implicit conversion and handle operations + * like "vector *= scalar" by letting the compiler implicitly convert a scalar + * to a vector (assuming the BASE<T> allows it). + */ + BASE<T>& operator *= (const BASE<T>& v) { + BASE<T>& rhs = static_cast<BASE<T>&>(*this); + for (size_t i=0 ; i<BASE<T>::size() ; i++) { + rhs[i] *= v[i]; + } + return rhs; + } + BASE<T>& operator /= (const BASE<T>& v) { + BASE<T>& rhs = static_cast<BASE<T>&>(*this); + for (size_t i=0 ; i<BASE<T>::size() ; i++) { + rhs[i] /= v[i]; + } + return rhs; + } + + /* + * NOTE: the functions below ARE NOT member methods. They are friend functions + * with they definition inlined with their declaration. This makes these + * template functions available to the compiler when (and only when) this class + * is instantiated, at which point they're only templated on the 2nd parameter + * (the first one, BASE<T> being known). + */ + + /* The operators below handle operation between vectors of the same side + * but of a different element type. + */ + template<typename RT> + friend inline + BASE<T> PURE operator *(const BASE<T>& lv, const BASE<RT>& rv) { + return BASE<T>(lv) *= rv; + } + template<typename RT> + friend inline + BASE<T> PURE operator /(const BASE<T>& lv, const BASE<RT>& rv) { + return BASE<T>(lv) /= rv; + } + + /* The operators below (which are not templates once this class is instanced, + * i.e.: BASE<T> is known) can be used for implicit conversion on both sides. + * These handle operations like "vector * scalar" and "scalar * vector" by + * letting the compiler implicitly convert a scalar to a vector (assuming + * the BASE<T> allows it). + */ + friend inline + BASE<T> PURE operator *(const BASE<T>& lv, const BASE<T>& rv) { + return BASE<T>(lv) *= rv; + } + friend inline + BASE<T> PURE operator /(const BASE<T>& lv, const BASE<T>& rv) { + return BASE<T>(lv) /= rv; + } +}; + +/* + * TVecUnaryOperators implements unary operators on a vector of type BASE<T>. + * + * BASE only needs to implement operator[] and size(). + * By simply inheriting from TVecUnaryOperators<BASE, T> BASE will automatically + * get all the functionality here. + * + * These operators are implemented as friend functions of TVecUnaryOperators<BASE, T> + */ +template <template<typename T> class BASE, typename T> +class TVecUnaryOperators { +public: + BASE<T>& operator ++ () { + BASE<T>& rhs = static_cast<BASE<T>&>(*this); + for (size_t i=0 ; i<BASE<T>::size() ; i++) { + ++rhs[i]; + } + return rhs; + } + BASE<T>& operator -- () { + BASE<T>& rhs = static_cast<BASE<T>&>(*this); + for (size_t i=0 ; i<BASE<T>::size() ; i++) { + --rhs[i]; + } + return rhs; + } + BASE<T> operator - () const { + BASE<T> r(BASE<T>::NO_INIT); + BASE<T> const& rv(static_cast<BASE<T> const&>(*this)); + for (size_t i=0 ; i<BASE<T>::size() ; i++) { + r[i] = -rv[i]; + } + return r; + } +}; + + +/* + * TVecComparisonOperators implements relational/comparison operators + * on a vector of type BASE<T>. + * + * BASE only needs to implement operator[] and size(). + * By simply inheriting from TVecComparisonOperators<BASE, T> BASE will automatically + * get all the functionality here. + */ +template <template<typename T> class BASE, typename T> +class TVecComparisonOperators { +public: + /* + * NOTE: the functions below ARE NOT member methods. They are friend functions + * with they definition inlined with their declaration. This makes these + * template functions available to the compiler when (and only when) this class + * is instantiated, at which point they're only templated on the 2nd parameter + * (the first one, BASE<T> being known). + */ + template<typename RT> + friend inline + bool PURE operator ==(const BASE<T>& lv, const BASE<RT>& rv) { + for (size_t i = 0; i < BASE<T>::size(); i++) + if (lv[i] != rv[i]) + return false; + return true; + } + + template<typename RT> + friend inline + bool PURE operator !=(const BASE<T>& lv, const BASE<RT>& rv) { + return !operator ==(lv, rv); + } + + template<typename RT> + friend inline + bool PURE operator >(const BASE<T>& lv, const BASE<RT>& rv) { + for (size_t i = 0; i < BASE<T>::size(); i++) + if (lv[i] <= rv[i]) + return false; + return true; + } + + template<typename RT> + friend inline + bool PURE operator <=(const BASE<T>& lv, const BASE<RT>& rv) { + return !(lv > rv); + } + + template<typename RT> + friend inline + bool PURE operator <(const BASE<T>& lv, const BASE<RT>& rv) { + for (size_t i = 0; i < BASE<T>::size(); i++) + if (lv[i] >= rv[i]) + return false; + return true; + } + + template<typename RT> + friend inline + bool PURE operator >=(const BASE<T>& lv, const BASE<RT>& rv) { + return !(lv < rv); + } +}; + + +/* + * TVecFunctions implements functions on a vector of type BASE<T>. + * + * BASE only needs to implement operator[] and size(). + * By simply inheriting from TVecFunctions<BASE, T> BASE will automatically + * get all the functionality here. + */ +template <template<typename T> class BASE, typename T> +class TVecFunctions { +public: + /* + * NOTE: the functions below ARE NOT member methods. They are friend functions + * with they definition inlined with their declaration. This makes these + * template functions available to the compiler when (and only when) this class + * is instantiated, at which point they're only templated on the 2nd parameter + * (the first one, BASE<T> being known). + */ + template<typename RT> + friend inline + T PURE dot(const BASE<T>& lv, const BASE<RT>& rv) { + T r(0); + for (size_t i = 0; i < BASE<T>::size(); i++) + r += lv[i]*rv[i]; + return r; + } + + friend inline + T PURE length(const BASE<T>& lv) { + return sqrt( dot(lv, lv) ); + } + + template<typename RT> + friend inline + T PURE distance(const BASE<T>& lv, const BASE<RT>& rv) { + return length(rv - lv); + } + + friend inline + BASE<T> PURE normalize(const BASE<T>& lv) { + return lv * (1 / length(lv)); + } +}; + +#undef PURE + +// ------------------------------------------------------------------------------------- +}; // namespace android + + +#endif /* UI_TVEC_HELPERS_H */ diff --git a/include/ui/mat4.h b/include/ui/mat4.h new file mode 100644 index 0000000..d9647cc --- /dev/null +++ b/include/ui/mat4.h @@ -0,0 +1,394 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef UI_MAT4_H +#define UI_MAT4_H + +#include <stdint.h> +#include <sys/types.h> + +#include <ui/vec4.h> +#include <utils/String8.h> + +#define TMAT_IMPLEMENTATION +#include <ui/TMatHelpers.h> + +#define PURE __attribute__((pure)) + +namespace android { +// ------------------------------------------------------------------------------------- + +template <typename T> +class tmat44 : public TVecUnaryOperators<tmat44, T>, + public TVecComparisonOperators<tmat44, T>, + public TVecAddOperators<tmat44, T>, + public TMatProductOperators<tmat44, T>, + public TMatSquareFunctions<tmat44, T>, + public TMatDebug<tmat44, T> +{ +public: + enum no_init { NO_INIT }; + typedef T value_type; + typedef T& reference; + typedef T const& const_reference; + typedef size_t size_type; + typedef tvec4<T> col_type; + typedef tvec4<T> row_type; + + // size of a column (i.e.: number of rows) + enum { COL_SIZE = col_type::SIZE }; + static inline size_t col_size() { return COL_SIZE; } + + // size of a row (i.e.: number of columns) + enum { ROW_SIZE = row_type::SIZE }; + static inline size_t row_size() { return ROW_SIZE; } + static inline size_t size() { return row_size(); } // for TVec*<> + +private: + + /* + * <-- N columns --> + * + * a00 a10 a20 ... aN0 ^ + * a01 a11 a21 ... aN1 | + * a02 a12 a22 ... aN2 M rows + * ... | + * a0M a1M a2M ... aNM v + * + * COL_SIZE = M + * ROW_SIZE = N + * m[0] = [a00 a01 a02 ... a01M] + */ + + col_type mValue[ROW_SIZE]; + +public: + // array access + inline col_type const& operator [] (size_t i) const { return mValue[i]; } + inline col_type& operator [] (size_t i) { return mValue[i]; } + + T const* asArray() const { return &mValue[0][0]; } + + // ----------------------------------------------------------------------- + // we don't provide copy-ctor and operator= on purpose + // because we want the compiler generated versions + + /* + * constructors + */ + + // leaves object uninitialized. use with caution. + explicit tmat44(no_init) { } + + // initialize to identity + tmat44(); + + // initialize to Identity*scalar. + template<typename U> + explicit tmat44(U v); + + // sets the diagonal to the passed vector + template <typename U> + explicit tmat44(const tvec4<U>& rhs); + + // construct from another matrix of the same size + template <typename U> + explicit tmat44(const tmat44<U>& rhs); + + // construct from 4 column vectors + template <typename A, typename B, typename C, typename D> + tmat44(const tvec4<A>& v0, const tvec4<B>& v1, const tvec4<C>& v2, const tvec4<D>& v3); + + // construct from 16 scalars + template < + typename A, typename B, typename C, typename D, + typename E, typename F, typename G, typename H, + typename I, typename J, typename K, typename L, + typename M, typename N, typename O, typename P> + tmat44( A m00, B m01, C m02, D m03, + E m10, F m11, G m12, H m13, + I m20, J m21, K m22, L m23, + M m30, N m31, O m32, P m33); + + // construct from a C array + template <typename U> + explicit tmat44(U const* rawArray); + + /* + * helpers + */ + + static tmat44 ortho(T left, T right, T bottom, T top, T near, T far); + + static tmat44 frustum(T left, T right, T bottom, T top, T near, T far); + + template <typename A, typename B, typename C> + static tmat44 lookAt(const tvec3<A>& eye, const tvec3<B>& center, const tvec3<C>& up); + + template <typename A> + static tmat44 translate(const tvec4<A>& t); + + template <typename A> + static tmat44 scale(const tvec4<A>& s); + + template <typename A, typename B> + static tmat44 rotate(A radian, const tvec3<B>& about); +}; + +// ---------------------------------------------------------------------------------------- +// Constructors +// ---------------------------------------------------------------------------------------- + +/* + * Since the matrix code could become pretty big quickly, we don't inline most + * operations. + */ + +template <typename T> +tmat44<T>::tmat44() { + mValue[0] = col_type(1,0,0,0); + mValue[1] = col_type(0,1,0,0); + mValue[2] = col_type(0,0,1,0); + mValue[3] = col_type(0,0,0,1); +} + +template <typename T> +template <typename U> +tmat44<T>::tmat44(U v) { + mValue[0] = col_type(v,0,0,0); + mValue[1] = col_type(0,v,0,0); + mValue[2] = col_type(0,0,v,0); + mValue[3] = col_type(0,0,0,v); +} + +template<typename T> +template<typename U> +tmat44<T>::tmat44(const tvec4<U>& v) { + mValue[0] = col_type(v.x,0,0,0); + mValue[1] = col_type(0,v.y,0,0); + mValue[2] = col_type(0,0,v.z,0); + mValue[3] = col_type(0,0,0,v.w); +} + +// construct from 16 scalars +template<typename T> +template < + typename A, typename B, typename C, typename D, + typename E, typename F, typename G, typename H, + typename I, typename J, typename K, typename L, + typename M, typename N, typename O, typename P> +tmat44<T>::tmat44( A m00, B m01, C m02, D m03, + E m10, F m11, G m12, H m13, + I m20, J m21, K m22, L m23, + M m30, N m31, O m32, P m33) { + mValue[0] = col_type(m00, m01, m02, m03); + mValue[1] = col_type(m10, m11, m12, m13); + mValue[2] = col_type(m20, m21, m22, m23); + mValue[3] = col_type(m30, m31, m32, m33); +} + +template <typename T> +template <typename U> +tmat44<T>::tmat44(const tmat44<U>& rhs) { + for (size_t r=0 ; r<row_size() ; r++) + mValue[r] = rhs[r]; +} + +template <typename T> +template <typename A, typename B, typename C, typename D> +tmat44<T>::tmat44(const tvec4<A>& v0, const tvec4<B>& v1, const tvec4<C>& v2, const tvec4<D>& v3) { + mValue[0] = v0; + mValue[1] = v1; + mValue[2] = v2; + mValue[3] = v3; +} + +template <typename T> +template <typename U> +tmat44<T>::tmat44(U const* rawArray) { + for (size_t r=0 ; r<row_size() ; r++) + for (size_t c=0 ; c<col_size() ; c++) + mValue[r][c] = *rawArray++; +} + +// ---------------------------------------------------------------------------------------- +// Helpers +// ---------------------------------------------------------------------------------------- + +template <typename T> +tmat44<T> tmat44<T>::ortho(T left, T right, T bottom, T top, T near, T far) { + tmat44<T> m; + m[0][0] = 2 / (right - left); + m[1][1] = 2 / (top - bottom); + m[2][2] = -2 / (far - near); + m[3][0] = -(right + left) / (right - left); + m[3][1] = -(top + bottom) / (top - bottom); + m[3][2] = -(far + near) / (far - near); + return m; +} + +template <typename T> +tmat44<T> tmat44<T>::frustum(T left, T right, T bottom, T top, T near, T far) { + tmat44<T> m; + T A = (right + left) / (right - left); + T B = (top + bottom) / (top - bottom); + T C = (far + near) / (far - near); + T D = (2 * far * near) / (far - near); + m[0][0] = (2 * near) / (right - left); + m[1][1] = (2 * near) / (top - bottom); + m[2][0] = A; + m[2][1] = B; + m[2][2] = C; + m[2][3] =-1; + m[3][2] = D; + m[3][3] = 0; + return m; +} + +template <typename T> +template <typename A, typename B, typename C> +tmat44<T> tmat44<T>::lookAt(const tvec3<A>& eye, const tvec3<B>& center, const tvec3<C>& up) { + tvec3<T> L(normalize(center - eye)); + tvec3<T> S(normalize( cross(L, up) )); + tvec3<T> U(cross(S, L)); + return tmat44<T>( + tvec4<T>( S, 0), + tvec4<T>( U, 0), + tvec4<T>(-L, 0), + tvec4<T>(-eye, 1)); +} + +template <typename T> +template <typename A> +tmat44<T> tmat44<T>::translate(const tvec4<A>& t) { + tmat44<T> r; + r[3] = t; + return r; +} + +template <typename T> +template <typename A> +tmat44<T> tmat44<T>::scale(const tvec4<A>& s) { + tmat44<T> r; + r[0][0] = s[0]; + r[1][1] = s[1]; + r[2][2] = s[2]; + r[3][3] = s[3]; + return r; +} + +template <typename T> +template <typename A, typename B> +tmat44<T> tmat44<T>::rotate(A radian, const tvec3<B>& about) { + tmat44<T> rotation; + T* r = const_cast<T*>(rotation.asArray()); + T c = cos(radian); + T s = sin(radian); + if (about.x==1 && about.y==0 && about.z==0) { + r[5] = c; r[10]= c; + r[6] = s; r[9] = -s; + } else if (about.x==0 && about.y==1 && about.z==0) { + r[0] = c; r[10]= c; + r[8] = s; r[2] = -s; + } else if (about.x==0 && about.y==0 && about.z==1) { + r[0] = c; r[5] = c; + r[1] = s; r[4] = -s; + } else { + tvec3<B> nabout = normalize(about); + B x = nabout.x; + B y = nabout.y; + B z = nabout.z; + T nc = 1 - c; + T xy = x * y; + T yz = y * z; + T zx = z * x; + T xs = x * s; + T ys = y * s; + T zs = z * s; + r[ 0] = x*x*nc + c; r[ 4] = xy*nc - zs; r[ 8] = zx*nc + ys; + r[ 1] = xy*nc + zs; r[ 5] = y*y*nc + c; r[ 9] = yz*nc - xs; + r[ 2] = zx*nc - ys; r[ 6] = yz*nc + xs; r[10] = z*z*nc + c; + } +} + +// ---------------------------------------------------------------------------------------- +// Arithmetic operators outside of class +// ---------------------------------------------------------------------------------------- + +/* We use non-friend functions here to prevent the compiler from using + * implicit conversions, for instance of a scalar to a vector. The result would + * not be what the caller expects. + * + * Also note that the order of the arguments in the inner loop is important since + * it determines the output type (only relevant when T != U). + */ + +// matrix * vector, result is a vector of the same type than the input vector +template <typename T, typename U> +typename tmat44<U>::col_type PURE operator *(const tmat44<T>& lv, const tvec4<U>& rv) { + typename tmat44<U>::col_type result; + for (size_t r=0 ; r<tmat44<T>::row_size() ; r++) + result += rv[r]*lv[r]; + return result; +} + +// vector * matrix, result is a vector of the same type than the input vector +template <typename T, typename U> +typename tmat44<U>::row_type PURE operator *(const tvec4<U>& rv, const tmat44<T>& lv) { + typename tmat44<U>::row_type result(tmat44<U>::row_type::NO_INIT); + for (size_t r=0 ; r<tmat44<T>::row_size() ; r++) + result[r] = dot(rv, lv[r]); + return result; +} + +// matrix * scalar, result is a matrix of the same type than the input matrix +template <typename T, typename U> +tmat44<T> PURE operator *(const tmat44<T>& lv, U rv) { + tmat44<T> result(tmat44<T>::NO_INIT); + for (size_t r=0 ; r<tmat44<T>::row_size() ; r++) + result[r] = lv[r]*rv; + return result; +} + +// scalar * matrix, result is a matrix of the same type than the input matrix +template <typename T, typename U> +tmat44<T> PURE operator *(U rv, const tmat44<T>& lv) { + tmat44<T> result(tmat44<T>::NO_INIT); + for (size_t r=0 ; r<tmat44<T>::row_size() ; r++) + result[r] = lv[r]*rv; + return result; +} + +// ---------------------------------------------------------------------------------------- + +/* FIXME: this should go into TMatSquareFunctions<> but for some reason + * BASE<T>::col_type is not accessible from there (???) + */ +template<typename T> +typename tmat44<T>::col_type PURE diag(const tmat44<T>& m) { + return matrix::diag(m); +} + +// ---------------------------------------------------------------------------------------- + +typedef tmat44<float> mat4; + +// ---------------------------------------------------------------------------------------- +}; // namespace android + +#undef PURE + +#endif /* UI_MAT4_H */ diff --git a/include/ui/vec2.h b/include/ui/vec2.h new file mode 100644 index 0000000..c31d0e4 --- /dev/null +++ b/include/ui/vec2.h @@ -0,0 +1,91 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef UI_VEC2_H +#define UI_VEC2_H + +#include <stdint.h> +#include <sys/types.h> + +#define TVEC_IMPLEMENTATION +#include <ui/TVecHelpers.h> + +namespace android { +// ------------------------------------------------------------------------------------- + +template <typename T> +class tvec2 : public TVecProductOperators<tvec2, T>, + public TVecAddOperators<tvec2, T>, + public TVecUnaryOperators<tvec2, T>, + public TVecComparisonOperators<tvec2, T>, + public TVecFunctions<tvec2, T> +{ +public: + enum no_init { NO_INIT }; + typedef T value_type; + typedef T& reference; + typedef T const& const_reference; + typedef size_t size_type; + + union { + struct { T x, y; }; + struct { T s, t; }; + struct { T r, g; }; + }; + + enum { SIZE = 2 }; + inline static size_type size() { return SIZE; } + + // array access + inline T const& operator [] (size_t i) const { return (&x)[i]; } + inline T& operator [] (size_t i) { return (&x)[i]; } + + // ----------------------------------------------------------------------- + // we don't provide copy-ctor and operator= on purpose + // because we want the compiler generated versions + + // constructors + + // leaves object uninitialized. use with caution. + explicit tvec2(no_init) { } + + // default constructor + tvec2() : x(0), y(0) { } + + // handles implicit conversion to a tvec4. must not be explicit. + template<typename A> + tvec2(A v) : x(v), y(v) { } + + template<typename A, typename B> + tvec2(A x, B y) : x(x), y(y) { } + + template<typename A> + explicit tvec2(const tvec2<A>& v) : x(v.x), y(v.y) { } + + template<typename A> + tvec2(const Impersonator< tvec2<A> >& v) + : x(((const tvec2<A>&)v).x), + y(((const tvec2<A>&)v).y) { } +}; + +// ---------------------------------------------------------------------------------------- + +typedef tvec2<float> vec2; + +// ---------------------------------------------------------------------------------------- +}; // namespace android + +#endif /* UI_VEC4_H */ diff --git a/include/ui/vec3.h b/include/ui/vec3.h new file mode 100644 index 0000000..dde59a9 --- /dev/null +++ b/include/ui/vec3.h @@ -0,0 +1,113 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef UI_VEC3_H +#define UI_VEC3_H + +#include <stdint.h> +#include <sys/types.h> + +#include <ui/vec2.h> + +namespace android { +// ------------------------------------------------------------------------------------- + +template <typename T> +class tvec3 : public TVecProductOperators<tvec3, T>, + public TVecAddOperators<tvec3, T>, + public TVecUnaryOperators<tvec3, T>, + public TVecComparisonOperators<tvec3, T>, + public TVecFunctions<tvec3, T> +{ +public: + enum no_init { NO_INIT }; + typedef T value_type; + typedef T& reference; + typedef T const& const_reference; + typedef size_t size_type; + + union { + struct { T x, y, z; }; + struct { T s, t, p; }; + struct { T r, g, b; }; + Impersonator< tvec2<T> > xy; + Impersonator< tvec2<T> > st; + Impersonator< tvec2<T> > rg; + }; + + enum { SIZE = 3 }; + inline static size_type size() { return SIZE; } + + // array access + inline T const& operator [] (size_t i) const { return (&x)[i]; } + inline T& operator [] (size_t i) { return (&x)[i]; } + + // ----------------------------------------------------------------------- + // we don't provide copy-ctor and operator= on purpose + // because we want the compiler generated versions + + // constructors + // leaves object uninitialized. use with caution. + explicit tvec3(no_init) { } + + // default constructor + tvec3() : x(0), y(0), z(0) { } + + // handles implicit conversion to a tvec4. must not be explicit. + template<typename A> + tvec3(A v) : x(v), y(v), z(v) { } + + template<typename A, typename B, typename C> + tvec3(A x, B y, C z) : x(x), y(y), z(z) { } + + template<typename A, typename B> + tvec3(const tvec2<A>& v, B z) : x(v.x), y(v.y), z(z) { } + + template<typename A> + explicit tvec3(const tvec3<A>& v) : x(v.x), y(v.y), z(v.z) { } + + template<typename A> + tvec3(const Impersonator< tvec3<A> >& v) + : x(((const tvec3<A>&)v).x), + y(((const tvec3<A>&)v).y), + z(((const tvec3<A>&)v).z) { } + + template<typename A, typename B> + tvec3(const Impersonator< tvec2<A> >& v, B z) + : x(((const tvec2<A>&)v).x), + y(((const tvec2<A>&)v).y), + z(z) { } + + // cross product works only on vectors of size 3 + template <typename RT> + friend inline + tvec3 __attribute__((pure)) cross(const tvec3& u, const tvec3<RT>& v) { + return tvec3( + u.y*v.z - u.z*v.y, + u.z*v.x - u.x*v.z, + u.x*v.y - u.y*v.x); + } +}; + + +// ---------------------------------------------------------------------------------------- + +typedef tvec3<float> vec3; + +// ---------------------------------------------------------------------------------------- +}; // namespace android + +#endif /* UI_VEC4_H */ diff --git a/include/ui/vec4.h b/include/ui/vec4.h new file mode 100644 index 0000000..e03d331 --- /dev/null +++ b/include/ui/vec4.h @@ -0,0 +1,118 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef UI_VEC4_H +#define UI_VEC4_H + +#include <stdint.h> +#include <sys/types.h> + +#include <ui/vec3.h> + +namespace android { +// ------------------------------------------------------------------------------------- + +template <typename T> +class tvec4 : public TVecProductOperators<tvec4, T>, + public TVecAddOperators<tvec4, T>, + public TVecUnaryOperators<tvec4, T>, + public TVecComparisonOperators<tvec4, T>, + public TVecFunctions<tvec4, T> +{ +public: + enum no_init { NO_INIT }; + typedef T value_type; + typedef T& reference; + typedef T const& const_reference; + typedef size_t size_type; + + union { + struct { T x, y, z, w; }; + struct { T s, t, p, q; }; + struct { T r, g, b, a; }; + Impersonator< tvec2<T> > xy; + Impersonator< tvec2<T> > st; + Impersonator< tvec2<T> > rg; + Impersonator< tvec3<T> > xyz; + Impersonator< tvec3<T> > stp; + Impersonator< tvec3<T> > rgb; + }; + + enum { SIZE = 4 }; + inline static size_type size() { return SIZE; } + + // array access + inline T const& operator [] (size_t i) const { return (&x)[i]; } + inline T& operator [] (size_t i) { return (&x)[i]; } + + // ----------------------------------------------------------------------- + // we don't provide copy-ctor and operator= on purpose + // because we want the compiler generated versions + + // constructors + + // leaves object uninitialized. use with caution. + explicit tvec4(no_init) { } + + // default constructor + tvec4() : x(0), y(0), z(0), w(0) { } + + // handles implicit conversion to a tvec4. must not be explicit. + template<typename A> + tvec4(A v) : x(v), y(v), z(v), w(v) { } + + template<typename A, typename B, typename C, typename D> + tvec4(A x, B y, C z, D w) : x(x), y(y), z(z), w(w) { } + + template<typename A, typename B, typename C> + tvec4(const tvec2<A>& v, B z, C w) : x(v.x), y(v.y), z(z), w(w) { } + + template<typename A, typename B> + tvec4(const tvec3<A>& v, B w) : x(v.x), y(v.y), z(v.z), w(w) { } + + template<typename A> + explicit tvec4(const tvec4<A>& v) : x(v.x), y(v.y), z(v.z), w(v.w) { } + + template<typename A> + tvec4(const Impersonator< tvec4<A> >& v) + : x(((const tvec4<A>&)v).x), + y(((const tvec4<A>&)v).y), + z(((const tvec4<A>&)v).z), + w(((const tvec4<A>&)v).w) { } + + template<typename A, typename B> + tvec4(const Impersonator< tvec3<A> >& v, B w) + : x(((const tvec3<A>&)v).x), + y(((const tvec3<A>&)v).y), + z(((const tvec3<A>&)v).z), + w(w) { } + + template<typename A, typename B, typename C> + tvec4(const Impersonator< tvec2<A> >& v, B z, C w) + : x(((const tvec2<A>&)v).x), + y(((const tvec2<A>&)v).y), + z(z), + w(w) { } +}; + +// ---------------------------------------------------------------------------------------- + +typedef tvec4<float> vec4; + +// ---------------------------------------------------------------------------------------- +}; // namespace android + +#endif /* UI_VEC4_H */ diff --git a/include/utils/AndroidThreads.h b/include/utils/AndroidThreads.h deleted file mode 100644 index 4eee14d..0000000 --- a/include/utils/AndroidThreads.h +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (C) 2007 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 _LIBS_UTILS_ANDROID_THREADS_H -#define _LIBS_UTILS_ANDROID_THREADS_H - -#include <stdint.h> -#include <sys/types.h> - -#if defined(HAVE_PTHREADS) -# include <pthread.h> -#endif - -#include <utils/ThreadDefs.h> - -// --------------------------------------------------------------------------- -// C API - -#ifdef __cplusplus -extern "C" { -#endif - -// Create and run a new thread. -extern int androidCreateThread(android_thread_func_t, void *); - -// Create thread with lots of parameters -extern int androidCreateThreadEtc(android_thread_func_t entryFunction, - void *userData, - const char* threadName, - int32_t threadPriority, - size_t threadStackSize, - android_thread_id_t *threadId); - -// Get some sort of unique identifier for the current thread. -extern android_thread_id_t androidGetThreadId(); - -// Low-level thread creation -- never creates threads that can -// interact with the Java VM. -extern int androidCreateRawThreadEtc(android_thread_func_t entryFunction, - void *userData, - const char* threadName, - int32_t threadPriority, - size_t threadStackSize, - android_thread_id_t *threadId); - -// set the same of the running thread -extern void androidSetThreadName(const char* name); - -// Used by the Java Runtime to control how threads are created, so that -// they can be proper and lovely Java threads. -typedef int (*android_create_thread_fn)(android_thread_func_t entryFunction, - void *userData, - const char* threadName, - int32_t threadPriority, - size_t threadStackSize, - android_thread_id_t *threadId); - -extern void androidSetCreateThreadFunc(android_create_thread_fn func); - -// ------------------------------------------------------------------ -// Extra functions working with raw pids. - -// Get pid for the current thread. -extern pid_t androidGetTid(); - -#ifdef HAVE_ANDROID_OS -// Change the priority AND scheduling group of a particular thread. The priority -// should be one of the ANDROID_PRIORITY constants. Returns INVALID_OPERATION -// if the priority set failed, else another value if just the group set failed; -// in either case errno is set. Thread ID zero means current thread. -extern int androidSetThreadPriority(pid_t tid, int prio); - -// Get the current priority of a particular thread. Returns one of the -// ANDROID_PRIORITY constants or a negative result in case of error. -extern int androidGetThreadPriority(pid_t tid); -#endif - -#ifdef __cplusplus -} // extern "C" -#endif - -// ---------------------------------------------------------------------------- -// C++ API -#ifdef __cplusplus -namespace android { -// ---------------------------------------------------------------------------- - -// Create and run a new thread. -inline bool createThread(thread_func_t f, void *a) { - return androidCreateThread(f, a) ? true : false; -} - -// Create thread with lots of parameters -inline bool createThreadEtc(thread_func_t entryFunction, - void *userData, - const char* threadName = "android:unnamed_thread", - int32_t threadPriority = PRIORITY_DEFAULT, - size_t threadStackSize = 0, - thread_id_t *threadId = 0) -{ - return androidCreateThreadEtc(entryFunction, userData, threadName, - threadPriority, threadStackSize, threadId) ? true : false; -} - -// Get some sort of unique identifier for the current thread. -inline thread_id_t getThreadId() { - return androidGetThreadId(); -} - -// ---------------------------------------------------------------------------- -}; // namespace android -#endif // __cplusplus -// ---------------------------------------------------------------------------- - -#endif // _LIBS_UTILS_ANDROID_THREADS_H diff --git a/include/utils/Atomic.h b/include/utils/Atomic.h deleted file mode 100644 index 7eb476c..0000000 --- a/include/utils/Atomic.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (C) 2005 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_UTILS_ATOMIC_H -#define ANDROID_UTILS_ATOMIC_H - -#include <cutils/atomic.h> - -#endif // ANDROID_UTILS_ATOMIC_H diff --git a/include/utils/BasicHashtable.h b/include/utils/BasicHashtable.h deleted file mode 100644 index 7a6c96c..0000000 --- a/include/utils/BasicHashtable.h +++ /dev/null @@ -1,401 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_BASIC_HASHTABLE_H -#define ANDROID_BASIC_HASHTABLE_H - -#include <stdint.h> -#include <sys/types.h> -#include <utils/SharedBuffer.h> -#include <utils/TypeHelpers.h> - -namespace android { - -/* Implementation type. Nothing to see here. */ -class BasicHashtableImpl { -protected: - struct Bucket { - // The collision flag indicates that the bucket is part of a collision chain - // such that at least two entries both hash to this bucket. When true, we - // may need to seek further along the chain to find the entry. - static const uint32_t COLLISION = 0x80000000UL; - - // The present flag indicates that the bucket contains an initialized entry value. - static const uint32_t PRESENT = 0x40000000UL; - - // Mask for 30 bits worth of the hash code that are stored within the bucket to - // speed up lookups and rehashing by eliminating the need to recalculate the - // hash code of the entry's key. - static const uint32_t HASH_MASK = 0x3fffffffUL; - - // Combined value that stores the collision and present flags as well as - // a 30 bit hash code. - uint32_t cookie; - - // Storage for the entry begins here. - char entry[0]; - }; - - BasicHashtableImpl(size_t entrySize, bool hasTrivialDestructor, - size_t minimumInitialCapacity, float loadFactor); - BasicHashtableImpl(const BasicHashtableImpl& other); - - void dispose(); - - inline void edit() { - if (mBuckets && !SharedBuffer::bufferFromData(mBuckets)->onlyOwner()) { - clone(); - } - } - - void setTo(const BasicHashtableImpl& other); - void clear(); - - ssize_t next(ssize_t index) const; - ssize_t find(ssize_t index, hash_t hash, const void* __restrict__ key) const; - size_t add(hash_t hash, const void* __restrict__ entry); - void removeAt(size_t index); - void rehash(size_t minimumCapacity, float loadFactor); - - const size_t mBucketSize; // number of bytes per bucket including the entry - const bool mHasTrivialDestructor; // true if the entry type does not require destruction - size_t mCapacity; // number of buckets that can be filled before exceeding load factor - float mLoadFactor; // load factor - size_t mSize; // number of elements actually in the table - size_t mFilledBuckets; // number of buckets for which collision or present is true - size_t mBucketCount; // number of slots in the mBuckets array - void* mBuckets; // array of buckets, as a SharedBuffer - - inline const Bucket& bucketAt(const void* __restrict__ buckets, size_t index) const { - return *reinterpret_cast<const Bucket*>( - static_cast<const uint8_t*>(buckets) + index * mBucketSize); - } - - inline Bucket& bucketAt(void* __restrict__ buckets, size_t index) const { - return *reinterpret_cast<Bucket*>(static_cast<uint8_t*>(buckets) + index * mBucketSize); - } - - virtual bool compareBucketKey(const Bucket& bucket, const void* __restrict__ key) const = 0; - virtual void initializeBucketEntry(Bucket& bucket, const void* __restrict__ entry) const = 0; - virtual void destroyBucketEntry(Bucket& bucket) const = 0; - -private: - void clone(); - - // Allocates a bucket array as a SharedBuffer. - void* allocateBuckets(size_t count) const; - - // Releases a bucket array's associated SharedBuffer. - void releaseBuckets(void* __restrict__ buckets, size_t count) const; - - // Destroys the contents of buckets (invokes destroyBucketEntry for each - // populated bucket if needed). - void destroyBuckets(void* __restrict__ buckets, size_t count) const; - - // Copies the content of buckets (copies the cookie and invokes copyBucketEntry - // for each populated bucket if needed). - void copyBuckets(const void* __restrict__ fromBuckets, - void* __restrict__ toBuckets, size_t count) const; - - // Determines the appropriate size of a bucket array to store a certain minimum - // number of entries and returns its effective capacity. - static void determineCapacity(size_t minimumCapacity, float loadFactor, - size_t* __restrict__ outBucketCount, size_t* __restrict__ outCapacity); - - // Trim a hash code to 30 bits to match what we store in the bucket's cookie. - inline static hash_t trimHash(hash_t hash) { - return (hash & Bucket::HASH_MASK) ^ (hash >> 30); - } - - // Returns the index of the first bucket that is in the collision chain - // for the specified hash code, given the total number of buckets. - // (Primary hash) - inline static size_t chainStart(hash_t hash, size_t count) { - return hash % count; - } - - // Returns the increment to add to a bucket index to seek to the next bucket - // in the collision chain for the specified hash code, given the total number of buckets. - // (Secondary hash) - inline static size_t chainIncrement(hash_t hash, size_t count) { - return ((hash >> 7) | (hash << 25)) % (count - 1) + 1; - } - - // Returns the index of the next bucket that is in the collision chain - // that is defined by the specified increment, given the total number of buckets. - inline static size_t chainSeek(size_t index, size_t increment, size_t count) { - return (index + increment) % count; - } -}; - -/* - * A BasicHashtable stores entries that are indexed by hash code in place - * within an array. The basic operations are finding entries by key, - * adding new entries and removing existing entries. - * - * This class provides a very limited set of operations with simple semantics. - * It is intended to be used as a building block to construct more complex - * and interesting data structures such as HashMap. Think very hard before - * adding anything extra to BasicHashtable, it probably belongs at a - * higher level of abstraction. - * - * TKey: The key type. - * TEntry: The entry type which is what is actually stored in the array. - * - * TKey must support the following contract: - * bool operator==(const TKey& other) const; // return true if equal - * bool operator!=(const TKey& other) const; // return true if unequal - * - * TEntry must support the following contract: - * const TKey& getKey() const; // get the key from the entry - * - * This class supports storing entries with duplicate keys. Of course, it can't - * tell them apart during removal so only the first entry will be removed. - * We do this because it means that operations like add() can't fail. - */ -template <typename TKey, typename TEntry> -class BasicHashtable : private BasicHashtableImpl { -public: - /* Creates a hashtable with the specified minimum initial capacity. - * The underlying array will be created when the first entry is added. - * - * minimumInitialCapacity: The minimum initial capacity for the hashtable. - * Default is 0. - * loadFactor: The desired load factor for the hashtable, between 0 and 1. - * Default is 0.75. - */ - BasicHashtable(size_t minimumInitialCapacity = 0, float loadFactor = 0.75f); - - /* Copies a hashtable. - * The underlying storage is shared copy-on-write. - */ - BasicHashtable(const BasicHashtable& other); - - /* Clears and destroys the hashtable. - */ - virtual ~BasicHashtable(); - - /* Making this hashtable a copy of the other hashtable. - * The underlying storage is shared copy-on-write. - * - * other: The hashtable to copy. - */ - inline BasicHashtable<TKey, TEntry>& operator =(const BasicHashtable<TKey, TEntry> & other) { - setTo(other); - return *this; - } - - /* Returns the number of entries in the hashtable. - */ - inline size_t size() const { - return mSize; - } - - /* Returns the capacity of the hashtable, which is the number of elements that can - * added to the hashtable without requiring it to be grown. - */ - inline size_t capacity() const { - return mCapacity; - } - - /* Returns the number of buckets that the hashtable has, which is the size of its - * underlying array. - */ - inline size_t bucketCount() const { - return mBucketCount; - } - - /* Returns the load factor of the hashtable. */ - inline float loadFactor() const { - return mLoadFactor; - }; - - /* Returns a const reference to the entry at the specified index. - * - * index: The index of the entry to retrieve. Must be a valid index within - * the bounds of the hashtable. - */ - inline const TEntry& entryAt(size_t index) const { - return entryFor(bucketAt(mBuckets, index)); - } - - /* Returns a non-const reference to the entry at the specified index. - * - * index: The index of the entry to edit. Must be a valid index within - * the bounds of the hashtable. - */ - inline TEntry& editEntryAt(size_t index) { - edit(); - return entryFor(bucketAt(mBuckets, index)); - } - - /* Clears the hashtable. - * All entries in the hashtable are destroyed immediately. - * If you need to do something special with the entries in the hashtable then iterate - * over them and do what you need before clearing the hashtable. - */ - inline void clear() { - BasicHashtableImpl::clear(); - } - - /* Returns the index of the next entry in the hashtable given the index of a previous entry. - * If the given index is -1, then returns the index of the first entry in the hashtable, - * if there is one, or -1 otherwise. - * If the given index is not -1, then returns the index of the next entry in the hashtable, - * in strictly increasing order, or -1 if there are none left. - * - * index: The index of the previous entry that was iterated, or -1 to begin - * iteration at the beginning of the hashtable. - */ - inline ssize_t next(ssize_t index) const { - return BasicHashtableImpl::next(index); - } - - /* Finds the index of an entry with the specified key. - * If the given index is -1, then returns the index of the first matching entry, - * otherwise returns the index of the next matching entry. - * If the hashtable contains multiple entries with keys that match the requested - * key, then the sequence of entries returned is arbitrary. - * Returns -1 if no entry was found. - * - * index: The index of the previous entry with the specified key, or -1 to - * find the first matching entry. - * hash: The hashcode of the key. - * key: The key. - */ - inline ssize_t find(ssize_t index, hash_t hash, const TKey& key) const { - return BasicHashtableImpl::find(index, hash, &key); - } - - /* Adds the entry to the hashtable. - * Returns the index of the newly added entry. - * If an entry with the same key already exists, then a duplicate entry is added. - * If the entry will not fit, then the hashtable's capacity is increased and - * its contents are rehashed. See rehash(). - * - * hash: The hashcode of the key. - * entry: The entry to add. - */ - inline size_t add(hash_t hash, const TEntry& entry) { - return BasicHashtableImpl::add(hash, &entry); - } - - /* Removes the entry with the specified index from the hashtable. - * The entry is destroyed immediately. - * The index must be valid. - * - * The hashtable is not compacted after an item is removed, so it is legal - * to continue iterating over the hashtable using next() or find(). - * - * index: The index of the entry to remove. Must be a valid index within the - * bounds of the hashtable, and it must refer to an existing entry. - */ - inline void removeAt(size_t index) { - BasicHashtableImpl::removeAt(index); - } - - /* Rehashes the contents of the hashtable. - * Grows the hashtable to at least the specified minimum capacity or the - * current number of elements, whichever is larger. - * - * Rehashing causes all entries to be copied and the entry indices may change. - * Although the hash codes are cached by the hashtable, rehashing can be an - * expensive operation and should be avoided unless the hashtable's size - * needs to be changed. - * - * Rehashing is the only way to change the capacity or load factor of the - * hashtable once it has been created. It can be used to compact the - * hashtable by choosing a minimum capacity that is smaller than the current - * capacity (such as 0). - * - * minimumCapacity: The desired minimum capacity after rehashing. - * loadFactor: The desired load factor after rehashing. - */ - inline void rehash(size_t minimumCapacity, float loadFactor) { - BasicHashtableImpl::rehash(minimumCapacity, loadFactor); - } - - /* Determines whether there is room to add another entry without rehashing. - * When this returns true, a subsequent add() operation is guaranteed to - * complete without performing a rehash. - */ - inline bool hasMoreRoom() const { - return mCapacity > mFilledBuckets; - } - -protected: - static inline const TEntry& entryFor(const Bucket& bucket) { - return reinterpret_cast<const TEntry&>(bucket.entry); - } - - static inline TEntry& entryFor(Bucket& bucket) { - return reinterpret_cast<TEntry&>(bucket.entry); - } - - virtual bool compareBucketKey(const Bucket& bucket, const void* __restrict__ key) const; - virtual void initializeBucketEntry(Bucket& bucket, const void* __restrict__ entry) const; - virtual void destroyBucketEntry(Bucket& bucket) const; - -private: - // For dumping the raw contents of a hashtable during testing. - friend class BasicHashtableTest; - inline uint32_t cookieAt(size_t index) const { - return bucketAt(mBuckets, index).cookie; - } -}; - -template <typename TKey, typename TEntry> -BasicHashtable<TKey, TEntry>::BasicHashtable(size_t minimumInitialCapacity, float loadFactor) : - BasicHashtableImpl(sizeof(TEntry), traits<TEntry>::has_trivial_dtor, - minimumInitialCapacity, loadFactor) { -} - -template <typename TKey, typename TEntry> -BasicHashtable<TKey, TEntry>::BasicHashtable(const BasicHashtable<TKey, TEntry>& other) : - BasicHashtableImpl(other) { -} - -template <typename TKey, typename TEntry> -BasicHashtable<TKey, TEntry>::~BasicHashtable() { - dispose(); -} - -template <typename TKey, typename TEntry> -bool BasicHashtable<TKey, TEntry>::compareBucketKey(const Bucket& bucket, - const void* __restrict__ key) const { - return entryFor(bucket).getKey() == *static_cast<const TKey*>(key); -} - -template <typename TKey, typename TEntry> -void BasicHashtable<TKey, TEntry>::initializeBucketEntry(Bucket& bucket, - const void* __restrict__ entry) const { - if (!traits<TEntry>::has_trivial_copy) { - new (&entryFor(bucket)) TEntry(*(static_cast<const TEntry*>(entry))); - } else { - memcpy(&entryFor(bucket), entry, sizeof(TEntry)); - } -} - -template <typename TKey, typename TEntry> -void BasicHashtable<TKey, TEntry>::destroyBucketEntry(Bucket& bucket) const { - if (!traits<TEntry>::has_trivial_dtor) { - entryFor(bucket).~TEntry(); - } -} - -}; // namespace android - -#endif // ANDROID_BASIC_HASHTABLE_H diff --git a/include/utils/BitSet.h b/include/utils/BitSet.h deleted file mode 100644 index e189d0c..0000000 --- a/include/utils/BitSet.h +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef UTILS_BITSET_H -#define UTILS_BITSET_H - -#include <stdint.h> -#include <utils/TypeHelpers.h> - -/* - * Contains some bit manipulation helpers. - */ - -namespace android { - -// A simple set of 32 bits that can be individually marked or cleared. -struct BitSet32 { - uint32_t value; - - inline BitSet32() : value(0) { } - explicit inline BitSet32(uint32_t value) : value(value) { } - - // Gets the value associated with a particular bit index. - static inline uint32_t valueForBit(uint32_t n) { return 0x80000000 >> n; } - - // Clears the bit set. - inline void clear() { value = 0; } - - // Returns the number of marked bits in the set. - inline uint32_t count() const { return __builtin_popcount(value); } - - // Returns true if the bit set does not contain any marked bits. - inline bool isEmpty() const { return ! value; } - - // Returns true if the bit set does not contain any unmarked bits. - inline bool isFull() const { return value == 0xffffffff; } - - // Returns true if the specified bit is marked. - inline bool hasBit(uint32_t n) const { return value & valueForBit(n); } - - // Marks the specified bit. - inline void markBit(uint32_t n) { value |= valueForBit(n); } - - // Clears the specified bit. - inline void clearBit(uint32_t n) { value &= ~ valueForBit(n); } - - // Finds the first marked bit in the set. - // Result is undefined if all bits are unmarked. - inline uint32_t firstMarkedBit() const { return __builtin_clz(value); } - - // Finds the first unmarked bit in the set. - // Result is undefined if all bits are marked. - inline uint32_t firstUnmarkedBit() const { return __builtin_clz(~ value); } - - // Finds the last marked bit in the set. - // Result is undefined if all bits are unmarked. - inline uint32_t lastMarkedBit() const { return 31 - __builtin_ctz(value); } - - // Finds the first marked bit in the set and clears it. Returns the bit index. - // Result is undefined if all bits are unmarked. - inline uint32_t clearFirstMarkedBit() { - uint32_t n = firstMarkedBit(); - clearBit(n); - return n; - } - - // Finds the first unmarked bit in the set and marks it. Returns the bit index. - // Result is undefined if all bits are marked. - inline uint32_t markFirstUnmarkedBit() { - uint32_t n = firstUnmarkedBit(); - markBit(n); - return n; - } - - // Finds the last marked bit in the set and clears it. Returns the bit index. - // Result is undefined if all bits are unmarked. - inline uint32_t clearLastMarkedBit() { - uint32_t n = lastMarkedBit(); - clearBit(n); - return n; - } - - // Gets the index of the specified bit in the set, which is the number of - // marked bits that appear before the specified bit. - inline uint32_t getIndexOfBit(uint32_t n) const { - return __builtin_popcount(value & ~(0xffffffffUL >> n)); - } - - inline bool operator== (const BitSet32& other) const { return value == other.value; } - inline bool operator!= (const BitSet32& other) const { return value != other.value; } -}; - -ANDROID_BASIC_TYPES_TRAITS(BitSet32) - -} // namespace android - -#endif // UTILS_BITSET_H diff --git a/include/utils/BlobCache.h b/include/utils/BlobCache.h deleted file mode 100644 index 4f342a2..0000000 --- a/include/utils/BlobCache.h +++ /dev/null @@ -1,251 +0,0 @@ -/* - ** 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 ANDROID_BLOB_CACHE_H -#define ANDROID_BLOB_CACHE_H - -#include <stddef.h> - -#include <utils/Flattenable.h> -#include <utils/RefBase.h> -#include <utils/SortedVector.h> -#include <utils/threads.h> - -namespace android { - -// A BlobCache is an in-memory cache for binary key/value pairs. A BlobCache -// does NOT provide any thread-safety guarantees. -// -// The cache contents can be serialized to an in-memory buffer or mmap'd file -// and then reloaded in a subsequent execution of the program. This -// serialization is non-portable and the data should only be used by the device -// that generated it. -class BlobCache : public RefBase, public Flattenable { -public: - - // Create an empty blob cache. The blob cache will cache key/value pairs - // with key and value sizes less than or equal to maxKeySize and - // maxValueSize, respectively. The total combined size of ALL cache entries - // (key sizes plus value sizes) will not exceed maxTotalSize. - BlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize); - - // set inserts a new binary value into the cache and associates it with the - // given binary key. If the key or value are too large for the cache then - // the cache remains unchanged. This includes the case where a different - // value was previously associated with the given key - the old value will - // remain in the cache. If the given key and value are small enough to be - // put in the cache (based on the maxKeySize, maxValueSize, and maxTotalSize - // values specified to the BlobCache constructor), then the key/value pair - // will be in the cache after set returns. Note, however, that a subsequent - // call to set may evict old key/value pairs from the cache. - // - // Preconditions: - // key != NULL - // 0 < keySize - // value != NULL - // 0 < valueSize - void set(const void* key, size_t keySize, const void* value, - size_t valueSize); - - // get retrieves from the cache the binary value associated with a given - // binary key. If the key is present in the cache then the length of the - // binary value associated with that key is returned. If the value argument - // is non-NULL and the size of the cached value is less than valueSize bytes - // then the cached value is copied into the buffer pointed to by the value - // argument. If the key is not present in the cache then 0 is returned and - // the buffer pointed to by the value argument is not modified. - // - // Note that when calling get multiple times with the same key, the later - // calls may fail, returning 0, even if earlier calls succeeded. The return - // value must be checked for each call. - // - // Preconditions: - // key != NULL - // 0 < keySize - // 0 <= valueSize - size_t get(const void* key, size_t keySize, void* value, size_t valueSize); - - // getFlattenedSize returns the number of bytes needed to store the entire - // serialized cache. - virtual size_t getFlattenedSize() const; - - // getFdCount returns the number of file descriptors that will result from - // flattening the cache. This will always return 0 so as to allow the - // flattened cache to be saved to disk and then later restored. - virtual size_t getFdCount() const; - - // flatten serializes the current contents of the cache into the memory - // pointed to by 'buffer'. The serialized cache contents can later be - // loaded into a BlobCache object using the unflatten method. The contents - // of the BlobCache object will not be modified. - // - // Preconditions: - // size >= this.getFlattenedSize() - // count == 0 - virtual status_t flatten(void* buffer, size_t size, int fds[], - size_t count) const; - - // unflatten replaces the contents of the cache with the serialized cache - // contents in the memory pointed to by 'buffer'. The previous contents of - // the BlobCache will be evicted from the cache. If an error occurs while - // unflattening the serialized cache contents then the BlobCache will be - // left in an empty state. - // - // Preconditions: - // count == 0 - virtual status_t unflatten(void const* buffer, size_t size, int fds[], - size_t count); - -private: - // Copying is disallowed. - BlobCache(const BlobCache&); - void operator=(const BlobCache&); - - // A random function helper to get around MinGW not having nrand48() - long int blob_random(); - - // clean evicts a randomly chosen set of entries from the cache such that - // the total size of all remaining entries is less than mMaxTotalSize/2. - void clean(); - - // isCleanable returns true if the cache is full enough for the clean method - // to have some effect, and false otherwise. - bool isCleanable() const; - - // A Blob is an immutable sized unstructured data blob. - class Blob : public RefBase { - public: - Blob(const void* data, size_t size, bool copyData); - ~Blob(); - - bool operator<(const Blob& rhs) const; - - const void* getData() const; - size_t getSize() const; - - private: - // Copying is not allowed. - Blob(const Blob&); - void operator=(const Blob&); - - // mData points to the buffer containing the blob data. - const void* mData; - - // mSize is the size of the blob data in bytes. - size_t mSize; - - // mOwnsData indicates whether or not this Blob object should free the - // memory pointed to by mData when the Blob gets destructed. - bool mOwnsData; - }; - - // A CacheEntry is a single key/value pair in the cache. - class CacheEntry { - public: - CacheEntry(); - CacheEntry(const sp<Blob>& key, const sp<Blob>& value); - CacheEntry(const CacheEntry& ce); - - bool operator<(const CacheEntry& rhs) const; - const CacheEntry& operator=(const CacheEntry&); - - sp<Blob> getKey() const; - sp<Blob> getValue() const; - - void setValue(const sp<Blob>& value); - - private: - - // mKey is the key that identifies the cache entry. - sp<Blob> mKey; - - // mValue is the cached data associated with the key. - sp<Blob> mValue; - }; - - // A Header is the header for the entire BlobCache serialization format. No - // need to make this portable, so we simply write the struct out. - struct Header { - // mMagicNumber is the magic number that identifies the data as - // serialized BlobCache contents. It must always contain 'Blb$'. - uint32_t mMagicNumber; - - // mBlobCacheVersion is the serialization format version. - uint32_t mBlobCacheVersion; - - // mDeviceVersion is the device-specific version of the cache. This can - // be used to invalidate the cache. - uint32_t mDeviceVersion; - - // mNumEntries is number of cache entries following the header in the - // data. - size_t mNumEntries; - }; - - // An EntryHeader is the header for a serialized cache entry. No need to - // make this portable, so we simply write the struct out. Each EntryHeader - // is followed imediately by the key data and then the value data. - // - // The beginning of each serialized EntryHeader is 4-byte aligned, so the - // number of bytes that a serialized cache entry will occupy is: - // - // ((sizeof(EntryHeader) + keySize + valueSize) + 3) & ~3 - // - struct EntryHeader { - // mKeySize is the size of the entry key in bytes. - size_t mKeySize; - - // mValueSize is the size of the entry value in bytes. - size_t mValueSize; - - // mData contains both the key and value data for the cache entry. The - // key comes first followed immediately by the value. - uint8_t mData[]; - }; - - // mMaxKeySize is the maximum key size that will be cached. Calls to - // BlobCache::set with a keySize parameter larger than mMaxKeySize will - // simply not add the key/value pair to the cache. - const size_t mMaxKeySize; - - // mMaxValueSize is the maximum value size that will be cached. Calls to - // BlobCache::set with a valueSize parameter larger than mMaxValueSize will - // simply not add the key/value pair to the cache. - const size_t mMaxValueSize; - - // mMaxTotalSize is the maximum size that all cache entries can occupy. This - // includes space for both keys and values. When a call to BlobCache::set - // would otherwise cause this limit to be exceeded, either the key/value - // pair passed to BlobCache::set will not be cached or other cache entries - // will be evicted from the cache to make room for the new entry. - const size_t mMaxTotalSize; - - // mTotalSize is the total combined size of all keys and values currently in - // the cache. - size_t mTotalSize; - - // mRandState is the pseudo-random number generator state. It is passed to - // nrand48 to generate random numbers when needed. - unsigned short mRandState[3]; - - // mCacheEntries stores all the cache entries that are resident in memory. - // Cache entries are added to it by the 'set' method. - SortedVector<CacheEntry> mCacheEntries; -}; - -} - -#endif // ANDROID_BLOB_CACHE_H diff --git a/include/utils/ByteOrder.h b/include/utils/ByteOrder.h deleted file mode 100644 index baa3a83..0000000 --- a/include/utils/ByteOrder.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2006 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 _LIBS_UTILS_BYTE_ORDER_H -#define _LIBS_UTILS_BYTE_ORDER_H - -#include <stdint.h> -#include <sys/types.h> -#ifdef HAVE_WINSOCK -#include <winsock2.h> -#else -#include <netinet/in.h> -#endif - -/* - * These macros are like the hton/ntoh byte swapping macros, - * except they allow you to swap to and from the "device" byte - * order. The device byte order is the endianness of the target - * device -- for the ARM CPUs we use today, this is little endian. - * - * Note that the byte swapping functions have not been optimized - * much; performance is currently not an issue for them since the - * intent is to allow us to avoid byte swapping on the device. - */ - -static inline uint32_t android_swap_long(uint32_t v) -{ - return (v<<24) | ((v<<8)&0x00FF0000) | ((v>>8)&0x0000FF00) | (v>>24); -} - -static inline uint16_t android_swap_short(uint16_t v) -{ - return (v<<8) | (v>>8); -} - -#define DEVICE_BYTE_ORDER LITTLE_ENDIAN - -#if BYTE_ORDER == DEVICE_BYTE_ORDER - -#define dtohl(x) (x) -#define dtohs(x) (x) -#define htodl(x) (x) -#define htods(x) (x) - -#else - -#define dtohl(x) (android_swap_long(x)) -#define dtohs(x) (android_swap_short(x)) -#define htodl(x) (android_swap_long(x)) -#define htods(x) (android_swap_short(x)) - -#endif - -#if BYTE_ORDER == LITTLE_ENDIAN -#define fromlel(x) (x) -#define fromles(x) (x) -#define tolel(x) (x) -#define toles(x) (x) -#else -#define fromlel(x) (android_swap_long(x)) -#define fromles(x) (android_swap_short(x)) -#define tolel(x) (android_swap_long(x)) -#define toles(x) (android_swap_short(x)) -#endif - -#endif // _LIBS_UTILS_BYTE_ORDER_H diff --git a/include/utils/CallStack.h b/include/utils/CallStack.h deleted file mode 100644 index 61dc832..0000000 --- a/include/utils/CallStack.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2007 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_CALLSTACK_H -#define ANDROID_CALLSTACK_H - -#include <stdint.h> -#include <sys/types.h> - -#include <utils/String8.h> -#include <corkscrew/backtrace.h> - -// --------------------------------------------------------------------------- - -namespace android { - -class CallStack -{ -public: - enum { - MAX_DEPTH = 31 - }; - - CallStack(); - CallStack(const char* logtag, int32_t ignoreDepth=1, - int32_t maxDepth=MAX_DEPTH); - CallStack(const CallStack& rhs); - ~CallStack(); - - CallStack& operator = (const CallStack& rhs); - - bool operator == (const CallStack& rhs) const; - bool operator != (const CallStack& rhs) const; - bool operator < (const CallStack& rhs) const; - bool operator >= (const CallStack& rhs) const; - bool operator > (const CallStack& rhs) const; - bool operator <= (const CallStack& rhs) const; - - const void* operator [] (int index) const; - - void clear(); - - void update(int32_t ignoreDepth=1, int32_t maxDepth=MAX_DEPTH); - - // Dump a stack trace to the log using the supplied logtag - void dump(const char* logtag, const char* prefix = 0) const; - - // Return a string (possibly very long) containing the complete stack trace - String8 toString(const char* prefix = 0) const; - - size_t size() const { return mCount; } - -private: - size_t mCount; - backtrace_frame_t mStack[MAX_DEPTH]; -}; - -}; // namespace android - - -// --------------------------------------------------------------------------- - -#endif // ANDROID_CALLSTACK_H diff --git a/include/utils/Compat.h b/include/utils/Compat.h deleted file mode 100644 index fb7748e..0000000 --- a/include/utils/Compat.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __LIB_UTILS_COMPAT_H -#define __LIB_UTILS_COMPAT_H - -#include <unistd.h> - -/* Compatibility definitions for non-Linux (i.e., BSD-based) hosts. */ -#ifndef HAVE_OFF64_T -#if _FILE_OFFSET_BITS < 64 -#error "_FILE_OFFSET_BITS < 64; large files are not supported on this platform" -#endif /* _FILE_OFFSET_BITS < 64 */ - -typedef off_t off64_t; - -static inline off64_t lseek64(int fd, off64_t offset, int whence) { - return lseek(fd, offset, whence); -} - -#ifdef HAVE_PREAD -static inline ssize_t pread64(int fd, void* buf, size_t nbytes, off64_t offset) { - return pread(fd, buf, nbytes, offset); -} -#endif - -#endif /* !HAVE_OFF64_T */ - -#if HAVE_PRINTF_ZD -# define ZD "%zd" -# define ZD_TYPE ssize_t -#else -# define ZD "%ld" -# define ZD_TYPE long -#endif - -/* - * TEMP_FAILURE_RETRY is defined by some, but not all, versions of - * <unistd.h>. (Alas, it is not as standard as we'd hoped!) So, if it's - * not already defined, then define it here. - */ -#ifndef TEMP_FAILURE_RETRY -/* Used to retry syscalls that can return EINTR. */ -#define TEMP_FAILURE_RETRY(exp) ({ \ - typeof (exp) _rc; \ - do { \ - _rc = (exp); \ - } while (_rc == -1 && errno == EINTR); \ - _rc; }) -#endif - -#endif /* __LIB_UTILS_COMPAT_H */ diff --git a/include/utils/Condition.h b/include/utils/Condition.h deleted file mode 100644 index e63ba7e..0000000 --- a/include/utils/Condition.h +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright (C) 2007 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 _LIBS_UTILS_CONDITION_H -#define _LIBS_UTILS_CONDITION_H - -#include <stdint.h> -#include <sys/types.h> -#include <time.h> - -#if defined(HAVE_PTHREADS) -# include <pthread.h> -#endif - -#include <utils/Errors.h> -#include <utils/Mutex.h> -#include <utils/Timers.h> - -// --------------------------------------------------------------------------- -namespace android { -// --------------------------------------------------------------------------- - -/* - * Condition variable class. The implementation is system-dependent. - * - * Condition variables are paired up with mutexes. Lock the mutex, - * call wait(), then either re-wait() if things aren't quite what you want, - * or unlock the mutex and continue. All threads calling wait() must - * use the same mutex for a given Condition. - */ -class Condition { -public: - enum { - PRIVATE = 0, - SHARED = 1 - }; - - enum WakeUpType { - WAKE_UP_ONE = 0, - WAKE_UP_ALL = 1 - }; - - Condition(); - Condition(int type); - ~Condition(); - // Wait on the condition variable. Lock the mutex before calling. - status_t wait(Mutex& mutex); - // same with relative timeout - status_t waitRelative(Mutex& mutex, nsecs_t reltime); - // Signal the condition variable, allowing one thread to continue. - void signal(); - // Signal the condition variable, allowing one or all threads to continue. - void signal(WakeUpType type) { - if (type == WAKE_UP_ONE) { - signal(); - } else { - broadcast(); - } - } - // Signal the condition variable, allowing all threads to continue. - void broadcast(); - -private: -#if defined(HAVE_PTHREADS) - pthread_cond_t mCond; -#else - void* mState; -#endif -}; - -// --------------------------------------------------------------------------- - -#if defined(HAVE_PTHREADS) - -inline Condition::Condition() { - pthread_cond_init(&mCond, NULL); -} -inline Condition::Condition(int type) { - if (type == SHARED) { - pthread_condattr_t attr; - pthread_condattr_init(&attr); - pthread_condattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); - pthread_cond_init(&mCond, &attr); - pthread_condattr_destroy(&attr); - } else { - pthread_cond_init(&mCond, NULL); - } -} -inline Condition::~Condition() { - pthread_cond_destroy(&mCond); -} -inline status_t Condition::wait(Mutex& mutex) { - return -pthread_cond_wait(&mCond, &mutex.mMutex); -} -inline status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime) { -#if defined(HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE) - struct timespec ts; - ts.tv_sec = reltime/1000000000; - ts.tv_nsec = reltime%1000000000; - return -pthread_cond_timedwait_relative_np(&mCond, &mutex.mMutex, &ts); -#else // HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE - struct timespec ts; -#if defined(HAVE_POSIX_CLOCKS) - clock_gettime(CLOCK_REALTIME, &ts); -#else // HAVE_POSIX_CLOCKS - // we don't support the clocks here. - struct timeval t; - gettimeofday(&t, NULL); - ts.tv_sec = t.tv_sec; - ts.tv_nsec= t.tv_usec*1000; -#endif // HAVE_POSIX_CLOCKS - ts.tv_sec += reltime/1000000000; - ts.tv_nsec+= reltime%1000000000; - if (ts.tv_nsec >= 1000000000) { - ts.tv_nsec -= 1000000000; - ts.tv_sec += 1; - } - return -pthread_cond_timedwait(&mCond, &mutex.mMutex, &ts); -#endif // HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE -} -inline void Condition::signal() { - pthread_cond_signal(&mCond); -} -inline void Condition::broadcast() { - pthread_cond_broadcast(&mCond); -} - -#endif // HAVE_PTHREADS - -// --------------------------------------------------------------------------- -}; // namespace android -// --------------------------------------------------------------------------- - -#endif // _LIBS_UTILS_CONDITON_H diff --git a/include/utils/Endian.h b/include/utils/Endian.h deleted file mode 100644 index 19f2504..0000000 --- a/include/utils/Endian.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2005 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. - */ - -// -// Android endian-ness defines. -// -#ifndef _LIBS_UTILS_ENDIAN_H -#define _LIBS_UTILS_ENDIAN_H - -#if defined(HAVE_ENDIAN_H) - -#include <endian.h> - -#else /*not HAVE_ENDIAN_H*/ - -#define __BIG_ENDIAN 0x1000 -#define __LITTLE_ENDIAN 0x0001 - -#if defined(HAVE_LITTLE_ENDIAN) -# define __BYTE_ORDER __LITTLE_ENDIAN -#else -# define __BYTE_ORDER __BIG_ENDIAN -#endif - -#endif /*not HAVE_ENDIAN_H*/ - -#endif /*_LIBS_UTILS_ENDIAN_H*/ diff --git a/include/utils/Errors.h b/include/utils/Errors.h deleted file mode 100644 index 0b75b19..0000000 --- a/include/utils/Errors.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2007 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_ERRORS_H -#define ANDROID_ERRORS_H - -#include <sys/types.h> -#include <errno.h> - -namespace android { - -// use this type to return error codes -#ifdef HAVE_MS_C_RUNTIME -typedef int status_t; -#else -typedef int32_t status_t; -#endif - -/* the MS C runtime lacks a few error codes */ - -/* - * Error codes. - * All error codes are negative values. - */ - -// Win32 #defines NO_ERROR as well. It has the same value, so there's no -// real conflict, though it's a bit awkward. -#ifdef _WIN32 -# undef NO_ERROR -#endif - -enum { - OK = 0, // Everything's swell. - NO_ERROR = 0, // No errors. - - UNKNOWN_ERROR = 0x80000000, - - NO_MEMORY = -ENOMEM, - INVALID_OPERATION = -ENOSYS, - BAD_VALUE = -EINVAL, - BAD_TYPE = 0x80000001, - NAME_NOT_FOUND = -ENOENT, - PERMISSION_DENIED = -EPERM, - NO_INIT = -ENODEV, - ALREADY_EXISTS = -EEXIST, - DEAD_OBJECT = -EPIPE, - FAILED_TRANSACTION = 0x80000002, - JPARKS_BROKE_IT = -EPIPE, -#if !defined(HAVE_MS_C_RUNTIME) - BAD_INDEX = -EOVERFLOW, - NOT_ENOUGH_DATA = -ENODATA, - WOULD_BLOCK = -EWOULDBLOCK, - TIMED_OUT = -ETIMEDOUT, - UNKNOWN_TRANSACTION = -EBADMSG, -#else - BAD_INDEX = -E2BIG, - NOT_ENOUGH_DATA = 0x80000003, - WOULD_BLOCK = 0x80000004, - TIMED_OUT = 0x80000005, - UNKNOWN_TRANSACTION = 0x80000006, -#endif - FDS_NOT_ALLOWED = 0x80000007, -}; - -// Restore define; enumeration is in "android" namespace, so the value defined -// there won't work for Win32 code in a different namespace. -#ifdef _WIN32 -# define NO_ERROR 0L -#endif - -}; // namespace android - -// --------------------------------------------------------------------------- - -#endif // ANDROID_ERRORS_H diff --git a/include/utils/FileMap.h b/include/utils/FileMap.h deleted file mode 100644 index dfe6d51..0000000 --- a/include/utils/FileMap.h +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (C) 2006 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. - */ - -// -// Encapsulate a shared file mapping. -// -#ifndef __LIBS_FILE_MAP_H -#define __LIBS_FILE_MAP_H - -#include <sys/types.h> - -#include <utils/Compat.h> - -#ifdef HAVE_WIN32_FILEMAP -#include <windows.h> -#endif - -namespace android { - -/* - * This represents a memory-mapped file. It might be the entire file or - * only part of it. This requires a little bookkeeping because the mapping - * needs to be aligned on page boundaries, and in some cases we'd like to - * have multiple references to the mapped area without creating additional - * maps. - * - * This always uses MAP_SHARED. - * - * TODO: we should be able to create a new FileMap that is a subset of - * an existing FileMap and shares the underlying mapped pages. Requires - * completing the refcounting stuff and possibly introducing the notion - * of a FileMap hierarchy. - */ -class FileMap { -public: - FileMap(void); - - /* - * Create a new mapping on an open file. - * - * Closing the file descriptor does not unmap the pages, so we don't - * claim ownership of the fd. - * - * Returns "false" on failure. - */ - bool create(const char* origFileName, int fd, - off64_t offset, size_t length, bool readOnly); - - /* - * Return the name of the file this map came from, if known. - */ - const char* getFileName(void) const { return mFileName; } - - /* - * Get a pointer to the piece of the file we requested. - */ - void* getDataPtr(void) const { return mDataPtr; } - - /* - * Get the length we requested. - */ - size_t getDataLength(void) const { return mDataLength; } - - /* - * Get the data offset used to create this map. - */ - off64_t getDataOffset(void) const { return mDataOffset; } - - /* - * Get a "copy" of the object. - */ - FileMap* acquire(void) { mRefCount++; return this; } - - /* - * Call this when mapping is no longer needed. - */ - void release(void) { - if (--mRefCount <= 0) - delete this; - } - - /* - * This maps directly to madvise() values, but allows us to avoid - * including <sys/mman.h> everywhere. - */ - enum MapAdvice { - NORMAL, RANDOM, SEQUENTIAL, WILLNEED, DONTNEED - }; - - /* - * Apply an madvise() call to the entire file. - * - * Returns 0 on success, -1 on failure. - */ - int advise(MapAdvice advice); - -protected: - // don't delete objects; call release() - ~FileMap(void); - -private: - // these are not implemented - FileMap(const FileMap& src); - const FileMap& operator=(const FileMap& src); - - int mRefCount; // reference count - char* mFileName; // original file name, if known - void* mBasePtr; // base of mmap area; page aligned - size_t mBaseLength; // length, measured from "mBasePtr" - off64_t mDataOffset; // offset used when map was created - void* mDataPtr; // start of requested data, offset from base - size_t mDataLength; // length, measured from "mDataPtr" -#ifdef HAVE_WIN32_FILEMAP - HANDLE mFileHandle; // Win32 file handle - HANDLE mFileMapping; // Win32 file mapping handle -#endif - - static long mPageSize; -}; - -}; // namespace android - -#endif // __LIBS_FILE_MAP_H diff --git a/include/utils/Flattenable.h b/include/utils/Flattenable.h deleted file mode 100644 index e40d289..0000000 --- a/include/utils/Flattenable.h +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_UTILS_FLATTENABLE_H -#define ANDROID_UTILS_FLATTENABLE_H - - -#include <stdint.h> -#include <sys/types.h> -#include <utils/Errors.h> - -namespace android { - -/* - * The Flattenable interface allows an object to serialize itself out - * to a byte-buffer and an array of file descriptors. - */ - -class Flattenable -{ -public: - // size in bytes of the flattened object - virtual size_t getFlattenedSize() const = 0; - - // number of file descriptors to flatten - virtual size_t getFdCount() const = 0; - - // flattens the object into buffer. - // size should be at least of getFlattenedSize() - // file descriptors are written in the fds[] array but ownership is - // not transfered (ie: they must be dupped by the caller of - // flatten() if needed). - virtual status_t flatten(void* buffer, size_t size, - int fds[], size_t count) const = 0; - - // unflattens the object from buffer. - // size should be equal to the value of getFlattenedSize() when the - // object was flattened. - // unflattened file descriptors are found in the fds[] array and - // don't need to be dupped(). ie: the caller of unflatten doesn't - // keep ownership. If a fd is not retained by unflatten() it must be - // explicitly closed. - virtual status_t unflatten(void const* buffer, size_t size, - int fds[], size_t count) = 0; - -protected: - virtual ~Flattenable() = 0; - -}; - -/* - * LightFlattenable is a protocol allowing object to serialize themselves out - * to a byte-buffer. - * - * LightFlattenable objects must implement this protocol. - * - * LightFlattenable doesn't require the object to be virtual. - */ -template <typename T> -class LightFlattenable { -public: - // returns whether this object always flatten into the same size. - // for efficiency, this should always be inline. - inline bool isFixedSize() const; - - // returns size in bytes of the flattened object. must be a constant. - inline size_t getSize() const; - - // flattens the object into buffer. - inline status_t flatten(void* buffer) const; - - // unflattens the object from buffer of given size. - inline status_t unflatten(void const* buffer, size_t size); -}; - -template <typename T> -inline bool LightFlattenable<T>::isFixedSize() const { - return static_cast<T const*>(this)->T::isFixedSize(); -} -template <typename T> -inline size_t LightFlattenable<T>::getSize() const { - return static_cast<T const*>(this)->T::getSize(); -} -template <typename T> -inline status_t LightFlattenable<T>::flatten(void* buffer) const { - return static_cast<T const*>(this)->T::flatten(buffer); -} -template <typename T> -inline status_t LightFlattenable<T>::unflatten(void const* buffer, size_t size) { - return static_cast<T*>(this)->T::unflatten(buffer, size); -} - -/* - * LightFlattenablePod is an implementation of the LightFlattenable protocol - * for POD (plain-old-data) objects. - */ -template <typename T> -class LightFlattenablePod : public LightFlattenable<T> { -public: - inline bool isFixedSize() const { - return true; - } - - inline size_t getSize() const { - return sizeof(T); - } - inline status_t flatten(void* buffer) const { - *reinterpret_cast<T*>(buffer) = *static_cast<T const*>(this); - return NO_ERROR; - } - inline status_t unflatten(void const* buffer, size_t) { - *static_cast<T*>(this) = *reinterpret_cast<T const*>(buffer); - return NO_ERROR; - } -}; - - -}; // namespace android - - -#endif /* ANDROID_UTILS_FLATTENABLE_H */ diff --git a/include/utils/Functor.h b/include/utils/Functor.h deleted file mode 100644 index e24ded4..0000000 --- a/include/utils/Functor.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_FUNCTOR_H -#define ANDROID_FUNCTOR_H - -#include <utils/Errors.h> - -namespace android { - -class Functor { -public: - Functor() {} - virtual ~Functor() {} - virtual status_t operator ()(int what, void* data) { return NO_ERROR; } -}; - -}; // namespace android - -#endif // ANDROID_FUNCTOR_H diff --git a/include/utils/GenerationCache.h b/include/utils/GenerationCache.h deleted file mode 100644 index 40722d1..0000000 --- a/include/utils/GenerationCache.h +++ /dev/null @@ -1,247 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_UTILS_GENERATION_CACHE_H -#define ANDROID_UTILS_GENERATION_CACHE_H - -#include <utils/KeyedVector.h> -#include <utils/RefBase.h> - -namespace android { - -/** - * GenerationCache callback used when an item is removed - */ -template<typename EntryKey, typename EntryValue> -class OnEntryRemoved { -public: - virtual ~OnEntryRemoved() { }; - virtual void operator()(EntryKey& key, EntryValue& value) = 0; -}; // class OnEntryRemoved - -template<typename EntryKey, typename EntryValue> -struct Entry: public LightRefBase<Entry<EntryKey, EntryValue> > { - Entry(const Entry<EntryKey, EntryValue>& e) : - key(e.key), value(e.value), - parent(e.parent), child(e.child) { } - Entry(const EntryKey& key, const EntryValue& value) : - key(key), value(value) { } - - EntryKey key; - EntryValue value; - - sp<Entry<EntryKey, EntryValue> > parent; // next older entry - sp<Entry<EntryKey, EntryValue> > child; // next younger entry -}; // struct Entry - -/** - * A LRU type cache - */ -template<typename K, typename V> -class GenerationCache { -public: - GenerationCache(uint32_t maxCapacity); - virtual ~GenerationCache(); - - enum Capacity { - kUnlimitedCapacity, - }; - - void setOnEntryRemovedListener(OnEntryRemoved<K, V>* listener); - - size_t size() const; - - void clear(); - - bool contains(const K& key) const; - const K& getKeyAt(size_t index) const; - const V& getValueAt(size_t index) const; - - const V& get(const K& key); - bool put(const K& key, const V& value); - - void removeAt(ssize_t index); - bool remove(const K& key); - bool removeOldest(); - -private: - KeyedVector<K, sp<Entry<K, V> > > mCache; - uint32_t mMaxCapacity; - - OnEntryRemoved<K, V>* mListener; - - sp<Entry<K, V> > mOldest; - sp<Entry<K, V> > mYoungest; - - void attachToCache(const sp<Entry<K, V> >& entry); - void detachFromCache(const sp<Entry<K, V> >& entry); - - const V mNullValue; -}; // class GenerationCache - -template<typename K, typename V> -GenerationCache<K, V>::GenerationCache(uint32_t maxCapacity): mMaxCapacity(maxCapacity), - mListener(NULL), mNullValue(NULL) { -}; - -template<typename K, typename V> -GenerationCache<K, V>::~GenerationCache() { - clear(); -}; - -template<typename K, typename V> -uint32_t GenerationCache<K, V>::size() const { - return mCache.size(); -} - -/** - * Should be set by the user of the Cache so that the callback is called whenever an item is - * removed from the cache - */ -template<typename K, typename V> -void GenerationCache<K, V>::setOnEntryRemovedListener(OnEntryRemoved<K, V>* listener) { - mListener = listener; -} - -template<typename K, typename V> -void GenerationCache<K, V>::clear() { - if (mListener) { - for (uint32_t i = 0; i < mCache.size(); i++) { - sp<Entry<K, V> > entry = mCache.valueAt(i); - if (mListener) { - (*mListener)(entry->key, entry->value); - } - } - } - mCache.clear(); - mYoungest.clear(); - mOldest.clear(); -} - -template<typename K, typename V> -bool GenerationCache<K, V>::contains(const K& key) const { - return mCache.indexOfKey(key) >= 0; -} - -template<typename K, typename V> -const K& GenerationCache<K, V>::getKeyAt(size_t index) const { - return mCache.keyAt(index); -} - -template<typename K, typename V> -const V& GenerationCache<K, V>::getValueAt(size_t index) const { - return mCache.valueAt(index)->value; -} - -template<typename K, typename V> -const V& GenerationCache<K, V>::get(const K& key) { - ssize_t index = mCache.indexOfKey(key); - if (index >= 0) { - const sp<Entry<K, V> >& entry = mCache.valueAt(index); - detachFromCache(entry); - attachToCache(entry); - return entry->value; - } - - return mNullValue; -} - -template<typename K, typename V> -bool GenerationCache<K, V>::put(const K& key, const V& value) { - if (mMaxCapacity != kUnlimitedCapacity && mCache.size() >= mMaxCapacity) { - removeOldest(); - } - - ssize_t index = mCache.indexOfKey(key); - if (index < 0) { - sp<Entry<K, V> > entry = new Entry<K, V>(key, value); - mCache.add(key, entry); - attachToCache(entry); - return true; - } - - return false; -} - -template<typename K, typename V> -bool GenerationCache<K, V>::remove(const K& key) { - ssize_t index = mCache.indexOfKey(key); - if (index >= 0) { - removeAt(index); - return true; - } - - return false; -} - -template<typename K, typename V> -void GenerationCache<K, V>::removeAt(ssize_t index) { - sp<Entry<K, V> > entry = mCache.valueAt(index); - if (mListener) { - (*mListener)(entry->key, entry->value); - } - mCache.removeItemsAt(index, 1); - detachFromCache(entry); -} - -template<typename K, typename V> -bool GenerationCache<K, V>::removeOldest() { - if (mOldest.get()) { - ssize_t index = mCache.indexOfKey(mOldest->key); - if (index >= 0) { - removeAt(index); - return true; - } - ALOGE("GenerationCache: removeOldest failed to find the item in the cache " - "with the given key, but we know it must be in there. " - "Is the key comparator kaput?"); - } - - return false; -} - -template<typename K, typename V> -void GenerationCache<K, V>::attachToCache(const sp<Entry<K, V> >& entry) { - if (!mYoungest.get()) { - mYoungest = mOldest = entry; - } else { - entry->parent = mYoungest; - mYoungest->child = entry; - mYoungest = entry; - } -} - -template<typename K, typename V> -void GenerationCache<K, V>::detachFromCache(const sp<Entry<K, V> >& entry) { - if (entry->parent.get()) { - entry->parent->child = entry->child; - } else { - mOldest = entry->child; - } - - if (entry->child.get()) { - entry->child->parent = entry->parent; - } else { - mYoungest = entry->parent; - } - - entry->parent.clear(); - entry->child.clear(); -} - -}; // namespace android - -#endif // ANDROID_UTILS_GENERATION_CACHE_H diff --git a/include/utils/JenkinsHash.h b/include/utils/JenkinsHash.h deleted file mode 100644 index 7da5dbd..0000000 --- a/include/utils/JenkinsHash.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2012 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. - */ - -/* Implementation of Jenkins one-at-a-time hash function. These choices are - * optimized for code size and portability, rather than raw speed. But speed - * should still be quite good. - **/ - -#ifndef ANDROID_JENKINS_HASH_H -#define ANDROID_JENKINS_HASH_H - -#include <utils/TypeHelpers.h> - -namespace android { - -/* The Jenkins hash of a sequence of 32 bit words A, B, C is: - * Whiten(Mix(Mix(Mix(0, A), B), C)) */ - -inline uint32_t JenkinsHashMix(uint32_t hash, uint32_t data) { - hash += data; - hash += (hash << 10); - hash ^= (hash >> 6); - return hash; -} - -hash_t JenkinsHashWhiten(uint32_t hash); - -/* Helpful utility functions for hashing data in 32 bit chunks */ -uint32_t JenkinsHashMixBytes(uint32_t hash, const uint8_t* bytes, size_t size); - -uint32_t JenkinsHashMixShorts(uint32_t hash, const uint16_t* shorts, size_t size); - -} - -#endif // ANDROID_JENKINS_HASH_H diff --git a/include/utils/KeyedVector.h b/include/utils/KeyedVector.h deleted file mode 100644 index c4faae0..0000000 --- a/include/utils/KeyedVector.h +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Copyright (C) 2005 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_KEYED_VECTOR_H -#define ANDROID_KEYED_VECTOR_H - -#include <assert.h> -#include <stdint.h> -#include <sys/types.h> - -#include <cutils/log.h> - -#include <utils/SortedVector.h> -#include <utils/TypeHelpers.h> -#include <utils/Errors.h> - -// --------------------------------------------------------------------------- - -namespace android { - -template <typename KEY, typename VALUE> -class KeyedVector -{ -public: - typedef KEY key_type; - typedef VALUE value_type; - - inline KeyedVector(); - - /* - * empty the vector - */ - - inline void clear() { mVector.clear(); } - - /*! - * vector stats - */ - - //! returns number of items in the vector - inline size_t size() const { return mVector.size(); } - //! returns whether or not the vector is empty - inline bool isEmpty() const { return mVector.isEmpty(); } - //! returns how many items can be stored without reallocating the backing store - inline size_t capacity() const { return mVector.capacity(); } - //! sets the capacity. capacity can never be reduced less than size() - inline ssize_t setCapacity(size_t size) { return mVector.setCapacity(size); } - - // returns true if the arguments is known to be identical to this vector - inline bool isIdenticalTo(const KeyedVector& rhs) const; - - /*! - * accessors - */ - const VALUE& valueFor(const KEY& key) const; - const VALUE& valueAt(size_t index) const; - const KEY& keyAt(size_t index) const; - ssize_t indexOfKey(const KEY& key) const; - const VALUE& operator[] (size_t index) const; - - /*! - * modifying the array - */ - - VALUE& editValueFor(const KEY& key); - VALUE& editValueAt(size_t index); - - /*! - * add/insert/replace items - */ - - ssize_t add(const KEY& key, const VALUE& item); - ssize_t replaceValueFor(const KEY& key, const VALUE& item); - ssize_t replaceValueAt(size_t index, const VALUE& item); - - /*! - * remove items - */ - - ssize_t removeItem(const KEY& key); - ssize_t removeItemsAt(size_t index, size_t count = 1); - -private: - SortedVector< key_value_pair_t<KEY, VALUE> > mVector; -}; - -// KeyedVector<KEY, VALUE> can be trivially moved using memcpy() because its -// underlying SortedVector can be trivially moved. -template<typename KEY, typename VALUE> struct trait_trivial_move<KeyedVector<KEY, VALUE> > { - enum { value = trait_trivial_move<SortedVector< key_value_pair_t<KEY, VALUE> > >::value }; -}; - - -// --------------------------------------------------------------------------- - -/** - * Variation of KeyedVector that holds a default value to return when - * valueFor() is called with a key that doesn't exist. - */ -template <typename KEY, typename VALUE> -class DefaultKeyedVector : public KeyedVector<KEY, VALUE> -{ -public: - inline DefaultKeyedVector(const VALUE& defValue = VALUE()); - const VALUE& valueFor(const KEY& key) const; - -private: - VALUE mDefault; -}; - -// --------------------------------------------------------------------------- - -template<typename KEY, typename VALUE> inline -KeyedVector<KEY,VALUE>::KeyedVector() -{ -} - -template<typename KEY, typename VALUE> inline -bool KeyedVector<KEY,VALUE>::isIdenticalTo(const KeyedVector<KEY,VALUE>& rhs) const { - return mVector.array() == rhs.mVector.array(); -} - -template<typename KEY, typename VALUE> inline -ssize_t KeyedVector<KEY,VALUE>::indexOfKey(const KEY& key) const { - return mVector.indexOf( key_value_pair_t<KEY,VALUE>(key) ); -} - -template<typename KEY, typename VALUE> inline -const VALUE& KeyedVector<KEY,VALUE>::valueFor(const KEY& key) const { - ssize_t i = this->indexOfKey(key); - LOG_ALWAYS_FATAL_IF(i<0, "%s: key not found", __PRETTY_FUNCTION__); - return mVector.itemAt(i).value; -} - -template<typename KEY, typename VALUE> inline -const VALUE& KeyedVector<KEY,VALUE>::valueAt(size_t index) const { - return mVector.itemAt(index).value; -} - -template<typename KEY, typename VALUE> inline -const VALUE& KeyedVector<KEY,VALUE>::operator[] (size_t index) const { - return valueAt(index); -} - -template<typename KEY, typename VALUE> inline -const KEY& KeyedVector<KEY,VALUE>::keyAt(size_t index) const { - return mVector.itemAt(index).key; -} - -template<typename KEY, typename VALUE> inline -VALUE& KeyedVector<KEY,VALUE>::editValueFor(const KEY& key) { - ssize_t i = this->indexOfKey(key); - LOG_ALWAYS_FATAL_IF(i<0, "%s: key not found", __PRETTY_FUNCTION__); - return mVector.editItemAt(i).value; -} - -template<typename KEY, typename VALUE> inline -VALUE& KeyedVector<KEY,VALUE>::editValueAt(size_t index) { - return mVector.editItemAt(index).value; -} - -template<typename KEY, typename VALUE> inline -ssize_t KeyedVector<KEY,VALUE>::add(const KEY& key, const VALUE& value) { - return mVector.add( key_value_pair_t<KEY,VALUE>(key, value) ); -} - -template<typename KEY, typename VALUE> inline -ssize_t KeyedVector<KEY,VALUE>::replaceValueFor(const KEY& key, const VALUE& value) { - key_value_pair_t<KEY,VALUE> pair(key, value); - mVector.remove(pair); - return mVector.add(pair); -} - -template<typename KEY, typename VALUE> inline -ssize_t KeyedVector<KEY,VALUE>::replaceValueAt(size_t index, const VALUE& item) { - if (index<size()) { - mVector.editItemAt(index).value = item; - return index; - } - return BAD_INDEX; -} - -template<typename KEY, typename VALUE> inline -ssize_t KeyedVector<KEY,VALUE>::removeItem(const KEY& key) { - return mVector.remove(key_value_pair_t<KEY,VALUE>(key)); -} - -template<typename KEY, typename VALUE> inline -ssize_t KeyedVector<KEY, VALUE>::removeItemsAt(size_t index, size_t count) { - return mVector.removeItemsAt(index, count); -} - -// --------------------------------------------------------------------------- - -template<typename KEY, typename VALUE> inline -DefaultKeyedVector<KEY,VALUE>::DefaultKeyedVector(const VALUE& defValue) - : mDefault(defValue) -{ -} - -template<typename KEY, typename VALUE> inline -const VALUE& DefaultKeyedVector<KEY,VALUE>::valueFor(const KEY& key) const { - ssize_t i = this->indexOfKey(key); - return i >= 0 ? KeyedVector<KEY,VALUE>::valueAt(i) : mDefault; -} - -}; // namespace android - -// --------------------------------------------------------------------------- - -#endif // ANDROID_KEYED_VECTOR_H diff --git a/include/utils/LinearAllocator.h b/include/utils/LinearAllocator.h deleted file mode 100644 index 4772bc8..0000000 --- a/include/utils/LinearAllocator.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2012, The Android Open Source Project - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef ANDROID_LINEARALLOCATOR_H -#define ANDROID_LINEARALLOCATOR_H - -#include <stddef.h> - -namespace android { - -/** - * A memory manager that internally allocates multi-kbyte buffers for placing objects in. It avoids - * the overhead of malloc when many objects are allocated. It is most useful when creating many - * small objects with a similar lifetime, and doesn't add significant overhead for large - * allocations. - */ -class LinearAllocator { -public: - LinearAllocator(); - ~LinearAllocator(); - - /** - * Reserves and returns a region of memory of at least size 'size', aligning as needed. - * Typically this is used in an object's overridden new() method or as a replacement for malloc. - * - * The lifetime of the returned buffers is tied to that of the LinearAllocator. If calling - * delete() on an object stored in a buffer is needed, it should be overridden to use - * rewindIfLastAlloc() - */ - void* alloc(size_t size); - - /** - * Attempt to deallocate the given buffer, with the LinearAllocator attempting to rewind its - * state if possible. No destructors are called. - */ - void rewindIfLastAlloc(void* ptr, size_t allocSize); - - /** - * Dump memory usage statistics to the log (allocated and wasted space) - */ - void dumpMemoryStats(const char* prefix = ""); - - /** - * The number of bytes used for buffers allocated in the LinearAllocator (does not count space - * wasted) - */ - size_t usedSize() const { return mTotalAllocated - mWastedSpace; } - -private: - LinearAllocator(const LinearAllocator& other); - - class Page; - - Page* newPage(size_t pageSize); - bool fitsInCurrentPage(size_t size); - void ensureNext(size_t size); - void* start(Page *p); - void* end(Page* p); - - size_t mPageSize; - size_t mMaxAllocSize; - void* mNext; - Page* mCurrentPage; - Page* mPages; - - // Memory usage tracking - size_t mTotalAllocated; - size_t mWastedSpace; - size_t mPageCount; - size_t mDedicatedPageCount; -}; - -}; // namespace android - -#endif // ANDROID_LINEARALLOCATOR_H diff --git a/include/utils/LinearTransform.h b/include/utils/LinearTransform.h deleted file mode 100644 index 04cb355..0000000 --- a/include/utils/LinearTransform.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _LIBS_UTILS_LINEAR_TRANSFORM_H -#define _LIBS_UTILS_LINEAR_TRANSFORM_H - -#include <stdint.h> - -namespace android { - -// LinearTransform defines a structure which hold the definition of a -// transformation from single dimensional coordinate system A into coordinate -// system B (and back again). Values in A and in B are 64 bit, the linear -// scale factor is expressed as a rational number using two 32 bit values. -// -// Specifically, let -// f(a) = b -// F(b) = f^-1(b) = a -// then -// -// f(a) = (((a - a_zero) * a_to_b_numer) / a_to_b_denom) + b_zero; -// -// and -// -// F(b) = (((b - b_zero) * a_to_b_denom) / a_to_b_numer) + a_zero; -// -struct LinearTransform { - int64_t a_zero; - int64_t b_zero; - int32_t a_to_b_numer; - uint32_t a_to_b_denom; - - // Transform from A->B - // Returns true on success, or false in the case of a singularity or an - // overflow. - bool doForwardTransform(int64_t a_in, int64_t* b_out) const; - - // Transform from B->A - // Returns true on success, or false in the case of a singularity or an - // overflow. - bool doReverseTransform(int64_t b_in, int64_t* a_out) const; - - // Helpers which will reduce the fraction N/D using Euclid's method. - template <class T> static void reduce(T* N, T* D); - static void reduce(int32_t* N, uint32_t* D); -}; - - -} - -#endif // _LIBS_UTILS_LINEAR_TRANSFORM_H diff --git a/include/utils/List.h b/include/utils/List.h deleted file mode 100644 index 403cd7f..0000000 --- a/include/utils/List.h +++ /dev/null @@ -1,332 +0,0 @@ -/* - * Copyright (C) 2005 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. - */ - -// -// Templated list class. Normally we'd use STL, but we don't have that. -// This class mimics STL's interfaces. -// -// Objects are copied into the list with the '=' operator or with copy- -// construction, so if the compiler's auto-generated versions won't work for -// you, define your own. -// -// The only class you want to use from here is "List". -// -#ifndef _LIBS_UTILS_LIST_H -#define _LIBS_UTILS_LIST_H - -#include <stddef.h> -#include <stdint.h> - -namespace android { - -/* - * Doubly-linked list. Instantiate with "List<MyClass> myList". - * - * Objects added to the list are copied using the assignment operator, - * so this must be defined. - */ -template<typename T> -class List -{ -protected: - /* - * One element in the list. - */ - class _Node { - public: - explicit _Node(const T& val) : mVal(val) {} - ~_Node() {} - inline T& getRef() { return mVal; } - inline const T& getRef() const { return mVal; } - inline _Node* getPrev() const { return mpPrev; } - inline _Node* getNext() const { return mpNext; } - inline void setVal(const T& val) { mVal = val; } - inline void setPrev(_Node* ptr) { mpPrev = ptr; } - inline void setNext(_Node* ptr) { mpNext = ptr; } - private: - friend class List; - friend class _ListIterator; - T mVal; - _Node* mpPrev; - _Node* mpNext; - }; - - /* - * Iterator for walking through the list. - */ - - template <typename TYPE> - struct CONST_ITERATOR { - typedef _Node const * NodePtr; - typedef const TYPE Type; - }; - - template <typename TYPE> - struct NON_CONST_ITERATOR { - typedef _Node* NodePtr; - typedef TYPE Type; - }; - - template< - typename U, - template <class> class Constness - > - class _ListIterator { - typedef _ListIterator<U, Constness> _Iter; - typedef typename Constness<U>::NodePtr _NodePtr; - typedef typename Constness<U>::Type _Type; - - explicit _ListIterator(_NodePtr ptr) : mpNode(ptr) {} - - public: - _ListIterator() {} - _ListIterator(const _Iter& rhs) : mpNode(rhs.mpNode) {} - ~_ListIterator() {} - - // this will handle conversions from iterator to const_iterator - // (and also all convertible iterators) - // Here, in this implementation, the iterators can be converted - // if the nodes can be converted - template<typename V> explicit - _ListIterator(const V& rhs) : mpNode(rhs.mpNode) {} - - - /* - * Dereference operator. Used to get at the juicy insides. - */ - _Type& operator*() const { return mpNode->getRef(); } - _Type* operator->() const { return &(mpNode->getRef()); } - - /* - * Iterator comparison. - */ - inline bool operator==(const _Iter& right) const { - return mpNode == right.mpNode; } - - inline bool operator!=(const _Iter& right) const { - return mpNode != right.mpNode; } - - /* - * handle comparisons between iterator and const_iterator - */ - template<typename OTHER> - inline bool operator==(const OTHER& right) const { - return mpNode == right.mpNode; } - - template<typename OTHER> - inline bool operator!=(const OTHER& right) const { - return mpNode != right.mpNode; } - - /* - * Incr/decr, used to move through the list. - */ - inline _Iter& operator++() { // pre-increment - mpNode = mpNode->getNext(); - return *this; - } - const _Iter operator++(int) { // post-increment - _Iter tmp(*this); - mpNode = mpNode->getNext(); - return tmp; - } - inline _Iter& operator--() { // pre-increment - mpNode = mpNode->getPrev(); - return *this; - } - const _Iter operator--(int) { // post-increment - _Iter tmp(*this); - mpNode = mpNode->getPrev(); - return tmp; - } - - inline _NodePtr getNode() const { return mpNode; } - - _NodePtr mpNode; /* should be private, but older gcc fails */ - private: - friend class List; - }; - -public: - List() { - prep(); - } - List(const List<T>& src) { // copy-constructor - prep(); - insert(begin(), src.begin(), src.end()); - } - virtual ~List() { - clear(); - delete[] (unsigned char*) mpMiddle; - } - - typedef _ListIterator<T, NON_CONST_ITERATOR> iterator; - typedef _ListIterator<T, CONST_ITERATOR> const_iterator; - - List<T>& operator=(const List<T>& right); - - /* returns true if the list is empty */ - inline bool empty() const { return mpMiddle->getNext() == mpMiddle; } - - /* return #of elements in list */ - size_t size() const { - return size_t(distance(begin(), end())); - } - - /* - * Return the first element or one past the last element. The - * _Node* we're returning is converted to an "iterator" by a - * constructor in _ListIterator. - */ - inline iterator begin() { - return iterator(mpMiddle->getNext()); - } - inline const_iterator begin() const { - return const_iterator(const_cast<_Node const*>(mpMiddle->getNext())); - } - inline iterator end() { - return iterator(mpMiddle); - } - inline const_iterator end() const { - return const_iterator(const_cast<_Node const*>(mpMiddle)); - } - - /* add the object to the head or tail of the list */ - void push_front(const T& val) { insert(begin(), val); } - void push_back(const T& val) { insert(end(), val); } - - /* insert before the current node; returns iterator at new node */ - iterator insert(iterator posn, const T& val) - { - _Node* newNode = new _Node(val); // alloc & copy-construct - newNode->setNext(posn.getNode()); - newNode->setPrev(posn.getNode()->getPrev()); - posn.getNode()->getPrev()->setNext(newNode); - posn.getNode()->setPrev(newNode); - return iterator(newNode); - } - - /* insert a range of elements before the current node */ - void insert(iterator posn, const_iterator first, const_iterator last) { - for ( ; first != last; ++first) - insert(posn, *first); - } - - /* remove one entry; returns iterator at next node */ - iterator erase(iterator posn) { - _Node* pNext = posn.getNode()->getNext(); - _Node* pPrev = posn.getNode()->getPrev(); - pPrev->setNext(pNext); - pNext->setPrev(pPrev); - delete posn.getNode(); - return iterator(pNext); - } - - /* remove a range of elements */ - iterator erase(iterator first, iterator last) { - while (first != last) - erase(first++); // don't erase than incr later! - return iterator(last); - } - - /* remove all contents of the list */ - void clear() { - _Node* pCurrent = mpMiddle->getNext(); - _Node* pNext; - - while (pCurrent != mpMiddle) { - pNext = pCurrent->getNext(); - delete pCurrent; - pCurrent = pNext; - } - mpMiddle->setPrev(mpMiddle); - mpMiddle->setNext(mpMiddle); - } - - /* - * Measure the distance between two iterators. On exist, "first" - * will be equal to "last". The iterators must refer to the same - * list. - * - * FIXME: This is actually a generic iterator function. It should be a - * template function at the top-level with specializations for things like - * vector<>, which can just do pointer math). Here we limit it to - * _ListIterator of the same type but different constness. - */ - template< - typename U, - template <class> class CL, - template <class> class CR - > - ptrdiff_t distance( - _ListIterator<U, CL> first, _ListIterator<U, CR> last) const - { - ptrdiff_t count = 0; - while (first != last) { - ++first; - ++count; - } - return count; - } - -private: - /* - * I want a _Node but don't need it to hold valid data. More - * to the point, I don't want T's constructor to fire, since it - * might have side-effects or require arguments. So, we do this - * slightly uncouth storage alloc. - */ - void prep() { - mpMiddle = (_Node*) new unsigned char[sizeof(_Node)]; - mpMiddle->setPrev(mpMiddle); - mpMiddle->setNext(mpMiddle); - } - - /* - * This node plays the role of "pointer to head" and "pointer to tail". - * It sits in the middle of a circular list of nodes. The iterator - * runs around the circle until it encounters this one. - */ - _Node* mpMiddle; -}; - -/* - * Assignment operator. - * - * The simplest way to do this would be to clear out the target list and - * fill it with the source. However, we can speed things along by - * re-using existing elements. - */ -template<class T> -List<T>& List<T>::operator=(const List<T>& right) -{ - if (this == &right) - return *this; // self-assignment - iterator firstDst = begin(); - iterator lastDst = end(); - const_iterator firstSrc = right.begin(); - const_iterator lastSrc = right.end(); - while (firstSrc != lastSrc && firstDst != lastDst) - *firstDst++ = *firstSrc++; - if (firstSrc == lastSrc) // ran out of elements in source? - erase(firstDst, lastDst); // yes, erase any extras - else - insert(lastDst, firstSrc, lastSrc); // copy remaining over - return *this; -} - -}; // namespace android - -#endif // _LIBS_UTILS_LIST_H diff --git a/include/utils/Log.h b/include/utils/Log.h deleted file mode 100644 index 4259c86..0000000 --- a/include/utils/Log.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2005 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. - */ - -// -// C/C++ logging functions. See the logging documentation for API details. -// -// We'd like these to be available from C code (in case we import some from -// somewhere), so this has a C interface. -// -// The output will be correct when the log file is shared between multiple -// threads and/or multiple processes so long as the operating system -// supports O_APPEND. These calls have mutex-protected data structures -// and so are NOT reentrant. Do not use LOG in a signal handler. -// -#ifndef _LIBS_UTILS_LOG_H -#define _LIBS_UTILS_LOG_H - -#include <cutils/log.h> -#include <sys/types.h> - -#ifdef __cplusplus - -namespace android { - -/* - * A very simple utility that yells in the log when an operation takes too long. - */ -class LogIfSlow { -public: - LogIfSlow(const char* tag, android_LogPriority priority, - int timeoutMillis, const char* message); - ~LogIfSlow(); - -private: - const char* const mTag; - const android_LogPriority mPriority; - const int mTimeoutMillis; - const char* const mMessage; - const int64_t mStart; -}; - -/* - * Writes the specified debug log message if this block takes longer than the - * specified number of milliseconds to run. Includes the time actually taken. - * - * { - * ALOGD_IF_SLOW(50, "Excessive delay doing something."); - * doSomething(); - * } - */ -#define ALOGD_IF_SLOW(timeoutMillis, message) \ - android::LogIfSlow _logIfSlow(LOG_TAG, ANDROID_LOG_DEBUG, timeoutMillis, message); - -} // namespace android - -#endif // __cplusplus - -#endif // _LIBS_UTILS_LOG_H diff --git a/include/utils/Looper.h b/include/utils/Looper.h deleted file mode 100644 index d4a0067..0000000 --- a/include/utils/Looper.h +++ /dev/null @@ -1,377 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef UTILS_LOOPER_H -#define UTILS_LOOPER_H - -#include <utils/threads.h> -#include <utils/RefBase.h> -#include <utils/KeyedVector.h> -#include <utils/Timers.h> - -#include <android/looper.h> - -#include <sys/epoll.h> - -/* - * Declare a concrete type for the NDK's looper forward declaration. - */ -struct ALooper { -}; - -namespace android { - -/** - * A message that can be posted to a Looper. - */ -struct Message { - Message() : what(0) { } - Message(int what) : what(what) { } - - /* The message type. (interpretation is left up to the handler) */ - int what; -}; - - -/** - * Interface for a Looper message handler. - * - * The Looper holds a strong reference to the message handler whenever it has - * a message to deliver to it. Make sure to call Looper::removeMessages - * to remove any pending messages destined for the handler so that the handler - * can be destroyed. - */ -class MessageHandler : public virtual RefBase { -protected: - virtual ~MessageHandler() { } - -public: - /** - * Handles a message. - */ - virtual void handleMessage(const Message& message) = 0; -}; - - -/** - * A simple proxy that holds a weak reference to a message handler. - */ -class WeakMessageHandler : public MessageHandler { -protected: - virtual ~WeakMessageHandler(); - -public: - WeakMessageHandler(const wp<MessageHandler>& handler); - virtual void handleMessage(const Message& message); - -private: - wp<MessageHandler> mHandler; -}; - - -/** - * A looper callback. - */ -class LooperCallback : public virtual RefBase { -protected: - virtual ~LooperCallback() { } - -public: - /** - * Handles a poll event for the given file descriptor. - * It is given the file descriptor it is associated with, - * a bitmask of the poll events that were triggered (typically ALOOPER_EVENT_INPUT), - * and the data pointer that was originally supplied. - * - * Implementations should return 1 to continue receiving callbacks, or 0 - * to have this file descriptor and callback unregistered from the looper. - */ - virtual int handleEvent(int fd, int events, void* data) = 0; -}; - - -/** - * Wraps a ALooper_callbackFunc function pointer. - */ -class SimpleLooperCallback : public LooperCallback { -protected: - virtual ~SimpleLooperCallback(); - -public: - SimpleLooperCallback(ALooper_callbackFunc callback); - virtual int handleEvent(int fd, int events, void* data); - -private: - ALooper_callbackFunc mCallback; -}; - - -/** - * A polling loop that supports monitoring file descriptor events, optionally - * using callbacks. The implementation uses epoll() internally. - * - * A looper can be associated with a thread although there is no requirement that it must be. - */ -class Looper : public ALooper, public RefBase { -protected: - virtual ~Looper(); - -public: - /** - * Creates a looper. - * - * If allowNonCallbaks is true, the looper will allow file descriptors to be - * registered without associated callbacks. This assumes that the caller of - * pollOnce() is prepared to handle callback-less events itself. - */ - Looper(bool allowNonCallbacks); - - /** - * Returns whether this looper instance allows the registration of file descriptors - * using identifiers instead of callbacks. - */ - bool getAllowNonCallbacks() const; - - /** - * Waits for events to be available, with optional timeout in milliseconds. - * Invokes callbacks for all file descriptors on which an event occurred. - * - * If the timeout is zero, returns immediately without blocking. - * If the timeout is negative, waits indefinitely until an event appears. - * - * Returns ALOOPER_POLL_WAKE if the poll was awoken using wake() before - * the timeout expired and no callbacks were invoked and no other file - * descriptors were ready. - * - * Returns ALOOPER_POLL_CALLBACK if one or more callbacks were invoked. - * - * Returns ALOOPER_POLL_TIMEOUT if there was no data before the given - * timeout expired. - * - * Returns ALOOPER_POLL_ERROR if an error occurred. - * - * Returns a value >= 0 containing an identifier if its file descriptor has data - * and it has no callback function (requiring the caller here to handle it). - * In this (and only this) case outFd, outEvents and outData will contain the poll - * events and data associated with the fd, otherwise they will be set to NULL. - * - * This method does not return until it has finished invoking the appropriate callbacks - * for all file descriptors that were signalled. - */ - int pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData); - inline int pollOnce(int timeoutMillis) { - return pollOnce(timeoutMillis, NULL, NULL, NULL); - } - - /** - * Like pollOnce(), but performs all pending callbacks until all - * data has been consumed or a file descriptor is available with no callback. - * This function will never return ALOOPER_POLL_CALLBACK. - */ - int pollAll(int timeoutMillis, int* outFd, int* outEvents, void** outData); - inline int pollAll(int timeoutMillis) { - return pollAll(timeoutMillis, NULL, NULL, NULL); - } - - /** - * Wakes the poll asynchronously. - * - * This method can be called on any thread. - * This method returns immediately. - */ - void wake(); - - /** - * Adds a new file descriptor to be polled by the looper. - * If the same file descriptor was previously added, it is replaced. - * - * "fd" is the file descriptor to be added. - * "ident" is an identifier for this event, which is returned from pollOnce(). - * The identifier must be >= 0, or ALOOPER_POLL_CALLBACK if providing a non-NULL callback. - * "events" are the poll events to wake up on. Typically this is ALOOPER_EVENT_INPUT. - * "callback" is the function to call when there is an event on the file descriptor. - * "data" is a private data pointer to supply to the callback. - * - * There are two main uses of this function: - * - * (1) If "callback" is non-NULL, then this function will be called when there is - * data on the file descriptor. It should execute any events it has pending, - * appropriately reading from the file descriptor. The 'ident' is ignored in this case. - * - * (2) If "callback" is NULL, the 'ident' will be returned by ALooper_pollOnce - * when its file descriptor has data available, requiring the caller to take - * care of processing it. - * - * Returns 1 if the file descriptor was added, 0 if the arguments were invalid. - * - * This method can be called on any thread. - * This method may block briefly if it needs to wake the poll. - * - * The callback may either be specified as a bare function pointer or as a smart - * pointer callback object. The smart pointer should be preferred because it is - * easier to avoid races when the callback is removed from a different thread. - * See removeFd() for details. - */ - int addFd(int fd, int ident, int events, ALooper_callbackFunc callback, void* data); - int addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data); - - /** - * Removes a previously added file descriptor from the looper. - * - * When this method returns, it is safe to close the file descriptor since the looper - * will no longer have a reference to it. However, it is possible for the callback to - * already be running or for it to run one last time if the file descriptor was already - * signalled. Calling code is responsible for ensuring that this case is safely handled. - * For example, if the callback takes care of removing itself during its own execution either - * by returning 0 or by calling this method, then it can be guaranteed to not be invoked - * again at any later time unless registered anew. - * - * A simple way to avoid this problem is to use the version of addFd() that takes - * a sp<LooperCallback> instead of a bare function pointer. The LooperCallback will - * be released at the appropriate time by the Looper. - * - * Returns 1 if the file descriptor was removed, 0 if none was previously registered. - * - * This method can be called on any thread. - * This method may block briefly if it needs to wake the poll. - */ - int removeFd(int fd); - - /** - * Enqueues a message to be processed by the specified handler. - * - * The handler must not be null. - * This method can be called on any thread. - */ - void sendMessage(const sp<MessageHandler>& handler, const Message& message); - - /** - * Enqueues a message to be processed by the specified handler after all pending messages - * after the specified delay. - * - * The time delay is specified in uptime nanoseconds. - * The handler must not be null. - * This method can be called on any thread. - */ - void sendMessageDelayed(nsecs_t uptimeDelay, const sp<MessageHandler>& handler, - const Message& message); - - /** - * Enqueues a message to be processed by the specified handler after all pending messages - * at the specified time. - * - * The time is specified in uptime nanoseconds. - * The handler must not be null. - * This method can be called on any thread. - */ - void sendMessageAtTime(nsecs_t uptime, const sp<MessageHandler>& handler, - const Message& message); - - /** - * Removes all messages for the specified handler from the queue. - * - * The handler must not be null. - * This method can be called on any thread. - */ - void removeMessages(const sp<MessageHandler>& handler); - - /** - * Removes all messages of a particular type for the specified handler from the queue. - * - * The handler must not be null. - * This method can be called on any thread. - */ - void removeMessages(const sp<MessageHandler>& handler, int what); - - /** - * Prepares a looper associated with the calling thread, and returns it. - * If the thread already has a looper, it is returned. Otherwise, a new - * one is created, associated with the thread, and returned. - * - * The opts may be ALOOPER_PREPARE_ALLOW_NON_CALLBACKS or 0. - */ - static sp<Looper> prepare(int opts); - - /** - * Sets the given looper to be associated with the calling thread. - * If another looper is already associated with the thread, it is replaced. - * - * If "looper" is NULL, removes the currently associated looper. - */ - static void setForThread(const sp<Looper>& looper); - - /** - * Returns the looper associated with the calling thread, or NULL if - * there is not one. - */ - static sp<Looper> getForThread(); - -private: - struct Request { - int fd; - int ident; - sp<LooperCallback> callback; - void* data; - }; - - struct Response { - int events; - Request request; - }; - - struct MessageEnvelope { - MessageEnvelope() : uptime(0) { } - - MessageEnvelope(nsecs_t uptime, const sp<MessageHandler> handler, - const Message& message) : uptime(uptime), handler(handler), message(message) { - } - - nsecs_t uptime; - sp<MessageHandler> handler; - Message message; - }; - - const bool mAllowNonCallbacks; // immutable - - int mWakeReadPipeFd; // immutable - int mWakeWritePipeFd; // immutable - Mutex mLock; - - Vector<MessageEnvelope> mMessageEnvelopes; // guarded by mLock - bool mSendingMessage; // guarded by mLock - - int mEpollFd; // immutable - - // Locked list of file descriptor monitoring requests. - KeyedVector<int, Request> mRequests; // guarded by mLock - - // This state is only used privately by pollOnce and does not require a lock since - // it runs on a single thread. - Vector<Response> mResponses; - size_t mResponseIndex; - nsecs_t mNextMessageUptime; // set to LLONG_MAX when none - - int pollInner(int timeoutMillis); - void awoken(); - void pushResponse(int events, const Request& request); - - static void initTLSKey(); - static void threadDestructor(void *st); -}; - -} // namespace android - -#endif // UTILS_LOOPER_H diff --git a/include/utils/LruCache.h b/include/utils/LruCache.h deleted file mode 100644 index 302b929..0000000 --- a/include/utils/LruCache.h +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Copyright (C) 2012 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_UTILS_LRU_CACHE_H -#define ANDROID_UTILS_LRU_CACHE_H - -#include <utils/BasicHashtable.h> -#include <utils/GenerationCache.h> -#include <utils/UniquePtr.h> - -namespace android { - -// OnEntryRemoved is defined in GenerationCache.h, but maybe should move here. - -template <typename TKey, typename TValue> -class LruCache { -public: - explicit LruCache(uint32_t maxCapacity); - - enum Capacity { - kUnlimitedCapacity, - }; - - void setOnEntryRemovedListener(OnEntryRemoved<TKey, TValue>* listener); - size_t size() const; - const TValue& get(const TKey& key); - bool put(const TKey& key, const TValue& value); - bool remove(const TKey& key); - bool removeOldest(); - void clear(); - - class Iterator { - public: - Iterator(const LruCache<TKey, TValue>& cache): mCache(cache), mIndex(-1) { - } - - bool next() { - mIndex = mCache.mTable->next(mIndex); - return mIndex != -1; - } - - size_t index() const { - return mIndex; - } - - const TValue& value() const { - return mCache.mTable->entryAt(mIndex).value; - } - - const TKey& key() const { - return mCache.mTable->entryAt(mIndex).key; - } - private: - const LruCache<TKey, TValue>& mCache; - size_t mIndex; - }; - -private: - LruCache(const LruCache& that); // disallow copy constructor - - struct Entry { - TKey key; - TValue value; - Entry* parent; - Entry* child; - - Entry(TKey key_, TValue value_) : key(key_), value(value_), parent(NULL), child(NULL) { - } - const TKey& getKey() const { return key; } - }; - - void attachToCache(Entry& entry); - void detachFromCache(Entry& entry); - void rehash(size_t newCapacity); - - UniquePtr<BasicHashtable<TKey, Entry> > mTable; - OnEntryRemoved<TKey, TValue>* mListener; - Entry* mOldest; - Entry* mYoungest; - uint32_t mMaxCapacity; - TValue mNullValue; -}; - -// Implementation is here, because it's fully templated -template <typename TKey, typename TValue> -LruCache<TKey, TValue>::LruCache(uint32_t maxCapacity): mMaxCapacity(maxCapacity), - mNullValue(NULL), mTable(new BasicHashtable<TKey, Entry>), mYoungest(NULL), mOldest(NULL), - mListener(NULL) { -}; - -template<typename K, typename V> -void LruCache<K, V>::setOnEntryRemovedListener(OnEntryRemoved<K, V>* listener) { - mListener = listener; -} - -template <typename TKey, typename TValue> -size_t LruCache<TKey, TValue>::size() const { - return mTable->size(); -} - -template <typename TKey, typename TValue> -const TValue& LruCache<TKey, TValue>::get(const TKey& key) { - hash_t hash = hash_type(key); - ssize_t index = mTable->find(-1, hash, key); - if (index == -1) { - return mNullValue; - } - Entry& entry = mTable->editEntryAt(index); - detachFromCache(entry); - attachToCache(entry); - return entry.value; -} - -template <typename TKey, typename TValue> -bool LruCache<TKey, TValue>::put(const TKey& key, const TValue& value) { - if (mMaxCapacity != kUnlimitedCapacity && size() >= mMaxCapacity) { - removeOldest(); - } - - hash_t hash = hash_type(key); - ssize_t index = mTable->find(-1, hash, key); - if (index >= 0) { - return false; - } - if (!mTable->hasMoreRoom()) { - rehash(mTable->capacity() * 2); - } - - // Would it be better to initialize a blank entry and assign key, value? - Entry initEntry(key, value); - index = mTable->add(hash, initEntry); - Entry& entry = mTable->editEntryAt(index); - attachToCache(entry); - return true; -} - -template <typename TKey, typename TValue> -bool LruCache<TKey, TValue>::remove(const TKey& key) { - hash_t hash = hash_type(key); - ssize_t index = mTable->find(-1, hash, key); - if (index < 0) { - return false; - } - Entry& entry = mTable->editEntryAt(index); - if (mListener) { - (*mListener)(entry.key, entry.value); - } - detachFromCache(entry); - mTable->removeAt(index); - return true; -} - -template <typename TKey, typename TValue> -bool LruCache<TKey, TValue>::removeOldest() { - if (mOldest != NULL) { - return remove(mOldest->key); - // TODO: should probably abort if false - } - return false; -} - -template <typename TKey, typename TValue> -void LruCache<TKey, TValue>::clear() { - if (mListener) { - for (Entry* p = mOldest; p != NULL; p = p->child) { - (*mListener)(p->key, p->value); - } - } - mYoungest = NULL; - mOldest = NULL; - mTable->clear(); -} - -template <typename TKey, typename TValue> -void LruCache<TKey, TValue>::attachToCache(Entry& entry) { - if (mYoungest == NULL) { - mYoungest = mOldest = &entry; - } else { - entry.parent = mYoungest; - mYoungest->child = &entry; - mYoungest = &entry; - } -} - -template <typename TKey, typename TValue> -void LruCache<TKey, TValue>::detachFromCache(Entry& entry) { - if (entry.parent != NULL) { - entry.parent->child = entry.child; - } else { - mOldest = entry.child; - } - if (entry.child != NULL) { - entry.child->parent = entry.parent; - } else { - mYoungest = entry.parent; - } - - entry.parent = NULL; - entry.child = NULL; -} - -template <typename TKey, typename TValue> -void LruCache<TKey, TValue>::rehash(size_t newCapacity) { - UniquePtr<BasicHashtable<TKey, Entry> > oldTable(mTable.release()); - Entry* oldest = mOldest; - - mOldest = NULL; - mYoungest = NULL; - mTable.reset(new BasicHashtable<TKey, Entry>(newCapacity)); - for (Entry* p = oldest; p != NULL; p = p->child) { - put(p->key, p->value); - } -} - -} - -#endif // ANDROID_UTILS_LRU_CACHE_H diff --git a/include/utils/Mutex.h b/include/utils/Mutex.h deleted file mode 100644 index dd201c8..0000000 --- a/include/utils/Mutex.h +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (C) 2007 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 _LIBS_UTILS_MUTEX_H -#define _LIBS_UTILS_MUTEX_H - -#include <stdint.h> -#include <sys/types.h> -#include <time.h> - -#if defined(HAVE_PTHREADS) -# include <pthread.h> -#endif - -#include <utils/Errors.h> - -// --------------------------------------------------------------------------- -namespace android { -// --------------------------------------------------------------------------- - -class Condition; - -/* - * Simple mutex class. The implementation is system-dependent. - * - * The mutex must be unlocked by the thread that locked it. They are not - * recursive, i.e. the same thread can't lock it multiple times. - */ -class Mutex { -public: - enum { - PRIVATE = 0, - SHARED = 1 - }; - - Mutex(); - Mutex(const char* name); - Mutex(int type, const char* name = NULL); - ~Mutex(); - - // lock or unlock the mutex - status_t lock(); - void unlock(); - - // lock if possible; returns 0 on success, error otherwise - status_t tryLock(); - - // Manages the mutex automatically. It'll be locked when Autolock is - // constructed and released when Autolock goes out of scope. - class Autolock { - public: - inline Autolock(Mutex& mutex) : mLock(mutex) { mLock.lock(); } - inline Autolock(Mutex* mutex) : mLock(*mutex) { mLock.lock(); } - inline ~Autolock() { mLock.unlock(); } - private: - Mutex& mLock; - }; - -private: - friend class Condition; - - // A mutex cannot be copied - Mutex(const Mutex&); - Mutex& operator = (const Mutex&); - -#if defined(HAVE_PTHREADS) - pthread_mutex_t mMutex; -#else - void _init(); - void* mState; -#endif -}; - -// --------------------------------------------------------------------------- - -#if defined(HAVE_PTHREADS) - -inline Mutex::Mutex() { - pthread_mutex_init(&mMutex, NULL); -} -inline Mutex::Mutex(__attribute__((unused)) const char* name) { - pthread_mutex_init(&mMutex, NULL); -} -inline Mutex::Mutex(int type, __attribute__((unused)) const char* name) { - if (type == SHARED) { - pthread_mutexattr_t attr; - pthread_mutexattr_init(&attr); - pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); - pthread_mutex_init(&mMutex, &attr); - pthread_mutexattr_destroy(&attr); - } else { - pthread_mutex_init(&mMutex, NULL); - } -} -inline Mutex::~Mutex() { - pthread_mutex_destroy(&mMutex); -} -inline status_t Mutex::lock() { - return -pthread_mutex_lock(&mMutex); -} -inline void Mutex::unlock() { - pthread_mutex_unlock(&mMutex); -} -inline status_t Mutex::tryLock() { - return -pthread_mutex_trylock(&mMutex); -} - -#endif // HAVE_PTHREADS - -// --------------------------------------------------------------------------- - -/* - * Automatic mutex. Declare one of these at the top of a function. - * When the function returns, it will go out of scope, and release the - * mutex. - */ - -typedef Mutex::Autolock AutoMutex; - -// --------------------------------------------------------------------------- -}; // namespace android -// --------------------------------------------------------------------------- - -#endif // _LIBS_UTILS_MUTEX_H diff --git a/include/utils/PropertyMap.h b/include/utils/PropertyMap.h deleted file mode 100644 index a9e674f..0000000 --- a/include/utils/PropertyMap.h +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _UTILS_PROPERTY_MAP_H -#define _UTILS_PROPERTY_MAP_H - -#include <utils/KeyedVector.h> -#include <utils/String8.h> -#include <utils/Errors.h> -#include <utils/Tokenizer.h> - -namespace android { - -/* - * Provides a mechanism for passing around string-based property key / value pairs - * and loading them from property files. - * - * The property files have the following simple structure: - * - * # Comment - * key = value - * - * Keys and values are any sequence of printable ASCII characters. - * The '=' separates the key from the value. - * The key and value may not contain whitespace. - * - * The '\' character is reserved for escape sequences and is not currently supported. - * The '"" character is reserved for quoting and is not currently supported. - * Files that contain the '\' or '"' character will fail to parse. - * - * The file must not contain duplicate keys. - * - * TODO Support escape sequences and quoted values when needed. - */ -class PropertyMap { -public: - /* Creates an empty property map. */ - PropertyMap(); - ~PropertyMap(); - - /* Clears the property map. */ - void clear(); - - /* Adds a property. - * Replaces the property with the same key if it is already present. - */ - void addProperty(const String8& key, const String8& value); - - /* Returns true if the property map contains the specified key. */ - bool hasProperty(const String8& key) const; - - /* Gets the value of a property and parses it. - * Returns true and sets outValue if the key was found and its value was parsed successfully. - * Otherwise returns false and does not modify outValue. (Also logs a warning.) - */ - bool tryGetProperty(const String8& key, String8& outValue) const; - bool tryGetProperty(const String8& key, bool& outValue) const; - bool tryGetProperty(const String8& key, int32_t& outValue) const; - bool tryGetProperty(const String8& key, float& outValue) const; - - /* Adds all values from the specified property map. */ - void addAll(const PropertyMap* map); - - /* Gets the underlying property map. */ - inline const KeyedVector<String8, String8>& getProperties() const { return mProperties; } - - /* Loads a property map from a file. */ - static status_t load(const String8& filename, PropertyMap** outMap); - -private: - class Parser { - PropertyMap* mMap; - Tokenizer* mTokenizer; - - public: - Parser(PropertyMap* map, Tokenizer* tokenizer); - ~Parser(); - status_t parse(); - - private: - status_t parseType(); - status_t parseKey(); - status_t parseKeyProperty(); - status_t parseModifier(const String8& token, int32_t* outMetaState); - status_t parseCharacterLiteral(char16_t* outCharacter); - }; - - KeyedVector<String8, String8> mProperties; -}; - -} // namespace android - -#endif // _UTILS_PROPERTY_MAP_H diff --git a/include/utils/RWLock.h b/include/utils/RWLock.h deleted file mode 100644 index 90beb5f..0000000 --- a/include/utils/RWLock.h +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (C) 2007 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 _LIBS_UTILS_RWLOCK_H -#define _LIBS_UTILS_RWLOCK_H - -#include <stdint.h> -#include <sys/types.h> - -#if defined(HAVE_PTHREADS) -# include <pthread.h> -#endif - -#include <utils/Errors.h> -#include <utils/ThreadDefs.h> - -// --------------------------------------------------------------------------- -namespace android { -// --------------------------------------------------------------------------- - -#if defined(HAVE_PTHREADS) - -/* - * Simple mutex class. The implementation is system-dependent. - * - * The mutex must be unlocked by the thread that locked it. They are not - * recursive, i.e. the same thread can't lock it multiple times. - */ -class RWLock { -public: - enum { - PRIVATE = 0, - SHARED = 1 - }; - - RWLock(); - RWLock(const char* name); - RWLock(int type, const char* name = NULL); - ~RWLock(); - - status_t readLock(); - status_t tryReadLock(); - status_t writeLock(); - status_t tryWriteLock(); - void unlock(); - - class AutoRLock { - public: - inline AutoRLock(RWLock& rwlock) : mLock(rwlock) { mLock.readLock(); } - inline ~AutoRLock() { mLock.unlock(); } - private: - RWLock& mLock; - }; - - class AutoWLock { - public: - inline AutoWLock(RWLock& rwlock) : mLock(rwlock) { mLock.writeLock(); } - inline ~AutoWLock() { mLock.unlock(); } - private: - RWLock& mLock; - }; - -private: - // A RWLock cannot be copied - RWLock(const RWLock&); - RWLock& operator = (const RWLock&); - - pthread_rwlock_t mRWLock; -}; - -inline RWLock::RWLock() { - pthread_rwlock_init(&mRWLock, NULL); -} -inline RWLock::RWLock(__attribute__((unused)) const char* name) { - pthread_rwlock_init(&mRWLock, NULL); -} -inline RWLock::RWLock(int type, __attribute__((unused)) const char* name) { - if (type == SHARED) { - pthread_rwlockattr_t attr; - pthread_rwlockattr_init(&attr); - pthread_rwlockattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); - pthread_rwlock_init(&mRWLock, &attr); - pthread_rwlockattr_destroy(&attr); - } else { - pthread_rwlock_init(&mRWLock, NULL); - } -} -inline RWLock::~RWLock() { - pthread_rwlock_destroy(&mRWLock); -} -inline status_t RWLock::readLock() { - return -pthread_rwlock_rdlock(&mRWLock); -} -inline status_t RWLock::tryReadLock() { - return -pthread_rwlock_tryrdlock(&mRWLock); -} -inline status_t RWLock::writeLock() { - return -pthread_rwlock_wrlock(&mRWLock); -} -inline status_t RWLock::tryWriteLock() { - return -pthread_rwlock_trywrlock(&mRWLock); -} -inline void RWLock::unlock() { - pthread_rwlock_unlock(&mRWLock); -} - -#endif // HAVE_PTHREADS - -// --------------------------------------------------------------------------- -}; // namespace android -// --------------------------------------------------------------------------- - -#endif // _LIBS_UTILS_RWLOCK_H diff --git a/include/utils/RefBase.h b/include/utils/RefBase.h deleted file mode 100644 index 033fe67..0000000 --- a/include/utils/RefBase.h +++ /dev/null @@ -1,546 +0,0 @@ -/* - * Copyright (C) 2005 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_REF_BASE_H -#define ANDROID_REF_BASE_H - -#include <cutils/atomic.h> - -#include <stdint.h> -#include <sys/types.h> -#include <stdlib.h> -#include <string.h> - -#include <utils/StrongPointer.h> -#include <utils/TypeHelpers.h> - -// --------------------------------------------------------------------------- -namespace android { - -class TextOutput; -TextOutput& printWeakPointer(TextOutput& to, const void* val); - -// --------------------------------------------------------------------------- - -#define COMPARE_WEAK(_op_) \ -inline bool operator _op_ (const sp<T>& o) const { \ - return m_ptr _op_ o.m_ptr; \ -} \ -inline bool operator _op_ (const T* o) const { \ - return m_ptr _op_ o; \ -} \ -template<typename U> \ -inline bool operator _op_ (const sp<U>& o) const { \ - return m_ptr _op_ o.m_ptr; \ -} \ -template<typename U> \ -inline bool operator _op_ (const U* o) const { \ - return m_ptr _op_ o; \ -} - -// --------------------------------------------------------------------------- - -class ReferenceRenamer { -protected: - // destructor is purposedly not virtual so we avoid code overhead from - // subclasses; we have to make it protected to guarantee that it - // cannot be called from this base class (and to make strict compilers - // happy). - ~ReferenceRenamer() { } -public: - virtual void operator()(size_t i) const = 0; -}; - -// --------------------------------------------------------------------------- - -class RefBase -{ -public: - void incStrong(const void* id) const; - void decStrong(const void* id) const; - - void forceIncStrong(const void* id) const; - - //! DEBUGGING ONLY: Get current strong ref count. - int32_t getStrongCount() const; - - class weakref_type - { - public: - RefBase* refBase() const; - - void incWeak(const void* id); - void decWeak(const void* id); - - // acquires a strong reference if there is already one. - bool attemptIncStrong(const void* id); - - // acquires a weak reference if there is already one. - // This is not always safe. see ProcessState.cpp and BpBinder.cpp - // for proper use. - bool attemptIncWeak(const void* id); - - //! DEBUGGING ONLY: Get current weak ref count. - int32_t getWeakCount() const; - - //! DEBUGGING ONLY: Print references held on object. - void printRefs() const; - - //! DEBUGGING ONLY: Enable tracking for this object. - // enable -- enable/disable tracking - // retain -- when tracking is enable, if true, then we save a stack trace - // for each reference and dereference; when retain == false, we - // match up references and dereferences and keep only the - // outstanding ones. - - void trackMe(bool enable, bool retain); - }; - - weakref_type* createWeak(const void* id) const; - - weakref_type* getWeakRefs() const; - - //! DEBUGGING ONLY: Print references held on object. - inline void printRefs() const { getWeakRefs()->printRefs(); } - - //! DEBUGGING ONLY: Enable tracking of object. - inline void trackMe(bool enable, bool retain) - { - getWeakRefs()->trackMe(enable, retain); - } - - typedef RefBase basetype; - -protected: - RefBase(); - virtual ~RefBase(); - - //! Flags for extendObjectLifetime() - enum { - OBJECT_LIFETIME_STRONG = 0x0000, - OBJECT_LIFETIME_WEAK = 0x0001, - OBJECT_LIFETIME_MASK = 0x0001 - }; - - void extendObjectLifetime(int32_t mode); - - //! Flags for onIncStrongAttempted() - enum { - FIRST_INC_STRONG = 0x0001 - }; - - virtual void onFirstRef(); - virtual void onLastStrongRef(const void* id); - virtual bool onIncStrongAttempted(uint32_t flags, const void* id); - virtual void onLastWeakRef(const void* id); - -private: - friend class weakref_type; - class weakref_impl; - - RefBase(const RefBase& o); - RefBase& operator=(const RefBase& o); - -private: - friend class ReferenceMover; - - static void renameRefs(size_t n, const ReferenceRenamer& renamer); - - static void renameRefId(weakref_type* ref, - const void* old_id, const void* new_id); - - static void renameRefId(RefBase* ref, - const void* old_id, const void* new_id); - - weakref_impl* const mRefs; -}; - -// --------------------------------------------------------------------------- - -template <class T> -class LightRefBase -{ -public: - inline LightRefBase() : mCount(0) { } - inline void incStrong(__attribute__((unused)) const void* id) const { - android_atomic_inc(&mCount); - } - inline void decStrong(__attribute__((unused)) const void* id) const { - if (android_atomic_dec(&mCount) == 1) { - delete static_cast<const T*>(this); - } - } - //! DEBUGGING ONLY: Get current strong ref count. - inline int32_t getStrongCount() const { - return mCount; - } - - typedef LightRefBase<T> basetype; - -protected: - inline ~LightRefBase() { } - -private: - friend class ReferenceMover; - inline static void renameRefs(size_t n, const ReferenceRenamer& renamer) { } - inline static void renameRefId(T* ref, - const void* old_id, const void* new_id) { } - -private: - mutable volatile int32_t mCount; -}; - -// --------------------------------------------------------------------------- - -template <typename T> -class wp -{ -public: - typedef typename RefBase::weakref_type weakref_type; - - inline wp() : m_ptr(0) { } - - wp(T* other); - wp(const wp<T>& other); - wp(const sp<T>& other); - template<typename U> wp(U* other); - template<typename U> wp(const sp<U>& other); - template<typename U> wp(const wp<U>& other); - - ~wp(); - - // Assignment - - wp& operator = (T* other); - wp& operator = (const wp<T>& other); - wp& operator = (const sp<T>& other); - - template<typename U> wp& operator = (U* other); - template<typename U> wp& operator = (const wp<U>& other); - template<typename U> wp& operator = (const sp<U>& other); - - void set_object_and_refs(T* other, weakref_type* refs); - - // promotion to sp - - sp<T> promote() const; - - // Reset - - void clear(); - - // Accessors - - inline weakref_type* get_refs() const { return m_refs; } - - inline T* unsafe_get() const { return m_ptr; } - - // Operators - - COMPARE_WEAK(==) - COMPARE_WEAK(!=) - COMPARE_WEAK(>) - COMPARE_WEAK(<) - COMPARE_WEAK(<=) - COMPARE_WEAK(>=) - - inline bool operator == (const wp<T>& o) const { - return (m_ptr == o.m_ptr) && (m_refs == o.m_refs); - } - template<typename U> - inline bool operator == (const wp<U>& o) const { - return m_ptr == o.m_ptr; - } - - inline bool operator > (const wp<T>& o) const { - return (m_ptr == o.m_ptr) ? (m_refs > o.m_refs) : (m_ptr > o.m_ptr); - } - template<typename U> - inline bool operator > (const wp<U>& o) const { - return (m_ptr == o.m_ptr) ? (m_refs > o.m_refs) : (m_ptr > o.m_ptr); - } - - inline bool operator < (const wp<T>& o) const { - return (m_ptr == o.m_ptr) ? (m_refs < o.m_refs) : (m_ptr < o.m_ptr); - } - template<typename U> - inline bool operator < (const wp<U>& o) const { - return (m_ptr == o.m_ptr) ? (m_refs < o.m_refs) : (m_ptr < o.m_ptr); - } - inline bool operator != (const wp<T>& o) const { return m_refs != o.m_refs; } - template<typename U> inline bool operator != (const wp<U>& o) const { return !operator == (o); } - inline bool operator <= (const wp<T>& o) const { return !operator > (o); } - template<typename U> inline bool operator <= (const wp<U>& o) const { return !operator > (o); } - inline bool operator >= (const wp<T>& o) const { return !operator < (o); } - template<typename U> inline bool operator >= (const wp<U>& o) const { return !operator < (o); } - -private: - template<typename Y> friend class sp; - template<typename Y> friend class wp; - - T* m_ptr; - weakref_type* m_refs; -}; - -template <typename T> -TextOutput& operator<<(TextOutput& to, const wp<T>& val); - -#undef COMPARE_WEAK - -// --------------------------------------------------------------------------- -// No user serviceable parts below here. - -template<typename T> -wp<T>::wp(T* other) - : m_ptr(other) -{ - if (other) m_refs = other->createWeak(this); -} - -template<typename T> -wp<T>::wp(const wp<T>& other) - : m_ptr(other.m_ptr), m_refs(other.m_refs) -{ - if (m_ptr) m_refs->incWeak(this); -} - -template<typename T> -wp<T>::wp(const sp<T>& other) - : m_ptr(other.m_ptr) -{ - if (m_ptr) { - m_refs = m_ptr->createWeak(this); - } -} - -template<typename T> template<typename U> -wp<T>::wp(U* other) - : m_ptr(other) -{ - if (other) m_refs = other->createWeak(this); -} - -template<typename T> template<typename U> -wp<T>::wp(const wp<U>& other) - : m_ptr(other.m_ptr) -{ - if (m_ptr) { - m_refs = other.m_refs; - m_refs->incWeak(this); - } -} - -template<typename T> template<typename U> -wp<T>::wp(const sp<U>& other) - : m_ptr(other.m_ptr) -{ - if (m_ptr) { - m_refs = m_ptr->createWeak(this); - } -} - -template<typename T> -wp<T>::~wp() -{ - if (m_ptr) m_refs->decWeak(this); -} - -template<typename T> -wp<T>& wp<T>::operator = (T* other) -{ - weakref_type* newRefs = - other ? other->createWeak(this) : 0; - if (m_ptr) m_refs->decWeak(this); - m_ptr = other; - m_refs = newRefs; - return *this; -} - -template<typename T> -wp<T>& wp<T>::operator = (const wp<T>& other) -{ - weakref_type* otherRefs(other.m_refs); - T* otherPtr(other.m_ptr); - if (otherPtr) otherRefs->incWeak(this); - if (m_ptr) m_refs->decWeak(this); - m_ptr = otherPtr; - m_refs = otherRefs; - return *this; -} - -template<typename T> -wp<T>& wp<T>::operator = (const sp<T>& other) -{ - weakref_type* newRefs = - other != NULL ? other->createWeak(this) : 0; - T* otherPtr(other.m_ptr); - if (m_ptr) m_refs->decWeak(this); - m_ptr = otherPtr; - m_refs = newRefs; - return *this; -} - -template<typename T> template<typename U> -wp<T>& wp<T>::operator = (U* other) -{ - weakref_type* newRefs = - other ? other->createWeak(this) : 0; - if (m_ptr) m_refs->decWeak(this); - m_ptr = other; - m_refs = newRefs; - return *this; -} - -template<typename T> template<typename U> -wp<T>& wp<T>::operator = (const wp<U>& other) -{ - weakref_type* otherRefs(other.m_refs); - U* otherPtr(other.m_ptr); - if (otherPtr) otherRefs->incWeak(this); - if (m_ptr) m_refs->decWeak(this); - m_ptr = otherPtr; - m_refs = otherRefs; - return *this; -} - -template<typename T> template<typename U> -wp<T>& wp<T>::operator = (const sp<U>& other) -{ - weakref_type* newRefs = - other != NULL ? other->createWeak(this) : 0; - U* otherPtr(other.m_ptr); - if (m_ptr) m_refs->decWeak(this); - m_ptr = otherPtr; - m_refs = newRefs; - return *this; -} - -template<typename T> -void wp<T>::set_object_and_refs(T* other, weakref_type* refs) -{ - if (other) refs->incWeak(this); - if (m_ptr) m_refs->decWeak(this); - m_ptr = other; - m_refs = refs; -} - -template<typename T> -sp<T> wp<T>::promote() const -{ - sp<T> result; - if (m_ptr && m_refs->attemptIncStrong(&result)) { - result.set_pointer(m_ptr); - } - return result; -} - -template<typename T> -void wp<T>::clear() -{ - if (m_ptr) { - m_refs->decWeak(this); - m_ptr = 0; - } -} - -template <typename T> -inline TextOutput& operator<<(TextOutput& to, const wp<T>& val) -{ - return printWeakPointer(to, val.unsafe_get()); -} - -// --------------------------------------------------------------------------- - -// this class just serves as a namespace so TYPE::moveReferences can stay -// private. -class ReferenceMover { -public: - // it would be nice if we could make sure no extra code is generated - // for sp<TYPE> or wp<TYPE> when TYPE is a descendant of RefBase: - // Using a sp<RefBase> override doesn't work; it's a bit like we wanted - // a template<typename TYPE inherits RefBase> template... - - template<typename TYPE> static inline - void move_references(sp<TYPE>* d, sp<TYPE> const* s, size_t n) { - - class Renamer : public ReferenceRenamer { - sp<TYPE>* d; - sp<TYPE> const* s; - virtual void operator()(size_t i) const { - // The id are known to be the sp<>'s this pointer - TYPE::renameRefId(d[i].get(), &s[i], &d[i]); - } - public: - Renamer(sp<TYPE>* d, sp<TYPE> const* s) : s(s), d(d) { } - }; - - memmove(d, s, n*sizeof(sp<TYPE>)); - TYPE::renameRefs(n, Renamer(d, s)); - } - - - template<typename TYPE> static inline - void move_references(wp<TYPE>* d, wp<TYPE> const* s, size_t n) { - - class Renamer : public ReferenceRenamer { - wp<TYPE>* d; - wp<TYPE> const* s; - virtual void operator()(size_t i) const { - // The id are known to be the wp<>'s this pointer - TYPE::renameRefId(d[i].get_refs(), &s[i], &d[i]); - } - public: - Renamer(wp<TYPE>* d, wp<TYPE> const* s) : s(s), d(d) { } - }; - - memmove(d, s, n*sizeof(wp<TYPE>)); - TYPE::renameRefs(n, Renamer(d, s)); - } -}; - -// specialization for moving sp<> and wp<> types. -// these are used by the [Sorted|Keyed]Vector<> implementations -// sp<> and wp<> need to be handled specially, because they do not -// have trivial copy operation in the general case (see RefBase.cpp -// when DEBUG ops are enabled), but can be implemented very -// efficiently in most cases. - -template<typename TYPE> inline -void move_forward_type(sp<TYPE>* d, sp<TYPE> const* s, size_t n) { - ReferenceMover::move_references(d, s, n); -} - -template<typename TYPE> inline -void move_backward_type(sp<TYPE>* d, sp<TYPE> const* s, size_t n) { - ReferenceMover::move_references(d, s, n); -} - -template<typename TYPE> inline -void move_forward_type(wp<TYPE>* d, wp<TYPE> const* s, size_t n) { - ReferenceMover::move_references(d, s, n); -} - -template<typename TYPE> inline -void move_backward_type(wp<TYPE>* d, wp<TYPE> const* s, size_t n) { - ReferenceMover::move_references(d, s, n); -} - - -}; // namespace android - -// --------------------------------------------------------------------------- - -#endif // ANDROID_REF_BASE_H diff --git a/include/utils/SharedBuffer.h b/include/utils/SharedBuffer.h deleted file mode 100644 index b670953..0000000 --- a/include/utils/SharedBuffer.h +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (C) 2005 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_SHARED_BUFFER_H -#define ANDROID_SHARED_BUFFER_H - -#include <stdint.h> -#include <sys/types.h> - -// --------------------------------------------------------------------------- - -namespace android { - -class SharedBuffer -{ -public: - - /* flags to use with release() */ - enum { - eKeepStorage = 0x00000001 - }; - - /*! allocate a buffer of size 'size' and acquire() it. - * call release() to free it. - */ - static SharedBuffer* alloc(size_t size); - - /*! free the memory associated with the SharedBuffer. - * Fails if there are any users associated with this SharedBuffer. - * In other words, the buffer must have been release by all its - * users. - */ - static ssize_t dealloc(const SharedBuffer* released); - - //! access the data for read - inline const void* data() const; - - //! access the data for read/write - inline void* data(); - - //! get size of the buffer - inline size_t size() const; - - //! get back a SharedBuffer object from its data - static inline SharedBuffer* bufferFromData(void* data); - - //! get back a SharedBuffer object from its data - static inline const SharedBuffer* bufferFromData(const void* data); - - //! get the size of a SharedBuffer object from its data - static inline size_t sizeFromData(const void* data); - - //! edit the buffer (get a writtable, or non-const, version of it) - SharedBuffer* edit() const; - - //! edit the buffer, resizing if needed - SharedBuffer* editResize(size_t size) const; - - //! like edit() but fails if a copy is required - SharedBuffer* attemptEdit() const; - - //! resize and edit the buffer, loose it's content. - SharedBuffer* reset(size_t size) const; - - //! acquire/release a reference on this buffer - void acquire() const; - - /*! release a reference on this buffer, with the option of not - * freeing the memory associated with it if it was the last reference - * returns the previous reference count - */ - int32_t release(uint32_t flags = 0) const; - - //! returns wether or not we're the only owner - inline bool onlyOwner() const; - - -private: - inline SharedBuffer() { } - inline ~SharedBuffer() { } - SharedBuffer(const SharedBuffer&); - SharedBuffer& operator = (const SharedBuffer&); - - // 16 bytes. must be sized to preserve correct alignment. - mutable int32_t mRefs; - size_t mSize; - uint32_t mReserved[2]; -}; - -// --------------------------------------------------------------------------- - -const void* SharedBuffer::data() const { - return this + 1; -} - -void* SharedBuffer::data() { - return this + 1; -} - -size_t SharedBuffer::size() const { - return mSize; -} - -SharedBuffer* SharedBuffer::bufferFromData(void* data) { - return data ? static_cast<SharedBuffer *>(data)-1 : 0; -} - -const SharedBuffer* SharedBuffer::bufferFromData(const void* data) { - return data ? static_cast<const SharedBuffer *>(data)-1 : 0; -} - -size_t SharedBuffer::sizeFromData(const void* data) { - return data ? bufferFromData(data)->mSize : 0; -} - -bool SharedBuffer::onlyOwner() const { - return (mRefs == 1); -} - -}; // namespace android - -// --------------------------------------------------------------------------- - -#endif // ANDROID_VECTOR_H diff --git a/include/utils/Singleton.h b/include/utils/Singleton.h deleted file mode 100644 index c60680e..0000000 --- a/include/utils/Singleton.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2007 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_UTILS_SINGLETON_H -#define ANDROID_UTILS_SINGLETON_H - -#include <stdint.h> -#include <sys/types.h> -#include <utils/threads.h> -#include <cutils/compiler.h> - -namespace android { -// --------------------------------------------------------------------------- - -template <typename TYPE> -class ANDROID_API Singleton -{ -public: - static TYPE& getInstance() { - Mutex::Autolock _l(sLock); - TYPE* instance = sInstance; - if (instance == 0) { - instance = new TYPE(); - sInstance = instance; - } - return *instance; - } - - static bool hasInstance() { - Mutex::Autolock _l(sLock); - return sInstance != 0; - } - -protected: - ~Singleton() { }; - Singleton() { }; - -private: - Singleton(const Singleton&); - Singleton& operator = (const Singleton&); - static Mutex sLock; - static TYPE* sInstance; -}; - -/* - * use ANDROID_SINGLETON_STATIC_INSTANCE(TYPE) in your implementation file - * (eg: <TYPE>.cpp) to create the static instance of Singleton<>'s attributes, - * and avoid to have a copy of them in each compilation units Singleton<TYPE> - * is used. - * NOTE: we use a version of Mutex ctor that takes a parameter, because - * for some unknown reason using the default ctor doesn't emit the variable! - */ - -#define ANDROID_SINGLETON_STATIC_INSTANCE(TYPE) \ - template<> Mutex Singleton< TYPE >::sLock(Mutex::PRIVATE); \ - template<> TYPE* Singleton< TYPE >::sInstance(0); \ - template class Singleton< TYPE >; - - -// --------------------------------------------------------------------------- -}; // namespace android - -#endif // ANDROID_UTILS_SINGLETON_H - diff --git a/include/utils/SortedVector.h b/include/utils/SortedVector.h deleted file mode 100644 index 2d3e82a..0000000 --- a/include/utils/SortedVector.h +++ /dev/null @@ -1,282 +0,0 @@ -/* - * Copyright (C) 2005 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_SORTED_VECTOR_H -#define ANDROID_SORTED_VECTOR_H - -#include <assert.h> -#include <stdint.h> -#include <sys/types.h> - -#include <cutils/log.h> - -#include <utils/Vector.h> -#include <utils/VectorImpl.h> -#include <utils/TypeHelpers.h> - -// --------------------------------------------------------------------------- - -namespace android { - -template <class TYPE> -class SortedVector : private SortedVectorImpl -{ - friend class Vector<TYPE>; - -public: - typedef TYPE value_type; - - /*! - * Constructors and destructors - */ - - SortedVector(); - SortedVector(const SortedVector<TYPE>& rhs); - virtual ~SortedVector(); - - /*! copy operator */ - const SortedVector<TYPE>& operator = (const SortedVector<TYPE>& rhs) const; - SortedVector<TYPE>& operator = (const SortedVector<TYPE>& rhs); - - /* - * empty the vector - */ - - inline void clear() { VectorImpl::clear(); } - - /*! - * vector stats - */ - - //! returns number of items in the vector - inline size_t size() const { return VectorImpl::size(); } - //! returns whether or not the vector is empty - inline bool isEmpty() const { return VectorImpl::isEmpty(); } - //! returns how many items can be stored without reallocating the backing store - inline size_t capacity() const { return VectorImpl::capacity(); } - //! sets the capacity. capacity can never be reduced less than size() - inline ssize_t setCapacity(size_t size) { return VectorImpl::setCapacity(size); } - - /*! - * C-style array access - */ - - //! read-only C-style access - inline const TYPE* array() const; - - //! read-write C-style access. BE VERY CAREFUL when modifying the array - //! you must keep it sorted! You usually don't use this function. - TYPE* editArray(); - - //! finds the index of an item - ssize_t indexOf(const TYPE& item) const; - - //! finds where this item should be inserted - size_t orderOf(const TYPE& item) const; - - - /*! - * accessors - */ - - //! read-only access to an item at a given index - inline const TYPE& operator [] (size_t index) const; - //! alternate name for operator [] - inline const TYPE& itemAt(size_t index) const; - //! stack-usage of the vector. returns the top of the stack (last element) - const TYPE& top() const; - - /*! - * modifying the array - */ - - //! add an item in the right place (and replace the one that is there) - ssize_t add(const TYPE& item); - - //! editItemAt() MUST NOT change the order of this item - TYPE& editItemAt(size_t index) { - return *( static_cast<TYPE *>(VectorImpl::editItemLocation(index)) ); - } - - //! merges a vector into this one - ssize_t merge(const Vector<TYPE>& vector); - ssize_t merge(const SortedVector<TYPE>& vector); - - //! removes an item - ssize_t remove(const TYPE&); - - //! remove several items - inline ssize_t removeItemsAt(size_t index, size_t count = 1); - //! remove one item - inline ssize_t removeAt(size_t index) { return removeItemsAt(index); } - -protected: - virtual void do_construct(void* storage, size_t num) const; - virtual void do_destroy(void* storage, size_t num) const; - virtual void do_copy(void* dest, const void* from, size_t num) const; - virtual void do_splat(void* dest, const void* item, size_t num) const; - virtual void do_move_forward(void* dest, const void* from, size_t num) const; - virtual void do_move_backward(void* dest, const void* from, size_t num) const; - virtual int do_compare(const void* lhs, const void* rhs) const; -}; - -// SortedVector<T> can be trivially moved using memcpy() because moving does not -// require any change to the underlying SharedBuffer contents or reference count. -template<typename T> struct trait_trivial_move<SortedVector<T> > { enum { value = true }; }; - -// --------------------------------------------------------------------------- -// No user serviceable parts from here... -// --------------------------------------------------------------------------- - -template<class TYPE> inline -SortedVector<TYPE>::SortedVector() - : SortedVectorImpl(sizeof(TYPE), - ((traits<TYPE>::has_trivial_ctor ? HAS_TRIVIAL_CTOR : 0) - |(traits<TYPE>::has_trivial_dtor ? HAS_TRIVIAL_DTOR : 0) - |(traits<TYPE>::has_trivial_copy ? HAS_TRIVIAL_COPY : 0)) - ) -{ -} - -template<class TYPE> inline -SortedVector<TYPE>::SortedVector(const SortedVector<TYPE>& rhs) - : SortedVectorImpl(rhs) { -} - -template<class TYPE> inline -SortedVector<TYPE>::~SortedVector() { - finish_vector(); -} - -template<class TYPE> inline -SortedVector<TYPE>& SortedVector<TYPE>::operator = (const SortedVector<TYPE>& rhs) { - SortedVectorImpl::operator = (rhs); - return *this; -} - -template<class TYPE> inline -const SortedVector<TYPE>& SortedVector<TYPE>::operator = (const SortedVector<TYPE>& rhs) const { - SortedVectorImpl::operator = (rhs); - return *this; -} - -template<class TYPE> inline -const TYPE* SortedVector<TYPE>::array() const { - return static_cast<const TYPE *>(arrayImpl()); -} - -template<class TYPE> inline -TYPE* SortedVector<TYPE>::editArray() { - return static_cast<TYPE *>(editArrayImpl()); -} - - -template<class TYPE> inline -const TYPE& SortedVector<TYPE>::operator[](size_t index) const { - LOG_FATAL_IF(index>=size(), - "%s: index=%u out of range (%u)", __PRETTY_FUNCTION__, - int(index), int(size())); - return *(array() + index); -} - -template<class TYPE> inline -const TYPE& SortedVector<TYPE>::itemAt(size_t index) const { - return operator[](index); -} - -template<class TYPE> inline -const TYPE& SortedVector<TYPE>::top() const { - return *(array() + size() - 1); -} - -template<class TYPE> inline -ssize_t SortedVector<TYPE>::add(const TYPE& item) { - return SortedVectorImpl::add(&item); -} - -template<class TYPE> inline -ssize_t SortedVector<TYPE>::indexOf(const TYPE& item) const { - return SortedVectorImpl::indexOf(&item); -} - -template<class TYPE> inline -size_t SortedVector<TYPE>::orderOf(const TYPE& item) const { - return SortedVectorImpl::orderOf(&item); -} - -template<class TYPE> inline -ssize_t SortedVector<TYPE>::merge(const Vector<TYPE>& vector) { - return SortedVectorImpl::merge(reinterpret_cast<const VectorImpl&>(vector)); -} - -template<class TYPE> inline -ssize_t SortedVector<TYPE>::merge(const SortedVector<TYPE>& vector) { - return SortedVectorImpl::merge(reinterpret_cast<const SortedVectorImpl&>(vector)); -} - -template<class TYPE> inline -ssize_t SortedVector<TYPE>::remove(const TYPE& item) { - return SortedVectorImpl::remove(&item); -} - -template<class TYPE> inline -ssize_t SortedVector<TYPE>::removeItemsAt(size_t index, size_t count) { - return VectorImpl::removeItemsAt(index, count); -} - -// --------------------------------------------------------------------------- - -template<class TYPE> -void SortedVector<TYPE>::do_construct(void* storage, size_t num) const { - construct_type( reinterpret_cast<TYPE*>(storage), num ); -} - -template<class TYPE> -void SortedVector<TYPE>::do_destroy(void* storage, size_t num) const { - destroy_type( reinterpret_cast<TYPE*>(storage), num ); -} - -template<class TYPE> -void SortedVector<TYPE>::do_copy(void* dest, const void* from, size_t num) const { - copy_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num ); -} - -template<class TYPE> -void SortedVector<TYPE>::do_splat(void* dest, const void* item, size_t num) const { - splat_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(item), num ); -} - -template<class TYPE> -void SortedVector<TYPE>::do_move_forward(void* dest, const void* from, size_t num) const { - move_forward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num ); -} - -template<class TYPE> -void SortedVector<TYPE>::do_move_backward(void* dest, const void* from, size_t num) const { - move_backward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num ); -} - -template<class TYPE> -int SortedVector<TYPE>::do_compare(const void* lhs, const void* rhs) const { - return compare_type( *reinterpret_cast<const TYPE*>(lhs), *reinterpret_cast<const TYPE*>(rhs) ); -} - -}; // namespace android - - -// --------------------------------------------------------------------------- - -#endif // ANDROID_SORTED_VECTOR_H diff --git a/include/utils/String16.h b/include/utils/String16.h deleted file mode 100644 index fe06c57..0000000 --- a/include/utils/String16.h +++ /dev/null @@ -1,243 +0,0 @@ -/* - * Copyright (C) 2005 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_STRING16_H -#define ANDROID_STRING16_H - -#include <utils/Errors.h> -#include <utils/SharedBuffer.h> -#include <utils/Unicode.h> -#include <utils/TypeHelpers.h> - -// --------------------------------------------------------------------------- - -extern "C" { - -} - -// --------------------------------------------------------------------------- - -namespace android { - -// --------------------------------------------------------------------------- - -class String8; -class TextOutput; - -//! This is a string holding UTF-16 characters. -class String16 -{ -public: - String16(); - String16(const String16& o); - String16(const String16& o, - size_t len, - size_t begin=0); - explicit String16(const char16_t* o); - explicit String16(const char16_t* o, size_t len); - explicit String16(const String8& o); - explicit String16(const char* o); - explicit String16(const char* o, size_t len); - - ~String16(); - - inline const char16_t* string() const; - inline size_t size() const; - - inline const SharedBuffer* sharedBuffer() const; - - void setTo(const String16& other); - status_t setTo(const char16_t* other); - status_t setTo(const char16_t* other, size_t len); - status_t setTo(const String16& other, - size_t len, - size_t begin=0); - - status_t append(const String16& other); - status_t append(const char16_t* other, size_t len); - - inline String16& operator=(const String16& other); - - inline String16& operator+=(const String16& other); - inline String16 operator+(const String16& other) const; - - status_t insert(size_t pos, const char16_t* chrs); - status_t insert(size_t pos, - const char16_t* chrs, size_t len); - - ssize_t findFirst(char16_t c) const; - ssize_t findLast(char16_t c) const; - - bool startsWith(const String16& prefix) const; - bool startsWith(const char16_t* prefix) const; - - status_t makeLower(); - - status_t replaceAll(char16_t replaceThis, - char16_t withThis); - - status_t remove(size_t len, size_t begin=0); - - inline int compare(const String16& other) const; - - inline bool operator<(const String16& other) const; - inline bool operator<=(const String16& other) const; - inline bool operator==(const String16& other) const; - inline bool operator!=(const String16& other) const; - inline bool operator>=(const String16& other) const; - inline bool operator>(const String16& other) const; - - inline bool operator<(const char16_t* other) const; - inline bool operator<=(const char16_t* other) const; - inline bool operator==(const char16_t* other) const; - inline bool operator!=(const char16_t* other) const; - inline bool operator>=(const char16_t* other) const; - inline bool operator>(const char16_t* other) const; - - inline operator const char16_t*() const; - -private: - const char16_t* mString; -}; - -// String16 can be trivially moved using memcpy() because moving does not -// require any change to the underlying SharedBuffer contents or reference count. -ANDROID_TRIVIAL_MOVE_TRAIT(String16) - -TextOutput& operator<<(TextOutput& to, const String16& val); - -// --------------------------------------------------------------------------- -// No user servicable parts below. - -inline int compare_type(const String16& lhs, const String16& rhs) -{ - return lhs.compare(rhs); -} - -inline int strictly_order_type(const String16& lhs, const String16& rhs) -{ - return compare_type(lhs, rhs) < 0; -} - -inline const char16_t* String16::string() const -{ - return mString; -} - -inline size_t String16::size() const -{ - return SharedBuffer::sizeFromData(mString)/sizeof(char16_t)-1; -} - -inline const SharedBuffer* String16::sharedBuffer() const -{ - return SharedBuffer::bufferFromData(mString); -} - -inline String16& String16::operator=(const String16& other) -{ - setTo(other); - return *this; -} - -inline String16& String16::operator+=(const String16& other) -{ - append(other); - return *this; -} - -inline String16 String16::operator+(const String16& other) const -{ - String16 tmp(*this); - tmp += other; - return tmp; -} - -inline int String16::compare(const String16& other) const -{ - return strzcmp16(mString, size(), other.mString, other.size()); -} - -inline bool String16::operator<(const String16& other) const -{ - return strzcmp16(mString, size(), other.mString, other.size()) < 0; -} - -inline bool String16::operator<=(const String16& other) const -{ - return strzcmp16(mString, size(), other.mString, other.size()) <= 0; -} - -inline bool String16::operator==(const String16& other) const -{ - return strzcmp16(mString, size(), other.mString, other.size()) == 0; -} - -inline bool String16::operator!=(const String16& other) const -{ - return strzcmp16(mString, size(), other.mString, other.size()) != 0; -} - -inline bool String16::operator>=(const String16& other) const -{ - return strzcmp16(mString, size(), other.mString, other.size()) >= 0; -} - -inline bool String16::operator>(const String16& other) const -{ - return strzcmp16(mString, size(), other.mString, other.size()) > 0; -} - -inline bool String16::operator<(const char16_t* other) const -{ - return strcmp16(mString, other) < 0; -} - -inline bool String16::operator<=(const char16_t* other) const -{ - return strcmp16(mString, other) <= 0; -} - -inline bool String16::operator==(const char16_t* other) const -{ - return strcmp16(mString, other) == 0; -} - -inline bool String16::operator!=(const char16_t* other) const -{ - return strcmp16(mString, other) != 0; -} - -inline bool String16::operator>=(const char16_t* other) const -{ - return strcmp16(mString, other) >= 0; -} - -inline bool String16::operator>(const char16_t* other) const -{ - return strcmp16(mString, other) > 0; -} - -inline String16::operator const char16_t*() const -{ - return mString; -} - -}; // namespace android - -// --------------------------------------------------------------------------- - -#endif // ANDROID_STRING16_H diff --git a/include/utils/String8.h b/include/utils/String8.h deleted file mode 100644 index 335e7f1..0000000 --- a/include/utils/String8.h +++ /dev/null @@ -1,388 +0,0 @@ -/* - * Copyright (C) 2005 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_STRING8_H -#define ANDROID_STRING8_H - -#include <utils/Errors.h> -#include <utils/SharedBuffer.h> -#include <utils/Unicode.h> -#include <utils/TypeHelpers.h> - -#include <string.h> // for strcmp -#include <stdarg.h> - -// --------------------------------------------------------------------------- - -namespace android { - -class String16; -class TextOutput; - -//! This is a string holding UTF-8 characters. Does not allow the value more -// than 0x10FFFF, which is not valid unicode codepoint. -class String8 -{ -public: - String8(); - String8(const String8& o); - explicit String8(const char* o); - explicit String8(const char* o, size_t numChars); - - explicit String8(const String16& o); - explicit String8(const char16_t* o); - explicit String8(const char16_t* o, size_t numChars); - explicit String8(const char32_t* o); - explicit String8(const char32_t* o, size_t numChars); - ~String8(); - - static inline const String8 empty(); - - static String8 format(const char* fmt, ...) __attribute__((format (printf, 1, 2))); - static String8 formatV(const char* fmt, va_list args); - - inline const char* string() const; - inline size_t size() const; - inline size_t length() const; - inline size_t bytes() const; - inline bool isEmpty() const; - - inline const SharedBuffer* sharedBuffer() const; - - void clear(); - - void setTo(const String8& other); - status_t setTo(const char* other); - status_t setTo(const char* other, size_t numChars); - status_t setTo(const char16_t* other, size_t numChars); - status_t setTo(const char32_t* other, - size_t length); - - status_t append(const String8& other); - status_t append(const char* other); - status_t append(const char* other, size_t numChars); - - status_t appendFormat(const char* fmt, ...) - __attribute__((format (printf, 2, 3))); - status_t appendFormatV(const char* fmt, va_list args); - - // Note that this function takes O(N) time to calculate the value. - // No cache value is stored. - size_t getUtf32Length() const; - int32_t getUtf32At(size_t index, - size_t *next_index) const; - void getUtf32(char32_t* dst) const; - - inline String8& operator=(const String8& other); - inline String8& operator=(const char* other); - - inline String8& operator+=(const String8& other); - inline String8 operator+(const String8& other) const; - - inline String8& operator+=(const char* other); - inline String8 operator+(const char* other) const; - - inline int compare(const String8& other) const; - - inline bool operator<(const String8& other) const; - inline bool operator<=(const String8& other) const; - inline bool operator==(const String8& other) const; - inline bool operator!=(const String8& other) const; - inline bool operator>=(const String8& other) const; - inline bool operator>(const String8& other) const; - - inline bool operator<(const char* other) const; - inline bool operator<=(const char* other) const; - inline bool operator==(const char* other) const; - inline bool operator!=(const char* other) const; - inline bool operator>=(const char* other) const; - inline bool operator>(const char* other) const; - - inline operator const char*() const; - - char* lockBuffer(size_t size); - void unlockBuffer(); - status_t unlockBuffer(size_t size); - - // return the index of the first byte of other in this at or after - // start, or -1 if not found - ssize_t find(const char* other, size_t start = 0) const; - - void toLower(); - void toLower(size_t start, size_t numChars); - void toUpper(); - void toUpper(size_t start, size_t numChars); - - /* - * These methods operate on the string as if it were a path name. - */ - - /* - * Set the filename field to a specific value. - * - * Normalizes the filename, removing a trailing '/' if present. - */ - void setPathName(const char* name); - void setPathName(const char* name, size_t numChars); - - /* - * Get just the filename component. - * - * "/tmp/foo/bar.c" --> "bar.c" - */ - String8 getPathLeaf(void) const; - - /* - * Remove the last (file name) component, leaving just the directory - * name. - * - * "/tmp/foo/bar.c" --> "/tmp/foo" - * "/tmp" --> "" // ????? shouldn't this be "/" ???? XXX - * "bar.c" --> "" - */ - String8 getPathDir(void) const; - - /* - * Retrieve the front (root dir) component. Optionally also return the - * remaining components. - * - * "/tmp/foo/bar.c" --> "tmp" (remain = "foo/bar.c") - * "/tmp" --> "tmp" (remain = "") - * "bar.c" --> "bar.c" (remain = "") - */ - String8 walkPath(String8* outRemains = NULL) const; - - /* - * Return the filename extension. This is the last '.' and any number - * of characters that follow it. The '.' is included in case we - * decide to expand our definition of what constitutes an extension. - * - * "/tmp/foo/bar.c" --> ".c" - * "/tmp" --> "" - * "/tmp/foo.bar/baz" --> "" - * "foo.jpeg" --> ".jpeg" - * "foo." --> "" - */ - String8 getPathExtension(void) const; - - /* - * Return the path without the extension. Rules for what constitutes - * an extension are described in the comment for getPathExtension(). - * - * "/tmp/foo/bar.c" --> "/tmp/foo/bar" - */ - String8 getBasePath(void) const; - - /* - * Add a component to the pathname. We guarantee that there is - * exactly one path separator between the old path and the new. - * If there is no existing name, we just copy the new name in. - * - * If leaf is a fully qualified path (i.e. starts with '/', it - * replaces whatever was there before. - */ - String8& appendPath(const char* leaf); - String8& appendPath(const String8& leaf) { return appendPath(leaf.string()); } - - /* - * Like appendPath(), but does not affect this string. Returns a new one instead. - */ - String8 appendPathCopy(const char* leaf) const - { String8 p(*this); p.appendPath(leaf); return p; } - String8 appendPathCopy(const String8& leaf) const { return appendPathCopy(leaf.string()); } - - /* - * Converts all separators in this string to /, the default path separator. - * - * If the default OS separator is backslash, this converts all - * backslashes to slashes, in-place. Otherwise it does nothing. - * Returns self. - */ - String8& convertToResPath(); - -private: - status_t real_append(const char* other, size_t numChars); - char* find_extension(void) const; - - const char* mString; -}; - -// String8 can be trivially moved using memcpy() because moving does not -// require any change to the underlying SharedBuffer contents or reference count. -ANDROID_TRIVIAL_MOVE_TRAIT(String8) - -TextOutput& operator<<(TextOutput& to, const String16& val); - -// --------------------------------------------------------------------------- -// No user servicable parts below. - -inline int compare_type(const String8& lhs, const String8& rhs) -{ - return lhs.compare(rhs); -} - -inline int strictly_order_type(const String8& lhs, const String8& rhs) -{ - return compare_type(lhs, rhs) < 0; -} - -inline const String8 String8::empty() { - return String8(); -} - -inline const char* String8::string() const -{ - return mString; -} - -inline size_t String8::length() const -{ - return SharedBuffer::sizeFromData(mString)-1; -} - -inline size_t String8::size() const -{ - return length(); -} - -inline bool String8::isEmpty() const -{ - return length() == 0; -} - -inline size_t String8::bytes() const -{ - return SharedBuffer::sizeFromData(mString)-1; -} - -inline const SharedBuffer* String8::sharedBuffer() const -{ - return SharedBuffer::bufferFromData(mString); -} - -inline String8& String8::operator=(const String8& other) -{ - setTo(other); - return *this; -} - -inline String8& String8::operator=(const char* other) -{ - setTo(other); - return *this; -} - -inline String8& String8::operator+=(const String8& other) -{ - append(other); - return *this; -} - -inline String8 String8::operator+(const String8& other) const -{ - String8 tmp(*this); - tmp += other; - return tmp; -} - -inline String8& String8::operator+=(const char* other) -{ - append(other); - return *this; -} - -inline String8 String8::operator+(const char* other) const -{ - String8 tmp(*this); - tmp += other; - return tmp; -} - -inline int String8::compare(const String8& other) const -{ - return strcmp(mString, other.mString); -} - -inline bool String8::operator<(const String8& other) const -{ - return strcmp(mString, other.mString) < 0; -} - -inline bool String8::operator<=(const String8& other) const -{ - return strcmp(mString, other.mString) <= 0; -} - -inline bool String8::operator==(const String8& other) const -{ - return strcmp(mString, other.mString) == 0; -} - -inline bool String8::operator!=(const String8& other) const -{ - return strcmp(mString, other.mString) != 0; -} - -inline bool String8::operator>=(const String8& other) const -{ - return strcmp(mString, other.mString) >= 0; -} - -inline bool String8::operator>(const String8& other) const -{ - return strcmp(mString, other.mString) > 0; -} - -inline bool String8::operator<(const char* other) const -{ - return strcmp(mString, other) < 0; -} - -inline bool String8::operator<=(const char* other) const -{ - return strcmp(mString, other) <= 0; -} - -inline bool String8::operator==(const char* other) const -{ - return strcmp(mString, other) == 0; -} - -inline bool String8::operator!=(const char* other) const -{ - return strcmp(mString, other) != 0; -} - -inline bool String8::operator>=(const char* other) const -{ - return strcmp(mString, other) >= 0; -} - -inline bool String8::operator>(const char* other) const -{ - return strcmp(mString, other) > 0; -} - -inline String8::operator const char*() const -{ - return mString; -} - -} // namespace android - -// --------------------------------------------------------------------------- - -#endif // ANDROID_STRING8_H diff --git a/include/utils/StringArray.h b/include/utils/StringArray.h deleted file mode 100644 index c244587..0000000 --- a/include/utils/StringArray.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (C) 2009 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. - */ - -// -// Sortable array of strings. STL-ish, but STL-free. -// -#ifndef _LIBS_UTILS_STRING_ARRAY_H -#define _LIBS_UTILS_STRING_ARRAY_H - -#include <stdlib.h> -#include <string.h> - -namespace android { - -// -// An expanding array of strings. Add, get, sort, delete. -// -class StringArray { -public: - StringArray(); - virtual ~StringArray(); - - // - // Add a string. A copy of the string is made. - // - bool push_back(const char* str); - - // - // Delete an entry. - // - void erase(int idx); - - // - // Sort the array. - // - void sort(int (*compare)(const void*, const void*)); - - // - // Pass this to the sort routine to do an ascending alphabetical sort. - // - static int cmpAscendingAlpha(const void* pstr1, const void* pstr2); - - // - // Get the #of items in the array. - // - inline int size(void) const { return mCurrent; } - - // - // Return entry N. - // [should use operator[] here] - // - const char* getEntry(int idx) const { - return (unsigned(idx) >= unsigned(mCurrent)) ? NULL : mArray[idx]; - } - - // - // Set entry N to specified string. - // [should use operator[] here] - // - void setEntry(int idx, const char* str); - -private: - int mMax; - int mCurrent; - char** mArray; -}; - -}; // namespace android - -#endif // _LIBS_UTILS_STRING_ARRAY_H diff --git a/include/utils/StrongPointer.h b/include/utils/StrongPointer.h deleted file mode 100644 index 49fa3a8..0000000 --- a/include/utils/StrongPointer.h +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Copyright (C) 2005 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_STRONG_POINTER_H -#define ANDROID_STRONG_POINTER_H - -#include <cutils/atomic.h> - -#include <stdint.h> -#include <sys/types.h> -#include <stdlib.h> - -// --------------------------------------------------------------------------- -namespace android { - -class TextOutput; -TextOutput& printStrongPointer(TextOutput& to, const void* val); - -template<typename T> class wp; - -// --------------------------------------------------------------------------- - -#define COMPARE(_op_) \ -inline bool operator _op_ (const sp<T>& o) const { \ - return m_ptr _op_ o.m_ptr; \ -} \ -inline bool operator _op_ (const T* o) const { \ - return m_ptr _op_ o; \ -} \ -template<typename U> \ -inline bool operator _op_ (const sp<U>& o) const { \ - return m_ptr _op_ o.m_ptr; \ -} \ -template<typename U> \ -inline bool operator _op_ (const U* o) const { \ - return m_ptr _op_ o; \ -} \ -inline bool operator _op_ (const wp<T>& o) const { \ - return m_ptr _op_ o.m_ptr; \ -} \ -template<typename U> \ -inline bool operator _op_ (const wp<U>& o) const { \ - return m_ptr _op_ o.m_ptr; \ -} - -// --------------------------------------------------------------------------- - -template <typename T> -class sp -{ -public: - inline sp() : m_ptr(0) { } - - sp(T* other); - sp(const sp<T>& other); - template<typename U> sp(U* other); - template<typename U> sp(const sp<U>& other); - - ~sp(); - - // Assignment - - sp& operator = (T* other); - sp& operator = (const sp<T>& other); - - template<typename U> sp& operator = (const sp<U>& other); - template<typename U> sp& operator = (U* other); - - //! Special optimization for use by ProcessState (and nobody else). - void force_set(T* other); - - // Reset - - void clear(); - - // Accessors - - inline T& operator* () const { return *m_ptr; } - inline T* operator-> () const { return m_ptr; } - inline T* get() const { return m_ptr; } - - // Operators - - COMPARE(==) - COMPARE(!=) - COMPARE(>) - COMPARE(<) - COMPARE(<=) - COMPARE(>=) - -private: - template<typename Y> friend class sp; - template<typename Y> friend class wp; - void set_pointer(T* ptr); - T* m_ptr; -}; - -#undef COMPARE - -template <typename T> -TextOutput& operator<<(TextOutput& to, const sp<T>& val); - -// --------------------------------------------------------------------------- -// No user serviceable parts below here. - -template<typename T> -sp<T>::sp(T* other) -: m_ptr(other) - { - if (other) other->incStrong(this); - } - -template<typename T> -sp<T>::sp(const sp<T>& other) -: m_ptr(other.m_ptr) - { - if (m_ptr) m_ptr->incStrong(this); - } - -template<typename T> template<typename U> -sp<T>::sp(U* other) : m_ptr(other) -{ - if (other) ((T*)other)->incStrong(this); -} - -template<typename T> template<typename U> -sp<T>::sp(const sp<U>& other) -: m_ptr(other.m_ptr) - { - if (m_ptr) m_ptr->incStrong(this); - } - -template<typename T> -sp<T>::~sp() -{ - if (m_ptr) m_ptr->decStrong(this); -} - -template<typename T> -sp<T>& sp<T>::operator = (const sp<T>& other) { - T* otherPtr(other.m_ptr); - if (otherPtr) otherPtr->incStrong(this); - if (m_ptr) m_ptr->decStrong(this); - m_ptr = otherPtr; - return *this; -} - -template<typename T> -sp<T>& sp<T>::operator = (T* other) -{ - if (other) other->incStrong(this); - if (m_ptr) m_ptr->decStrong(this); - m_ptr = other; - return *this; -} - -template<typename T> template<typename U> -sp<T>& sp<T>::operator = (const sp<U>& other) -{ - T* otherPtr(other.m_ptr); - if (otherPtr) otherPtr->incStrong(this); - if (m_ptr) m_ptr->decStrong(this); - m_ptr = otherPtr; - return *this; -} - -template<typename T> template<typename U> -sp<T>& sp<T>::operator = (U* other) -{ - if (other) ((T*)other)->incStrong(this); - if (m_ptr) m_ptr->decStrong(this); - m_ptr = other; - return *this; -} - -template<typename T> -void sp<T>::force_set(T* other) -{ - other->forceIncStrong(this); - m_ptr = other; -} - -template<typename T> -void sp<T>::clear() -{ - if (m_ptr) { - m_ptr->decStrong(this); - m_ptr = 0; - } -} - -template<typename T> -void sp<T>::set_pointer(T* ptr) { - m_ptr = ptr; -} - -template <typename T> -inline TextOutput& operator<<(TextOutput& to, const sp<T>& val) -{ - return printStrongPointer(to, val.get()); -} - -}; // namespace android - -// --------------------------------------------------------------------------- - -#endif // ANDROID_STRONG_POINTER_H diff --git a/include/utils/Thread.h b/include/utils/Thread.h deleted file mode 100644 index df30611..0000000 --- a/include/utils/Thread.h +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (C) 2007 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 _LIBS_UTILS_THREAD_H -#define _LIBS_UTILS_THREAD_H - -#include <stdint.h> -#include <sys/types.h> -#include <time.h> - -#if defined(HAVE_PTHREADS) -# include <pthread.h> -#endif - -#include <utils/Condition.h> -#include <utils/Errors.h> -#include <utils/Mutex.h> -#include <utils/RefBase.h> -#include <utils/Timers.h> -#include <utils/ThreadDefs.h> - -// --------------------------------------------------------------------------- -namespace android { -// --------------------------------------------------------------------------- - -class Thread : virtual public RefBase -{ -public: - // Create a Thread object, but doesn't create or start the associated - // thread. See the run() method. - Thread(bool canCallJava = true); - virtual ~Thread(); - - // Start the thread in threadLoop() which needs to be implemented. - virtual status_t run( const char* name = 0, - int32_t priority = PRIORITY_DEFAULT, - size_t stack = 0); - - // Ask this object's thread to exit. This function is asynchronous, when the - // function returns the thread might still be running. Of course, this - // function can be called from a different thread. - virtual void requestExit(); - - // Good place to do one-time initializations - virtual status_t readyToRun(); - - // Call requestExit() and wait until this object's thread exits. - // BE VERY CAREFUL of deadlocks. In particular, it would be silly to call - // this function from this object's thread. Will return WOULD_BLOCK in - // that case. - status_t requestExitAndWait(); - - // Wait until this object's thread exits. Returns immediately if not yet running. - // Do not call from this object's thread; will return WOULD_BLOCK in that case. - status_t join(); - - // Indicates whether this thread is running or not. - bool isRunning() const; - -#ifdef HAVE_ANDROID_OS - // Return the thread's kernel ID, same as the thread itself calling gettid() or - // androidGetTid(), or -1 if the thread is not running. - pid_t getTid() const; -#endif - -protected: - // exitPending() returns true if requestExit() has been called. - bool exitPending() const; - -private: - // Derived class must implement threadLoop(). The thread starts its life - // here. There are two ways of using the Thread object: - // 1) loop: if threadLoop() returns true, it will be called again if - // requestExit() wasn't called. - // 2) once: if threadLoop() returns false, the thread will exit upon return. - virtual bool threadLoop() = 0; - -private: - Thread& operator=(const Thread&); - static int _threadLoop(void* user); - const bool mCanCallJava; - // always hold mLock when reading or writing - thread_id_t mThread; - mutable Mutex mLock; - Condition mThreadExitedCondition; - status_t mStatus; - // note that all accesses of mExitPending and mRunning need to hold mLock - volatile bool mExitPending; - volatile bool mRunning; - sp<Thread> mHoldSelf; -#ifdef HAVE_ANDROID_OS - // legacy for debugging, not used by getTid() as it is set by the child thread - // and so is not initialized until the child reaches that point - pid_t mTid; -#endif -}; - - -}; // namespace android - -// --------------------------------------------------------------------------- -#endif // _LIBS_UTILS_THREAD_H -// --------------------------------------------------------------------------- diff --git a/include/utils/ThreadDefs.h b/include/utils/ThreadDefs.h deleted file mode 100644 index a8f8eb3..0000000 --- a/include/utils/ThreadDefs.h +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (C) 2007 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 _LIBS_UTILS_THREAD_DEFS_H -#define _LIBS_UTILS_THREAD_DEFS_H - -#include <stdint.h> -#include <sys/types.h> -#include <system/graphics.h> - -// --------------------------------------------------------------------------- -// C API - -#ifdef __cplusplus -extern "C" { -#endif - -typedef void* android_thread_id_t; - -typedef int (*android_thread_func_t)(void*); - -enum { - /* - * *********************************************** - * ** Keep in sync with android.os.Process.java ** - * *********************************************** - * - * This maps directly to the "nice" priorities we use in Android. - * A thread priority should be chosen inverse-proportionally to - * the amount of work the thread is expected to do. The more work - * a thread will do, the less favorable priority it should get so that - * it doesn't starve the system. Threads not behaving properly might - * be "punished" by the kernel. - * Use the levels below when appropriate. Intermediate values are - * acceptable, preferably use the {MORE|LESS}_FAVORABLE constants below. - */ - ANDROID_PRIORITY_LOWEST = 19, - - /* use for background tasks */ - ANDROID_PRIORITY_BACKGROUND = 10, - - /* most threads run at normal priority */ - ANDROID_PRIORITY_NORMAL = 0, - - /* threads currently running a UI that the user is interacting with */ - ANDROID_PRIORITY_FOREGROUND = -2, - - /* the main UI thread has a slightly more favorable priority */ - ANDROID_PRIORITY_DISPLAY = -4, - - /* ui service treads might want to run at a urgent display (uncommon) */ - ANDROID_PRIORITY_URGENT_DISPLAY = HAL_PRIORITY_URGENT_DISPLAY, - - /* all normal audio threads */ - ANDROID_PRIORITY_AUDIO = -16, - - /* service audio threads (uncommon) */ - ANDROID_PRIORITY_URGENT_AUDIO = -19, - - /* should never be used in practice. regular process might not - * be allowed to use this level */ - ANDROID_PRIORITY_HIGHEST = -20, - - ANDROID_PRIORITY_DEFAULT = ANDROID_PRIORITY_NORMAL, - ANDROID_PRIORITY_MORE_FAVORABLE = -1, - ANDROID_PRIORITY_LESS_FAVORABLE = +1, -}; - -#ifdef __cplusplus -} // extern "C" -#endif - -// --------------------------------------------------------------------------- -// C++ API -#ifdef __cplusplus -namespace android { -// --------------------------------------------------------------------------- - -typedef android_thread_id_t thread_id_t; -typedef android_thread_func_t thread_func_t; - -enum { - PRIORITY_LOWEST = ANDROID_PRIORITY_LOWEST, - PRIORITY_BACKGROUND = ANDROID_PRIORITY_BACKGROUND, - PRIORITY_NORMAL = ANDROID_PRIORITY_NORMAL, - PRIORITY_FOREGROUND = ANDROID_PRIORITY_FOREGROUND, - PRIORITY_DISPLAY = ANDROID_PRIORITY_DISPLAY, - PRIORITY_URGENT_DISPLAY = ANDROID_PRIORITY_URGENT_DISPLAY, - PRIORITY_AUDIO = ANDROID_PRIORITY_AUDIO, - PRIORITY_URGENT_AUDIO = ANDROID_PRIORITY_URGENT_AUDIO, - PRIORITY_HIGHEST = ANDROID_PRIORITY_HIGHEST, - PRIORITY_DEFAULT = ANDROID_PRIORITY_DEFAULT, - PRIORITY_MORE_FAVORABLE = ANDROID_PRIORITY_MORE_FAVORABLE, - PRIORITY_LESS_FAVORABLE = ANDROID_PRIORITY_LESS_FAVORABLE, -}; - -// --------------------------------------------------------------------------- -}; // namespace android -#endif // __cplusplus -// --------------------------------------------------------------------------- - - -#endif // _LIBS_UTILS_THREAD_DEFS_H diff --git a/include/utils/Timers.h b/include/utils/Timers.h deleted file mode 100644 index 92f66c9..0000000 --- a/include/utils/Timers.h +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright (C) 2005 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. - */ - -// -// Timer functions. -// -#ifndef _LIBS_UTILS_TIMERS_H -#define _LIBS_UTILS_TIMERS_H - -#include <stdint.h> -#include <sys/types.h> -#include <sys/time.h> - -// ------------------------------------------------------------------ -// C API - -#ifdef __cplusplus -extern "C" { -#endif - -typedef int64_t nsecs_t; // nano-seconds - -static inline nsecs_t seconds_to_nanoseconds(nsecs_t secs) -{ - return secs*1000000000; -} - -static inline nsecs_t milliseconds_to_nanoseconds(nsecs_t secs) -{ - return secs*1000000; -} - -static inline nsecs_t microseconds_to_nanoseconds(nsecs_t secs) -{ - return secs*1000; -} - -static inline nsecs_t nanoseconds_to_seconds(nsecs_t secs) -{ - return secs/1000000000; -} - -static inline nsecs_t nanoseconds_to_milliseconds(nsecs_t secs) -{ - return secs/1000000; -} - -static inline nsecs_t nanoseconds_to_microseconds(nsecs_t secs) -{ - return secs/1000; -} - -static inline nsecs_t s2ns(nsecs_t v) {return seconds_to_nanoseconds(v);} -static inline nsecs_t ms2ns(nsecs_t v) {return milliseconds_to_nanoseconds(v);} -static inline nsecs_t us2ns(nsecs_t v) {return microseconds_to_nanoseconds(v);} -static inline nsecs_t ns2s(nsecs_t v) {return nanoseconds_to_seconds(v);} -static inline nsecs_t ns2ms(nsecs_t v) {return nanoseconds_to_milliseconds(v);} -static inline nsecs_t ns2us(nsecs_t v) {return nanoseconds_to_microseconds(v);} - -static inline nsecs_t seconds(nsecs_t v) { return s2ns(v); } -static inline nsecs_t milliseconds(nsecs_t v) { return ms2ns(v); } -static inline nsecs_t microseconds(nsecs_t v) { return us2ns(v); } - -enum { - SYSTEM_TIME_REALTIME = 0, // system-wide realtime clock - SYSTEM_TIME_MONOTONIC = 1, // monotonic time since unspecified starting point - SYSTEM_TIME_PROCESS = 2, // high-resolution per-process clock - SYSTEM_TIME_THREAD = 3, // high-resolution per-thread clock - SYSTEM_TIME_BOOTTIME = 4 // same as SYSTEM_TIME_MONOTONIC, but including CPU suspend time -}; - -// return the system-time according to the specified clock -#ifdef __cplusplus -nsecs_t systemTime(int clock = SYSTEM_TIME_MONOTONIC); -#else -nsecs_t systemTime(int clock); -#endif // def __cplusplus - -/** - * Returns the number of milliseconds to wait between the reference time and the timeout time. - * If the timeout is in the past relative to the reference time, returns 0. - * If the timeout is more than INT_MAX milliseconds in the future relative to the reference time, - * such as when timeoutTime == LLONG_MAX, returns -1 to indicate an infinite timeout delay. - * Otherwise, returns the difference between the reference time and timeout time - * rounded up to the next millisecond. - */ -int toMillisecondTimeoutDelay(nsecs_t referenceTime, nsecs_t timeoutTime); - -#ifdef __cplusplus -} // extern "C" -#endif - -// ------------------------------------------------------------------ -// C++ API - -#ifdef __cplusplus - -namespace android { -/* - * Time the duration of something. - * - * Includes some timeval manipulation functions. - */ -class DurationTimer { -public: - DurationTimer() {} - ~DurationTimer() {} - - // Start the timer. - void start(); - // Stop the timer. - void stop(); - // Get the duration in microseconds. - long long durationUsecs() const; - - // Subtract two timevals. Returns the difference (ptv1-ptv2) in - // microseconds. - static long long subtractTimevals(const struct timeval* ptv1, - const struct timeval* ptv2); - - // Add the specified amount of time to the timeval. - static void addToTimeval(struct timeval* ptv, long usec); - -private: - struct timeval mStartWhen; - struct timeval mStopWhen; -}; - -}; // android -#endif // def __cplusplus - -#endif // _LIBS_UTILS_TIMERS_H diff --git a/include/utils/Tokenizer.h b/include/utils/Tokenizer.h deleted file mode 100644 index bb25f37..0000000 --- a/include/utils/Tokenizer.h +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _UTILS_TOKENIZER_H -#define _UTILS_TOKENIZER_H - -#include <assert.h> -#include <utils/Errors.h> -#include <utils/FileMap.h> -#include <utils/String8.h> - -namespace android { - -/** - * A simple tokenizer for loading and parsing ASCII text files line by line. - */ -class Tokenizer { - Tokenizer(const String8& filename, FileMap* fileMap, char* buffer, - bool ownBuffer, size_t length); - -public: - ~Tokenizer(); - - /** - * Opens a file and maps it into memory. - * - * Returns NO_ERROR and a tokenizer for the file, if successful. - * Otherwise returns an error and sets outTokenizer to NULL. - */ - static status_t open(const String8& filename, Tokenizer** outTokenizer); - - /** - * Prepares to tokenize the contents of a string. - * - * Returns NO_ERROR and a tokenizer for the string, if successful. - * Otherwise returns an error and sets outTokenizer to NULL. - */ - static status_t fromContents(const String8& filename, - const char* contents, Tokenizer** outTokenizer); - - /** - * Returns true if at the end of the file. - */ - inline bool isEof() const { return mCurrent == getEnd(); } - - /** - * Returns true if at the end of the line or end of the file. - */ - inline bool isEol() const { return isEof() || *mCurrent == '\n'; } - - /** - * Gets the name of the file. - */ - inline String8 getFilename() const { return mFilename; } - - /** - * Gets a 1-based line number index for the current position. - */ - inline int32_t getLineNumber() const { return mLineNumber; } - - /** - * Formats a location string consisting of the filename and current line number. - * Returns a string like "MyFile.txt:33". - */ - String8 getLocation() const; - - /** - * Gets the character at the current position. - * Returns null at end of file. - */ - inline char peekChar() const { return isEof() ? '\0' : *mCurrent; } - - /** - * Gets the remainder of the current line as a string, excluding the newline character. - */ - String8 peekRemainderOfLine() const; - - /** - * Gets the character at the current position and advances past it. - * Returns null at end of file. - */ - inline char nextChar() { return isEof() ? '\0' : *(mCurrent++); } - - /** - * Gets the next token on this line stopping at the specified delimiters - * or the end of the line whichever comes first and advances past it. - * Also stops at embedded nulls. - * Returns the token or an empty string if the current character is a delimiter - * or is at the end of the line. - */ - String8 nextToken(const char* delimiters); - - /** - * Advances to the next line. - * Does nothing if already at the end of the file. - */ - void nextLine(); - - /** - * Skips over the specified delimiters in the line. - * Also skips embedded nulls. - */ - void skipDelimiters(const char* delimiters); - -private: - Tokenizer(const Tokenizer& other); // not copyable - - String8 mFilename; - FileMap* mFileMap; - char* mBuffer; - bool mOwnBuffer; - size_t mLength; - - const char* mCurrent; - int32_t mLineNumber; - - inline const char* getEnd() const { return mBuffer + mLength; } - -}; - -} // namespace android - -#endif // _UTILS_TOKENIZER_H diff --git a/include/utils/Trace.h b/include/utils/Trace.h deleted file mode 100644 index 49578c4..0000000 --- a/include/utils/Trace.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2012 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_TRACE_H -#define ANDROID_TRACE_H - -#include <fcntl.h> -#include <stdint.h> -#include <stdio.h> -#include <string.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <unistd.h> - -#include <cutils/compiler.h> -#include <utils/threads.h> -#include <cutils/trace.h> - -// See <cutils/trace.h> for more ATRACE_* macros. - -// ATRACE_NAME traces the beginning and end of the current scope. To trace -// the correct start and end times this macro should be declared first in the -// scope body. -#define ATRACE_NAME(name) android::ScopedTrace ___tracer(ATRACE_TAG, name) -// ATRACE_CALL is an ATRACE_NAME that uses the current function name. -#define ATRACE_CALL() ATRACE_NAME(__FUNCTION__) - -namespace android { - -class ScopedTrace { -public: -inline ScopedTrace(uint64_t tag, const char* name) - : mTag(tag) { - atrace_begin(mTag,name); -} - -inline ~ScopedTrace() { - atrace_end(mTag); -} - -private: - uint64_t mTag; -}; - -}; // namespace android - -#endif // ANDROID_TRACE_H diff --git a/include/utils/TypeHelpers.h b/include/utils/TypeHelpers.h deleted file mode 100644 index 13c9081..0000000 --- a/include/utils/TypeHelpers.h +++ /dev/null @@ -1,302 +0,0 @@ -/* - * Copyright (C) 2005 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_TYPE_HELPERS_H -#define ANDROID_TYPE_HELPERS_H - -#include <new> -#include <stdint.h> -#include <string.h> -#include <sys/types.h> - -// --------------------------------------------------------------------------- - -namespace android { - -/* - * Types traits - */ - -template <typename T> struct trait_trivial_ctor { enum { value = false }; }; -template <typename T> struct trait_trivial_dtor { enum { value = false }; }; -template <typename T> struct trait_trivial_copy { enum { value = false }; }; -template <typename T> struct trait_trivial_move { enum { value = false }; }; -template <typename T> struct trait_pointer { enum { value = false }; }; -template <typename T> struct trait_pointer<T*> { enum { value = true }; }; - -template <typename TYPE> -struct traits { - enum { - // whether this type is a pointer - is_pointer = trait_pointer<TYPE>::value, - // whether this type's constructor is a no-op - has_trivial_ctor = is_pointer || trait_trivial_ctor<TYPE>::value, - // whether this type's destructor is a no-op - has_trivial_dtor = is_pointer || trait_trivial_dtor<TYPE>::value, - // whether this type type can be copy-constructed with memcpy - has_trivial_copy = is_pointer || trait_trivial_copy<TYPE>::value, - // whether this type can be moved with memmove - has_trivial_move = is_pointer || trait_trivial_move<TYPE>::value - }; -}; - -template <typename T, typename U> -struct aggregate_traits { - enum { - is_pointer = false, - has_trivial_ctor = - traits<T>::has_trivial_ctor && traits<U>::has_trivial_ctor, - has_trivial_dtor = - traits<T>::has_trivial_dtor && traits<U>::has_trivial_dtor, - has_trivial_copy = - traits<T>::has_trivial_copy && traits<U>::has_trivial_copy, - has_trivial_move = - traits<T>::has_trivial_move && traits<U>::has_trivial_move - }; -}; - -#define ANDROID_TRIVIAL_CTOR_TRAIT( T ) \ - template<> struct trait_trivial_ctor< T > { enum { value = true }; }; - -#define ANDROID_TRIVIAL_DTOR_TRAIT( T ) \ - template<> struct trait_trivial_dtor< T > { enum { value = true }; }; - -#define ANDROID_TRIVIAL_COPY_TRAIT( T ) \ - template<> struct trait_trivial_copy< T > { enum { value = true }; }; - -#define ANDROID_TRIVIAL_MOVE_TRAIT( T ) \ - template<> struct trait_trivial_move< T > { enum { value = true }; }; - -#define ANDROID_BASIC_TYPES_TRAITS( T ) \ - ANDROID_TRIVIAL_CTOR_TRAIT( T ) \ - ANDROID_TRIVIAL_DTOR_TRAIT( T ) \ - ANDROID_TRIVIAL_COPY_TRAIT( T ) \ - ANDROID_TRIVIAL_MOVE_TRAIT( T ) - -// --------------------------------------------------------------------------- - -/* - * basic types traits - */ - -ANDROID_BASIC_TYPES_TRAITS( void ) -ANDROID_BASIC_TYPES_TRAITS( bool ) -ANDROID_BASIC_TYPES_TRAITS( char ) -ANDROID_BASIC_TYPES_TRAITS( unsigned char ) -ANDROID_BASIC_TYPES_TRAITS( short ) -ANDROID_BASIC_TYPES_TRAITS( unsigned short ) -ANDROID_BASIC_TYPES_TRAITS( int ) -ANDROID_BASIC_TYPES_TRAITS( unsigned int ) -ANDROID_BASIC_TYPES_TRAITS( long ) -ANDROID_BASIC_TYPES_TRAITS( unsigned long ) -ANDROID_BASIC_TYPES_TRAITS( long long ) -ANDROID_BASIC_TYPES_TRAITS( unsigned long long ) -ANDROID_BASIC_TYPES_TRAITS( float ) -ANDROID_BASIC_TYPES_TRAITS( double ) - -// --------------------------------------------------------------------------- - - -/* - * compare and order types - */ - -template<typename TYPE> inline -int strictly_order_type(const TYPE& lhs, const TYPE& rhs) { - return (lhs < rhs) ? 1 : 0; -} - -template<typename TYPE> inline -int compare_type(const TYPE& lhs, const TYPE& rhs) { - return strictly_order_type(rhs, lhs) - strictly_order_type(lhs, rhs); -} - -/* - * create, destroy, copy and move types... - */ - -template<typename TYPE> inline -void construct_type(TYPE* p, size_t n) { - if (!traits<TYPE>::has_trivial_ctor) { - while (n--) { - new(p++) TYPE; - } - } -} - -template<typename TYPE> inline -void destroy_type(TYPE* p, size_t n) { - if (!traits<TYPE>::has_trivial_dtor) { - while (n--) { - p->~TYPE(); - p++; - } - } -} - -template<typename TYPE> inline -void copy_type(TYPE* d, const TYPE* s, size_t n) { - if (!traits<TYPE>::has_trivial_copy) { - while (n--) { - new(d) TYPE(*s); - d++, s++; - } - } else { - memcpy(d,s,n*sizeof(TYPE)); - } -} - -template<typename TYPE> inline -void splat_type(TYPE* where, const TYPE* what, size_t n) { - if (!traits<TYPE>::has_trivial_copy) { - while (n--) { - new(where) TYPE(*what); - where++; - } - } else { - while (n--) { - *where++ = *what; - } - } -} - -template<typename TYPE> inline -void move_forward_type(TYPE* d, const TYPE* s, size_t n = 1) { - if ((traits<TYPE>::has_trivial_dtor && traits<TYPE>::has_trivial_copy) - || traits<TYPE>::has_trivial_move) - { - memmove(d,s,n*sizeof(TYPE)); - } else { - d += n; - s += n; - while (n--) { - --d, --s; - if (!traits<TYPE>::has_trivial_copy) { - new(d) TYPE(*s); - } else { - *d = *s; - } - if (!traits<TYPE>::has_trivial_dtor) { - s->~TYPE(); - } - } - } -} - -template<typename TYPE> inline -void move_backward_type(TYPE* d, const TYPE* s, size_t n = 1) { - if ((traits<TYPE>::has_trivial_dtor && traits<TYPE>::has_trivial_copy) - || traits<TYPE>::has_trivial_move) - { - memmove(d,s,n*sizeof(TYPE)); - } else { - while (n--) { - if (!traits<TYPE>::has_trivial_copy) { - new(d) TYPE(*s); - } else { - *d = *s; - } - if (!traits<TYPE>::has_trivial_dtor) { - s->~TYPE(); - } - d++, s++; - } - } -} - -// --------------------------------------------------------------------------- - -/* - * a key/value pair - */ - -template <typename KEY, typename VALUE> -struct key_value_pair_t { - typedef KEY key_t; - typedef VALUE value_t; - - KEY key; - VALUE value; - key_value_pair_t() { } - key_value_pair_t(const key_value_pair_t& o) : key(o.key), value(o.value) { } - key_value_pair_t(const KEY& k, const VALUE& v) : key(k), value(v) { } - key_value_pair_t(const KEY& k) : key(k) { } - inline bool operator < (const key_value_pair_t& o) const { - return strictly_order_type(key, o.key); - } - inline const KEY& getKey() const { - return key; - } - inline const VALUE& getValue() const { - return value; - } -}; - -template <typename K, typename V> -struct trait_trivial_ctor< key_value_pair_t<K, V> > -{ enum { value = aggregate_traits<K,V>::has_trivial_ctor }; }; -template <typename K, typename V> -struct trait_trivial_dtor< key_value_pair_t<K, V> > -{ enum { value = aggregate_traits<K,V>::has_trivial_dtor }; }; -template <typename K, typename V> -struct trait_trivial_copy< key_value_pair_t<K, V> > -{ enum { value = aggregate_traits<K,V>::has_trivial_copy }; }; -template <typename K, typename V> -struct trait_trivial_move< key_value_pair_t<K, V> > -{ enum { value = aggregate_traits<K,V>::has_trivial_move }; }; - -// --------------------------------------------------------------------------- - -/* - * Hash codes. - */ -typedef uint32_t hash_t; - -template <typename TKey> -hash_t hash_type(const TKey& key); - -/* Built-in hash code specializations. - * Assumes pointers are 32bit. */ -#define ANDROID_INT32_HASH(T) \ - template <> inline hash_t hash_type(const T& value) { return hash_t(value); } -#define ANDROID_INT64_HASH(T) \ - template <> inline hash_t hash_type(const T& value) { \ - return hash_t((value >> 32) ^ value); } -#define ANDROID_REINTERPRET_HASH(T, R) \ - template <> inline hash_t hash_type(const T& value) { \ - return hash_type(*reinterpret_cast<const R*>(&value)); } - -ANDROID_INT32_HASH(bool) -ANDROID_INT32_HASH(int8_t) -ANDROID_INT32_HASH(uint8_t) -ANDROID_INT32_HASH(int16_t) -ANDROID_INT32_HASH(uint16_t) -ANDROID_INT32_HASH(int32_t) -ANDROID_INT32_HASH(uint32_t) -ANDROID_INT64_HASH(int64_t) -ANDROID_INT64_HASH(uint64_t) -ANDROID_REINTERPRET_HASH(float, uint32_t) -ANDROID_REINTERPRET_HASH(double, uint64_t) - -template <typename T> inline hash_t hash_type(T* const & value) { - return hash_type(uintptr_t(value)); -} - -}; // namespace android - -// --------------------------------------------------------------------------- - -#endif // ANDROID_TYPE_HELPERS_H diff --git a/include/utils/Unicode.h b/include/utils/Unicode.h deleted file mode 100644 index 9273533..0000000 --- a/include/utils/Unicode.h +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright (C) 2005 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_UNICODE_H -#define ANDROID_UNICODE_H - -#include <sys/types.h> -#include <stdint.h> - -extern "C" { - -typedef uint32_t char32_t; -typedef uint16_t char16_t; - -// Standard string functions on char16_t strings. -int strcmp16(const char16_t *, const char16_t *); -int strncmp16(const char16_t *s1, const char16_t *s2, size_t n); -size_t strlen16(const char16_t *); -size_t strnlen16(const char16_t *, size_t); -char16_t *strcpy16(char16_t *, const char16_t *); -char16_t *strncpy16(char16_t *, const char16_t *, size_t); - -// Version of comparison that supports embedded nulls. -// This is different than strncmp() because we don't stop -// at a nul character and consider the strings to be different -// if the lengths are different (thus we need to supply the -// lengths of both strings). This can also be used when -// your string is not nul-terminated as it will have the -// equivalent result as strcmp16 (unlike strncmp16). -int strzcmp16(const char16_t *s1, size_t n1, const char16_t *s2, size_t n2); - -// Version of strzcmp16 for comparing strings in different endianness. -int strzcmp16_h_n(const char16_t *s1H, size_t n1, const char16_t *s2N, size_t n2); - -// Standard string functions on char32_t strings. -size_t strlen32(const char32_t *); -size_t strnlen32(const char32_t *, size_t); - -/** - * Measure the length of a UTF-32 string in UTF-8. If the string is invalid - * such as containing a surrogate character, -1 will be returned. - */ -ssize_t utf32_to_utf8_length(const char32_t *src, size_t src_len); - -/** - * Stores a UTF-8 string converted from "src" in "dst", if "dst_length" is not - * large enough to store the string, the part of the "src" string is stored - * into "dst" as much as possible. See the examples for more detail. - * Returns the size actually used for storing the string. - * dst" is not null-terminated when dst_len is fully used (like strncpy). - * - * Example 1 - * "src" == \u3042\u3044 (\xE3\x81\x82\xE3\x81\x84) - * "src_len" == 2 - * "dst_len" >= 7 - * -> - * Returned value == 6 - * "dst" becomes \xE3\x81\x82\xE3\x81\x84\0 - * (note that "dst" is null-terminated) - * - * Example 2 - * "src" == \u3042\u3044 (\xE3\x81\x82\xE3\x81\x84) - * "src_len" == 2 - * "dst_len" == 5 - * -> - * Returned value == 3 - * "dst" becomes \xE3\x81\x82\0 - * (note that "dst" is null-terminated, but \u3044 is not stored in "dst" - * since "dst" does not have enough size to store the character) - * - * Example 3 - * "src" == \u3042\u3044 (\xE3\x81\x82\xE3\x81\x84) - * "src_len" == 2 - * "dst_len" == 6 - * -> - * Returned value == 6 - * "dst" becomes \xE3\x81\x82\xE3\x81\x84 - * (note that "dst" is NOT null-terminated, like strncpy) - */ -void utf32_to_utf8(const char32_t* src, size_t src_len, char* dst); - -/** - * Returns the unicode value at "index". - * Returns -1 when the index is invalid (equals to or more than "src_len"). - * If returned value is positive, it is able to be converted to char32_t, which - * is unsigned. Then, if "next_index" is not NULL, the next index to be used is - * stored in "next_index". "next_index" can be NULL. - */ -int32_t utf32_from_utf8_at(const char *src, size_t src_len, size_t index, size_t *next_index); - - -/** - * Returns the UTF-8 length of UTF-16 string "src". - */ -ssize_t utf16_to_utf8_length(const char16_t *src, size_t src_len); - -/** - * Converts a UTF-16 string to UTF-8. The destination buffer must be large - * enough to fit the UTF-16 as measured by utf16_to_utf8_length with an added - * NULL terminator. - */ -void utf16_to_utf8(const char16_t* src, size_t src_len, char* dst); - -/** - * Returns the length of "src" when "src" is valid UTF-8 string. - * Returns 0 if src is NULL or 0-length string. Returns -1 when the source - * is an invalid string. - * - * This function should be used to determine whether "src" is valid UTF-8 - * characters with valid unicode codepoints. "src" must be null-terminated. - * - * If you are going to use other utf8_to_... functions defined in this header - * with string which may not be valid UTF-8 with valid codepoint (form 0 to - * 0x10FFFF), you should use this function before calling others, since the - * other functions do not check whether the string is valid UTF-8 or not. - * - * If you do not care whether "src" is valid UTF-8 or not, you should use - * strlen() as usual, which should be much faster. - */ -ssize_t utf8_length(const char *src); - -/** - * Measure the length of a UTF-32 string. - */ -size_t utf8_to_utf32_length(const char *src, size_t src_len); - -/** - * Stores a UTF-32 string converted from "src" in "dst". "dst" must be large - * enough to store the entire converted string as measured by - * utf8_to_utf32_length plus space for a NULL terminator. - */ -void utf8_to_utf32(const char* src, size_t src_len, char32_t* dst); - -/** - * Returns the UTF-16 length of UTF-8 string "src". - */ -ssize_t utf8_to_utf16_length(const uint8_t* src, size_t srcLen); - -/** - * Convert UTF-8 to UTF-16 including surrogate pairs. - * Returns a pointer to the end of the string (where a null terminator might go - * if you wanted to add one). - */ -char16_t* utf8_to_utf16_no_null_terminator(const uint8_t* src, size_t srcLen, char16_t* dst); - -/** - * Convert UTF-8 to UTF-16 including surrogate pairs. The destination buffer - * must be large enough to hold the result as measured by utf8_to_utf16_length - * plus an added NULL terminator. - */ -void utf8_to_utf16(const uint8_t* src, size_t srcLen, char16_t* dst); - -} - -#endif diff --git a/include/utils/UniquePtr.h b/include/utils/UniquePtr.h deleted file mode 100644 index bc62fe6..0000000 --- a/include/utils/UniquePtr.h +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* === NOTE === NOTE === NOTE === NOTE === NOTE === NOTE === NOTE === NOTE === - * - * THIS IS A COPY OF libcore/include/UniquePtr.h AND AS SUCH THAT IS THE - * CANONICAL SOURCE OF THIS FILE. PLEASE KEEP THEM IN SYNC. - * - * === NOTE === NOTE === NOTE === NOTE === NOTE === NOTE === NOTE === NOTE === - */ - -#ifndef UNIQUE_PTR_H_included -#define UNIQUE_PTR_H_included - -#include <cstdlib> // For NULL. - -// Default deleter for pointer types. -template <typename T> -struct DefaultDelete { - enum { type_must_be_complete = sizeof(T) }; - DefaultDelete() {} - void operator()(T* p) const { - delete p; - } -}; - -// Default deleter for array types. -template <typename T> -struct DefaultDelete<T[]> { - enum { type_must_be_complete = sizeof(T) }; - void operator()(T* p) const { - delete[] p; - } -}; - -// A smart pointer that deletes the given pointer on destruction. -// Equivalent to C++0x's std::unique_ptr (a combination of boost::scoped_ptr -// and boost::scoped_array). -// Named to be in keeping with Android style but also to avoid -// collision with any other implementation, until we can switch over -// to unique_ptr. -// Use thus: -// UniquePtr<C> c(new C); -template <typename T, typename D = DefaultDelete<T> > -class UniquePtr { -public: - // Construct a new UniquePtr, taking ownership of the given raw pointer. - explicit UniquePtr(T* ptr = NULL) : mPtr(ptr) { - } - - ~UniquePtr() { - reset(); - } - - // Accessors. - T& operator*() const { return *mPtr; } - T* operator->() const { return mPtr; } - T* get() const { return mPtr; } - - // Returns the raw pointer and hands over ownership to the caller. - // The pointer will not be deleted by UniquePtr. - T* release() __attribute__((warn_unused_result)) { - T* result = mPtr; - mPtr = NULL; - return result; - } - - // Takes ownership of the given raw pointer. - // If this smart pointer previously owned a different raw pointer, that - // raw pointer will be freed. - void reset(T* ptr = NULL) { - if (ptr != mPtr) { - D()(mPtr); - mPtr = ptr; - } - } - -private: - // The raw pointer. - T* mPtr; - - // Comparing unique pointers is probably a mistake, since they're unique. - template <typename T2> bool operator==(const UniquePtr<T2>& p) const; - template <typename T2> bool operator!=(const UniquePtr<T2>& p) const; - - // Disallow copy and assignment. - UniquePtr(const UniquePtr&); - void operator=(const UniquePtr&); -}; - -// Partial specialization for array types. Like std::unique_ptr, this removes -// operator* and operator-> but adds operator[]. -template <typename T, typename D> -class UniquePtr<T[], D> { -public: - explicit UniquePtr(T* ptr = NULL) : mPtr(ptr) { - } - - ~UniquePtr() { - reset(); - } - - T& operator[](size_t i) const { - return mPtr[i]; - } - T* get() const { return mPtr; } - - T* release() __attribute__((warn_unused_result)) { - T* result = mPtr; - mPtr = NULL; - return result; - } - - void reset(T* ptr = NULL) { - if (ptr != mPtr) { - D()(mPtr); - mPtr = ptr; - } - } - -private: - T* mPtr; - - // Disallow copy and assignment. - UniquePtr(const UniquePtr&); - void operator=(const UniquePtr&); -}; - -#if UNIQUE_PTR_TESTS - -// Run these tests with: -// g++ -g -DUNIQUE_PTR_TESTS -x c++ UniquePtr.h && ./a.out - -#include <stdio.h> - -static void assert(bool b) { - if (!b) { - fprintf(stderr, "FAIL\n"); - abort(); - } - fprintf(stderr, "OK\n"); -} -static int cCount = 0; -struct C { - C() { ++cCount; } - ~C() { --cCount; } -}; -static bool freed = false; -struct Freer { - void operator()(int* p) { - assert(*p == 123); - free(p); - freed = true; - } -}; - -int main(int argc, char* argv[]) { - // - // UniquePtr<T> tests... - // - - // Can we free a single object? - { - UniquePtr<C> c(new C); - assert(cCount == 1); - } - assert(cCount == 0); - // Does release work? - C* rawC; - { - UniquePtr<C> c(new C); - assert(cCount == 1); - rawC = c.release(); - } - assert(cCount == 1); - delete rawC; - // Does reset work? - { - UniquePtr<C> c(new C); - assert(cCount == 1); - c.reset(new C); - assert(cCount == 1); - } - assert(cCount == 0); - - // - // UniquePtr<T[]> tests... - // - - // Can we free an array? - { - UniquePtr<C[]> cs(new C[4]); - assert(cCount == 4); - } - assert(cCount == 0); - // Does release work? - { - UniquePtr<C[]> c(new C[4]); - assert(cCount == 4); - rawC = c.release(); - } - assert(cCount == 4); - delete[] rawC; - // Does reset work? - { - UniquePtr<C[]> c(new C[4]); - assert(cCount == 4); - c.reset(new C[2]); - assert(cCount == 2); - } - assert(cCount == 0); - - // - // Custom deleter tests... - // - assert(!freed); - { - UniquePtr<int, Freer> i(reinterpret_cast<int*>(malloc(sizeof(int)))); - *i = 123; - } - assert(freed); - return 0; -} -#endif - -#endif // UNIQUE_PTR_H_included diff --git a/include/utils/Vector.h b/include/utils/Vector.h deleted file mode 100644 index ed7b725..0000000 --- a/include/utils/Vector.h +++ /dev/null @@ -1,423 +0,0 @@ -/* - * Copyright (C) 2005 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_VECTOR_H -#define ANDROID_VECTOR_H - -#include <new> -#include <stdint.h> -#include <sys/types.h> - -#include <cutils/log.h> - -#include <utils/VectorImpl.h> -#include <utils/TypeHelpers.h> - -// --------------------------------------------------------------------------- - -namespace android { - -template <typename TYPE> -class SortedVector; - -/*! - * The main templated vector class ensuring type safety - * while making use of VectorImpl. - * This is the class users want to use. - */ - -template <class TYPE> -class Vector : private VectorImpl -{ -public: - typedef TYPE value_type; - - /*! - * Constructors and destructors - */ - - Vector(); - Vector(const Vector<TYPE>& rhs); - explicit Vector(const SortedVector<TYPE>& rhs); - virtual ~Vector(); - - /*! copy operator */ - const Vector<TYPE>& operator = (const Vector<TYPE>& rhs) const; - Vector<TYPE>& operator = (const Vector<TYPE>& rhs); - - const Vector<TYPE>& operator = (const SortedVector<TYPE>& rhs) const; - Vector<TYPE>& operator = (const SortedVector<TYPE>& rhs); - - /* - * empty the vector - */ - - inline void clear() { VectorImpl::clear(); } - - /*! - * vector stats - */ - - //! returns number of items in the vector - inline size_t size() const { return VectorImpl::size(); } - //! returns whether or not the vector is empty - inline bool isEmpty() const { return VectorImpl::isEmpty(); } - //! returns how many items can be stored without reallocating the backing store - inline size_t capacity() const { return VectorImpl::capacity(); } - //! sets the capacity. capacity can never be reduced less than size() - inline ssize_t setCapacity(size_t size) { return VectorImpl::setCapacity(size); } - - /*! - * set the size of the vector. items are appended with the default - * constructor, or removed from the end as needed. - */ - inline ssize_t resize(size_t size) { return VectorImpl::resize(size); } - - /*! - * C-style array access - */ - - //! read-only C-style access - inline const TYPE* array() const; - //! read-write C-style access - TYPE* editArray(); - - /*! - * accessors - */ - - //! read-only access to an item at a given index - inline const TYPE& operator [] (size_t index) const; - //! alternate name for operator [] - inline const TYPE& itemAt(size_t index) const; - //! stack-usage of the vector. returns the top of the stack (last element) - const TYPE& top() const; - - /*! - * modifying the array - */ - - //! copy-on write support, grants write access to an item - TYPE& editItemAt(size_t index); - //! grants right access to the top of the stack (last element) - TYPE& editTop(); - - /*! - * append/insert another vector - */ - - //! insert another vector at a given index - ssize_t insertVectorAt(const Vector<TYPE>& vector, size_t index); - - //! append another vector at the end of this one - ssize_t appendVector(const Vector<TYPE>& vector); - - - //! insert an array at a given index - ssize_t insertArrayAt(const TYPE* array, size_t index, size_t length); - - //! append an array at the end of this vector - ssize_t appendArray(const TYPE* array, size_t length); - - /*! - * add/insert/replace items - */ - - //! insert one or several items initialized with their default constructor - inline ssize_t insertAt(size_t index, size_t numItems = 1); - //! insert one or several items initialized from a prototype item - ssize_t insertAt(const TYPE& prototype_item, size_t index, size_t numItems = 1); - //! pop the top of the stack (removes the last element). No-op if the stack's empty - inline void pop(); - //! pushes an item initialized with its default constructor - inline void push(); - //! pushes an item on the top of the stack - void push(const TYPE& item); - //! same as push() but returns the index the item was added at (or an error) - inline ssize_t add(); - //! same as push() but returns the index the item was added at (or an error) - ssize_t add(const TYPE& item); - //! replace an item with a new one initialized with its default constructor - inline ssize_t replaceAt(size_t index); - //! replace an item with a new one - ssize_t replaceAt(const TYPE& item, size_t index); - - /*! - * remove items - */ - - //! remove several items - inline ssize_t removeItemsAt(size_t index, size_t count = 1); - //! remove one item - inline ssize_t removeAt(size_t index) { return removeItemsAt(index); } - - /*! - * sort (stable) the array - */ - - typedef int (*compar_t)(const TYPE* lhs, const TYPE* rhs); - typedef int (*compar_r_t)(const TYPE* lhs, const TYPE* rhs, void* state); - - inline status_t sort(compar_t cmp); - inline status_t sort(compar_r_t cmp, void* state); - - // for debugging only - inline size_t getItemSize() const { return itemSize(); } - - - /* - * these inlines add some level of compatibility with STL. eventually - * we should probably turn things around. - */ - typedef TYPE* iterator; - typedef TYPE const* const_iterator; - - inline iterator begin() { return editArray(); } - inline iterator end() { return editArray() + size(); } - inline const_iterator begin() const { return array(); } - inline const_iterator end() const { return array() + size(); } - inline void reserve(size_t n) { setCapacity(n); } - inline bool empty() const{ return isEmpty(); } - inline void push_back(const TYPE& item) { insertAt(item, size(), 1); } - inline void push_front(const TYPE& item) { insertAt(item, 0, 1); } - inline iterator erase(iterator pos) { - ssize_t index = removeItemsAt(pos-array()); - return begin() + index; - } - -protected: - virtual void do_construct(void* storage, size_t num) const; - virtual void do_destroy(void* storage, size_t num) const; - virtual void do_copy(void* dest, const void* from, size_t num) const; - virtual void do_splat(void* dest, const void* item, size_t num) const; - virtual void do_move_forward(void* dest, const void* from, size_t num) const; - virtual void do_move_backward(void* dest, const void* from, size_t num) const; -}; - -// Vector<T> can be trivially moved using memcpy() because moving does not -// require any change to the underlying SharedBuffer contents or reference count. -template<typename T> struct trait_trivial_move<Vector<T> > { enum { value = true }; }; - -// --------------------------------------------------------------------------- -// No user serviceable parts from here... -// --------------------------------------------------------------------------- - -template<class TYPE> inline -Vector<TYPE>::Vector() - : VectorImpl(sizeof(TYPE), - ((traits<TYPE>::has_trivial_ctor ? HAS_TRIVIAL_CTOR : 0) - |(traits<TYPE>::has_trivial_dtor ? HAS_TRIVIAL_DTOR : 0) - |(traits<TYPE>::has_trivial_copy ? HAS_TRIVIAL_COPY : 0)) - ) -{ -} - -template<class TYPE> inline -Vector<TYPE>::Vector(const Vector<TYPE>& rhs) - : VectorImpl(rhs) { -} - -template<class TYPE> inline -Vector<TYPE>::Vector(const SortedVector<TYPE>& rhs) - : VectorImpl(static_cast<const VectorImpl&>(rhs)) { -} - -template<class TYPE> inline -Vector<TYPE>::~Vector() { - finish_vector(); -} - -template<class TYPE> inline -Vector<TYPE>& Vector<TYPE>::operator = (const Vector<TYPE>& rhs) { - VectorImpl::operator = (rhs); - return *this; -} - -template<class TYPE> inline -const Vector<TYPE>& Vector<TYPE>::operator = (const Vector<TYPE>& rhs) const { - VectorImpl::operator = (static_cast<const VectorImpl&>(rhs)); - return *this; -} - -template<class TYPE> inline -Vector<TYPE>& Vector<TYPE>::operator = (const SortedVector<TYPE>& rhs) { - VectorImpl::operator = (static_cast<const VectorImpl&>(rhs)); - return *this; -} - -template<class TYPE> inline -const Vector<TYPE>& Vector<TYPE>::operator = (const SortedVector<TYPE>& rhs) const { - VectorImpl::operator = (rhs); - return *this; -} - -template<class TYPE> inline -const TYPE* Vector<TYPE>::array() const { - return static_cast<const TYPE *>(arrayImpl()); -} - -template<class TYPE> inline -TYPE* Vector<TYPE>::editArray() { - return static_cast<TYPE *>(editArrayImpl()); -} - - -template<class TYPE> inline -const TYPE& Vector<TYPE>::operator[](size_t index) const { - LOG_FATAL_IF(index>=size(), - "%s: index=%u out of range (%u)", __PRETTY_FUNCTION__, - int(index), int(size())); - return *(array() + index); -} - -template<class TYPE> inline -const TYPE& Vector<TYPE>::itemAt(size_t index) const { - return operator[](index); -} - -template<class TYPE> inline -const TYPE& Vector<TYPE>::top() const { - return *(array() + size() - 1); -} - -template<class TYPE> inline -TYPE& Vector<TYPE>::editItemAt(size_t index) { - return *( static_cast<TYPE *>(editItemLocation(index)) ); -} - -template<class TYPE> inline -TYPE& Vector<TYPE>::editTop() { - return *( static_cast<TYPE *>(editItemLocation(size()-1)) ); -} - -template<class TYPE> inline -ssize_t Vector<TYPE>::insertVectorAt(const Vector<TYPE>& vector, size_t index) { - return VectorImpl::insertVectorAt(reinterpret_cast<const VectorImpl&>(vector), index); -} - -template<class TYPE> inline -ssize_t Vector<TYPE>::appendVector(const Vector<TYPE>& vector) { - return VectorImpl::appendVector(reinterpret_cast<const VectorImpl&>(vector)); -} - -template<class TYPE> inline -ssize_t Vector<TYPE>::insertArrayAt(const TYPE* array, size_t index, size_t length) { - return VectorImpl::insertArrayAt(array, index, length); -} - -template<class TYPE> inline -ssize_t Vector<TYPE>::appendArray(const TYPE* array, size_t length) { - return VectorImpl::appendArray(array, length); -} - -template<class TYPE> inline -ssize_t Vector<TYPE>::insertAt(const TYPE& item, size_t index, size_t numItems) { - return VectorImpl::insertAt(&item, index, numItems); -} - -template<class TYPE> inline -void Vector<TYPE>::push(const TYPE& item) { - return VectorImpl::push(&item); -} - -template<class TYPE> inline -ssize_t Vector<TYPE>::add(const TYPE& item) { - return VectorImpl::add(&item); -} - -template<class TYPE> inline -ssize_t Vector<TYPE>::replaceAt(const TYPE& item, size_t index) { - return VectorImpl::replaceAt(&item, index); -} - -template<class TYPE> inline -ssize_t Vector<TYPE>::insertAt(size_t index, size_t numItems) { - return VectorImpl::insertAt(index, numItems); -} - -template<class TYPE> inline -void Vector<TYPE>::pop() { - VectorImpl::pop(); -} - -template<class TYPE> inline -void Vector<TYPE>::push() { - VectorImpl::push(); -} - -template<class TYPE> inline -ssize_t Vector<TYPE>::add() { - return VectorImpl::add(); -} - -template<class TYPE> inline -ssize_t Vector<TYPE>::replaceAt(size_t index) { - return VectorImpl::replaceAt(index); -} - -template<class TYPE> inline -ssize_t Vector<TYPE>::removeItemsAt(size_t index, size_t count) { - return VectorImpl::removeItemsAt(index, count); -} - -template<class TYPE> inline -status_t Vector<TYPE>::sort(Vector<TYPE>::compar_t cmp) { - return VectorImpl::sort((VectorImpl::compar_t)cmp); -} - -template<class TYPE> inline -status_t Vector<TYPE>::sort(Vector<TYPE>::compar_r_t cmp, void* state) { - return VectorImpl::sort((VectorImpl::compar_r_t)cmp, state); -} - -// --------------------------------------------------------------------------- - -template<class TYPE> -void Vector<TYPE>::do_construct(void* storage, size_t num) const { - construct_type( reinterpret_cast<TYPE*>(storage), num ); -} - -template<class TYPE> -void Vector<TYPE>::do_destroy(void* storage, size_t num) const { - destroy_type( reinterpret_cast<TYPE*>(storage), num ); -} - -template<class TYPE> -void Vector<TYPE>::do_copy(void* dest, const void* from, size_t num) const { - copy_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num ); -} - -template<class TYPE> -void Vector<TYPE>::do_splat(void* dest, const void* item, size_t num) const { - splat_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(item), num ); -} - -template<class TYPE> -void Vector<TYPE>::do_move_forward(void* dest, const void* from, size_t num) const { - move_forward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num ); -} - -template<class TYPE> -void Vector<TYPE>::do_move_backward(void* dest, const void* from, size_t num) const { - move_backward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num ); -} - -}; // namespace android - - -// --------------------------------------------------------------------------- - -#endif // ANDROID_VECTOR_H diff --git a/include/utils/VectorImpl.h b/include/utils/VectorImpl.h deleted file mode 100644 index 9bc50e6..0000000 --- a/include/utils/VectorImpl.h +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright (C) 2005 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_VECTOR_IMPL_H -#define ANDROID_VECTOR_IMPL_H - -#include <assert.h> -#include <stdint.h> -#include <sys/types.h> -#include <utils/Errors.h> - -// --------------------------------------------------------------------------- -// No user serviceable parts in here... -// --------------------------------------------------------------------------- - -namespace android { - -/*! - * Implementation of the guts of the vector<> class - * this ensures backward binary compatibility and - * reduces code size. - * For performance reasons, we expose mStorage and mCount - * so these fields are set in stone. - * - */ - -class VectorImpl -{ -public: - enum { // flags passed to the ctor - HAS_TRIVIAL_CTOR = 0x00000001, - HAS_TRIVIAL_DTOR = 0x00000002, - HAS_TRIVIAL_COPY = 0x00000004, - }; - - VectorImpl(size_t itemSize, uint32_t flags); - VectorImpl(const VectorImpl& rhs); - virtual ~VectorImpl(); - - /*! must be called from subclasses destructor */ - void finish_vector(); - - VectorImpl& operator = (const VectorImpl& rhs); - - /*! C-style array access */ - inline const void* arrayImpl() const { return mStorage; } - void* editArrayImpl(); - - /*! vector stats */ - inline size_t size() const { return mCount; } - inline bool isEmpty() const { return mCount == 0; } - size_t capacity() const; - ssize_t setCapacity(size_t size); - ssize_t resize(size_t size); - - /*! append/insert another vector or array */ - ssize_t insertVectorAt(const VectorImpl& vector, size_t index); - ssize_t appendVector(const VectorImpl& vector); - ssize_t insertArrayAt(const void* array, size_t index, size_t length); - ssize_t appendArray(const void* array, size_t length); - - /*! add/insert/replace items */ - ssize_t insertAt(size_t where, size_t numItems = 1); - ssize_t insertAt(const void* item, size_t where, size_t numItems = 1); - void pop(); - void push(); - void push(const void* item); - ssize_t add(); - ssize_t add(const void* item); - ssize_t replaceAt(size_t index); - ssize_t replaceAt(const void* item, size_t index); - - /*! remove items */ - ssize_t removeItemsAt(size_t index, size_t count = 1); - void clear(); - - const void* itemLocation(size_t index) const; - void* editItemLocation(size_t index); - - typedef int (*compar_t)(const void* lhs, const void* rhs); - typedef int (*compar_r_t)(const void* lhs, const void* rhs, void* state); - status_t sort(compar_t cmp); - status_t sort(compar_r_t cmp, void* state); - -protected: - size_t itemSize() const; - void release_storage(); - - virtual void do_construct(void* storage, size_t num) const = 0; - virtual void do_destroy(void* storage, size_t num) const = 0; - virtual void do_copy(void* dest, const void* from, size_t num) const = 0; - virtual void do_splat(void* dest, const void* item, size_t num) const = 0; - virtual void do_move_forward(void* dest, const void* from, size_t num) const = 0; - virtual void do_move_backward(void* dest, const void* from, size_t num) const = 0; - - // take care of FBC... - virtual void reservedVectorImpl1(); - virtual void reservedVectorImpl2(); - virtual void reservedVectorImpl3(); - virtual void reservedVectorImpl4(); - virtual void reservedVectorImpl5(); - virtual void reservedVectorImpl6(); - virtual void reservedVectorImpl7(); - virtual void reservedVectorImpl8(); - -private: - void* _grow(size_t where, size_t amount); - void _shrink(size_t where, size_t amount); - - inline void _do_construct(void* storage, size_t num) const; - inline void _do_destroy(void* storage, size_t num) const; - inline void _do_copy(void* dest, const void* from, size_t num) const; - inline void _do_splat(void* dest, const void* item, size_t num) const; - inline void _do_move_forward(void* dest, const void* from, size_t num) const; - inline void _do_move_backward(void* dest, const void* from, size_t num) const; - - // These 2 fields are exposed in the inlines below, - // so they're set in stone. - void * mStorage; // base address of the vector - size_t mCount; // number of items - - const uint32_t mFlags; - const size_t mItemSize; -}; - - - -class SortedVectorImpl : public VectorImpl -{ -public: - SortedVectorImpl(size_t itemSize, uint32_t flags); - SortedVectorImpl(const VectorImpl& rhs); - virtual ~SortedVectorImpl(); - - SortedVectorImpl& operator = (const SortedVectorImpl& rhs); - - //! finds the index of an item - ssize_t indexOf(const void* item) const; - - //! finds where this item should be inserted - size_t orderOf(const void* item) const; - - //! add an item in the right place (or replaces it if there is one) - ssize_t add(const void* item); - - //! merges a vector into this one - ssize_t merge(const VectorImpl& vector); - ssize_t merge(const SortedVectorImpl& vector); - - //! removes an item - ssize_t remove(const void* item); - -protected: - virtual int do_compare(const void* lhs, const void* rhs) const = 0; - - // take care of FBC... - virtual void reservedSortedVectorImpl1(); - virtual void reservedSortedVectorImpl2(); - virtual void reservedSortedVectorImpl3(); - virtual void reservedSortedVectorImpl4(); - virtual void reservedSortedVectorImpl5(); - virtual void reservedSortedVectorImpl6(); - virtual void reservedSortedVectorImpl7(); - virtual void reservedSortedVectorImpl8(); - -private: - ssize_t _indexOrderOf(const void* item, size_t* order = 0) const; - - // these are made private, because they can't be used on a SortedVector - // (they don't have an implementation either) - ssize_t add(); - void pop(); - void push(); - void push(const void* item); - ssize_t insertVectorAt(const VectorImpl& vector, size_t index); - ssize_t appendVector(const VectorImpl& vector); - ssize_t insertArrayAt(const void* array, size_t index, size_t length); - ssize_t appendArray(const void* array, size_t length); - ssize_t insertAt(size_t where, size_t numItems = 1); - ssize_t insertAt(const void* item, size_t where, size_t numItems = 1); - ssize_t replaceAt(size_t index); - ssize_t replaceAt(const void* item, size_t index); -}; - -}; // namespace android - - -// --------------------------------------------------------------------------- - -#endif // ANDROID_VECTOR_IMPL_H diff --git a/include/utils/WorkQueue.h b/include/utils/WorkQueue.h deleted file mode 100644 index e3c75b2..0000000 --- a/include/utils/WorkQueue.h +++ /dev/null @@ -1,119 +0,0 @@ -/*] - * Copyright (C) 2012 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 _LIBS_UTILS_WORK_QUEUE_H -#define _LIBS_UTILS_WORK_QUEUE_H - -#include <utils/Errors.h> -#include <utils/Vector.h> -#include <utils/threads.h> - -namespace android { - -/* - * A threaded work queue. - * - * This class is designed to make it easy to run a bunch of isolated work - * units in parallel, using up to the specified number of threads. - * To use it, write a loop to post work units to the work queue, then synchronize - * on the queue at the end. - */ -class WorkQueue { -public: - class WorkUnit { - public: - WorkUnit() { } - virtual ~WorkUnit() { } - - /* - * Runs the work unit. - * If the result is 'true' then the work queue continues scheduling work as usual. - * If the result is 'false' then the work queue is canceled. - */ - virtual bool run() = 0; - }; - - /* Creates a work queue with the specified maximum number of work threads. */ - WorkQueue(size_t maxThreads, bool canCallJava = true); - - /* Destroys the work queue. - * Cancels pending work and waits for all remaining threads to complete. - */ - ~WorkQueue(); - - /* Posts a work unit to run later. - * If the work queue has been canceled or is already finished, returns INVALID_OPERATION - * and does not take ownership of the work unit (caller must destroy it itself). - * Otherwise, returns OK and takes ownership of the work unit (the work queue will - * destroy it automatically). - * - * For flow control, this method blocks when the size of the pending work queue is more - * 'backlog' times the number of threads. This condition reduces the rate of entry into - * the pending work queue and prevents it from growing much more rapidly than the - * work threads can actually handle. - * - * If 'backlog' is 0, then no throttle is applied. - */ - status_t schedule(WorkUnit* workUnit, size_t backlog = 2); - - /* Cancels all pending work. - * If the work queue is already finished, returns INVALID_OPERATION. - * If the work queue is already canceled, returns OK and does nothing else. - * Otherwise, returns OK, discards all pending work units and prevents additional - * work units from being scheduled. - * - * Call finish() after cancel() to wait for all remaining work to complete. - */ - status_t cancel(); - - /* Waits for all work to complete. - * If the work queue is already finished, returns INVALID_OPERATION. - * Otherwise, waits for all work to complete and returns OK. - */ - status_t finish(); - -private: - class WorkThread : public Thread { - public: - WorkThread(WorkQueue* workQueue, bool canCallJava); - virtual ~WorkThread(); - - private: - virtual bool threadLoop(); - - WorkQueue* const mWorkQueue; - }; - - status_t cancelLocked(); - bool threadLoop(); // called from each work thread - - const size_t mMaxThreads; - const bool mCanCallJava; - - Mutex mLock; - Condition mWorkChangedCondition; - Condition mWorkDequeuedCondition; - - bool mCanceled; - bool mFinished; - size_t mIdleThreads; - Vector<sp<WorkThread> > mWorkThreads; - Vector<WorkUnit*> mWorkUnits; -}; - -}; // namespace android - -#endif // _LIBS_UTILS_WORK_QUEUE_H diff --git a/include/utils/ZipFileCRO.h b/include/utils/ZipFileCRO.h deleted file mode 100644 index 3e42a95..0000000 --- a/include/utils/ZipFileCRO.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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. - */ - -// -// C API for ead-only access to Zip archives, with minimal heap allocation. -// -#ifndef __LIBS_ZIPFILECRO_H -#define __LIBS_ZIPFILECRO_H - -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> - -#include <utils/Compat.h> - -#ifdef __cplusplus -extern "C" { -#endif - -/* - * Trivial typedef to ensure that ZipFileCRO is not treated as a simple integer. - */ -typedef void* ZipFileCRO; - -/* - * Trivial typedef to ensure that ZipEntryCRO is not treated as a simple - * integer. We use NULL to indicate an invalid value. - */ -typedef void* ZipEntryCRO; - -extern ZipFileCRO ZipFileXRO_open(const char* path); - -extern void ZipFileCRO_destroy(ZipFileCRO zip); - -extern ZipEntryCRO ZipFileCRO_findEntryByName(ZipFileCRO zip, - const char* fileName); - -extern bool ZipFileCRO_getEntryInfo(ZipFileCRO zip, ZipEntryCRO entry, - int* pMethod, size_t* pUncompLen, - size_t* pCompLen, off64_t* pOffset, long* pModWhen, long* pCrc32); - -extern bool ZipFileCRO_uncompressEntry(ZipFileCRO zip, ZipEntryCRO entry, int fd); - -#ifdef __cplusplus -} -#endif - -#endif /*__LIBS_ZIPFILECRO_H*/ diff --git a/include/utils/ZipFileRO.h b/include/utils/ZipFileRO.h deleted file mode 100644 index 547e36a..0000000 --- a/include/utils/ZipFileRO.h +++ /dev/null @@ -1,262 +0,0 @@ -/* - * Copyright (C) 2007 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. - */ - -/* - * Read-only access to Zip archives, with minimal heap allocation. - * - * This is similar to the more-complete ZipFile class, but no attempt - * has been made to make them interchangeable. This class operates under - * a very different set of assumptions and constraints. - * - * One such assumption is that if you're getting file descriptors for - * use with this class as a child of a fork() operation, you must be on - * a pread() to guarantee correct operation. This is because pread() can - * atomically read at a file offset without worrying about a lock around an - * lseek() + read() pair. - */ -#ifndef __LIBS_ZIPFILERO_H -#define __LIBS_ZIPFILERO_H - -#include <utils/Compat.h> -#include <utils/Errors.h> -#include <utils/FileMap.h> -#include <utils/threads.h> - -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <time.h> - -namespace android { - -/* - * Trivial typedef to ensure that ZipEntryRO is not treated as a simple - * integer. We use NULL to indicate an invalid value. - */ -typedef void* ZipEntryRO; - -/* - * Open a Zip archive for reading. - * - * We want "open" and "find entry by name" to be fast operations, and we - * want to use as little memory as possible. We memory-map the file, - * and load a hash table with pointers to the filenames (which aren't - * null-terminated). The other fields are at a fixed offset from the - * filename, so we don't need to extract those (but we do need to byte-read - * and endian-swap them every time we want them). - * - * To speed comparisons when doing a lookup by name, we could make the mapping - * "private" (copy-on-write) and null-terminate the filenames after verifying - * the record structure. However, this requires a private mapping of - * every page that the Central Directory touches. Easier to tuck a copy - * of the string length into the hash table entry. - * - * NOTE: If this is used on file descriptors inherited from a fork() operation, - * you must be on a platform that implements pread() to guarantee correctness - * on the shared file descriptors. - */ -class ZipFileRO { -public: - ZipFileRO() - : mFd(-1), mFileName(NULL), mFileLength(-1), - mDirectoryMap(NULL), - mNumEntries(-1), mDirectoryOffset(-1), - mHashTableSize(-1), mHashTable(NULL) - {} - - ~ZipFileRO(); - - /* - * Open an archive. - */ - status_t open(const char* zipFileName); - - /* - * Find an entry, by name. Returns the entry identifier, or NULL if - * not found. - * - * If two entries have the same name, one will be chosen at semi-random. - */ - ZipEntryRO findEntryByName(const char* fileName) const; - - /* - * Return the #of entries in the Zip archive. - */ - int getNumEntries(void) const { - return mNumEntries; - } - - /* - * Return the Nth entry. Zip file entries are not stored in sorted - * order, and updated entries may appear at the end, so anyone walking - * the archive needs to avoid making ordering assumptions. We take - * that further by returning the Nth non-empty entry in the hash table - * rather than the Nth entry in the archive. - * - * Valid values are [0..numEntries). - * - * [This is currently O(n). If it needs to be fast we can allocate an - * additional data structure or provide an iterator interface.] - */ - ZipEntryRO findEntryByIndex(int idx) const; - - /* - * Copy the filename into the supplied buffer. Returns 0 on success, - * -1 if "entry" is invalid, or the filename length if it didn't fit. The - * length, and the returned string, include the null-termination. - */ - int getEntryFileName(ZipEntryRO entry, char* buffer, int bufLen) const; - - /* - * Get the vital stats for an entry. Pass in NULL pointers for anything - * you don't need. - * - * "*pOffset" holds the Zip file offset of the entry's data. - * - * Returns "false" if "entry" is bogus or if the data in the Zip file - * appears to be bad. - */ - bool getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, - size_t* pCompLen, off64_t* pOffset, long* pModWhen, long* pCrc32) const; - - /* - * Create a new FileMap object that maps a subset of the archive. For - * an uncompressed entry this effectively provides a pointer to the - * actual data, for a compressed entry this provides the input buffer - * for inflate(). - */ - FileMap* createEntryFileMap(ZipEntryRO entry) const; - - /* - * Uncompress the data into a buffer. Depending on the compression - * format, this is either an "inflate" operation or a memcpy. - * - * Use "uncompLen" from getEntryInfo() to determine the required - * buffer size. - * - * Returns "true" on success. - */ - bool uncompressEntry(ZipEntryRO entry, void* buffer) const; - - /* - * Uncompress the data to an open file descriptor. - */ - bool uncompressEntry(ZipEntryRO entry, int fd) const; - - /* Zip compression methods we support */ - enum { - kCompressStored = 0, // no compression - kCompressDeflated = 8, // standard deflate - }; - - /* - * Utility function: uncompress deflated data, buffer to buffer. - */ - static bool inflateBuffer(void* outBuf, const void* inBuf, - size_t uncompLen, size_t compLen); - - /* - * Utility function: uncompress deflated data, buffer to fd. - */ - static bool inflateBuffer(int fd, const void* inBuf, - size_t uncompLen, size_t compLen); - - /* - * Utility function to convert ZIP's time format to a timespec struct. - */ - static inline void zipTimeToTimespec(long when, struct tm* timespec) { - const long date = when >> 16; - timespec->tm_year = ((date >> 9) & 0x7F) + 80; // Zip is years since 1980 - timespec->tm_mon = (date >> 5) & 0x0F; - timespec->tm_mday = date & 0x1F; - - timespec->tm_hour = (when >> 11) & 0x1F; - timespec->tm_min = (when >> 5) & 0x3F; - timespec->tm_sec = (when & 0x1F) << 1; - } - - /* - * Some basic functions for raw data manipulation. "LE" means - * Little Endian. - */ - static inline unsigned short get2LE(const unsigned char* buf) { - return buf[0] | (buf[1] << 8); - } - static inline unsigned long get4LE(const unsigned char* buf) { - return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); - } - -private: - /* these are private and not defined */ - ZipFileRO(const ZipFileRO& src); - ZipFileRO& operator=(const ZipFileRO& src); - - /* locate and parse the central directory */ - bool mapCentralDirectory(void); - - /* parse the archive, prepping internal structures */ - bool parseZipArchive(void); - - /* add a new entry to the hash table */ - void addToHash(const char* str, int strLen, unsigned int hash); - - /* compute string hash code */ - static unsigned int computeHash(const char* str, int len); - - /* convert a ZipEntryRO back to a hash table index */ - int entryToIndex(const ZipEntryRO entry) const; - - /* - * One entry in the hash table. - */ - typedef struct HashEntry { - const char* name; - unsigned short nameLen; - //unsigned int hash; - } HashEntry; - - /* open Zip archive */ - int mFd; - - /* Lock for handling the file descriptor (seeks, etc) */ - mutable Mutex mFdLock; - - /* zip file name */ - char* mFileName; - - /* length of file */ - size_t mFileLength; - - /* mapped file */ - FileMap* mDirectoryMap; - - /* number of entries in the Zip archive */ - int mNumEntries; - - /* CD directory offset in the Zip archive */ - off64_t mDirectoryOffset; - - /* - * We know how many entries are in the Zip archive, so we have a - * fixed-size hash table. We probe for an empty slot. - */ - int mHashTableSize; - HashEntry* mHashTable; -}; - -}; // namespace android - -#endif /*__LIBS_ZIPFILERO_H*/ diff --git a/include/utils/ZipUtils.h b/include/utils/ZipUtils.h deleted file mode 100644 index 42c42b6..0000000 --- a/include/utils/ZipUtils.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2007 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. - */ - -// -// Miscellaneous zip/gzip utility functions. -// -#ifndef __LIBS_ZIPUTILS_H -#define __LIBS_ZIPUTILS_H - -#include <stdio.h> - -namespace android { - -/* - * Container class for utility functions, primarily for namespace reasons. - */ -class ZipUtils { -public: - /* - * General utility function for uncompressing "deflate" data from a file - * to a buffer. - */ - static bool inflateToBuffer(int fd, void* buf, long uncompressedLen, - long compressedLen); - static bool inflateToBuffer(FILE* fp, void* buf, long uncompressedLen, - long compressedLen); - - /* - * Someday we might want to make this generic and handle bzip2 ".bz2" - * files too. - * - * We could declare gzip to be a sub-class of zip that has exactly - * one always-compressed entry, but we currently want to treat Zip - * and gzip as distinct, so there's no value. - * - * The zlib library has some gzip utilities, but it has no interface - * for extracting the uncompressed length of the file (you do *not* - * want to gzseek to the end). - * - * Pass in a seeked file pointer for the gzip file. If this is a gzip - * file, we set our return values appropriately and return "true" with - * the file seeked to the start of the compressed data. - */ - static bool examineGzip(FILE* fp, int* pCompressionMethod, - long* pUncompressedLen, long* pCompressedLen, unsigned long* pCRC32); - -private: - ZipUtils() {} - ~ZipUtils() {} -}; - -}; // namespace android - -#endif /*__LIBS_ZIPUTILS_H*/ diff --git a/include/utils/ashmem.h b/include/utils/ashmem.h deleted file mode 100644 index 0854775..0000000 --- a/include/utils/ashmem.h +++ /dev/null @@ -1,41 +0,0 @@ -/* utils/ashmem.h - ** - ** Copyright 2008 The Android Open Source Project - ** - ** This file is dual licensed. It may be redistributed and/or modified - ** under the terms of the Apache 2.0 License OR version 2 of the GNU - ** General Public License. - */ - -#ifndef _UTILS_ASHMEM_H -#define _UTILS_ASHMEM_H - -#include <linux/limits.h> -#include <linux/ioctl.h> - -#define ASHMEM_NAME_LEN 256 - -#define ASHMEM_NAME_DEF "dev/ashmem" - -/* Return values from ASHMEM_PIN: Was the mapping purged while unpinned? */ -#define ASHMEM_NOT_REAPED 0 -#define ASHMEM_WAS_REAPED 1 - -/* Return values from ASHMEM_UNPIN: Is the mapping now pinned or unpinned? */ -#define ASHMEM_NOW_UNPINNED 0 -#define ASHMEM_NOW_PINNED 1 - -#define __ASHMEMIOC 0x77 - -#define ASHMEM_SET_NAME _IOW(__ASHMEMIOC, 1, char[ASHMEM_NAME_LEN]) -#define ASHMEM_GET_NAME _IOR(__ASHMEMIOC, 2, char[ASHMEM_NAME_LEN]) -#define ASHMEM_SET_SIZE _IOW(__ASHMEMIOC, 3, size_t) -#define ASHMEM_GET_SIZE _IO(__ASHMEMIOC, 4) -#define ASHMEM_SET_PROT_MASK _IOW(__ASHMEMIOC, 5, unsigned long) -#define ASHMEM_GET_PROT_MASK _IO(__ASHMEMIOC, 6) -#define ASHMEM_PIN _IO(__ASHMEMIOC, 7) -#define ASHMEM_UNPIN _IO(__ASHMEMIOC, 8) -#define ASHMEM_ISPINNED _IO(__ASHMEMIOC, 9) -#define ASHMEM_PURGE_ALL_CACHES _IO(__ASHMEMIOC, 10) - -#endif /* _UTILS_ASHMEM_H */ diff --git a/include/utils/misc.h b/include/utils/misc.h deleted file mode 100644 index f1aa432..0000000 --- a/include/utils/misc.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2005 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. - */ - -// -// Handy utility functions and portability code. -// -#ifndef _LIBS_UTILS_MISC_H -#define _LIBS_UTILS_MISC_H - -#include <sys/time.h> -#include <utils/Endian.h> - -/* get #of elements in a static array */ -#ifndef NELEM -# define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0]))) -#endif - -namespace android { - -/* - * Some utility functions for working with files. These could be made - * part of a "File" class. - */ -typedef enum FileType { - kFileTypeUnknown = 0, - kFileTypeNonexistent, // i.e. ENOENT - kFileTypeRegular, - kFileTypeDirectory, - kFileTypeCharDev, - kFileTypeBlockDev, - kFileTypeFifo, - kFileTypeSymlink, - kFileTypeSocket, -} FileType; -/* get the file's type; follows symlinks */ -FileType getFileType(const char* fileName); -/* get the file's modification date; returns -1 w/errno set on failure */ -time_t getFileModDate(const char* fileName); - -typedef void (*sysprop_change_callback)(void); -void add_sysprop_change_callback(sysprop_change_callback cb, int priority); -void report_sysprop_change(); - -}; // namespace android - -#endif // _LIBS_UTILS_MISC_H diff --git a/include/utils/threads.h b/include/utils/threads.h deleted file mode 100644 index 9de3382..0000000 --- a/include/utils/threads.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2007 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 _LIBS_UTILS_THREADS_H -#define _LIBS_UTILS_THREADS_H - -/* - * Please, DO NOT USE! - * - * This file is here only for legacy reasons. Instead, include directly - * the headers you need below. - * - */ - -#include <utils/AndroidThreads.h> - -#ifdef __cplusplus -#include <utils/Condition.h> -#include <utils/Errors.h> -#include <utils/Mutex.h> -#include <utils/RWLock.h> -#include <utils/Thread.h> -#endif - -#endif // _LIBS_UTILS_THREADS_H diff --git a/libs/binder/Android.mk b/libs/binder/Android.mk index 994d3db..f3f8daf 100644 --- a/libs/binder/Android.mk +++ b/libs/binder/Android.mk @@ -17,6 +17,8 @@ sources := \ AppOpsManager.cpp \ Binder.cpp \ BpBinder.cpp \ + BufferedTextOutput.cpp \ + Debug.cpp \ IAppOpsCallback.cpp \ IAppOpsService.cpp \ IInterface.cpp \ @@ -30,7 +32,8 @@ sources := \ Parcel.cpp \ PermissionCache.cpp \ ProcessState.cpp \ - Static.cpp + Static.cpp \ + TextOutput.cpp \ LOCAL_PATH:= $(call my-dir) @@ -44,5 +47,6 @@ include $(BUILD_SHARED_LIBRARY) include $(CLEAR_VARS) LOCAL_LDLIBS += -lpthread LOCAL_MODULE := libbinder +LOCAL_STATIC_LIBRARIES += libutils LOCAL_SRC_FILES := $(sources) include $(BUILD_STATIC_LIBRARY) diff --git a/libs/binder/AppOpsManager.cpp b/libs/binder/AppOpsManager.cpp index 7ac1b11..61b4f7d 100644 --- a/libs/binder/AppOpsManager.cpp +++ b/libs/binder/AppOpsManager.cpp @@ -15,6 +15,7 @@ */ #include <binder/AppOpsManager.h> +#include <binder/Binder.h> #include <binder/IServiceManager.h> #include <utils/SystemClock.h> @@ -22,6 +23,17 @@ namespace android { static String16 _appops("appops"); +static pthread_mutex_t gTokenMutex = PTHREAD_MUTEX_INITIALIZER; +static sp<IBinder> gToken; + +static const sp<IBinder>& getToken(const sp<IAppOpsService>& service) { + pthread_mutex_lock(&gTokenMutex); + if (gToken == NULL) { + gToken = service->getToken(new BBinder()); + } + pthread_mutex_unlock(&gTokenMutex); + return gToken; +} AppOpsManager::AppOpsManager() { @@ -66,13 +78,14 @@ int32_t AppOpsManager::noteOp(int32_t op, int32_t uid, const String16& callingPa int32_t AppOpsManager::startOp(int32_t op, int32_t uid, const String16& callingPackage) { sp<IAppOpsService> service = getService(); - return service != NULL ? service->startOperation(op, uid, callingPackage) : MODE_IGNORED; + return service != NULL ? service->startOperation(getToken(service), op, uid, callingPackage) + : MODE_IGNORED; } void AppOpsManager::finishOp(int32_t op, int32_t uid, const String16& callingPackage) { sp<IAppOpsService> service = getService(); if (service != NULL) { - service->finishOperation(op, uid, callingPackage); + service->finishOperation(getToken(service), op, uid, callingPackage); } } diff --git a/libs/utils/BufferedTextOutput.cpp b/libs/binder/BufferedTextOutput.cpp index 989662e..2d493c1 100644 --- a/libs/utils/BufferedTextOutput.cpp +++ b/libs/binder/BufferedTextOutput.cpp @@ -14,16 +14,16 @@ * limitations under the License. */ -#include <utils/BufferedTextOutput.h> +#include <binder/BufferedTextOutput.h> +#include <binder/Debug.h> #include <utils/Atomic.h> -#include <utils/Debug.h> #include <utils/Log.h> #include <utils/RefBase.h> #include <utils/Vector.h> #include <cutils/threads.h> -#include <private/utils/Static.h> +#include <private/binder/Static.h> #include <stdio.h> #include <stdlib.h> diff --git a/libs/utils/Debug.cpp b/libs/binder/Debug.cpp index e8ac983..a8b6e83 100644 --- a/libs/utils/Debug.cpp +++ b/libs/binder/Debug.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include <utils/Debug.h> +#include <binder/Debug.h> #include <utils/misc.h> @@ -70,20 +70,6 @@ static char* appendhexnum(uint32_t val, char* out) return out; } -static inline char makeupperhexdigit(uint32_t val) -{ - return "0123456789ABCDEF"[val&0xF]; -} - -static char* appendupperhexnum(uint32_t val, char* out) -{ - for( int32_t i=28; i>=0; i-=4 ) { - *out++ = makeupperhexdigit( val>>i ); - } - *out = 0; - return out; -} - static char* appendcharornum(char c, char* out, bool skipzero = true) { if (skipzero && c == 0) return out; diff --git a/libs/binder/IAppOpsService.cpp b/libs/binder/IAppOpsService.cpp index 282b30f..471e3e9 100644 --- a/libs/binder/IAppOpsService.cpp +++ b/libs/binder/IAppOpsService.cpp @@ -61,9 +61,11 @@ public: return reply.readInt32(); } - virtual int32_t startOperation(int32_t code, int32_t uid, const String16& packageName) { + virtual int32_t startOperation(const sp<IBinder>& token, int32_t code, int32_t uid, + const String16& packageName) { Parcel data, reply; data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor()); + data.writeStrongBinder(token); data.writeInt32(code); data.writeInt32(uid); data.writeString16(packageName); @@ -73,9 +75,11 @@ public: return reply.readInt32(); } - virtual void finishOperation(int32_t code, int32_t uid, const String16& packageName) { + virtual void finishOperation(const sp<IBinder>& token, int32_t code, int32_t uid, + const String16& packageName) { Parcel data, reply; data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor()); + data.writeStrongBinder(token); data.writeInt32(code); data.writeInt32(uid); data.writeString16(packageName); @@ -98,6 +102,16 @@ public: data.writeStrongBinder(callback->asBinder()); remote()->transact(STOP_WATCHING_MODE_TRANSACTION, data, &reply); } + + virtual sp<IBinder> getToken(const sp<IBinder>& clientToken) { + Parcel data, reply; + data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor()); + data.writeStrongBinder(clientToken); + remote()->transact(GET_TOKEN_TRANSACTION, data, &reply); + // fail on exception + if (reply.readExceptionCode() != 0) return NULL; + return reply.readStrongBinder(); + } }; IMPLEMENT_META_INTERFACE(AppOpsService, "com.android.internal.app.IAppOpsService"); @@ -131,20 +145,22 @@ status_t BnAppOpsService::onTransact( } break; case START_OPERATION_TRANSACTION: { CHECK_INTERFACE(IAppOpsService, data, reply); + sp<IBinder> token = data.readStrongBinder(); int32_t code = data.readInt32(); int32_t uid = data.readInt32(); String16 packageName = data.readString16(); - int32_t res = startOperation(code, uid, packageName); + int32_t res = startOperation(token, code, uid, packageName); reply->writeNoException(); reply->writeInt32(res); return NO_ERROR; } break; case FINISH_OPERATION_TRANSACTION: { CHECK_INTERFACE(IAppOpsService, data, reply); + sp<IBinder> token = data.readStrongBinder(); int32_t code = data.readInt32(); int32_t uid = data.readInt32(); String16 packageName = data.readString16(); - finishOperation(code, uid, packageName); + finishOperation(token, code, uid, packageName); reply->writeNoException(); return NO_ERROR; } break; @@ -164,6 +180,14 @@ status_t BnAppOpsService::onTransact( reply->writeNoException(); return NO_ERROR; } break; + case GET_TOKEN_TRANSACTION: { + CHECK_INTERFACE(IAppOpsService, data, reply); + sp<IBinder> clientToken = data.readStrongBinder(); + sp<IBinder> token = getToken(clientToken); + reply->writeNoException(); + reply->writeStrongBinder(token); + return NO_ERROR; + } break; default: return BBinder::onTransact(code, data, reply, flags); } diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp index 2ffa927..5951a3f 100644 --- a/libs/binder/IPCThreadState.cpp +++ b/libs/binder/IPCThreadState.cpp @@ -20,10 +20,11 @@ #include <binder/Binder.h> #include <binder/BpBinder.h> +#include <binder/TextOutput.h> + #include <cutils/sched_policy.h> #include <utils/Debug.h> #include <utils/Log.h> -#include <utils/TextOutput.h> #include <utils/threads.h> #include <private/binder/binder_module.h> @@ -361,12 +362,12 @@ status_t IPCThreadState::clearLastError() return err; } -int IPCThreadState::getCallingPid() +int IPCThreadState::getCallingPid() const { return mCallingPid; } -int IPCThreadState::getCallingUid() +int IPCThreadState::getCallingUid() const { return mCallingUid; } @@ -417,6 +418,60 @@ void IPCThreadState::flushCommands() talkWithDriver(false); } +status_t IPCThreadState::getAndExecuteCommand() +{ + status_t result; + int32_t cmd; + + result = talkWithDriver(); + if (result >= NO_ERROR) { + size_t IN = mIn.dataAvail(); + if (IN < sizeof(int32_t)) return result; + cmd = mIn.readInt32(); + IF_LOG_COMMANDS() { + alog << "Processing top-level Command: " + << getReturnString(cmd) << endl; + } + + result = executeCommand(cmd); + + // After executing the command, ensure that the thread is returned to the + // foreground cgroup before rejoining the pool. The driver takes care of + // restoring the priority, but doesn't do anything with cgroups so we + // need to take care of that here in userspace. Note that we do make + // sure to go in the foreground after executing a transaction, but + // there are other callbacks into user code that could have changed + // our group so we want to make absolutely sure it is put back. + set_sched_policy(mMyThreadId, SP_FOREGROUND); + } + + return result; +} + +// When we've cleared the incoming command queue, process any pending derefs +void IPCThreadState::processPendingDerefs() +{ + if (mIn.dataPosition() >= mIn.dataSize()) { + size_t numPending = mPendingWeakDerefs.size(); + if (numPending > 0) { + for (size_t i = 0; i < numPending; i++) { + RefBase::weakref_type* refs = mPendingWeakDerefs[i]; + refs->decWeak(mProcess.get()); + } + mPendingWeakDerefs.clear(); + } + + numPending = mPendingStrongDerefs.size(); + if (numPending > 0) { + for (size_t i = 0; i < numPending; i++) { + BBinder* obj = mPendingStrongDerefs[i]; + obj->decStrong(mProcess.get()); + } + mPendingStrongDerefs.clear(); + } + } +} + void IPCThreadState::joinThreadPool(bool isMain) { LOG_THREADPOOL("**** THREAD %p (PID %d) IS JOINING THE THREAD POOL\n", (void*)pthread_self(), getpid()); @@ -430,57 +485,16 @@ void IPCThreadState::joinThreadPool(bool isMain) status_t result; do { - int32_t cmd; - - // When we've cleared the incoming command queue, process any pending derefs - if (mIn.dataPosition() >= mIn.dataSize()) { - size_t numPending = mPendingWeakDerefs.size(); - if (numPending > 0) { - for (size_t i = 0; i < numPending; i++) { - RefBase::weakref_type* refs = mPendingWeakDerefs[i]; - refs->decWeak(mProcess.get()); - } - mPendingWeakDerefs.clear(); - } - - numPending = mPendingStrongDerefs.size(); - if (numPending > 0) { - for (size_t i = 0; i < numPending; i++) { - BBinder* obj = mPendingStrongDerefs[i]; - obj->decStrong(mProcess.get()); - } - mPendingStrongDerefs.clear(); - } - } - + processPendingDerefs(); // now get the next command to be processed, waiting if necessary - result = talkWithDriver(); - if (result >= NO_ERROR) { - size_t IN = mIn.dataAvail(); - if (IN < sizeof(int32_t)) continue; - cmd = mIn.readInt32(); - IF_LOG_COMMANDS() { - alog << "Processing top-level Command: " - << getReturnString(cmd) << endl; - } + result = getAndExecuteCommand(); - - result = executeCommand(cmd); - } else if (result != TIMED_OUT && result != -ECONNREFUSED && result != -EBADF) { - ALOGE("talkWithDriver(fd=%d) returned unexpected error %d, aborting", + if (result < NO_ERROR && result != TIMED_OUT && result != -ECONNREFUSED && result != -EBADF) { + ALOGE("getAndExecuteCommand(fd=%d) returned unexpected error %d, aborting", mProcess->mDriverFD, result); abort(); } - // After executing the command, ensure that the thread is returned to the - // foreground cgroup before rejoining the pool. The driver takes care of - // restoring the priority, but doesn't do anything with cgroups so we - // need to take care of that here in userspace. Note that we do make - // sure to go in the foreground after executing a transaction, but - // there are other callbacks into user code that could have changed - // our group so we want to make absolutely sure it is put back. - set_sched_policy(mMyThreadId, SP_FOREGROUND); - // Let this thread exit the thread pool if it is no longer // needed and it is not the main process thread. if(result == TIMED_OUT && !isMain) { @@ -495,6 +509,30 @@ void IPCThreadState::joinThreadPool(bool isMain) talkWithDriver(false); } +int IPCThreadState::setupPolling(int* fd) +{ + if (mProcess->mDriverFD <= 0) { + return -EBADF; + } + + mOut.writeInt32(BC_ENTER_LOOPER); + *fd = mProcess->mDriverFD; + return 0; +} + +status_t IPCThreadState::handlePolledCommands() +{ + status_t result; + + do { + result = getAndExecuteCommand(); + } while (mIn.dataPosition() < mIn.dataSize()); + + processPendingDerefs(); + flushCommands(); + return result; +} + void IPCThreadState::stopProcess(bool immediate) { //ALOGI("**** STOPPING PROCESS"); @@ -825,7 +863,7 @@ status_t IPCThreadState::talkWithDriver(bool doReceive) IF_LOG_COMMANDS() { alog << "Our err: " << (void*)err << ", write consumed: " << bwr.write_consumed << " (of " << mOut.dataSize() - << "), read consumed: " << bwr.read_consumed << endl; + << "), read consumed: " << bwr.read_consumed << endl; } if (err >= NO_ERROR) { @@ -1103,16 +1141,16 @@ status_t IPCThreadState::executeCommand(int32_t cmd) void IPCThreadState::threadDestructor(void *st) { - IPCThreadState* const self = static_cast<IPCThreadState*>(st); - if (self) { - self->flushCommands(); + IPCThreadState* const self = static_cast<IPCThreadState*>(st); + if (self) { + self->flushCommands(); #if defined(HAVE_ANDROID_OS) if (self->mProcess->mDriverFD > 0) { ioctl(self->mProcess->mDriverFD, BINDER_THREAD_EXIT, 0); } #endif - delete self; - } + delete self; + } } diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp index 1750640..a341ca8 100644 --- a/libs/binder/IServiceManager.cpp +++ b/libs/binder/IServiceManager.cpp @@ -37,9 +37,11 @@ sp<IServiceManager> defaultServiceManager() { AutoMutex _l(gDefaultServiceManagerLock); - if (gDefaultServiceManager == NULL) { + while (gDefaultServiceManager == NULL) { gDefaultServiceManager = interface_cast<IServiceManager>( ProcessState::self()->getContextObject(NULL)); + if (gDefaultServiceManager == NULL) + sleep(1); } } diff --git a/libs/binder/MemoryHeapBase.cpp b/libs/binder/MemoryHeapBase.cpp index d1cbf1c..43a01e4 100644 --- a/libs/binder/MemoryHeapBase.cpp +++ b/libs/binder/MemoryHeapBase.cpp @@ -31,11 +31,6 @@ #include <binder/MemoryHeapBase.h> -#ifdef HAVE_ANDROID_OS -#include <linux/android_pmem.h> -#endif - - namespace android { // --------------------------------------------------------------------------- @@ -108,18 +103,9 @@ status_t MemoryHeapBase::mapfd(int fd, size_t size, uint32_t offset) { if (size == 0) { // try to figure out the size automatically -#ifdef HAVE_ANDROID_OS - // first try the PMEM ioctl - pmem_region reg; - int err = ioctl(fd, PMEM_GET_TOTAL_SIZE, ®); - if (err == 0) - size = reg.len; -#endif - if (size == 0) { // try fstat - struct stat sb; - if (fstat(fd, &sb) == 0) - size = sb.st_size; - } + struct stat sb; + if (fstat(fd, &sb) == 0) + size = sb.st_size; // if it didn't work, let mmap() fail. } diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index c7bdcbc..7a5919f 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -22,12 +22,13 @@ #include <binder/IPCThreadState.h> #include <binder/Binder.h> #include <binder/BpBinder.h> -#include <utils/Debug.h> #include <binder/ProcessState.h> +#include <binder/TextOutput.h> + +#include <utils/Debug.h> #include <utils/Log.h> #include <utils/String8.h> #include <utils/String16.h> -#include <utils/TextOutput.h> #include <utils/misc.h> #include <utils/Flattenable.h> #include <cutils/ashmem.h> @@ -797,13 +798,13 @@ status_t Parcel::writeBlob(size_t len, WritableBlob* outBlob) return status; } -status_t Parcel::write(const Flattenable& val) +status_t Parcel::write(const FlattenableHelperInterface& val) { status_t err; // size if needed - size_t len = val.getFlattenedSize(); - size_t fd_count = val.getFdCount(); + const size_t len = val.getFlattenedSize(); + const size_t fd_count = val.getFdCount(); err = this->writeInt32(len); if (err) return err; @@ -812,7 +813,7 @@ status_t Parcel::write(const Flattenable& val) if (err) return err; // payload - void* buf = this->writeInplace(PAD_SIZE(len)); + void* const buf = this->writeInplace(PAD_SIZE(len)); if (buf == NULL) return BAD_VALUE; @@ -1173,14 +1174,14 @@ status_t Parcel::readBlob(size_t len, ReadableBlob* outBlob) const return NO_ERROR; } -status_t Parcel::read(Flattenable& val) const +status_t Parcel::read(FlattenableHelperInterface& val) const { // size const size_t len = this->readInt32(); const size_t fd_count = this->readInt32(); // payload - void const* buf = this->readInplace(PAD_SIZE(len)); + void const* const buf = this->readInplace(PAD_SIZE(len)); if (buf == NULL) return BAD_VALUE; diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp index 294e1d4..c1e49bc 100644 --- a/libs/binder/ProcessState.cpp +++ b/libs/binder/ProcessState.cpp @@ -194,6 +194,33 @@ sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle) // in getWeakProxyForHandle() for more info about this. IBinder* b = e->binder; if (b == NULL || !e->refs->attemptIncWeak(this)) { + if (handle == 0) { + // Special case for context manager... + // The context manager is the only object for which we create + // a BpBinder proxy without already holding a reference. + // Perform a dummy transaction to ensure the context manager + // is registered before we create the first local reference + // to it (which will occur when creating the BpBinder). + // If a local reference is created for the BpBinder when the + // context manager is not present, the driver will fail to + // provide a reference to the context manager, but the + // driver API does not return status. + // + // Note that this is not race-free if the context manager + // dies while this code runs. + // + // TODO: add a driver API to wait for context manager, or + // stop special casing handle 0 for context manager and add + // a driver API to get a handle to the context manager with + // proper reference counting. + + Parcel data; + status_t status = IPCThreadState::self()->transact( + 0, IBinder::PING_TRANSACTION, data, NULL, 0); + if (status == DEAD_OBJECT) + return NULL; + } + b = new BpBinder(handle); e->binder = b; if (b) e->refs = b->getWeakRefs(); diff --git a/libs/binder/Static.cpp b/libs/binder/Static.cpp index 12b0308..2062692 100644 --- a/libs/binder/Static.cpp +++ b/libs/binder/Static.cpp @@ -19,30 +19,76 @@ #include <private/binder/Static.h> +#include <binder/BufferedTextOutput.h> #include <binder/IPCThreadState.h> #include <utils/Log.h> namespace android { +// ------------ Text output streams + +Vector<int32_t> gTextBuffers; + +class LogTextOutput : public BufferedTextOutput +{ +public: + LogTextOutput() : BufferedTextOutput(MULTITHREADED) { } + virtual ~LogTextOutput() { }; + +protected: + virtual status_t writeLines(const struct iovec& vec, size_t N) + { + //android_writevLog(&vec, N); <-- this is now a no-op + if (N != 1) ALOGI("WARNING: writeLines N=%zu\n", N); + ALOGI("%.*s", (int)vec.iov_len, (const char*) vec.iov_base); + return NO_ERROR; + } +}; + +class FdTextOutput : public BufferedTextOutput +{ +public: + FdTextOutput(int fd) : BufferedTextOutput(MULTITHREADED), mFD(fd) { } + virtual ~FdTextOutput() { }; + +protected: + virtual status_t writeLines(const struct iovec& vec, size_t N) + { + writev(mFD, &vec, N); + return NO_ERROR; + } + +private: + int mFD; +}; + +static LogTextOutput gLogTextOutput; +static FdTextOutput gStdoutTextOutput(STDOUT_FILENO); +static FdTextOutput gStderrTextOutput(STDERR_FILENO); + +TextOutput& alog(gLogTextOutput); +TextOutput& aout(gStdoutTextOutput); +TextOutput& aerr(gStderrTextOutput); + // ------------ ProcessState.cpp Mutex gProcessMutex; sp<ProcessState> gProcess; -class LibUtilsIPCtStatics +class LibBinderIPCtStatics { public: - LibUtilsIPCtStatics() + LibBinderIPCtStatics() { } - ~LibUtilsIPCtStatics() + ~LibBinderIPCtStatics() { IPCThreadState::shutdown(); } }; -static LibUtilsIPCtStatics gIPCStatics; +static LibBinderIPCtStatics gIPCStatics; // ------------ ServiceManager.cpp diff --git a/libs/utils/TextOutput.cpp b/libs/binder/TextOutput.cpp index e04823d..db3e858 100644 --- a/libs/utils/TextOutput.cpp +++ b/libs/binder/TextOutput.cpp @@ -14,9 +14,12 @@ * limitations under the License. */ -#include <utils/TextOutput.h> +#include <binder/TextOutput.h> -#include <utils/Debug.h> +#include <binder/Debug.h> + +#include <utils/String8.h> +#include <utils/String16.h> #include <stdio.h> #include <stdlib.h> @@ -119,6 +122,18 @@ TextOutput& operator<<(TextOutput& to, const void* val) return to; } +TextOutput& operator<<(TextOutput& to, const String8& val) +{ + to << val.string(); + return to; +} + +TextOutput& operator<<(TextOutput& to, const String16& val) +{ + to << String8(val).string(); + return to; +} + static void textOutputPrinter(void* cookie, const char* txt) { ((TextOutput*)cookie)->print(txt, strlen(txt)); diff --git a/libs/cpustats/Android.mk b/libs/cpustats/Android.mk deleted file mode 100644 index b506353..0000000 --- a/libs/cpustats/Android.mk +++ /dev/null @@ -1,11 +0,0 @@ -LOCAL_PATH:= $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := \ - CentralTendencyStatistics.cpp \ - ThreadCpuUsage.cpp - -LOCAL_MODULE := libcpustats - -include $(BUILD_STATIC_LIBRARY) diff --git a/libs/cpustats/CentralTendencyStatistics.cpp b/libs/cpustats/CentralTendencyStatistics.cpp deleted file mode 100644 index 42ab62b..0000000 --- a/libs/cpustats/CentralTendencyStatistics.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <stdlib.h> - -#include <cpustats/CentralTendencyStatistics.h> - -void CentralTendencyStatistics::sample(double x) -{ - // update min and max - if (x < mMinimum) - mMinimum = x; - if (x > mMaximum) - mMaximum = x; - // Knuth - if (mN == 0) { - mMean = 0; - } - ++mN; - double delta = x - mMean; - mMean += delta / mN; - mM2 += delta * (x - mMean); -} - -void CentralTendencyStatistics::reset() -{ - mMean = NAN; - mMedian = NAN; - mMinimum = INFINITY; - mMaximum = -INFINITY; - mN = 0; - mM2 = 0; - mVariance = NAN; - mVarianceKnownForN = 0; - mStddev = NAN; - mStddevKnownForN = 0; -} - -double CentralTendencyStatistics::variance() const -{ - double variance; - if (mVarianceKnownForN != mN) { - if (mN > 1) { - // double variance_n = M2/n; - variance = mM2 / (mN - 1); - } else { - variance = NAN; - } - mVariance = variance; - mVarianceKnownForN = mN; - } else { - variance = mVariance; - } - return variance; -} - -double CentralTendencyStatistics::stddev() const -{ - double stddev; - if (mStddevKnownForN != mN) { - stddev = sqrt(variance()); - mStddev = stddev; - mStddevKnownForN = mN; - } else { - stddev = mStddev; - } - return stddev; -} diff --git a/libs/cpustats/ThreadCpuUsage.cpp b/libs/cpustats/ThreadCpuUsage.cpp deleted file mode 100644 index 637402a..0000000 --- a/libs/cpustats/ThreadCpuUsage.cpp +++ /dev/null @@ -1,255 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "ThreadCpuUsage" -//#define LOG_NDEBUG 0 - -#include <errno.h> -#include <stdlib.h> -#include <time.h> - -#include <utils/Debug.h> -#include <utils/Log.h> - -#include <cpustats/ThreadCpuUsage.h> - -namespace android { - -bool ThreadCpuUsage::setEnabled(bool isEnabled) -{ - bool wasEnabled = mIsEnabled; - // only do something if there is a change - if (isEnabled != wasEnabled) { - ALOGV("setEnabled(%d)", isEnabled); - int rc; - // enabling - if (isEnabled) { - rc = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &mPreviousTs); - if (rc) { - ALOGE("clock_gettime(CLOCK_THREAD_CPUTIME_ID) errno=%d", errno); - isEnabled = false; - } else { - mWasEverEnabled = true; - // record wall clock time at first enable - if (!mMonotonicKnown) { - rc = clock_gettime(CLOCK_MONOTONIC, &mMonotonicTs); - if (rc) { - ALOGE("clock_gettime(CLOCK_MONOTONIC) errno=%d", errno); - } else { - mMonotonicKnown = true; - } - } - } - // disabling - } else { - struct timespec ts; - rc = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts); - if (rc) { - ALOGE("clock_gettime(CLOCK_THREAD_CPUTIME_ID) errno=%d", errno); - } else { - long long delta = (ts.tv_sec - mPreviousTs.tv_sec) * 1000000000LL + - (ts.tv_nsec - mPreviousTs.tv_nsec); - mAccumulator += delta; -#if 0 - mPreviousTs = ts; -#endif - } - } - mIsEnabled = isEnabled; - } - return wasEnabled; -} - -bool ThreadCpuUsage::sampleAndEnable(double& ns) -{ - bool ret; - bool wasEverEnabled = mWasEverEnabled; - if (enable()) { - // already enabled, so add a new sample relative to previous - return sample(ns); - } else if (wasEverEnabled) { - // was disabled, but add sample for accumulated time while enabled - ns = (double) mAccumulator; - mAccumulator = 0; - ALOGV("sampleAndEnable %.0f", ns); - return true; - } else { - // first time called - ns = 0.0; - ALOGV("sampleAndEnable false"); - return false; - } -} - -bool ThreadCpuUsage::sample(double &ns) -{ - if (mWasEverEnabled) { - if (mIsEnabled) { - struct timespec ts; - int rc; - rc = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts); - if (rc) { - ALOGE("clock_gettime(CLOCK_THREAD_CPUTIME_ID) errno=%d", errno); - ns = 0.0; - return false; - } else { - long long delta = (ts.tv_sec - mPreviousTs.tv_sec) * 1000000000LL + - (ts.tv_nsec - mPreviousTs.tv_nsec); - mAccumulator += delta; - mPreviousTs = ts; - } - } else { - mWasEverEnabled = false; - } - ns = (double) mAccumulator; - ALOGV("sample %.0f", ns); - mAccumulator = 0; - return true; - } else { - ALOGW("Can't add sample because measurements have never been enabled"); - ns = 0.0; - return false; - } -} - -long long ThreadCpuUsage::elapsed() const -{ - long long elapsed; - if (mMonotonicKnown) { - struct timespec ts; - int rc; - rc = clock_gettime(CLOCK_MONOTONIC, &ts); - if (rc) { - ALOGE("clock_gettime(CLOCK_MONOTONIC) errno=%d", errno); - elapsed = 0; - } else { - // mMonotonicTs is updated only at first enable and resetStatistics - elapsed = (ts.tv_sec - mMonotonicTs.tv_sec) * 1000000000LL + - (ts.tv_nsec - mMonotonicTs.tv_nsec); - } - } else { - ALOGW("Can't compute elapsed time because measurements have never been enabled"); - elapsed = 0; - } - ALOGV("elapsed %lld", elapsed); - return elapsed; -} - -void ThreadCpuUsage::resetElapsed() -{ - ALOGV("resetElapsed"); - if (mMonotonicKnown) { - int rc; - rc = clock_gettime(CLOCK_MONOTONIC, &mMonotonicTs); - if (rc) { - ALOGE("clock_gettime(CLOCK_MONOTONIC) errno=%d", errno); - mMonotonicKnown = false; - } - } -} - -/*static*/ -int ThreadCpuUsage::sScalingFds[ThreadCpuUsage::MAX_CPU]; -pthread_once_t ThreadCpuUsage::sOnceControl = PTHREAD_ONCE_INIT; -int ThreadCpuUsage::sKernelMax; -pthread_mutex_t ThreadCpuUsage::sMutex = PTHREAD_MUTEX_INITIALIZER; - -/*static*/ -void ThreadCpuUsage::init() -{ - // read the number of CPUs - sKernelMax = 1; - int fd = open("/sys/devices/system/cpu/kernel_max", O_RDONLY); - if (fd >= 0) { -#define KERNEL_MAX_SIZE 12 - char kernelMax[KERNEL_MAX_SIZE]; - ssize_t actual = read(fd, kernelMax, sizeof(kernelMax)); - if (actual >= 2 && kernelMax[actual-1] == '\n') { - sKernelMax = atoi(kernelMax); - if (sKernelMax >= MAX_CPU - 1) { - ALOGW("kernel_max %d but MAX_CPU %d", sKernelMax, MAX_CPU); - sKernelMax = MAX_CPU; - } else if (sKernelMax < 0) { - ALOGW("kernel_max invalid %d", sKernelMax); - sKernelMax = 1; - } else { - ++sKernelMax; - ALOGV("number of CPUs %d", sKernelMax); - } - } else { - ALOGW("Can't read number of CPUs"); - } - (void) close(fd); - } else { - ALOGW("Can't open number of CPUs"); - } - int i; - for (i = 0; i < MAX_CPU; ++i) { - sScalingFds[i] = -1; - } -} - -uint32_t ThreadCpuUsage::getCpukHz(int cpuNum) -{ - if (cpuNum < 0 || cpuNum >= MAX_CPU) { - ALOGW("getCpukHz called with invalid CPU %d", cpuNum); - return 0; - } - // double-checked locking idiom is not broken for atomic values such as fd - int fd = sScalingFds[cpuNum]; - if (fd < 0) { - // some kernels can't open a scaling file until hot plug complete - pthread_mutex_lock(&sMutex); - fd = sScalingFds[cpuNum]; - if (fd < 0) { -#define FREQ_SIZE 64 - char freq_path[FREQ_SIZE]; -#define FREQ_DIGIT 27 - COMPILE_TIME_ASSERT_FUNCTION_SCOPE(MAX_CPU <= 10); -#define FREQ_PATH "/sys/devices/system/cpu/cpu?/cpufreq/scaling_cur_freq" - strlcpy(freq_path, FREQ_PATH, sizeof(freq_path)); - freq_path[FREQ_DIGIT] = cpuNum + '0'; - fd = open(freq_path, O_RDONLY | O_CLOEXEC); - // keep this fd until process exit or exec - sScalingFds[cpuNum] = fd; - } - pthread_mutex_unlock(&sMutex); - if (fd < 0) { - ALOGW("getCpukHz can't open CPU %d", cpuNum); - return 0; - } - } -#define KHZ_SIZE 12 - char kHz[KHZ_SIZE]; // kHz base 10 - ssize_t actual = pread(fd, kHz, sizeof(kHz), (off_t) 0); - uint32_t ret; - if (actual >= 2 && kHz[actual-1] == '\n') { - ret = atoi(kHz); - } else { - ret = 0; - } - if (ret != mCurrentkHz[cpuNum]) { - if (ret > 0) { - ALOGV("CPU %d frequency %u kHz", cpuNum, ret); - } else { - ALOGW("Can't read CPU %d frequency", cpuNum); - } - mCurrentkHz[cpuNum] = ret; - } - return ret; -} - -} // namespace android diff --git a/libs/gui/Android.mk b/libs/gui/Android.mk index c080f47..c14c950 100644 --- a/libs/gui/Android.mk +++ b/libs/gui/Android.mk @@ -2,13 +2,14 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ + IGraphicBufferConsumer.cpp \ + IConsumerListener.cpp \ BitTube.cpp \ BufferItemConsumer.cpp \ BufferQueue.cpp \ ConsumerBase.cpp \ CpuConsumer.cpp \ DisplayEventReceiver.cpp \ - DummyConsumer.cpp \ GLConsumer.cpp \ GraphicBufferAlloc.cpp \ GuiConfig.cpp \ diff --git a/libs/gui/BitTube.cpp b/libs/gui/BitTube.cpp index cf44bb9..85a7de7 100644 --- a/libs/gui/BitTube.cpp +++ b/libs/gui/BitTube.cpp @@ -32,39 +32,26 @@ namespace android { // Socket buffer size. The default is typically about 128KB, which is much larger than // we really need. So we make it smaller. -static const size_t SOCKET_BUFFER_SIZE = 4 * 1024; +static const size_t DEFAULT_SOCKET_BUFFER_SIZE = 4 * 1024; BitTube::BitTube() : mSendFd(-1), mReceiveFd(-1) { - int sockets[2]; - if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets) == 0) { - int size = SOCKET_BUFFER_SIZE; - setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)); - setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)); - setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)); - setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)); - fcntl(sockets[0], F_SETFL, O_NONBLOCK); - fcntl(sockets[1], F_SETFL, O_NONBLOCK); - mReceiveFd = sockets[0]; - mSendFd = sockets[1]; - } else { - mReceiveFd = -errno; - ALOGE("BitTube: pipe creation failed (%s)", strerror(-mReceiveFd)); - } + init(DEFAULT_SOCKET_BUFFER_SIZE, DEFAULT_SOCKET_BUFFER_SIZE); +} + +BitTube::BitTube(size_t bufsize) + : mSendFd(-1), mReceiveFd(-1) +{ + init(bufsize, bufsize); } BitTube::BitTube(const Parcel& data) : mSendFd(-1), mReceiveFd(-1) { mReceiveFd = dup(data.readFileDescriptor()); - if (mReceiveFd >= 0) { - int size = SOCKET_BUFFER_SIZE; - setsockopt(mReceiveFd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)); - setsockopt(mReceiveFd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)); - fcntl(mReceiveFd, F_SETFL, O_NONBLOCK); - } else { + if (mReceiveFd < 0) { mReceiveFd = -errno; ALOGE("BitTube(Parcel): can't dup filedescriptor (%s)", strerror(-mReceiveFd)); @@ -80,6 +67,25 @@ BitTube::~BitTube() close(mReceiveFd); } +void BitTube::init(size_t rcvbuf, size_t sndbuf) { + int sockets[2]; + if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets) == 0) { + size_t size = DEFAULT_SOCKET_BUFFER_SIZE; + setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf)); + setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf)); + // sine we don't use the "return channel", we keep it small... + setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)); + setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)); + fcntl(sockets[0], F_SETFL, O_NONBLOCK); + fcntl(sockets[1], F_SETFL, O_NONBLOCK); + mReceiveFd = sockets[0]; + mSendFd = sockets[1]; + } else { + mReceiveFd = -errno; + ALOGE("BitTube: pipe creation failed (%s)", strerror(-mReceiveFd)); + } +} + status_t BitTube::initCheck() const { if (mReceiveFd < 0) { @@ -98,10 +104,10 @@ ssize_t BitTube::write(void const* vaddr, size_t size) ssize_t err, len; do { len = ::send(mSendFd, vaddr, size, MSG_DONTWAIT | MSG_NOSIGNAL); + // cannot return less than size, since we're using SOCK_SEQPACKET err = len < 0 ? errno : 0; } while (err == EINTR); return err == 0 ? len : -err; - } ssize_t BitTube::read(void* vaddr, size_t size) @@ -134,39 +140,31 @@ status_t BitTube::writeToParcel(Parcel* reply) const ssize_t BitTube::sendObjects(const sp<BitTube>& tube, void const* events, size_t count, size_t objSize) { - ssize_t numObjects = 0; - for (size_t i=0 ; i<count ; i++) { - const char* vaddr = reinterpret_cast<const char*>(events) + objSize * i; - ssize_t size = tube->write(vaddr, objSize); - if (size < 0) { - // error occurred - return size; - } else if (size == 0) { - // no more space - break; - } - numObjects++; - } - return numObjects; + const char* vaddr = reinterpret_cast<const char*>(events); + ssize_t size = tube->write(vaddr, count*objSize); + + // should never happen because of SOCK_SEQPACKET + LOG_ALWAYS_FATAL_IF((size >= 0) && (size % objSize), + "BitTube::sendObjects(count=%d, size=%d), res=%d (partial events were sent!)", + count, objSize, size); + + //ALOGE_IF(size<0, "error %d sending %d events", size, count); + return size < 0 ? size : size / objSize; } ssize_t BitTube::recvObjects(const sp<BitTube>& tube, void* events, size_t count, size_t objSize) { - ssize_t numObjects = 0; - for (size_t i=0 ; i<count ; i++) { - char* vaddr = reinterpret_cast<char*>(events) + objSize * i; - ssize_t size = tube->read(vaddr, objSize); - if (size < 0) { - // error occurred - return size; - } else if (size == 0) { - // no more messages - break; - } - numObjects++; - } - return numObjects; + char* vaddr = reinterpret_cast<char*>(events); + ssize_t size = tube->read(vaddr, count*objSize); + + // should never happen because of SOCK_SEQPACKET + LOG_ALWAYS_FATAL_IF((size >= 0) && (size % objSize), + "BitTube::recvObjects(count=%d, size=%d), res=%d (partial events were received!)", + count, objSize, size); + + //ALOGE_IF(size<0, "error %d receiving %d events", size, count); + return size < 0 ? size : size / objSize; } // ---------------------------------------------------------------------------- diff --git a/libs/gui/BufferItemConsumer.cpp b/libs/gui/BufferItemConsumer.cpp index 7db1b84..350887a 100644 --- a/libs/gui/BufferItemConsumer.cpp +++ b/libs/gui/BufferItemConsumer.cpp @@ -29,13 +29,12 @@ namespace android { -BufferItemConsumer::BufferItemConsumer(uint32_t consumerUsage, - int bufferCount, bool synchronousMode) : - ConsumerBase(new BufferQueue(true) ) +BufferItemConsumer::BufferItemConsumer(const sp<BufferQueue>& bq, + uint32_t consumerUsage, int bufferCount, bool controlledByApp) : + ConsumerBase(bq, controlledByApp) { - mBufferQueue->setConsumerUsageBits(consumerUsage); - mBufferQueue->setSynchronousMode(synchronousMode); - mBufferQueue->setMaxAcquiredBufferCount(bufferCount); + mConsumer->setConsumerUsageBits(consumerUsage); + mConsumer->setMaxAcquiredBufferCount(bufferCount); } BufferItemConsumer::~BufferItemConsumer() { @@ -44,17 +43,18 @@ BufferItemConsumer::~BufferItemConsumer() { void BufferItemConsumer::setName(const String8& name) { Mutex::Autolock _l(mMutex); mName = name; - mBufferQueue->setConsumerName(name); + mConsumer->setConsumerName(name); } -status_t BufferItemConsumer::acquireBuffer(BufferItem *item, bool waitForFence) { +status_t BufferItemConsumer::acquireBuffer(BufferItem *item, + nsecs_t presentWhen, bool waitForFence) { status_t err; if (!item) return BAD_VALUE; Mutex::Autolock _l(mMutex); - err = acquireBufferLocked(item); + err = acquireBufferLocked(item, presentWhen); if (err != OK) { if (err != NO_BUFFER_AVAILABLE) { BI_LOGE("Error acquiring buffer: %s (%d)", strerror(err), err); @@ -82,9 +82,9 @@ status_t BufferItemConsumer::releaseBuffer(const BufferItem &item, Mutex::Autolock _l(mMutex); - err = addReleaseFenceLocked(item.mBuf, releaseFence); + err = addReleaseFenceLocked(item.mBuf, item.mGraphicBuffer, releaseFence); - err = releaseBufferLocked(item.mBuf, EGL_NO_DISPLAY, + err = releaseBufferLocked(item.mBuf, item.mGraphicBuffer, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR); if (err != OK) { BI_LOGE("Failed to release buffer: %s (%d)", @@ -95,12 +95,12 @@ status_t BufferItemConsumer::releaseBuffer(const BufferItem &item, status_t BufferItemConsumer::setDefaultBufferSize(uint32_t w, uint32_t h) { Mutex::Autolock _l(mMutex); - return mBufferQueue->setDefaultBufferSize(w, h); + return mConsumer->setDefaultBufferSize(w, h); } status_t BufferItemConsumer::setDefaultBufferFormat(uint32_t defaultFormat) { Mutex::Autolock _l(mMutex); - return mBufferQueue->setDefaultBufferFormat(defaultFormat); + return mConsumer->setDefaultBufferFormat(defaultFormat); } } // namespace android diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp index b4c7231..c165a68 100644 --- a/libs/gui/BufferQueue.cpp +++ b/libs/gui/BufferQueue.cpp @@ -25,11 +25,13 @@ #include <EGL/eglext.h> #include <gui/BufferQueue.h> +#include <gui/IConsumerListener.h> #include <gui/ISurfaceComposer.h> #include <private/gui/ComposerService.h> #include <utils/Log.h> #include <utils/Trace.h> +#include <utils/CallStack.h> // Macros for including the BufferQueue name in log messages #define ST_LOGV(x, ...) ALOGV("[%s] "x, mConsumerName.string(), ##__VA_ARGS__) @@ -63,15 +65,15 @@ static const char* scalingModeName(int scalingMode) { } } -BufferQueue::BufferQueue(bool allowSynchronousMode, - const sp<IGraphicBufferAlloc>& allocator) : +BufferQueue::BufferQueue(const sp<IGraphicBufferAlloc>& allocator) : mDefaultWidth(1), mDefaultHeight(1), mMaxAcquiredBufferCount(1), mDefaultMaxBufferCount(2), mOverrideMaxBufferCount(0), - mSynchronousMode(false), - mAllowSynchronousMode(allowSynchronousMode), + mConsumerControlledByApp(false), + mDequeueBufferCannotBlock(false), + mUseAsyncBuffer(true), mConnectedApi(NO_CONNECTED_API), mAbandoned(false), mFrameCounter(0), @@ -100,7 +102,8 @@ BufferQueue::~BufferQueue() { } status_t BufferQueue::setDefaultMaxBufferCountLocked(int count) { - if (count < 2 || count > NUM_BUFFER_SLOTS) + const int minBufferCount = mUseAsyncBuffer ? 2 : 1; + if (count < minBufferCount || count > NUM_BUFFER_SLOTS) return BAD_VALUE; mDefaultMaxBufferCount = count; @@ -109,11 +112,6 @@ status_t BufferQueue::setDefaultMaxBufferCountLocked(int count) { return NO_ERROR; } -bool BufferQueue::isSynchronousMode() const { - Mutex::Autolock lock(mMutex); - return mSynchronousMode; -} - void BufferQueue::setConsumerName(const String8& name) { Mutex::Autolock lock(mMutex); mConsumerName = name; @@ -141,7 +139,7 @@ status_t BufferQueue::setTransformHint(uint32_t hint) { status_t BufferQueue::setBufferCount(int bufferCount) { ST_LOGV("setBufferCount: count=%d", bufferCount); - sp<ConsumerListener> listener; + sp<IConsumerListener> listener; { Mutex::Autolock lock(mMutex); @@ -156,21 +154,21 @@ status_t BufferQueue::setBufferCount(int bufferCount) { } // Error out if the user has dequeued buffers - int maxBufferCount = getMaxBufferCountLocked(); - for (int i=0 ; i<maxBufferCount; i++) { + for (int i=0 ; i<NUM_BUFFER_SLOTS; i++) { if (mSlots[i].mBufferState == BufferSlot::DEQUEUED) { ST_LOGE("setBufferCount: client owns some buffers"); return -EINVAL; } } - const int minBufferSlots = getMinMaxBufferCountLocked(); if (bufferCount == 0) { mOverrideMaxBufferCount = 0; mDequeueCondition.broadcast(); return NO_ERROR; } + // fine to assume async to false before we're setting the buffer count + const int minBufferSlots = getMinMaxBufferCountLocked(false); if (bufferCount < minBufferSlots) { ST_LOGE("setBufferCount: requested buffer count (%d) is less than " "minimum (%d)", bufferCount, minBufferSlots); @@ -178,12 +176,10 @@ status_t BufferQueue::setBufferCount(int bufferCount) { } // here we're guaranteed that the client doesn't have dequeued buffers - // and will release all of its buffer references. - // - // XXX: Should this use drainQueueAndFreeBuffersLocked instead? + // and will release all of its buffer references. We don't clear the + // queue, however, so currently queued buffers still get displayed. freeAllBuffersLocked(); mOverrideMaxBufferCount = bufferCount; - mBufferHasBeenQueued = false; mDequeueCondition.broadcast(); listener = mConsumerListener; } // scope for lock @@ -217,11 +213,14 @@ int BufferQueue::query(int what, int* outValue) value = mDefaultBufferFormat; break; case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS: - value = getMinUndequeuedBufferCountLocked(); + value = getMinUndequeuedBufferCount(false); break; case NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND: value = (mQueue.size() >= 2); break; + case NATIVE_WINDOW_CONSUMER_USAGE_BITS: + value = mConsumerUsageBits; + break; default: return BAD_VALUE; } @@ -237,15 +236,11 @@ status_t BufferQueue::requestBuffer(int slot, sp<GraphicBuffer>* buf) { ST_LOGE("requestBuffer: BufferQueue has been abandoned!"); return NO_INIT; } - int maxBufferCount = getMaxBufferCountLocked(); - if (slot < 0 || maxBufferCount <= slot) { + if (slot < 0 || slot >= NUM_BUFFER_SLOTS) { ST_LOGE("requestBuffer: slot index out of range [0, %d]: %d", - maxBufferCount, slot); + NUM_BUFFER_SLOTS, slot); return BAD_VALUE; } else if (mSlots[slot].mBufferState != BufferSlot::DEQUEUED) { - // XXX: I vaguely recall there was some reason this can be valid, but - // for the life of me I can't recall under what circumstances that's - // the case. ST_LOGE("requestBuffer: slot %d is not owned by the client (state=%d)", slot, mSlots[slot].mBufferState); return BAD_VALUE; @@ -255,7 +250,7 @@ status_t BufferQueue::requestBuffer(int slot, sp<GraphicBuffer>* buf) { return NO_ERROR; } -status_t BufferQueue::dequeueBuffer(int *outBuf, sp<Fence>* outFence, +status_t BufferQueue::dequeueBuffer(int *outBuf, sp<Fence>* outFence, bool async, uint32_t w, uint32_t h, uint32_t format, uint32_t usage) { ATRACE_CALL(); ST_LOGV("dequeueBuffer: w=%d h=%d fmt=%#x usage=%#x", w, h, format, usage); @@ -279,7 +274,6 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, sp<Fence>* outFence, usage |= mConsumerUsageBits; int found = -1; - int dequeuedCount = 0; bool tryAgain = true; while (tryAgain) { if (mAbandoned) { @@ -287,7 +281,16 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, sp<Fence>* outFence, return NO_INIT; } - const int maxBufferCount = getMaxBufferCountLocked(); + const int maxBufferCount = getMaxBufferCountLocked(async); + if (async && mOverrideMaxBufferCount) { + // FIXME: some drivers are manually setting the buffer-count (which they + // shouldn't), so we do this extra test here to handle that case. + // This is TEMPORARY, until we get this fixed. + if (mOverrideMaxBufferCount < maxBufferCount) { + ST_LOGE("dequeueBuffer: async mode is invalid with buffercount override"); + return BAD_VALUE; + } + } // Free up any buffers that are in slots beyond the max buffer // count. @@ -301,23 +304,28 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, sp<Fence>* outFence, // look for a free buffer to give to the client found = INVALID_BUFFER_SLOT; - dequeuedCount = 0; + int dequeuedCount = 0; + int acquiredCount = 0; for (int i = 0; i < maxBufferCount; i++) { const int state = mSlots[i].mBufferState; - if (state == BufferSlot::DEQUEUED) { - dequeuedCount++; - } - - if (state == BufferSlot::FREE) { - /* We return the oldest of the free buffers to avoid - * stalling the producer if possible. This is because - * the consumer may still have pending reads of the - * buffers in flight. - */ - if ((found < 0) || - mSlots[i].mFrameNumber < mSlots[found].mFrameNumber) { - found = i; - } + switch (state) { + case BufferSlot::DEQUEUED: + dequeuedCount++; + break; + case BufferSlot::ACQUIRED: + acquiredCount++; + break; + case BufferSlot::FREE: + /* We return the oldest of the free buffers to avoid + * stalling the producer if possible. This is because + * the consumer may still have pending reads of the + * buffers in flight. + */ + if ((found < 0) || + mSlots[i].mFrameNumber < mSlots[found].mFrameNumber) { + found = i; + } + break; } } @@ -336,7 +344,7 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, sp<Fence>* outFence, // make sure the client is not trying to dequeue more buffers // than allowed. const int newUndequeuedCount = maxBufferCount - (dequeuedCount+1); - const int minUndequeuedCount = getMinUndequeuedBufferCountLocked(); + const int minUndequeuedCount = getMinUndequeuedBufferCount(async); if (newUndequeuedCount < minUndequeuedCount) { ST_LOGE("dequeueBuffer: min undequeued buffer count (%d) " "exceeded (dequeued=%d undequeudCount=%d)", @@ -350,6 +358,16 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, sp<Fence>* outFence, // the max buffer count to change. tryAgain = found == INVALID_BUFFER_SLOT; if (tryAgain) { + // return an error if we're in "cannot block" mode (producer and consumer + // are controlled by the application) -- however, the consumer is allowed + // to acquire briefly an extra buffer (which could cause us to have to wait here) + // and that's okay because we know the wait will be brief (it happens + // if we dequeue a buffer while the consumer has acquired one but not released + // the old one yet -- for e.g.: see GLConsumer::updateTexImage()). + if (mDequeueBufferCannotBlock && (acquiredCount <= mMaxAcquiredBufferCount)) { + ST_LOGE("dequeueBuffer: would block! returning an error instead."); + return WOULD_BLOCK; + } mDequeueCondition.wait(mMutex); } } @@ -392,6 +410,13 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, sp<Fence>* outFence, returnFlags |= IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION; } + + if (CC_UNLIKELY(mSlots[buf].mFence == NULL)) { + ST_LOGE("dequeueBuffer: about to return a NULL fence from mSlot. " + "buf=%d, w=%d, h=%d, format=%d", + buf, buffer->width, buffer->height, buffer->format); + } + dpy = mSlots[buf].mEglDisplay; eglFence = mSlots[buf].mEglFence; *outFence = mSlots[buf].mFence; @@ -402,11 +427,9 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, sp<Fence>* outFence, if (returnFlags & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) { status_t error; sp<GraphicBuffer> graphicBuffer( - mGraphicBufferAlloc->createGraphicBuffer( - w, h, format, usage, &error)); + mGraphicBufferAlloc->createGraphicBuffer(w, h, format, usage, &error)); if (graphicBuffer == 0) { - ST_LOGE("dequeueBuffer: SurfaceComposer::createGraphicBuffer " - "failed"); + ST_LOGE("dequeueBuffer: SurfaceComposer::createGraphicBuffer failed"); return error; } @@ -418,6 +441,7 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, sp<Fence>* outFence, return NO_INIT; } + mSlots[*outBuf].mFrameNumber = ~0; mSlots[*outBuf].mGraphicBuffer = graphicBuffer; } } @@ -435,44 +459,13 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, sp<Fence>* outFence, eglDestroySyncKHR(dpy, eglFence); } - ST_LOGV("dequeueBuffer: returning slot=%d buf=%p flags=%#x", *outBuf, + ST_LOGV("dequeueBuffer: returning slot=%d/%llu buf=%p flags=%#x", *outBuf, + mSlots[*outBuf].mFrameNumber, mSlots[*outBuf].mGraphicBuffer->handle, returnFlags); return returnFlags; } -status_t BufferQueue::setSynchronousMode(bool enabled) { - ATRACE_CALL(); - ST_LOGV("setSynchronousMode: enabled=%d", enabled); - Mutex::Autolock lock(mMutex); - - if (mAbandoned) { - ST_LOGE("setSynchronousMode: BufferQueue has been abandoned!"); - return NO_INIT; - } - - status_t err = OK; - if (!mAllowSynchronousMode && enabled) - return err; - - if (!enabled) { - // going to asynchronous mode, drain the queue - err = drainQueueLocked(); - if (err != NO_ERROR) - return err; - } - - if (mSynchronousMode != enabled) { - // - if we're going to asynchronous mode, the queue is guaranteed to be - // empty here - // - if the client set the number of buffers, we're guaranteed that - // we have at least 3 (because we don't allow less) - mSynchronousMode = enabled; - mDequeueCondition.broadcast(); - } - return err; -} - status_t BufferQueue::queueBuffer(int buf, const QueueBufferInput& input, QueueBufferOutput* output) { ATRACE_CALL(); @@ -482,29 +475,49 @@ status_t BufferQueue::queueBuffer(int buf, uint32_t transform; int scalingMode; int64_t timestamp; + bool isAutoTimestamp; + bool async; sp<Fence> fence; - input.deflate(×tamp, &crop, &scalingMode, &transform, &fence); + input.deflate(×tamp, &isAutoTimestamp, &crop, &scalingMode, &transform, + &async, &fence); if (fence == NULL) { ST_LOGE("queueBuffer: fence is NULL"); return BAD_VALUE; } - ST_LOGV("queueBuffer: slot=%d time=%#llx crop=[%d,%d,%d,%d] tr=%#x " - "scale=%s", - buf, timestamp, crop.left, crop.top, crop.right, crop.bottom, - transform, scalingModeName(scalingMode)); + switch (scalingMode) { + case NATIVE_WINDOW_SCALING_MODE_FREEZE: + case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW: + case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP: + case NATIVE_WINDOW_SCALING_MODE_NO_SCALE_CROP: + break; + default: + ST_LOGE("unknown scaling mode: %d", scalingMode); + return -EINVAL; + } - sp<ConsumerListener> listener; + sp<IConsumerListener> listener; { // scope for the lock Mutex::Autolock lock(mMutex); + if (mAbandoned) { ST_LOGE("queueBuffer: BufferQueue has been abandoned!"); return NO_INIT; } - int maxBufferCount = getMaxBufferCountLocked(); + + const int maxBufferCount = getMaxBufferCountLocked(async); + if (async && mOverrideMaxBufferCount) { + // FIXME: some drivers are manually setting the buffer-count (which they + // shouldn't), so we do this extra test here to handle that case. + // This is TEMPORARY, until we get this fixed. + if (mOverrideMaxBufferCount < maxBufferCount) { + ST_LOGE("queueBuffer: async mode is invalid with buffercount override"); + return BAD_VALUE; + } + } if (buf < 0 || buf >= maxBufferCount) { ST_LOGE("queueBuffer: slot index out of range [0, %d]: %d", maxBufferCount, buf); @@ -519,6 +532,12 @@ status_t BufferQueue::queueBuffer(int buf, return -EINVAL; } + ST_LOGV("queueBuffer: slot=%d/%llu time=%#llx crop=[%d,%d,%d,%d] " + "tr=%#x scale=%s", + buf, mFrameCounter + 1, timestamp, + crop.left, crop.top, crop.right, crop.bottom, + transform, scalingModeName(scalingMode)); + const sp<GraphicBuffer>& graphicBuffer(mSlots[buf].mGraphicBuffer); Rect bufferRect(graphicBuffer->getWidth(), graphicBuffer->getHeight()); Rect croppedCrop; @@ -529,52 +548,50 @@ status_t BufferQueue::queueBuffer(int buf, return -EINVAL; } - if (mSynchronousMode) { - // In synchronous mode we queue all buffers in a FIFO. - mQueue.push_back(buf); + mSlots[buf].mFence = fence; + mSlots[buf].mBufferState = BufferSlot::QUEUED; + mFrameCounter++; + mSlots[buf].mFrameNumber = mFrameCounter; + + BufferItem item; + item.mAcquireCalled = mSlots[buf].mAcquireCalled; + item.mGraphicBuffer = mSlots[buf].mGraphicBuffer; + item.mCrop = crop; + item.mTransform = transform & ~NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY; + item.mTransformToDisplayInverse = bool(transform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY); + item.mScalingMode = scalingMode; + item.mTimestamp = timestamp; + item.mIsAutoTimestamp = isAutoTimestamp; + item.mFrameNumber = mFrameCounter; + item.mBuf = buf; + item.mFence = fence; + item.mIsDroppable = mDequeueBufferCannotBlock || async; - // Synchronous mode always signals that an additional frame should - // be consumed. + if (mQueue.empty()) { + // when the queue is empty, we can ignore "mDequeueBufferCannotBlock", and + // simply queue this buffer. + mQueue.push_back(item); listener = mConsumerListener; } else { - // In asynchronous mode we only keep the most recent buffer. - if (mQueue.empty()) { - mQueue.push_back(buf); - - // Asynchronous mode only signals that a frame should be - // consumed if no previous frame was pending. If a frame were - // pending then the consumer would have already been notified. - listener = mConsumerListener; + // when the queue is not empty, we need to look at the front buffer + // state and see if we need to replace it. + Fifo::iterator front(mQueue.begin()); + if (front->mIsDroppable) { + // buffer slot currently queued is marked free if still tracked + if (stillTracking(front)) { + mSlots[front->mBuf].mBufferState = BufferSlot::FREE; + // reset the frame number of the freed buffer so that it is the first in + // line to be dequeued again. + mSlots[front->mBuf].mFrameNumber = 0; + } + // and we record the new buffer in the queued list + *front = item; } else { - Fifo::iterator front(mQueue.begin()); - // buffer currently queued is freed - mSlots[*front].mBufferState = BufferSlot::FREE; - // and we record the new buffer index in the queued list - *front = buf; + mQueue.push_back(item); + listener = mConsumerListener; } } - mSlots[buf].mTimestamp = timestamp; - mSlots[buf].mCrop = crop; - mSlots[buf].mTransform = transform; - mSlots[buf].mFence = fence; - - switch (scalingMode) { - case NATIVE_WINDOW_SCALING_MODE_FREEZE: - case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW: - case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP: - break; - default: - ST_LOGE("unknown scaling mode: %d (ignoring)", scalingMode); - scalingMode = mSlots[buf].mScalingMode; - break; - } - - mSlots[buf].mBufferState = BufferSlot::QUEUED; - mSlots[buf].mScalingMode = scalingMode; - mFrameCounter++; - mSlots[buf].mFrameNumber = mFrameCounter; - mBufferHasBeenQueued = true; mDequeueCondition.broadcast(); @@ -601,10 +618,9 @@ void BufferQueue::cancelBuffer(int buf, const sp<Fence>& fence) { return; } - int maxBufferCount = getMaxBufferCountLocked(); - if (buf < 0 || buf >= maxBufferCount) { + if (buf < 0 || buf >= NUM_BUFFER_SLOTS) { ST_LOGE("cancelBuffer: slot index out of range [0, %d]: %d", - maxBufferCount, buf); + NUM_BUFFER_SLOTS, buf); return; } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) { ST_LOGE("cancelBuffer: slot %d is not owned by the client (state=%d)", @@ -620,9 +636,12 @@ void BufferQueue::cancelBuffer(int buf, const sp<Fence>& fence) { mDequeueCondition.broadcast(); } -status_t BufferQueue::connect(int api, QueueBufferOutput* output) { + +status_t BufferQueue::connect(const sp<IBinder>& token, + int api, bool producerControlledByApp, QueueBufferOutput* output) { ATRACE_CALL(); - ST_LOGV("connect: api=%d", api); + ST_LOGV("connect: api=%d producerControlledByApp=%s", api, + producerControlledByApp ? "true" : "false"); Mutex::Autolock lock(mMutex); if (mAbandoned) { @@ -647,8 +666,18 @@ status_t BufferQueue::connect(int api, QueueBufferOutput* output) { err = -EINVAL; } else { mConnectedApi = api; - output->inflate(mDefaultWidth, mDefaultHeight, mTransformHint, - mQueue.size()); + output->inflate(mDefaultWidth, mDefaultHeight, mTransformHint, mQueue.size()); + + // set-up a death notification so that we can disconnect + // automatically when/if the remote producer dies. + if (token != NULL && token->remoteBinder() != NULL) { + status_t err = token->linkToDeath(static_cast<IBinder::DeathRecipient*>(this)); + if (err == NO_ERROR) { + mConnectedProducerToken = token; + } else { + ALOGE("linkToDeath failed: %s (%d)", strerror(-err), err); + } + } } break; default: @@ -657,16 +686,27 @@ status_t BufferQueue::connect(int api, QueueBufferOutput* output) { } mBufferHasBeenQueued = false; + mDequeueBufferCannotBlock = mConsumerControlledByApp && producerControlledByApp; return err; } +void BufferQueue::binderDied(const wp<IBinder>& who) { + // If we're here, it means that a producer we were connected to died. + // We're GUARANTEED that we still are connected to it because it has no other way + // to get disconnected -- or -- we wouldn't be here because we're removing this + // callback upon disconnect. Therefore, it's okay to read mConnectedApi without + // synchronization here. + int api = mConnectedApi; + this->disconnect(api); +} + status_t BufferQueue::disconnect(int api) { ATRACE_CALL(); ST_LOGV("disconnect: api=%d", api); int err = NO_ERROR; - sp<ConsumerListener> listener; + sp<IConsumerListener> listener; { // Scope for the lock Mutex::Autolock lock(mMutex); @@ -683,7 +723,15 @@ status_t BufferQueue::disconnect(int api) { case NATIVE_WINDOW_API_MEDIA: case NATIVE_WINDOW_API_CAMERA: if (mConnectedApi == api) { - drainQueueAndFreeBuffersLocked(); + freeAllBuffersLocked(); + // remove our death notification callback if we have one + sp<IBinder> token = mConnectedProducerToken; + if (token != NULL) { + // this can fail if we're here because of the death notification + // either way, we just ignore. + token->unlinkToDeath(static_cast<IBinder::DeathRecipient*>(this)); + } + mConnectedProducerToken = NULL; mConnectedApi = NO_CONNECTED_API; mDequeueCondition.broadcast(); listener = mConsumerListener; @@ -707,36 +755,31 @@ status_t BufferQueue::disconnect(int api) { return err; } -void BufferQueue::dump(String8& result) const -{ - char buffer[1024]; - BufferQueue::dump(result, "", buffer, 1024); -} - -void BufferQueue::dump(String8& result, const char* prefix, - char* buffer, size_t SIZE) const -{ +void BufferQueue::dump(String8& result, const char* prefix) const { Mutex::Autolock _l(mMutex); String8 fifo; int fifoSize = 0; Fifo::const_iterator i(mQueue.begin()); while (i != mQueue.end()) { - snprintf(buffer, SIZE, "%02d ", *i++); - fifoSize++; - fifo.append(buffer); + fifo.appendFormat("%02d:%p crop=[%d,%d,%d,%d], " + "xform=0x%02x, time=%#llx, scale=%s\n", + i->mBuf, i->mGraphicBuffer.get(), + i->mCrop.left, i->mCrop.top, i->mCrop.right, + i->mCrop.bottom, i->mTransform, i->mTimestamp, + scalingModeName(i->mScalingMode) + ); + i++; + fifoSize++; } - int maxBufferCount = getMaxBufferCountLocked(); - snprintf(buffer, SIZE, - "%s-BufferQueue maxBufferCount=%d, mSynchronousMode=%d, default-size=[%dx%d], " + result.appendFormat( + "%s-BufferQueue mMaxAcquiredBufferCount=%d, mDequeueBufferCannotBlock=%d, default-size=[%dx%d], " "default-format=%d, transform-hint=%02x, FIFO(%d)={%s}\n", - prefix, maxBufferCount, mSynchronousMode, mDefaultWidth, + prefix, mMaxAcquiredBufferCount, mDequeueBufferCannotBlock, mDefaultWidth, mDefaultHeight, mDefaultBufferFormat, mTransformHint, fifoSize, fifo.string()); - result.append(buffer); - struct { const char * operator()(int state) const { @@ -750,27 +793,30 @@ void BufferQueue::dump(String8& result, const char* prefix, } } stateName; + // just trim the free buffers to not spam the dump + int maxBufferCount = 0; + for (int i=NUM_BUFFER_SLOTS-1 ; i>=0 ; i--) { + const BufferSlot& slot(mSlots[i]); + if ((slot.mBufferState != BufferSlot::FREE) || (slot.mGraphicBuffer != NULL)) { + maxBufferCount = i+1; + break; + } + } + for (int i=0 ; i<maxBufferCount ; i++) { const BufferSlot& slot(mSlots[i]); - snprintf(buffer, SIZE, - "%s%s[%02d] " - "state=%-8s, crop=[%d,%d,%d,%d], " - "xform=0x%02x, time=%#llx, scale=%s", - prefix, (slot.mBufferState == BufferSlot::ACQUIRED)?">":" ", i, - stateName(slot.mBufferState), - slot.mCrop.left, slot.mCrop.top, slot.mCrop.right, - slot.mCrop.bottom, slot.mTransform, slot.mTimestamp, - scalingModeName(slot.mScalingMode) + const sp<GraphicBuffer>& buf(slot.mGraphicBuffer); + result.appendFormat( + "%s%s[%02d:%p] state=%-8s", + prefix, (slot.mBufferState == BufferSlot::ACQUIRED)?">":" ", i, buf.get(), + stateName(slot.mBufferState) ); - result.append(buffer); - const sp<GraphicBuffer>& buf(slot.mGraphicBuffer); if (buf != NULL) { - snprintf(buffer, SIZE, + result.appendFormat( ", %p [%4ux%4u:%4u,%3X]", buf->handle, buf->width, buf->height, buf->stride, buf->format); - result.append(buffer); } result.append("\n"); } @@ -795,16 +841,13 @@ void BufferQueue::freeBufferLocked(int slot) { } void BufferQueue::freeAllBuffersLocked() { - ALOGW_IF(!mQueue.isEmpty(), - "freeAllBuffersLocked called but mQueue is not empty"); - mQueue.clear(); mBufferHasBeenQueued = false; for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { freeBufferLocked(i); } } -status_t BufferQueue::acquireBuffer(BufferItem *buffer) { +status_t BufferQueue::acquireBuffer(BufferItem *buffer, nsecs_t expectedPresent) { ATRACE_CALL(); Mutex::Autolock _l(mMutex); @@ -827,58 +870,153 @@ status_t BufferQueue::acquireBuffer(BufferItem *buffer) { // check if queue is empty // In asynchronous mode the list is guaranteed to be one buffer // deep, while in synchronous mode we use the oldest buffer. - if (!mQueue.empty()) { - Fifo::iterator front(mQueue.begin()); - int buf = *front; + if (mQueue.empty()) { + return NO_BUFFER_AVAILABLE; + } - ATRACE_BUFFER_INDEX(buf); + Fifo::iterator front(mQueue.begin()); - if (mSlots[buf].mAcquireCalled) { - buffer->mGraphicBuffer = NULL; - } else { - buffer->mGraphicBuffer = mSlots[buf].mGraphicBuffer; + // If expectedPresent is specified, we may not want to return a buffer yet. + // If it's specified and there's more than one buffer queued, we may + // want to drop a buffer. + if (expectedPresent != 0) { + const int MAX_REASONABLE_NSEC = 1000000000ULL; // 1 second + + // The "expectedPresent" argument indicates when the buffer is expected + // to be presented on-screen. If the buffer's desired-present time + // is earlier (less) than expectedPresent, meaning it'll be displayed + // on time or possibly late if we show it ASAP, we acquire and return + // it. If we don't want to display it until after the expectedPresent + // time, we return PRESENT_LATER without acquiring it. + // + // To be safe, we don't defer acquisition if expectedPresent is + // more than one second in the future beyond the desired present time + // (i.e. we'd be holding the buffer for a long time). + // + // NOTE: code assumes monotonic time values from the system clock are + // positive. + + // Start by checking to see if we can drop frames. We skip this check + // if the timestamps are being auto-generated by Surface -- if the + // app isn't generating timestamps explicitly, they probably don't + // want frames to be discarded based on them. + while (mQueue.size() > 1 && !mQueue[0].mIsAutoTimestamp) { + // If entry[1] is timely, drop entry[0] (and repeat). We apply + // an additional criteria here: we only drop the earlier buffer if + // our desiredPresent falls within +/- 1 second of the expected + // present. Otherwise, bogus desiredPresent times (e.g. 0 or + // a small relative timestamp), which normally mean "ignore the + // timestamp and acquire immediately", would cause us to drop + // frames. + // + // We may want to add an additional criteria: don't drop the + // earlier buffer if entry[1]'s fence hasn't signaled yet. + // + // (Vector front is [0], back is [size()-1]) + const BufferItem& bi(mQueue[1]); + nsecs_t desiredPresent = bi.mTimestamp; + if (desiredPresent < expectedPresent - MAX_REASONABLE_NSEC || + desiredPresent > expectedPresent) { + // This buffer is set to display in the near future, or + // desiredPresent is garbage. Either way we don't want to + // drop the previous buffer just to get this on screen sooner. + ST_LOGV("pts nodrop: des=%lld expect=%lld (%lld) now=%lld", + desiredPresent, expectedPresent, desiredPresent - expectedPresent, + systemTime(CLOCK_MONOTONIC)); + break; + } + ST_LOGV("pts drop: queue1des=%lld expect=%lld size=%d", + desiredPresent, expectedPresent, mQueue.size()); + if (stillTracking(front)) { + // front buffer is still in mSlots, so mark the slot as free + mSlots[front->mBuf].mBufferState = BufferSlot::FREE; + } + mQueue.erase(front); + front = mQueue.begin(); + } + + // See if the front buffer is due. + nsecs_t desiredPresent = front->mTimestamp; + if (desiredPresent > expectedPresent && + desiredPresent < expectedPresent + MAX_REASONABLE_NSEC) { + ST_LOGV("pts defer: des=%lld expect=%lld (%lld) now=%lld", + desiredPresent, expectedPresent, desiredPresent - expectedPresent, + systemTime(CLOCK_MONOTONIC)); + return PRESENT_LATER; } - buffer->mCrop = mSlots[buf].mCrop; - buffer->mTransform = mSlots[buf].mTransform; - buffer->mScalingMode = mSlots[buf].mScalingMode; - buffer->mFrameNumber = mSlots[buf].mFrameNumber; - buffer->mTimestamp = mSlots[buf].mTimestamp; - buffer->mBuf = buf; - buffer->mFence = mSlots[buf].mFence; + ST_LOGV("pts accept: des=%lld expect=%lld (%lld) now=%lld", + desiredPresent, expectedPresent, desiredPresent - expectedPresent, + systemTime(CLOCK_MONOTONIC)); + } + + int buf = front->mBuf; + *buffer = *front; + ATRACE_BUFFER_INDEX(buf); + + ST_LOGV("acquireBuffer: acquiring { slot=%d/%llu, buffer=%p }", + front->mBuf, front->mFrameNumber, + front->mGraphicBuffer->handle); + // if front buffer still being tracked update slot state + if (stillTracking(front)) { mSlots[buf].mAcquireCalled = true; mSlots[buf].mNeedsCleanupOnRelease = false; mSlots[buf].mBufferState = BufferSlot::ACQUIRED; mSlots[buf].mFence = Fence::NO_FENCE; + } - mQueue.erase(front); - mDequeueCondition.broadcast(); - - ATRACE_INT(mConsumerName.string(), mQueue.size()); - } else { - return NO_BUFFER_AVAILABLE; + // If the buffer has previously been acquired by the consumer, set + // mGraphicBuffer to NULL to avoid unnecessarily remapping this + // buffer on the consumer side. + if (buffer->mAcquireCalled) { + buffer->mGraphicBuffer = NULL; } + mQueue.erase(front); + mDequeueCondition.broadcast(); + + ATRACE_INT(mConsumerName.string(), mQueue.size()); + return NO_ERROR; } -status_t BufferQueue::releaseBuffer(int buf, EGLDisplay display, +status_t BufferQueue::releaseBuffer( + int buf, uint64_t frameNumber, EGLDisplay display, EGLSyncKHR eglFence, const sp<Fence>& fence) { ATRACE_CALL(); ATRACE_BUFFER_INDEX(buf); - Mutex::Autolock _l(mMutex); - if (buf == INVALID_BUFFER_SLOT || fence == NULL) { return BAD_VALUE; } - mSlots[buf].mEglDisplay = display; - mSlots[buf].mEglFence = eglFence; - mSlots[buf].mFence = fence; + Mutex::Autolock _l(mMutex); + + // If the frame number has changed because buffer has been reallocated, + // we can ignore this releaseBuffer for the old buffer. + if (frameNumber != mSlots[buf].mFrameNumber) { + return STALE_BUFFER_SLOT; + } + + + // Internal state consistency checks: + // Make sure this buffers hasn't been queued while we were owning it (acquired) + Fifo::iterator front(mQueue.begin()); + Fifo::const_iterator const end(mQueue.end()); + while (front != end) { + if (front->mBuf == buf) { + LOG_ALWAYS_FATAL("[%s] received new buffer(#%lld) on slot #%d that has not yet been " + "acquired", mConsumerName.string(), frameNumber, buf); + break; // never reached + } + front++; + } // The buffer can now only be released if its in the acquired state if (mSlots[buf].mBufferState == BufferSlot::ACQUIRED) { + mSlots[buf].mEglDisplay = display; + mSlots[buf].mEglFence = eglFence; + mSlots[buf].mFence = fence; mSlots[buf].mBufferState = BufferSlot::FREE; } else if (mSlots[buf].mNeedsCleanupOnRelease) { ST_LOGV("releasing a stale buf %d its state was %d", buf, mSlots[buf].mBufferState); @@ -893,8 +1031,10 @@ status_t BufferQueue::releaseBuffer(int buf, EGLDisplay display, return NO_ERROR; } -status_t BufferQueue::consumerConnect(const sp<ConsumerListener>& consumerListener) { - ST_LOGV("consumerConnect"); +status_t BufferQueue::consumerConnect(const sp<IConsumerListener>& consumerListener, + bool controlledByApp) { + ST_LOGV("consumerConnect controlledByApp=%s", + controlledByApp ? "true" : "false"); Mutex::Autolock lock(mMutex); if (mAbandoned) { @@ -907,6 +1047,7 @@ status_t BufferQueue::consumerConnect(const sp<ConsumerListener>& consumerListen } mConsumerListener = consumerListener; + mConsumerControlledByApp = controlledByApp; return NO_ERROR; } @@ -943,14 +1084,24 @@ status_t BufferQueue::getReleasedBuffers(uint32_t* slotMask) { mask |= 1 << i; } } + + // Remove buffers in flight (on the queue) from the mask where acquire has + // been called, as the consumer will not receive the buffer address, so + // it should not free these slots. + Fifo::iterator front(mQueue.begin()); + while (front != mQueue.end()) { + if (front->mAcquireCalled) + mask &= ~(1 << front->mBuf); + front++; + } + *slotMask = mask; ST_LOGV("getReleasedBuffers: returning mask %#x", mask); return NO_ERROR; } -status_t BufferQueue::setDefaultBufferSize(uint32_t w, uint32_t h) -{ +status_t BufferQueue::setDefaultBufferSize(uint32_t w, uint32_t h) { ST_LOGV("setDefaultBufferSize: w=%d, h=%d", w, h); if (!w || !h) { ST_LOGE("setDefaultBufferSize: dimensions cannot be 0 (w=%d, h=%d)", @@ -970,6 +1121,17 @@ status_t BufferQueue::setDefaultMaxBufferCount(int bufferCount) { return setDefaultMaxBufferCountLocked(bufferCount); } +status_t BufferQueue::disableAsyncBuffer() { + ATRACE_CALL(); + Mutex::Autolock lock(mMutex); + if (mConsumerListener != NULL) { + ST_LOGE("disableAsyncBuffer: consumer already connected!"); + return INVALID_OPERATION; + } + mUseAsyncBuffer = false; + return NO_ERROR; +} + status_t BufferQueue::setMaxAcquiredBufferCount(int maxAcquiredBuffers) { ATRACE_CALL(); Mutex::Autolock lock(mMutex); @@ -985,58 +1147,26 @@ status_t BufferQueue::setMaxAcquiredBufferCount(int maxAcquiredBuffers) { return NO_ERROR; } -void BufferQueue::freeAllBuffersExceptHeadLocked() { - int head = -1; - if (!mQueue.empty()) { - Fifo::iterator front(mQueue.begin()); - head = *front; - } - mBufferHasBeenQueued = false; - for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { - if (i != head) { - freeBufferLocked(i); - } - } -} - -status_t BufferQueue::drainQueueLocked() { - while (mSynchronousMode && mQueue.size() > 1) { - mDequeueCondition.wait(mMutex); - if (mAbandoned) { - ST_LOGE("drainQueueLocked: BufferQueue has been abandoned!"); - return NO_INIT; - } - if (mConnectedApi == NO_CONNECTED_API) { - ST_LOGE("drainQueueLocked: BufferQueue is not connected!"); - return NO_INIT; - } - } - return NO_ERROR; -} +int BufferQueue::getMinUndequeuedBufferCount(bool async) const { + // if dequeueBuffer is allowed to error out, we don't have to + // add an extra buffer. + if (!mUseAsyncBuffer) + return mMaxAcquiredBufferCount; -status_t BufferQueue::drainQueueAndFreeBuffersLocked() { - status_t err = drainQueueLocked(); - if (err == NO_ERROR) { - if (mQueue.empty()) { - freeAllBuffersLocked(); - } else { - freeAllBuffersExceptHeadLocked(); - } - } - return err; -} + // we're in async mode, or we want to prevent the app to + // deadlock itself, we throw-in an extra buffer to guarantee it. + if (mDequeueBufferCannotBlock || async) + return mMaxAcquiredBufferCount+1; -int BufferQueue::getMinMaxBufferCountLocked() const { - return getMinUndequeuedBufferCountLocked() + 1; + return mMaxAcquiredBufferCount; } -int BufferQueue::getMinUndequeuedBufferCountLocked() const { - return mSynchronousMode ? mMaxAcquiredBufferCount : - mMaxAcquiredBufferCount + 1; +int BufferQueue::getMinMaxBufferCountLocked(bool async) const { + return getMinUndequeuedBufferCount(async) + 1; } -int BufferQueue::getMaxBufferCountLocked() const { - int minMaxBufferCount = getMinMaxBufferCountLocked(); +int BufferQueue::getMaxBufferCountLocked(bool async) const { + int minMaxBufferCount = getMinMaxBufferCountLocked(async); int maxBufferCount = mDefaultMaxBufferCount; if (maxBufferCount < minMaxBufferCount) { @@ -1061,21 +1191,37 @@ int BufferQueue::getMaxBufferCountLocked() const { return maxBufferCount; } +bool BufferQueue::stillTracking(const BufferItem *item) const { + const BufferSlot &slot = mSlots[item->mBuf]; + + ST_LOGV("stillTracking?: item: { slot=%d/%llu, buffer=%p }, " + "slot: { slot=%d/%llu, buffer=%p }", + item->mBuf, item->mFrameNumber, + (item->mGraphicBuffer.get() ? item->mGraphicBuffer->handle : 0), + item->mBuf, slot.mFrameNumber, + (slot.mGraphicBuffer.get() ? slot.mGraphicBuffer->handle : 0)); + + // Compare item with its original buffer slot. We can check the slot + // as the buffer would not be moved to a different slot by the producer. + return (slot.mGraphicBuffer != NULL && + item->mGraphicBuffer->handle == slot.mGraphicBuffer->handle); +} + BufferQueue::ProxyConsumerListener::ProxyConsumerListener( - const wp<BufferQueue::ConsumerListener>& consumerListener): + const wp<ConsumerListener>& consumerListener): mConsumerListener(consumerListener) {} BufferQueue::ProxyConsumerListener::~ProxyConsumerListener() {} void BufferQueue::ProxyConsumerListener::onFrameAvailable() { - sp<BufferQueue::ConsumerListener> listener(mConsumerListener.promote()); + sp<ConsumerListener> listener(mConsumerListener.promote()); if (listener != NULL) { listener->onFrameAvailable(); } } void BufferQueue::ProxyConsumerListener::onBuffersReleased() { - sp<BufferQueue::ConsumerListener> listener(mConsumerListener.promote()); + sp<ConsumerListener> listener(mConsumerListener.promote()); if (listener != NULL) { listener->onBuffersReleased(); } diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp index 4937b17..c4ec857 100644 --- a/libs/gui/ConsumerBase.cpp +++ b/libs/gui/ConsumerBase.cpp @@ -51,9 +51,9 @@ static int32_t createProcessUniqueId() { return android_atomic_inc(&globalCounter); } -ConsumerBase::ConsumerBase(const sp<BufferQueue>& bufferQueue) : +ConsumerBase::ConsumerBase(const sp<IGraphicBufferConsumer>& bufferQueue, bool controlledByApp) : mAbandoned(false), - mBufferQueue(bufferQueue) { + mConsumer(bufferQueue) { // Choose a name using the PID and a process-unique ID. mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId()); @@ -61,17 +61,15 @@ ConsumerBase::ConsumerBase(const sp<BufferQueue>& bufferQueue) : // reference once the ctor ends, as that would cause the refcount of 'this' // dropping to 0 at the end of the ctor. Since all we need is a wp<...> // that's what we create. - wp<BufferQueue::ConsumerListener> listener; - sp<BufferQueue::ConsumerListener> proxy; - listener = static_cast<BufferQueue::ConsumerListener*>(this); - proxy = new BufferQueue::ProxyConsumerListener(listener); + wp<ConsumerListener> listener = static_cast<ConsumerListener*>(this); + sp<IConsumerListener> proxy = new BufferQueue::ProxyConsumerListener(listener); - status_t err = mBufferQueue->consumerConnect(proxy); + status_t err = mConsumer->consumerConnect(proxy, controlledByApp); if (err != NO_ERROR) { CB_LOGE("ConsumerBase: error connecting to BufferQueue: %s (%d)", strerror(-err), err); } else { - mBufferQueue->setConsumerName(mName); + mConsumer->setConsumerName(mName); } } @@ -95,12 +93,7 @@ void ConsumerBase::freeBufferLocked(int slotIndex) { CB_LOGV("freeBufferLocked: slotIndex=%d", slotIndex); mSlots[slotIndex].mGraphicBuffer = 0; mSlots[slotIndex].mFence = Fence::NO_FENCE; -} - -// Used for refactoring, should not be in final interface -sp<BufferQueue> ConsumerBase::getBufferQueue() const { - Mutex::Autolock lock(mMutex); - return mBufferQueue; + mSlots[slotIndex].mFrameNumber = 0; } void ConsumerBase::onFrameAvailable() { @@ -129,7 +122,7 @@ void ConsumerBase::onBuffersReleased() { } uint32_t mask = 0; - mBufferQueue->getReleasedBuffers(&mask); + mConsumer->getReleasedBuffers(&mask); for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { if (mask & (1 << i)) { freeBufferLocked(i); @@ -153,8 +146,8 @@ void ConsumerBase::abandonLocked() { freeBufferLocked(i); } // disconnect from the BufferQueue - mBufferQueue->consumerDisconnect(); - mBufferQueue.clear(); + mConsumer->consumerDisconnect(); + mConsumer.clear(); } void ConsumerBase::setFrameAvailableListener( @@ -165,28 +158,25 @@ void ConsumerBase::setFrameAvailableListener( } void ConsumerBase::dump(String8& result) const { - char buffer[1024]; - dump(result, "", buffer, 1024); + dump(result, ""); } -void ConsumerBase::dump(String8& result, const char* prefix, - char* buffer, size_t size) const { +void ConsumerBase::dump(String8& result, const char* prefix) const { Mutex::Autolock _l(mMutex); - dumpLocked(result, prefix, buffer, size); + dumpLocked(result, prefix); } -void ConsumerBase::dumpLocked(String8& result, const char* prefix, - char* buffer, size_t SIZE) const { - snprintf(buffer, SIZE, "%smAbandoned=%d\n", prefix, int(mAbandoned)); - result.append(buffer); +void ConsumerBase::dumpLocked(String8& result, const char* prefix) const { + result.appendFormat("%smAbandoned=%d\n", prefix, int(mAbandoned)); if (!mAbandoned) { - mBufferQueue->dump(result, prefix, buffer, SIZE); + mConsumer->dump(result, prefix); } } -status_t ConsumerBase::acquireBufferLocked(BufferQueue::BufferItem *item) { - status_t err = mBufferQueue->acquireBuffer(item); +status_t ConsumerBase::acquireBufferLocked(BufferQueue::BufferItem *item, + nsecs_t presentWhen) { + status_t err = mConsumer->acquireBuffer(item, presentWhen); if (err != NO_ERROR) { return err; } @@ -195,21 +185,31 @@ status_t ConsumerBase::acquireBufferLocked(BufferQueue::BufferItem *item) { mSlots[item->mBuf].mGraphicBuffer = item->mGraphicBuffer; } + mSlots[item->mBuf].mFrameNumber = item->mFrameNumber; mSlots[item->mBuf].mFence = item->mFence; - CB_LOGV("acquireBufferLocked: -> slot=%d", item->mBuf); + CB_LOGV("acquireBufferLocked: -> slot=%d/%llu", + item->mBuf, item->mFrameNumber); return OK; } -status_t ConsumerBase::addReleaseFence(int slot, const sp<Fence>& fence) { +status_t ConsumerBase::addReleaseFence(int slot, + const sp<GraphicBuffer> graphicBuffer, const sp<Fence>& fence) { Mutex::Autolock lock(mMutex); - return addReleaseFenceLocked(slot, fence); + return addReleaseFenceLocked(slot, graphicBuffer, fence); } -status_t ConsumerBase::addReleaseFenceLocked(int slot, const sp<Fence>& fence) { +status_t ConsumerBase::addReleaseFenceLocked(int slot, + const sp<GraphicBuffer> graphicBuffer, const sp<Fence>& fence) { CB_LOGV("addReleaseFenceLocked: slot=%d", slot); + // If consumer no longer tracks this graphicBuffer, we can safely + // drop this fence, as it will never be received by the producer. + if (!stillTracking(slot, graphicBuffer)) { + return OK; + } + if (!mSlots[slot].mFence.get()) { mSlots[slot].mFence = fence; } else { @@ -229,11 +229,20 @@ status_t ConsumerBase::addReleaseFenceLocked(int slot, const sp<Fence>& fence) { return OK; } -status_t ConsumerBase::releaseBufferLocked(int slot, EGLDisplay display, - EGLSyncKHR eglFence) { - CB_LOGV("releaseBufferLocked: slot=%d", slot); - status_t err = mBufferQueue->releaseBuffer(slot, display, eglFence, - mSlots[slot].mFence); +status_t ConsumerBase::releaseBufferLocked( + int slot, const sp<GraphicBuffer> graphicBuffer, + EGLDisplay display, EGLSyncKHR eglFence) { + // If consumer no longer tracks this graphicBuffer (we received a new + // buffer on the same slot), the buffer producer is definitely no longer + // tracking it. + if (!stillTracking(slot, graphicBuffer)) { + return OK; + } + + CB_LOGV("releaseBufferLocked: slot=%d/%llu", + slot, mSlots[slot].mFrameNumber); + status_t err = mConsumer->releaseBuffer(slot, mSlots[slot].mFrameNumber, + display, eglFence, mSlots[slot].mFence); if (err == BufferQueue::STALE_BUFFER_SLOT) { freeBufferLocked(slot); } @@ -243,4 +252,13 @@ status_t ConsumerBase::releaseBufferLocked(int slot, EGLDisplay display, return err; } +bool ConsumerBase::stillTracking(int slot, + const sp<GraphicBuffer> graphicBuffer) { + if (slot < 0 || slot >= BufferQueue::NUM_BUFFER_SLOTS) { + return false; + } + return (mSlots[slot].mGraphicBuffer != NULL && + mSlots[slot].mGraphicBuffer->handle == graphicBuffer->handle); +} + } // namespace android diff --git a/libs/gui/CpuConsumer.cpp b/libs/gui/CpuConsumer.cpp index 0543649..bff55d1 100644 --- a/libs/gui/CpuConsumer.cpp +++ b/libs/gui/CpuConsumer.cpp @@ -30,17 +30,17 @@ namespace android { -CpuConsumer::CpuConsumer(uint32_t maxLockedBuffers, bool synchronousMode) : - ConsumerBase(new BufferQueue(true) ), +CpuConsumer::CpuConsumer(const sp<IGraphicBufferConsumer>& bq, + uint32_t maxLockedBuffers, bool controlledByApp) : + ConsumerBase(bq, controlledByApp), mMaxLockedBuffers(maxLockedBuffers), mCurrentLockedBuffers(0) { // Create tracking entries for locked buffers mAcquiredBuffers.insertAt(0, maxLockedBuffers); - mBufferQueue->setSynchronousMode(synchronousMode); - mBufferQueue->setConsumerUsageBits(GRALLOC_USAGE_SW_READ_OFTEN); - mBufferQueue->setMaxAcquiredBufferCount(maxLockedBuffers); + mConsumer->setConsumerUsageBits(GRALLOC_USAGE_SW_READ_OFTEN); + mConsumer->setMaxAcquiredBufferCount(maxLockedBuffers); } CpuConsumer::~CpuConsumer() { @@ -52,7 +52,19 @@ CpuConsumer::~CpuConsumer() { void CpuConsumer::setName(const String8& name) { Mutex::Autolock _l(mMutex); mName = name; - mBufferQueue->setConsumerName(name); + mConsumer->setConsumerName(name); +} + +status_t CpuConsumer::setDefaultBufferSize(uint32_t width, uint32_t height) +{ + Mutex::Autolock _l(mMutex); + return mConsumer->setDefaultBufferSize(width, height); +} + +status_t CpuConsumer::setDefaultBufferFormat(uint32_t defaultFormat) +{ + Mutex::Autolock _l(mMutex); + return mConsumer->setDefaultBufferFormat(defaultFormat); } status_t CpuConsumer::lockNextBuffer(LockedBuffer *nativeBuffer) { @@ -60,14 +72,16 @@ status_t CpuConsumer::lockNextBuffer(LockedBuffer *nativeBuffer) { if (!nativeBuffer) return BAD_VALUE; if (mCurrentLockedBuffers == mMaxLockedBuffers) { - return INVALID_OPERATION; + CC_LOGW("Max buffers have been locked (%d), cannot lock anymore.", + mMaxLockedBuffers); + return NOT_ENOUGH_DATA; } BufferQueue::BufferItem b; Mutex::Autolock _l(mMutex); - err = acquireBufferLocked(&b); + err = acquireBufferLocked(&b, 0); if (err != OK) { if (err == BufferQueue::NO_BUFFER_AVAILABLE) { return BAD_VALUE; @@ -189,7 +203,9 @@ status_t CpuConsumer::releaseAcquiredBufferLocked(int lockedIdx) { // disconnected after this buffer was acquired. if (CC_LIKELY(mAcquiredBuffers[lockedIdx].mGraphicBuffer == mSlots[buf].mGraphicBuffer)) { - releaseBufferLocked(buf, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR); + releaseBufferLocked( + buf, mAcquiredBuffers[lockedIdx].mGraphicBuffer, + EGL_NO_DISPLAY, EGL_NO_SYNC_KHR); } AcquiredBuffer &ab = mAcquiredBuffers.editItemAt(lockedIdx); diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp index dc46a51..7ee3081 100644 --- a/libs/gui/GLConsumer.cpp +++ b/libs/gui/GLConsumer.cpp @@ -25,6 +25,7 @@ #include <EGL/eglext.h> #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> +#include <cutils/compiler.h> #include <hardware/hardware.h> @@ -40,6 +41,9 @@ #include <utils/String8.h> #include <utils/Trace.h> +EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name); +#define CROP_EXT_STR "EGL_ANDROID_image_crop" + namespace android { // Macros for including the GLConsumer name in log messages @@ -49,6 +53,14 @@ namespace android { #define ST_LOGW(x, ...) ALOGW("[%s] "x, mName.string(), ##__VA_ARGS__) #define ST_LOGE(x, ...) ALOGE("[%s] "x, mName.string(), ##__VA_ARGS__) +static const struct { + size_t width, height; + char const* bits; +} kDebugData = { 15, 12, + "___________________________________XX_XX_______X_X_____X_X____X_XXXXXXX_X____XXXXXXXXXXX__" + "___XX_XXX_XX_______XXXXXXX_________X___X_________X_____X__________________________________" +}; + // Transform matrices static float mtxIdentity[16] = { 1, 0, 0, 0, @@ -77,21 +89,41 @@ static float mtxRot90[16] = { static void mtxMul(float out[16], const float a[16], const float b[16]); +Mutex GLConsumer::sStaticInitLock; +sp<GraphicBuffer> GLConsumer::sReleasedTexImageBuffer; + +static bool hasEglAndroidImageCropImpl() { + EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); + const char* exts = eglQueryStringImplementationANDROID(dpy, EGL_EXTENSIONS); + size_t cropExtLen = strlen(CROP_EXT_STR); + size_t extsLen = strlen(exts); + bool equal = !strcmp(CROP_EXT_STR, exts); + bool atStart = !strncmp(CROP_EXT_STR " ", exts, cropExtLen+1); + bool atEnd = (cropExtLen+1) < extsLen && + !strcmp(" " CROP_EXT_STR, exts + extsLen - (cropExtLen+1)); + bool inMiddle = strstr(exts, " " CROP_EXT_STR " "); + return equal || atStart || atEnd || inMiddle; +} -GLConsumer::GLConsumer(const sp<BufferQueue>& bq, GLuint tex, - GLenum texTarget, bool useFenceSync) : - ConsumerBase(bq), - mUseFenceSync(useFenceSync), - mTexTarget(texTarget) {} +static bool hasEglAndroidImageCrop() { + // Only compute whether the extension is present once the first time this + // function is called. + static bool hasIt = hasEglAndroidImageCropImpl(); + return hasIt; +} +static bool isEglImageCroppable(const Rect& crop) { + return hasEglAndroidImageCrop() && (crop.left == 0 && crop.top == 0); +} -GLConsumer::GLConsumer(GLuint tex, bool allowSynchronousMode, - GLenum texTarget, bool useFenceSync, const sp<BufferQueue> &bufferQueue) : - ConsumerBase(bufferQueue == 0 ? new BufferQueue(allowSynchronousMode) : bufferQueue), +GLConsumer::GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t tex, + uint32_t texTarget, bool useFenceSync, bool isControlledByApp) : + ConsumerBase(bq, isControlledByApp), mCurrentTransform(0), mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), mCurrentFence(Fence::NO_FENCE), mCurrentTimestamp(0), + mCurrentFrameNumber(0), mDefaultWidth(1), mDefaultHeight(1), mFilteringEnabled(true), @@ -108,12 +140,12 @@ GLConsumer::GLConsumer(GLuint tex, bool allowSynchronousMode, memcpy(mCurrentTransformMatrix, mtxIdentity, sizeof(mCurrentTransformMatrix)); - mBufferQueue->setConsumerUsageBits(DEFAULT_USAGE_FLAGS); + mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS); } status_t GLConsumer::setDefaultMaxBufferCount(int bufferCount) { Mutex::Autolock lock(mMutex); - return mBufferQueue->setDefaultMaxBufferCount(bufferCount); + return mConsumer->setDefaultMaxBufferCount(bufferCount); } @@ -122,7 +154,7 @@ status_t GLConsumer::setDefaultBufferSize(uint32_t w, uint32_t h) Mutex::Autolock lock(mMutex); mDefaultWidth = w; mDefaultHeight = h; - return mBufferQueue->setDefaultBufferSize(w, h); + return mConsumer->setDefaultBufferSize(w, h); } status_t GLConsumer::updateTexImage() { @@ -146,7 +178,7 @@ status_t GLConsumer::updateTexImage() { // Acquire the next buffer. // In asynchronous mode the list is guaranteed to be one buffer // deep, while in synchronous mode we use the oldest buffer. - err = acquireBufferLocked(&item); + err = acquireBufferLocked(&item, 0); if (err != NO_ERROR) { if (err == BufferQueue::NO_BUFFER_AVAILABLE) { // We always bind the texture even if we don't update its contents. @@ -161,7 +193,7 @@ status_t GLConsumer::updateTexImage() { } // Release the previous buffer. - err = releaseAndUpdateLocked(item); + err = updateAndReleaseLocked(item); if (err != NO_ERROR) { // We always bind the texture. glBindTexture(mTexTarget, mTexName); @@ -172,44 +204,154 @@ status_t GLConsumer::updateTexImage() { return bindTextureImageLocked(); } -status_t GLConsumer::acquireBufferLocked(BufferQueue::BufferItem *item) { - status_t err = ConsumerBase::acquireBufferLocked(item); + +status_t GLConsumer::releaseTexImage() { + ATRACE_CALL(); + ST_LOGV("releaseTexImage"); + Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + ST_LOGE("releaseTexImage: GLConsumer is abandoned!"); + return NO_INIT; + } + + // Make sure the EGL state is the same as in previous calls. + status_t err = NO_ERROR; + + if (mAttached) { + err = checkAndUpdateEglStateLocked(true); + if (err != NO_ERROR) { + return err; + } + } else { + // if we're detached, no need to validate EGL's state -- we won't use it. + } + + // Update the GLConsumer state. + int buf = mCurrentTexture; + if (buf != BufferQueue::INVALID_BUFFER_SLOT) { + + ST_LOGV("releaseTexImage: (slot=%d, mAttached=%d)", buf, mAttached); + + if (mAttached) { + // Do whatever sync ops we need to do before releasing the slot. + err = syncForReleaseLocked(mEglDisplay); + if (err != NO_ERROR) { + ST_LOGE("syncForReleaseLocked failed (slot=%d), err=%d", buf, err); + return err; + } + } else { + // if we're detached, we just use the fence that was created in detachFromContext() + // so... basically, nothing more to do here. + } + + err = releaseBufferLocked(buf, mSlots[buf].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR); + if (err < NO_ERROR) { + ST_LOGE("releaseTexImage: failed to release buffer: %s (%d)", + strerror(-err), err); + return err; + } + + mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT; + mCurrentTextureBuf = getDebugTexImageBuffer(); + mCurrentCrop.makeInvalid(); + mCurrentTransform = 0; + mCurrentScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE; + mCurrentTimestamp = 0; + mCurrentFence = Fence::NO_FENCE; + + if (mAttached) { + // bind a dummy texture + glBindTexture(mTexTarget, mTexName); + bindUnslottedBufferLocked(mEglDisplay); + } else { + // detached, don't touch the texture (and we may not even have an + // EGLDisplay here. + } + } + + return NO_ERROR; +} + +sp<GraphicBuffer> GLConsumer::getDebugTexImageBuffer() { + Mutex::Autolock _l(sStaticInitLock); + if (CC_UNLIKELY(sReleasedTexImageBuffer == NULL)) { + // The first time, create the debug texture in case the application + // continues to use it. + sp<GraphicBuffer> buffer = new GraphicBuffer( + kDebugData.width, kDebugData.height, PIXEL_FORMAT_RGBA_8888, + GraphicBuffer::USAGE_SW_WRITE_RARELY); + uint32_t* bits; + buffer->lock(GraphicBuffer::USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&bits)); + size_t w = buffer->getStride(); + size_t h = buffer->getHeight(); + memset(bits, 0, w*h*4); + for (size_t y=0 ; y<kDebugData.height ; y++) { + for (size_t x=0 ; x<kDebugData.width ; x++) { + bits[x] = (kDebugData.bits[y*kDebugData.width+x] == 'X') ? 0xFF000000 : 0xFFFFFFFF; + } + bits += w; + } + buffer->unlock(); + sReleasedTexImageBuffer = buffer; + } + return sReleasedTexImageBuffer; +} + +status_t GLConsumer::acquireBufferLocked(BufferQueue::BufferItem *item, + nsecs_t presentWhen) { + status_t err = ConsumerBase::acquireBufferLocked(item, presentWhen); if (err != NO_ERROR) { return err; } int slot = item->mBuf; - if (item->mGraphicBuffer != NULL) { - // This buffer has not been acquired before, so we must assume - // that any EGLImage in mEglSlots is stale. - if (mEglSlots[slot].mEglImage != EGL_NO_IMAGE_KHR) { - if (!eglDestroyImageKHR(mEglDisplay, mEglSlots[slot].mEglImage)) { - ST_LOGW("acquireBufferLocked: eglDestroyImageKHR failed for slot=%d", - slot); - // keep going - } - mEglSlots[slot].mEglImage = EGL_NO_IMAGE_KHR; + bool destroyEglImage = false; + + if (mEglSlots[slot].mEglImage != EGL_NO_IMAGE_KHR) { + if (item->mGraphicBuffer != NULL) { + // This buffer has not been acquired before, so we must assume + // that any EGLImage in mEglSlots is stale. + destroyEglImage = true; + } else if (mEglSlots[slot].mCropRect != item->mCrop) { + // We've already seen this buffer before, but it now has a + // different crop rect, so we'll need to recreate the EGLImage if + // we're using the EGL_ANDROID_image_crop extension. + destroyEglImage = hasEglAndroidImageCrop(); } } + if (destroyEglImage) { + if (!eglDestroyImageKHR(mEglDisplay, mEglSlots[slot].mEglImage)) { + ST_LOGW("acquireBufferLocked: eglDestroyImageKHR failed for slot=%d", + slot); + // keep going + } + mEglSlots[slot].mEglImage = EGL_NO_IMAGE_KHR; + } + return NO_ERROR; } -status_t GLConsumer::releaseBufferLocked(int buf, EGLDisplay display, - EGLSyncKHR eglFence) { - status_t err = ConsumerBase::releaseBufferLocked(buf, display, eglFence); - +status_t GLConsumer::releaseBufferLocked(int buf, + sp<GraphicBuffer> graphicBuffer, + EGLDisplay display, EGLSyncKHR eglFence) { + // release the buffer if it hasn't already been discarded by the + // BufferQueue. This can happen, for example, when the producer of this + // buffer has reallocated the original buffer slot after this buffer + // was acquired. + status_t err = ConsumerBase::releaseBufferLocked( + buf, graphicBuffer, display, eglFence); mEglSlots[buf].mEglFence = EGL_NO_SYNC_KHR; - return err; } -status_t GLConsumer::releaseAndUpdateLocked(const BufferQueue::BufferItem& item) +status_t GLConsumer::updateAndReleaseLocked(const BufferQueue::BufferItem& item) { status_t err = NO_ERROR; if (!mAttached) { - ST_LOGE("releaseAndUpdate: GLConsumer is not attached to an OpenGL " + ST_LOGE("updateAndRelease: GLConsumer is not attached to an OpenGL " "ES context"); return INVALID_OPERATION; } @@ -230,13 +372,15 @@ status_t GLConsumer::releaseAndUpdateLocked(const BufferQueue::BufferItem& item) // EGLImage when detaching from a context but the buffer has not been // re-allocated. if (mEglSlots[buf].mEglImage == EGL_NO_IMAGE_KHR) { - EGLImageKHR image = createImage(mEglDisplay, mSlots[buf].mGraphicBuffer); + EGLImageKHR image = createImage(mEglDisplay, + mSlots[buf].mGraphicBuffer, item.mCrop); if (image == EGL_NO_IMAGE_KHR) { - ST_LOGW("releaseAndUpdate: unable to createImage on display=%p slot=%d", + ST_LOGW("updateAndRelease: unable to createImage on display=%p slot=%d", mEglDisplay, buf); return UNKNOWN_ERROR; } mEglSlots[buf].mEglImage = image; + mEglSlots[buf].mCropRect = item.mCrop; } // Do whatever sync ops we need to do before releasing the old slot. @@ -244,21 +388,25 @@ status_t GLConsumer::releaseAndUpdateLocked(const BufferQueue::BufferItem& item) if (err != NO_ERROR) { // Release the buffer we just acquired. It's not safe to // release the old buffer, so instead we just drop the new frame. - releaseBufferLocked(buf, mEglDisplay, EGL_NO_SYNC_KHR); + // As we are still under lock since acquireBuffer, it is safe to + // release by slot. + releaseBufferLocked(buf, mSlots[buf].mGraphicBuffer, + mEglDisplay, EGL_NO_SYNC_KHR); return err; } - ST_LOGV("releaseAndUpdate: (slot=%d buf=%p) -> (slot=%d buf=%p)", + ST_LOGV("updateAndRelease: (slot=%d buf=%p) -> (slot=%d buf=%p)", mCurrentTexture, mCurrentTextureBuf != NULL ? mCurrentTextureBuf->handle : 0, buf, mSlots[buf].mGraphicBuffer->handle); // release old buffer if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { - status_t status = releaseBufferLocked(mCurrentTexture, mEglDisplay, + status_t status = releaseBufferLocked( + mCurrentTexture, mCurrentTextureBuf, mEglDisplay, mEglSlots[mCurrentTexture].mEglFence); - if (status != NO_ERROR && status != BufferQueue::STALE_BUFFER_SLOT) { - ST_LOGE("releaseAndUpdate: failed to release buffer: %s (%d)", + if (status < NO_ERROR) { + ST_LOGE("updateAndRelease: failed to release buffer: %s (%d)", strerror(-status), status); err = status; // keep going, with error raised [?] @@ -273,6 +421,7 @@ status_t GLConsumer::releaseAndUpdateLocked(const BufferQueue::BufferItem& item) mCurrentScalingMode = item.mScalingMode; mCurrentTimestamp = item.mTimestamp; mCurrentFence = item.mFence; + mCurrentFrameNumber = item.mFrameNumber; computeCurrentTransformMatrixLocked(); @@ -317,18 +466,27 @@ status_t GLConsumer::bindTextureImageLocked() { } -status_t GLConsumer::checkAndUpdateEglStateLocked() { +status_t GLConsumer::checkAndUpdateEglStateLocked(bool contextCheck) { EGLDisplay dpy = eglGetCurrentDisplay(); EGLContext ctx = eglGetCurrentContext(); - if ((mEglDisplay != dpy && mEglDisplay != EGL_NO_DISPLAY) || - dpy == EGL_NO_DISPLAY) { + if (!contextCheck) { + // if this is the first time we're called, mEglDisplay/mEglContext have + // never been set, so don't error out (below). + if (mEglDisplay == EGL_NO_DISPLAY) { + mEglDisplay = dpy; + } + if (mEglContext == EGL_NO_DISPLAY) { + mEglContext = ctx; + } + } + + if (mEglDisplay != dpy || dpy == EGL_NO_DISPLAY) { ST_LOGE("checkAndUpdateEglState: invalid current EGLDisplay"); return INVALID_OPERATION; } - if ((mEglContext != ctx && mEglContext != EGL_NO_CONTEXT) || - ctx == EGL_NO_CONTEXT) { + if (mEglContext != ctx || ctx == EGL_NO_CONTEXT) { ST_LOGE("checkAndUpdateEglState: invalid current EGLContext"); return INVALID_OPERATION; } @@ -341,7 +499,8 @@ status_t GLConsumer::checkAndUpdateEglStateLocked() { void GLConsumer::setReleaseFence(const sp<Fence>& fence) { if (fence->isValid() && mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { - status_t err = addReleaseFence(mCurrentTexture, fence); + status_t err = addReleaseFence(mCurrentTexture, + mCurrentTextureBuf, fence); if (err != OK) { ST_LOGE("setReleaseFence: failed to add the fence: %s (%d)", strerror(-err), err); @@ -406,7 +565,7 @@ status_t GLConsumer::detachFromContext() { return OK; } -status_t GLConsumer::attachToContext(GLuint tex) { +status_t GLConsumer::attachToContext(uint32_t tex) { ATRACE_CALL(); ST_LOGV("attachToContext"); Mutex::Autolock lock(mMutex); @@ -437,7 +596,7 @@ status_t GLConsumer::attachToContext(GLuint tex) { // We need to bind the texture regardless of whether there's a current // buffer. - glBindTexture(mTexTarget, tex); + glBindTexture(mTexTarget, GLuint(tex)); if (mCurrentTextureBuf != NULL) { // The EGLImageKHR that was associated with the slot was destroyed when @@ -462,7 +621,8 @@ status_t GLConsumer::bindUnslottedBufferLocked(EGLDisplay dpy) { mCurrentTexture, mCurrentTextureBuf.get()); // Create a temporary EGLImageKHR. - EGLImageKHR image = createImage(dpy, mCurrentTextureBuf); + Rect crop; + EGLImageKHR image = createImage(dpy, mCurrentTextureBuf, mCurrentCrop); if (image == EGL_NO_IMAGE_KHR) { return UNKNOWN_ERROR; } @@ -510,7 +670,8 @@ status_t GLConsumer::syncForReleaseLocked(EGLDisplay dpy) { return UNKNOWN_ERROR; } sp<Fence> fence(new Fence(fenceFd)); - status_t err = addReleaseFenceLocked(mCurrentTexture, fence); + status_t err = addReleaseFenceLocked(mCurrentTexture, + mCurrentTextureBuf, fence); if (err != OK) { ST_LOGE("syncForReleaseLocked: error adding release fence: " "%s (%d)", strerror(-err), err); @@ -571,7 +732,7 @@ bool GLConsumer::isExternalFormat(uint32_t format) return false; } -GLenum GLConsumer::getCurrentTextureTarget() const { +uint32_t GLConsumer::getCurrentTextureTarget() const { return mTexTarget; } @@ -633,62 +794,66 @@ void GLConsumer::computeCurrentTransformMatrixLocked() { ST_LOGD("computeCurrentTransformMatrixLocked: mCurrentTextureBuf is NULL"); } - Rect cropRect = mCurrentCrop; - float tx = 0.0f, ty = 0.0f, sx = 1.0f, sy = 1.0f; - float bufferWidth = buf->getWidth(); - float bufferHeight = buf->getHeight(); - if (!cropRect.isEmpty()) { - float shrinkAmount = 0.0f; - if (mFilteringEnabled) { - // In order to prevent bilinear sampling beyond the edge of the - // crop rectangle we may need to shrink it by 2 texels in each - // dimension. Normally this would just need to take 1/2 a texel - // off each end, but because the chroma channels of YUV420 images - // are subsampled we may need to shrink the crop region by a whole - // texel on each side. - switch (buf->getPixelFormat()) { - case PIXEL_FORMAT_RGBA_8888: - case PIXEL_FORMAT_RGBX_8888: - case PIXEL_FORMAT_RGB_888: - case PIXEL_FORMAT_RGB_565: - case PIXEL_FORMAT_BGRA_8888: - case PIXEL_FORMAT_RGBA_5551: - case PIXEL_FORMAT_RGBA_4444: - // We know there's no subsampling of any channels, so we - // only need to shrink by a half a pixel. - shrinkAmount = 0.5; - break; - - default: - // If we don't recognize the format, we must assume the - // worst case (that we care about), which is YUV420. - shrinkAmount = 1.0; - break; + float mtxBeforeFlipV[16]; + if (!isEglImageCroppable(mCurrentCrop)) { + Rect cropRect = mCurrentCrop; + float tx = 0.0f, ty = 0.0f, sx = 1.0f, sy = 1.0f; + float bufferWidth = buf->getWidth(); + float bufferHeight = buf->getHeight(); + if (!cropRect.isEmpty()) { + float shrinkAmount = 0.0f; + if (mFilteringEnabled) { + // In order to prevent bilinear sampling beyond the edge of the + // crop rectangle we may need to shrink it by 2 texels in each + // dimension. Normally this would just need to take 1/2 a texel + // off each end, but because the chroma channels of YUV420 images + // are subsampled we may need to shrink the crop region by a whole + // texel on each side. + switch (buf->getPixelFormat()) { + case PIXEL_FORMAT_RGBA_8888: + case PIXEL_FORMAT_RGBX_8888: + case PIXEL_FORMAT_RGB_888: + case PIXEL_FORMAT_RGB_565: + case PIXEL_FORMAT_BGRA_8888: + // We know there's no subsampling of any channels, so we + // only need to shrink by a half a pixel. + shrinkAmount = 0.5; + break; + + default: + // If we don't recognize the format, we must assume the + // worst case (that we care about), which is YUV420. + shrinkAmount = 1.0; + break; + } } - } - // Only shrink the dimensions that are not the size of the buffer. - if (cropRect.width() < bufferWidth) { - tx = (float(cropRect.left) + shrinkAmount) / bufferWidth; - sx = (float(cropRect.width()) - (2.0f * shrinkAmount)) / - bufferWidth; + // Only shrink the dimensions that are not the size of the buffer. + if (cropRect.width() < bufferWidth) { + tx = (float(cropRect.left) + shrinkAmount) / bufferWidth; + sx = (float(cropRect.width()) - (2.0f * shrinkAmount)) / + bufferWidth; + } + if (cropRect.height() < bufferHeight) { + ty = (float(bufferHeight - cropRect.bottom) + shrinkAmount) / + bufferHeight; + sy = (float(cropRect.height()) - (2.0f * shrinkAmount)) / + bufferHeight; + } } - if (cropRect.height() < bufferHeight) { - ty = (float(bufferHeight - cropRect.bottom) + shrinkAmount) / - bufferHeight; - sy = (float(cropRect.height()) - (2.0f * shrinkAmount)) / - bufferHeight; + float crop[16] = { + sx, 0, 0, 0, + 0, sy, 0, 0, + 0, 0, 1, 0, + tx, ty, 0, 1, + }; + + mtxMul(mtxBeforeFlipV, crop, xform); + } else { + for (int i = 0; i < 16; i++) { + mtxBeforeFlipV[i] = xform[i]; } } - float crop[16] = { - sx, 0, 0, 0, - 0, sy, 0, 0, - 0, 0, 1, 0, - tx, ty, 0, 1, - }; - - float mtxBeforeFlipV[16]; - mtxMul(mtxBeforeFlipV, crop, xform); // SurfaceFlinger expects the top of its window textures to be at a Y // coordinate of 0, so GLConsumer must behave the same way. We don't @@ -703,13 +868,33 @@ nsecs_t GLConsumer::getTimestamp() { return mCurrentTimestamp; } +nsecs_t GLConsumer::getFrameNumber() { + ST_LOGV("getFrameNumber"); + Mutex::Autolock lock(mMutex); + return mCurrentFrameNumber; +} + EGLImageKHR GLConsumer::createImage(EGLDisplay dpy, - const sp<GraphicBuffer>& graphicBuffer) { + const sp<GraphicBuffer>& graphicBuffer, const Rect& crop) { EGLClientBuffer cbuf = (EGLClientBuffer)graphicBuffer->getNativeBuffer(); EGLint attrs[] = { - EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, + EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, + EGL_IMAGE_CROP_LEFT_ANDROID, crop.left, + EGL_IMAGE_CROP_TOP_ANDROID, crop.top, + EGL_IMAGE_CROP_RIGHT_ANDROID, crop.right, + EGL_IMAGE_CROP_BOTTOM_ANDROID, crop.bottom, EGL_NONE, }; + if (!crop.isValid()) { + // No crop rect to set, so terminate the attrib array before the crop. + attrs[2] = EGL_NONE; + } else if (!isEglImageCroppable(crop)) { + // The crop rect is not at the origin, so we can't set the crop on the + // EGLImage because that's not allowed by the EGL_ANDROID_image_crop + // extension. In the future we can add a layered extension that + // removes this restriction if there is hardware that can support it. + attrs[2] = EGL_NONE; + } EGLImageKHR image = eglCreateImageKHR(dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, cbuf, attrs); if (image == EGL_NO_IMAGE_KHR) { @@ -840,11 +1025,6 @@ status_t GLConsumer::doGLFenceWaitLocked() const { return NO_ERROR; } -bool GLConsumer::isSynchronousMode() const { - Mutex::Autolock lock(mMutex); - return mBufferQueue->isSynchronousMode(); -} - void GLConsumer::freeBufferLocked(int slotIndex) { ST_LOGV("freeBufferLocked: slotIndex=%d", slotIndex); if (slotIndex == mCurrentTexture) { @@ -868,44 +1048,35 @@ void GLConsumer::abandonLocked() { void GLConsumer::setName(const String8& name) { Mutex::Autolock _l(mMutex); mName = name; - mBufferQueue->setConsumerName(name); + mConsumer->setConsumerName(name); } status_t GLConsumer::setDefaultBufferFormat(uint32_t defaultFormat) { Mutex::Autolock lock(mMutex); - return mBufferQueue->setDefaultBufferFormat(defaultFormat); + return mConsumer->setDefaultBufferFormat(defaultFormat); } status_t GLConsumer::setConsumerUsageBits(uint32_t usage) { Mutex::Autolock lock(mMutex); usage |= DEFAULT_USAGE_FLAGS; - return mBufferQueue->setConsumerUsageBits(usage); + return mConsumer->setConsumerUsageBits(usage); } status_t GLConsumer::setTransformHint(uint32_t hint) { Mutex::Autolock lock(mMutex); - return mBufferQueue->setTransformHint(hint); -} - -// Used for refactoring BufferQueue from GLConsumer -// Should not be in final interface once users of GLConsumer are clean up. -status_t GLConsumer::setSynchronousMode(bool enabled) { - Mutex::Autolock lock(mMutex); - return mBufferQueue->setSynchronousMode(enabled); + return mConsumer->setTransformHint(hint); } -void GLConsumer::dumpLocked(String8& result, const char* prefix, - char* buffer, size_t size) const +void GLConsumer::dumpLocked(String8& result, const char* prefix) const { - snprintf(buffer, size, + result.appendFormat( "%smTexName=%d mCurrentTexture=%d\n" "%smCurrentCrop=[%d,%d,%d,%d] mCurrentTransform=%#x\n", prefix, mTexName, mCurrentTexture, prefix, mCurrentCrop.left, mCurrentCrop.top, mCurrentCrop.right, mCurrentCrop.bottom, mCurrentTransform); - result.append(buffer); - ConsumerBase::dumpLocked(result, prefix, buffer, size); + ConsumerBase::dumpLocked(result, prefix); } static void mtxMul(float out[16], const float a[16], const float b[16]) { diff --git a/libs/gui/IConsumerListener.cpp b/libs/gui/IConsumerListener.cpp new file mode 100644 index 0000000..5304462 --- /dev/null +++ b/libs/gui/IConsumerListener.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdint.h> +#include <sys/types.h> + +#include <binder/IInterface.h> +#include <binder/Parcel.h> + +#include <gui/IConsumerListener.h> + +// --------------------------------------------------------------------------- +namespace android { +// --------------------------------------------------------------------------- + +enum { + ON_FRAME_AVAILABLE = IBinder::FIRST_CALL_TRANSACTION, + ON_BUFFER_RELEASED +}; + +class BpConsumerListener : public BpInterface<IConsumerListener> +{ +public: + BpConsumerListener(const sp<IBinder>& impl) + : BpInterface<IConsumerListener>(impl) { + } + + virtual void onFrameAvailable() { + Parcel data, reply; + data.writeInterfaceToken(IConsumerListener::getInterfaceDescriptor()); + remote()->transact(ON_FRAME_AVAILABLE, data, &reply, IBinder::FLAG_ONEWAY); + } + + virtual void onBuffersReleased() { + Parcel data, reply; + data.writeInterfaceToken(IConsumerListener::getInterfaceDescriptor()); + remote()->transact(ON_BUFFER_RELEASED, data, &reply, IBinder::FLAG_ONEWAY); + } +}; + +IMPLEMENT_META_INTERFACE(ConsumerListener, "android.gui.IConsumerListener"); + +// ---------------------------------------------------------------------- + +status_t BnConsumerListener::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case ON_FRAME_AVAILABLE: + CHECK_INTERFACE(IConsumerListener, data, reply); + onFrameAvailable(); + return NO_ERROR; + case ON_BUFFER_RELEASED: + CHECK_INTERFACE(IConsumerListener, data, reply); + onBuffersReleased(); + return NO_ERROR; + } + return BBinder::onTransact(code, data, reply, flags); +} + + +// --------------------------------------------------------------------------- +}; // namespace android +// --------------------------------------------------------------------------- diff --git a/libs/gui/IGraphicBufferConsumer.cpp b/libs/gui/IGraphicBufferConsumer.cpp new file mode 100644 index 0000000..9574b61 --- /dev/null +++ b/libs/gui/IGraphicBufferConsumer.cpp @@ -0,0 +1,485 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define EGL_EGLEXT_PROTOTYPES + +#include <EGL/egl.h> +#include <EGL/eglext.h> + + +#include <stdint.h> +#include <sys/types.h> + +#include <utils/Errors.h> + +#include <binder/Parcel.h> +#include <binder/IInterface.h> + +#include <gui/IConsumerListener.h> +#include <gui/IGraphicBufferConsumer.h> + +#include <ui/GraphicBuffer.h> +#include <ui/Fence.h> + +#include <system/window.h> + +namespace android { +// --------------------------------------------------------------------------- + +IGraphicBufferConsumer::BufferItem::BufferItem() : + mTransform(0), + mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), + mTimestamp(0), + mIsAutoTimestamp(false), + mFrameNumber(0), + mBuf(INVALID_BUFFER_SLOT), + mIsDroppable(false), + mAcquireCalled(false), + mTransformToDisplayInverse(false) { + mCrop.makeInvalid(); +} + +size_t IGraphicBufferConsumer::BufferItem::getPodSize() const { + size_t c = sizeof(mCrop) + + sizeof(mTransform) + + sizeof(mScalingMode) + + sizeof(mTimestamp) + + sizeof(mIsAutoTimestamp) + + sizeof(mFrameNumber) + + sizeof(mBuf) + + sizeof(mIsDroppable) + + sizeof(mAcquireCalled) + + sizeof(mTransformToDisplayInverse); + return c; +} + +size_t IGraphicBufferConsumer::BufferItem::getFlattenedSize() const { + size_t c = 0; + if (mGraphicBuffer != 0) { + c += mGraphicBuffer->getFlattenedSize(); + FlattenableUtils::align<4>(c); + } + if (mFence != 0) { + c += mFence->getFlattenedSize(); + FlattenableUtils::align<4>(c); + } + return sizeof(int32_t) + c + getPodSize(); +} + +size_t IGraphicBufferConsumer::BufferItem::getFdCount() const { + size_t c = 0; + if (mGraphicBuffer != 0) { + c += mGraphicBuffer->getFdCount(); + } + if (mFence != 0) { + c += mFence->getFdCount(); + } + return c; +} + +status_t IGraphicBufferConsumer::BufferItem::flatten( + void*& buffer, size_t& size, int*& fds, size_t& count) const { + + // make sure we have enough space + if (count < BufferItem::getFlattenedSize()) { + return NO_MEMORY; + } + + // content flags are stored first + uint32_t& flags = *static_cast<uint32_t*>(buffer); + + // advance the pointer + FlattenableUtils::advance(buffer, size, sizeof(uint32_t)); + + flags = 0; + if (mGraphicBuffer != 0) { + status_t err = mGraphicBuffer->flatten(buffer, size, fds, count); + if (err) return err; + size -= FlattenableUtils::align<4>(buffer); + flags |= 1; + } + if (mFence != 0) { + status_t err = mFence->flatten(buffer, size, fds, count); + if (err) return err; + size -= FlattenableUtils::align<4>(buffer); + flags |= 2; + } + + // check we have enough space (in case flattening the fence/graphicbuffer lied to us) + if (size < getPodSize()) { + return NO_MEMORY; + } + + FlattenableUtils::write(buffer, size, mCrop); + FlattenableUtils::write(buffer, size, mTransform); + FlattenableUtils::write(buffer, size, mScalingMode); + FlattenableUtils::write(buffer, size, mTimestamp); + FlattenableUtils::write(buffer, size, mIsAutoTimestamp); + FlattenableUtils::write(buffer, size, mFrameNumber); + FlattenableUtils::write(buffer, size, mBuf); + FlattenableUtils::write(buffer, size, mIsDroppable); + FlattenableUtils::write(buffer, size, mAcquireCalled); + FlattenableUtils::write(buffer, size, mTransformToDisplayInverse); + + return NO_ERROR; +} + +status_t IGraphicBufferConsumer::BufferItem::unflatten( + void const*& buffer, size_t& size, int const*& fds, size_t& count) { + + if (size < sizeof(uint32_t)) + return NO_MEMORY; + + uint32_t flags = 0; + FlattenableUtils::read(buffer, size, flags); + + if (flags & 1) { + mGraphicBuffer = new GraphicBuffer(); + status_t err = mGraphicBuffer->unflatten(buffer, size, fds, count); + if (err) return err; + size -= FlattenableUtils::align<4>(buffer); + } + + if (flags & 2) { + mFence = new Fence(); + status_t err = mFence->unflatten(buffer, size, fds, count); + if (err) return err; + size -= FlattenableUtils::align<4>(buffer); + } + + // check we have enough space + if (size < getPodSize()) { + return NO_MEMORY; + } + + FlattenableUtils::read(buffer, size, mCrop); + FlattenableUtils::read(buffer, size, mTransform); + FlattenableUtils::read(buffer, size, mScalingMode); + FlattenableUtils::read(buffer, size, mTimestamp); + FlattenableUtils::read(buffer, size, mIsAutoTimestamp); + FlattenableUtils::read(buffer, size, mFrameNumber); + FlattenableUtils::read(buffer, size, mBuf); + FlattenableUtils::read(buffer, size, mIsDroppable); + FlattenableUtils::read(buffer, size, mAcquireCalled); + FlattenableUtils::read(buffer, size, mTransformToDisplayInverse); + + return NO_ERROR; +} + +// --------------------------------------------------------------------------- + +enum { + ACQUIRE_BUFFER = IBinder::FIRST_CALL_TRANSACTION, + RELEASE_BUFFER, + CONSUMER_CONNECT, + CONSUMER_DISCONNECT, + GET_RELEASED_BUFFERS, + SET_DEFAULT_BUFFER_SIZE, + SET_DEFAULT_MAX_BUFFER_COUNT, + DISABLE_ASYNC_BUFFER, + SET_MAX_ACQUIRED_BUFFER_COUNT, + SET_CONSUMER_NAME, + SET_DEFAULT_BUFFER_FORMAT, + SET_CONSUMER_USAGE_BITS, + SET_TRANSFORM_HINT, + DUMP, +}; + + +class BpGraphicBufferConsumer : public BpInterface<IGraphicBufferConsumer> +{ +public: + BpGraphicBufferConsumer(const sp<IBinder>& impl) + : BpInterface<IGraphicBufferConsumer>(impl) + { + } + + virtual status_t acquireBuffer(BufferItem *buffer, nsecs_t presentWhen) { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor()); + data.writeInt64(presentWhen); + status_t result = remote()->transact(ACQUIRE_BUFFER, data, &reply); + if (result != NO_ERROR) { + return result; + } + result = reply.read(*buffer); + if (result != NO_ERROR) { + return result; + } + return reply.readInt32(); + } + + virtual status_t releaseBuffer(int buf, uint64_t frameNumber, + EGLDisplay display, EGLSyncKHR fence, + const sp<Fence>& releaseFence) { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor()); + data.writeInt32(buf); + data.writeInt64(frameNumber); + data.write(*releaseFence); + status_t result = remote()->transact(RELEASE_BUFFER, data, &reply); + if (result != NO_ERROR) { + return result; + } + return reply.readInt32(); + } + + virtual status_t consumerConnect(const sp<IConsumerListener>& consumer, bool controlledByApp) { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor()); + data.writeStrongBinder(consumer->asBinder()); + data.writeInt32(controlledByApp); + status_t result = remote()->transact(CONSUMER_CONNECT, data, &reply); + if (result != NO_ERROR) { + return result; + } + return reply.readInt32(); + } + + virtual status_t consumerDisconnect() { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor()); + status_t result = remote()->transact(CONSUMER_DISCONNECT, data, &reply); + if (result != NO_ERROR) { + return result; + } + return reply.readInt32(); + } + + virtual status_t getReleasedBuffers(uint32_t* slotMask) { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor()); + status_t result = remote()->transact(GET_RELEASED_BUFFERS, data, &reply); + if (result != NO_ERROR) { + return result; + } + *slotMask = reply.readInt32(); + return reply.readInt32(); + } + + virtual status_t setDefaultBufferSize(uint32_t w, uint32_t h) { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor()); + data.writeInt32(w); + data.writeInt32(h); + status_t result = remote()->transact(SET_DEFAULT_BUFFER_SIZE, data, &reply); + if (result != NO_ERROR) { + return result; + } + return reply.readInt32(); + } + + virtual status_t setDefaultMaxBufferCount(int bufferCount) { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor()); + data.writeInt32(bufferCount); + status_t result = remote()->transact(SET_DEFAULT_MAX_BUFFER_COUNT, data, &reply); + if (result != NO_ERROR) { + return result; + } + return reply.readInt32(); + } + + virtual status_t disableAsyncBuffer() { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor()); + status_t result = remote()->transact(DISABLE_ASYNC_BUFFER, data, &reply); + if (result != NO_ERROR) { + return result; + } + return reply.readInt32(); + } + + virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor()); + data.writeInt32(maxAcquiredBuffers); + status_t result = remote()->transact(SET_MAX_ACQUIRED_BUFFER_COUNT, data, &reply); + if (result != NO_ERROR) { + return result; + } + return reply.readInt32(); + } + + virtual void setConsumerName(const String8& name) { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor()); + data.writeString8(name); + remote()->transact(SET_CONSUMER_NAME, data, &reply); + } + + virtual status_t setDefaultBufferFormat(uint32_t defaultFormat) { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor()); + data.writeInt32(defaultFormat); + status_t result = remote()->transact(SET_DEFAULT_BUFFER_FORMAT, data, &reply); + if (result != NO_ERROR) { + return result; + } + return reply.readInt32(); + } + + virtual status_t setConsumerUsageBits(uint32_t usage) { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor()); + data.writeInt32(usage); + status_t result = remote()->transact(SET_CONSUMER_USAGE_BITS, data, &reply); + if (result != NO_ERROR) { + return result; + } + return reply.readInt32(); + } + + virtual status_t setTransformHint(uint32_t hint) { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor()); + data.writeInt32(hint); + status_t result = remote()->transact(SET_TRANSFORM_HINT, data, &reply); + if (result != NO_ERROR) { + return result; + } + return reply.readInt32(); + } + + virtual void dump(String8& result, const char* prefix) const { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor()); + data.writeString8(result); + data.writeString8(String8(prefix ? prefix : "")); + remote()->transact(DUMP, data, &reply); + reply.readString8(); + } +}; + +IMPLEMENT_META_INTERFACE(GraphicBufferConsumer, "android.gui.IGraphicBufferConsumer"); + +// ---------------------------------------------------------------------- + +status_t BnGraphicBufferConsumer::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case ACQUIRE_BUFFER: { + CHECK_INTERFACE(IGraphicBufferConsumer, data, reply); + BufferItem item; + int64_t presentWhen = data.readInt64(); + status_t result = acquireBuffer(&item, presentWhen); + status_t err = reply->write(item); + if (err) return err; + reply->writeInt32(result); + return NO_ERROR; + } break; + case RELEASE_BUFFER: { + CHECK_INTERFACE(IGraphicBufferConsumer, data, reply); + int buf = data.readInt32(); + uint64_t frameNumber = data.readInt64(); + sp<Fence> releaseFence = new Fence(); + status_t err = data.read(*releaseFence); + if (err) return err; + status_t result = releaseBuffer(buf, frameNumber, + EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, releaseFence); + reply->writeInt32(result); + return NO_ERROR; + } break; + case CONSUMER_CONNECT: { + CHECK_INTERFACE(IGraphicBufferConsumer, data, reply); + sp<IConsumerListener> consumer = IConsumerListener::asInterface( data.readStrongBinder() ); + bool controlledByApp = data.readInt32(); + status_t result = consumerConnect(consumer, controlledByApp); + reply->writeInt32(result); + return NO_ERROR; + } break; + case CONSUMER_DISCONNECT: { + CHECK_INTERFACE(IGraphicBufferConsumer, data, reply); + status_t result = consumerDisconnect(); + reply->writeInt32(result); + return NO_ERROR; + } break; + case GET_RELEASED_BUFFERS: { + CHECK_INTERFACE(IGraphicBufferConsumer, data, reply); + uint32_t slotMask; + status_t result = getReleasedBuffers(&slotMask); + reply->writeInt32(slotMask); + reply->writeInt32(result); + return NO_ERROR; + } break; + case SET_DEFAULT_BUFFER_SIZE: { + CHECK_INTERFACE(IGraphicBufferConsumer, data, reply); + uint32_t w = data.readInt32(); + uint32_t h = data.readInt32(); + status_t result = setDefaultBufferSize(w, h); + reply->writeInt32(result); + return NO_ERROR; + } break; + case SET_DEFAULT_MAX_BUFFER_COUNT: { + CHECK_INTERFACE(IGraphicBufferConsumer, data, reply); + uint32_t bufferCount = data.readInt32(); + status_t result = setDefaultMaxBufferCount(bufferCount); + reply->writeInt32(result); + return NO_ERROR; + } break; + case DISABLE_ASYNC_BUFFER: { + CHECK_INTERFACE(IGraphicBufferConsumer, data, reply); + status_t result = disableAsyncBuffer(); + reply->writeInt32(result); + return NO_ERROR; + } break; + case SET_MAX_ACQUIRED_BUFFER_COUNT: { + CHECK_INTERFACE(IGraphicBufferConsumer, data, reply); + uint32_t maxAcquiredBuffers = data.readInt32(); + status_t result = setMaxAcquiredBufferCount(maxAcquiredBuffers); + reply->writeInt32(result); + return NO_ERROR; + } break; + case SET_CONSUMER_NAME: { + CHECK_INTERFACE(IGraphicBufferConsumer, data, reply); + setConsumerName( data.readString8() ); + return NO_ERROR; + } break; + case SET_DEFAULT_BUFFER_FORMAT: { + CHECK_INTERFACE(IGraphicBufferConsumer, data, reply); + uint32_t defaultFormat = data.readInt32(); + status_t result = setDefaultBufferFormat(defaultFormat); + reply->writeInt32(result); + return NO_ERROR; + } break; + case SET_CONSUMER_USAGE_BITS: { + CHECK_INTERFACE(IGraphicBufferConsumer, data, reply); + uint32_t usage = data.readInt32(); + status_t result = setConsumerUsageBits(usage); + reply->writeInt32(result); + return NO_ERROR; + } break; + case SET_TRANSFORM_HINT: { + CHECK_INTERFACE(IGraphicBufferConsumer, data, reply); + uint32_t hint = data.readInt32(); + status_t result = setTransformHint(hint); + reply->writeInt32(result); + return NO_ERROR; + } break; + case DUMP: { + CHECK_INTERFACE(IGraphicBufferConsumer, data, reply); + String8 result = data.readString8(); + String8 prefix = data.readString8(); + static_cast<IGraphicBufferConsumer*>(this)->dump(result, prefix); + reply->writeString8(result); + return NO_ERROR; + } + } + return BBinder::onTransact(code, data, reply, flags); +} + +}; // namespace android diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp index 63d7628..0f461e5 100644 --- a/libs/gui/IGraphicBufferProducer.cpp +++ b/libs/gui/IGraphicBufferProducer.cpp @@ -37,12 +37,10 @@ enum { QUEUE_BUFFER, CANCEL_BUFFER, QUERY, - SET_SYNCHRONOUS_MODE, CONNECT, DISCONNECT, }; - class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer> { public: @@ -62,7 +60,11 @@ public: bool nonNull = reply.readInt32(); if (nonNull) { *buf = new GraphicBuffer(); - reply.read(**buf); + result = reply.read(**buf); + if(result != NO_ERROR) { + (*buf).clear(); + return result; + } } result = reply.readInt32(); return result; @@ -81,10 +83,11 @@ public: return result; } - virtual status_t dequeueBuffer(int *buf, sp<Fence>* fence, + virtual status_t dequeueBuffer(int *buf, sp<Fence>* fence, bool async, uint32_t w, uint32_t h, uint32_t format, uint32_t usage) { Parcel data, reply; data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); + data.writeInt32(async); data.writeInt32(w); data.writeInt32(h); data.writeInt32(format); @@ -94,13 +97,10 @@ public: return result; } *buf = reply.readInt32(); - bool fenceWasWritten = reply.readInt32(); - if (fenceWasWritten) { - // If the fence was written by the callee, then overwrite the - // caller's fence here. If it wasn't written then don't touch the - // caller's fence. + bool nonNull = reply.readInt32(); + if (nonNull) { *fence = new Fence(); - reply.read(*(fence->get())); + reply.read(**fence); } result = reply.readInt32(); return result; @@ -142,22 +142,13 @@ public: return result; } - virtual status_t setSynchronousMode(bool enabled) { - Parcel data, reply; - data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); - data.writeInt32(enabled); - status_t result = remote()->transact(SET_SYNCHRONOUS_MODE, data, &reply); - if (result != NO_ERROR) { - return result; - } - result = reply.readInt32(); - return result; - } - - virtual status_t connect(int api, QueueBufferOutput* output) { + virtual status_t connect(const sp<IBinder>& token, + int api, bool producerControlledByApp, QueueBufferOutput* output) { Parcel data, reply; data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); + data.writeStrongBinder(token); data.writeInt32(api); + data.writeInt32(producerControlledByApp); status_t result = remote()->transact(CONNECT, data, &reply); if (result != NO_ERROR) { return result; @@ -209,17 +200,18 @@ status_t BnGraphicBufferProducer::onTransact( } break; case DEQUEUE_BUFFER: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); + bool async = data.readInt32(); uint32_t w = data.readInt32(); uint32_t h = data.readInt32(); uint32_t format = data.readInt32(); uint32_t usage = data.readInt32(); int buf; sp<Fence> fence; - int result = dequeueBuffer(&buf, &fence, w, h, format, usage); + int result = dequeueBuffer(&buf, &fence, async, w, h, format, usage); reply->writeInt32(buf); reply->writeInt32(fence != NULL); if (fence != NULL) { - reply->write(*fence.get()); + reply->write(*fence); } reply->writeInt32(result); return NO_ERROR; @@ -252,20 +244,15 @@ status_t BnGraphicBufferProducer::onTransact( reply->writeInt32(res); return NO_ERROR; } break; - case SET_SYNCHRONOUS_MODE: { - CHECK_INTERFACE(IGraphicBufferProducer, data, reply); - bool enabled = data.readInt32(); - status_t res = setSynchronousMode(enabled); - reply->writeInt32(res); - return NO_ERROR; - } break; case CONNECT: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); + sp<IBinder> token = data.readStrongBinder(); int api = data.readInt32(); + bool producerControlledByApp = data.readInt32(); QueueBufferOutput* const output = reinterpret_cast<QueueBufferOutput *>( reply->writeInplace(sizeof(QueueBufferOutput))); - status_t res = connect(api, output); + status_t res = connect(token, api, producerControlledByApp, output); reply->writeInt32(res); return NO_ERROR; } break; @@ -286,45 +273,59 @@ IGraphicBufferProducer::QueueBufferInput::QueueBufferInput(const Parcel& parcel) parcel.read(*this); } -size_t IGraphicBufferProducer::QueueBufferInput::getFlattenedSize() const -{ +size_t IGraphicBufferProducer::QueueBufferInput::getFlattenedSize() const { return sizeof(timestamp) + + sizeof(isAutoTimestamp) + sizeof(crop) + sizeof(scalingMode) + sizeof(transform) + + sizeof(async) + fence->getFlattenedSize(); } -size_t IGraphicBufferProducer::QueueBufferInput::getFdCount() const -{ +size_t IGraphicBufferProducer::QueueBufferInput::getFdCount() const { return fence->getFdCount(); } -status_t IGraphicBufferProducer::QueueBufferInput::flatten(void* buffer, size_t size, - int fds[], size_t count) const +status_t IGraphicBufferProducer::QueueBufferInput::flatten( + void*& buffer, size_t& size, int*& fds, size_t& count) const { - status_t err = NO_ERROR; - char* p = (char*)buffer; - memcpy(p, ×tamp, sizeof(timestamp)); p += sizeof(timestamp); - memcpy(p, &crop, sizeof(crop)); p += sizeof(crop); - memcpy(p, &scalingMode, sizeof(scalingMode)); p += sizeof(scalingMode); - memcpy(p, &transform, sizeof(transform)); p += sizeof(transform); - err = fence->flatten(p, size - (p - (char*)buffer), fds, count); - return err; + if (size < getFlattenedSize()) { + return NO_MEMORY; + } + FlattenableUtils::write(buffer, size, timestamp); + FlattenableUtils::write(buffer, size, isAutoTimestamp); + FlattenableUtils::write(buffer, size, crop); + FlattenableUtils::write(buffer, size, scalingMode); + FlattenableUtils::write(buffer, size, transform); + FlattenableUtils::write(buffer, size, async); + return fence->flatten(buffer, size, fds, count); } -status_t IGraphicBufferProducer::QueueBufferInput::unflatten(void const* buffer, - size_t size, int fds[], size_t count) +status_t IGraphicBufferProducer::QueueBufferInput::unflatten( + void const*& buffer, size_t& size, int const*& fds, size_t& count) { - status_t err = NO_ERROR; - const char* p = (const char*)buffer; - memcpy(×tamp, p, sizeof(timestamp)); p += sizeof(timestamp); - memcpy(&crop, p, sizeof(crop)); p += sizeof(crop); - memcpy(&scalingMode, p, sizeof(scalingMode)); p += sizeof(scalingMode); - memcpy(&transform, p, sizeof(transform)); p += sizeof(transform); + size_t minNeeded = + sizeof(timestamp) + + sizeof(isAutoTimestamp) + + sizeof(crop) + + sizeof(scalingMode) + + sizeof(transform) + + sizeof(async); + + if (size < minNeeded) { + return NO_MEMORY; + } + + FlattenableUtils::read(buffer, size, timestamp); + FlattenableUtils::read(buffer, size, isAutoTimestamp); + FlattenableUtils::read(buffer, size, crop); + FlattenableUtils::read(buffer, size, scalingMode); + FlattenableUtils::read(buffer, size, transform); + FlattenableUtils::read(buffer, size, async); + fence = new Fence(); - err = fence->unflatten(p, size - (p - (const char*)buffer), fds, count); - return err; + return fence->unflatten(buffer, size, fds, count); } }; // namespace android diff --git a/libs/gui/ISensorEventConnection.cpp b/libs/gui/ISensorEventConnection.cpp index 0e51e8e..28fcb53 100644 --- a/libs/gui/ISensorEventConnection.cpp +++ b/libs/gui/ISensorEventConnection.cpp @@ -33,7 +33,8 @@ namespace android { enum { GET_SENSOR_CHANNEL = IBinder::FIRST_CALL_TRANSACTION, ENABLE_DISABLE, - SET_EVENT_RATE + SET_EVENT_RATE, + FLUSH_SENSOR }; class BpSensorEventConnection : public BpInterface<ISensorEventConnection> @@ -52,12 +53,16 @@ public: return new BitTube(reply); } - virtual status_t enableDisable(int handle, bool enabled) + virtual status_t enableDisable(int handle, bool enabled, nsecs_t samplingPeriodNs, + nsecs_t maxBatchReportLatencyNs, int reservedFlags) { Parcel data, reply; data.writeInterfaceToken(ISensorEventConnection::getInterfaceDescriptor()); data.writeInt32(handle); data.writeInt32(enabled); + data.writeInt64(samplingPeriodNs); + data.writeInt64(maxBatchReportLatencyNs); + data.writeInt32(reservedFlags); remote()->transact(ENABLE_DISABLE, data, &reply); return reply.readInt32(); } @@ -71,6 +76,13 @@ public: remote()->transact(SET_EVENT_RATE, data, &reply); return reply.readInt32(); } + + virtual status_t flush() { + Parcel data, reply; + data.writeInterfaceToken(ISensorEventConnection::getInterfaceDescriptor()); + remote()->transact(FLUSH_SENSOR, data, &reply); + return reply.readInt32(); + } }; IMPLEMENT_META_INTERFACE(SensorEventConnection, "android.gui.SensorEventConnection"); @@ -91,7 +103,11 @@ status_t BnSensorEventConnection::onTransact( CHECK_INTERFACE(ISensorEventConnection, data, reply); int handle = data.readInt32(); int enabled = data.readInt32(); - status_t result = enableDisable(handle, enabled); + nsecs_t samplingPeriodNs = data.readInt64(); + nsecs_t maxBatchReportLatencyNs = data.readInt64(); + int reservedFlags = data.readInt32(); + status_t result = enableDisable(handle, enabled, samplingPeriodNs, + maxBatchReportLatencyNs, reservedFlags); reply->writeInt32(result); return NO_ERROR; } break; @@ -103,6 +119,12 @@ status_t BnSensorEventConnection::onTransact( reply->writeInt32(result); return NO_ERROR; } break; + case FLUSH_SENSOR: { + CHECK_INTERFACE(ISensorEventConnection, data, reply); + status_t result = flush(); + reply->writeInt32(result); + return NO_ERROR; + } break; } return BBinder::onTransact(code, data, reply, flags); } diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp index 6442a86..aab0604 100644 --- a/libs/gui/ISurfaceComposer.cpp +++ b/libs/gui/ISurfaceComposer.cpp @@ -105,8 +105,7 @@ public: virtual status_t captureScreen(const sp<IBinder>& display, const sp<IGraphicBufferProducer>& producer, uint32_t reqWidth, uint32_t reqHeight, - uint32_t minLayerZ, uint32_t maxLayerZ, - bool isCpuConsumer) + uint32_t minLayerZ, uint32_t maxLayerZ) { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); @@ -116,7 +115,6 @@ public: data.writeInt32(reqHeight); data.writeInt32(minLayerZ); data.writeInt32(maxLayerZ); - data.writeInt32(isCpuConsumer); remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN, data, &reply); return reply.readInt32(); } @@ -187,6 +185,14 @@ public: return reply.readStrongBinder(); } + virtual void destroyDisplay(const sp<IBinder>& display) + { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + data.writeStrongBinder(display); + remote()->transact(BnSurfaceComposer::DESTROY_DISPLAY, data, &reply); + } + virtual sp<IBinder> getBuiltInDisplay(int32_t id) { Parcel data, reply; @@ -235,12 +241,14 @@ status_t BnSurfaceComposer::onTransact( CHECK_INTERFACE(ISurfaceComposer, data, reply); sp<IBinder> b = createConnection()->asBinder(); reply->writeStrongBinder(b); - } break; + return NO_ERROR; + } case CREATE_GRAPHIC_BUFFER_ALLOC: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp<IBinder> b = createGraphicBufferAlloc()->asBinder(); reply->writeStrongBinder(b); - } break; + return NO_ERROR; + } case SET_TRANSACTION_STATE: { CHECK_INTERFACE(ISurfaceComposer, data, reply); size_t count = data.readInt32(); @@ -261,11 +269,13 @@ status_t BnSurfaceComposer::onTransact( } uint32_t flags = data.readInt32(); setTransactionState(state, displays, flags); - } break; + return NO_ERROR; + } case BOOT_FINISHED: { CHECK_INTERFACE(ISurfaceComposer, data, reply); bootFinished(); - } break; + return NO_ERROR; + } case CAPTURE_SCREEN: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp<IBinder> display = data.readStrongBinder(); @@ -275,25 +285,25 @@ status_t BnSurfaceComposer::onTransact( uint32_t reqHeight = data.readInt32(); uint32_t minLayerZ = data.readInt32(); uint32_t maxLayerZ = data.readInt32(); - bool isCpuConsumer = data.readInt32(); status_t res = captureScreen(display, producer, - reqWidth, reqHeight, minLayerZ, maxLayerZ, - isCpuConsumer); + reqWidth, reqHeight, minLayerZ, maxLayerZ); reply->writeInt32(res); - } break; + return NO_ERROR; + } case AUTHENTICATE_SURFACE: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp<IGraphicBufferProducer> bufferProducer = interface_cast<IGraphicBufferProducer>(data.readStrongBinder()); int32_t result = authenticateSurfaceTexture(bufferProducer) ? 1 : 0; reply->writeInt32(result); - } break; + return NO_ERROR; + } case CREATE_DISPLAY_EVENT_CONNECTION: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp<IDisplayEventConnection> connection(createDisplayEventConnection()); reply->writeStrongBinder(connection->asBinder()); return NO_ERROR; - } break; + } case CREATE_DISPLAY: { CHECK_INTERFACE(ISurfaceComposer, data, reply); String8 displayName = data.readString8(); @@ -301,24 +311,32 @@ status_t BnSurfaceComposer::onTransact( sp<IBinder> display(createDisplay(displayName, secure)); reply->writeStrongBinder(display); return NO_ERROR; - } break; + } + case DESTROY_DISPLAY: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + sp<IBinder> display = data.readStrongBinder(); + destroyDisplay(display); + return NO_ERROR; + } case GET_BUILT_IN_DISPLAY: { CHECK_INTERFACE(ISurfaceComposer, data, reply); int32_t id = data.readInt32(); sp<IBinder> display(getBuiltInDisplay(id)); reply->writeStrongBinder(display); return NO_ERROR; - } break; + } case BLANK: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp<IBinder> display = data.readStrongBinder(); blank(display); - } break; + return NO_ERROR; + } case UNBLANK: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp<IBinder> display = data.readStrongBinder(); unblank(display); - } break; + return NO_ERROR; + } case GET_DISPLAY_INFO: { CHECK_INTERFACE(ISurfaceComposer, data, reply); DisplayInfo info; @@ -326,10 +344,13 @@ status_t BnSurfaceComposer::onTransact( status_t result = getDisplayInfo(display, &info); memcpy(reply->writeInplace(sizeof(DisplayInfo)), &info, sizeof(DisplayInfo)); reply->writeInt32(result); - } break; - default: + return NO_ERROR; + } + default: { return BBinder::onTransact(code, data, reply, flags); + } } + // should be unreachable return NO_ERROR; } diff --git a/libs/gui/Sensor.cpp b/libs/gui/Sensor.cpp index c52a88f..da6b0f9 100644 --- a/libs/gui/Sensor.cpp +++ b/libs/gui/Sensor.cpp @@ -32,11 +32,11 @@ namespace android { Sensor::Sensor() : mHandle(0), mType(0), mMinValue(0), mMaxValue(0), mResolution(0), - mPower(0), mMinDelay(0) + mPower(0), mMinDelay(0), mFifoReservedEventCount(0), mFifoMaxEventCount(0) { } -Sensor::Sensor(struct sensor_t const* hwSensor) +Sensor::Sensor(struct sensor_t const* hwSensor, int halVersion) { mName = hwSensor->name; mVendor = hwSensor->vendor; @@ -48,6 +48,15 @@ Sensor::Sensor(struct sensor_t const* hwSensor) mResolution = hwSensor->resolution; mPower = hwSensor->power; mMinDelay = hwSensor->minDelay; + // Set fifo event count zero for older devices which do not support batching. Fused + // sensors also have their fifo counts set to zero. + if (halVersion >= SENSORS_DEVICE_API_VERSION_1_1) { + mFifoReservedEventCount = hwSensor->fifoReservedEventCount; + mFifoMaxEventCount = hwSensor->fifoMaxEventCount; + } else { + mFifoReservedEventCount = 0; + mFifoMaxEventCount = 0; + } } Sensor::~Sensor() @@ -98,85 +107,97 @@ int32_t Sensor::getVersion() const { return mVersion; } -size_t Sensor::getSize() const -{ - return sizeof(int32_t) + ((mName.length() + 3) & ~3) + - sizeof(int32_t) + ((mVendor.length() + 3) & ~3) + - sizeof(int32_t) * 3 + - sizeof(float) * 4 + - sizeof(int32_t); -} - -static inline -size_t write(void* buffer, size_t offset, const String8& value) { - memcpy(static_cast<char*>(buffer) + offset, value.string(), value.length()); - return (value.length() + 3) & ~3; -} - -static inline -size_t write(void* buffer, size_t offset, float value) { - *reinterpret_cast<float*>(static_cast<char*>(buffer) + offset) = value; - return sizeof(float); +int32_t Sensor::getFifoReservedEventCount() const { + return mFifoReservedEventCount; } -static inline -size_t write(void* buffer, size_t offset, int32_t value) { - *reinterpret_cast<int32_t*>(static_cast<char*>(buffer) + offset) = value; - return sizeof(int32_t); +int32_t Sensor::getFifoMaxEventCount() const { + return mFifoMaxEventCount; } -status_t Sensor::flatten(void* buffer) const +size_t Sensor::getFlattenedSize() const { - size_t offset = 0; - offset += write(buffer, offset, int32_t(mName.length())); - offset += write(buffer, offset, mName); - offset += write(buffer, offset, int32_t(mVendor.length())); - offset += write(buffer, offset, mVendor); - offset += write(buffer, offset, mVersion); - offset += write(buffer, offset, mHandle); - offset += write(buffer, offset, mType); - offset += write(buffer, offset, mMinValue); - offset += write(buffer, offset, mMaxValue); - offset += write(buffer, offset, mResolution); - offset += write(buffer, offset, mPower); - offset += write(buffer, offset, mMinDelay); + size_t fixedSize = + sizeof(int32_t) * 3 + + sizeof(float) * 4 + + sizeof(int32_t) * 3; + + size_t variableSize = + sizeof(int32_t) + FlattenableUtils::align<4>(mName.length()) + + sizeof(int32_t) + FlattenableUtils::align<4>(mVendor.length()); + + return fixedSize + variableSize; +} + +status_t Sensor::flatten(void* buffer, size_t size) const { + if (size < getFlattenedSize()) { + return NO_MEMORY; + } + + FlattenableUtils::write(buffer, size, mName.length()); + memcpy(static_cast<char*>(buffer), mName.string(), mName.length()); + FlattenableUtils::advance(buffer, size, FlattenableUtils::align<4>(mName.length())); + + FlattenableUtils::write(buffer, size, mVendor.length()); + memcpy(static_cast<char*>(buffer), mVendor.string(), mVendor.length()); + FlattenableUtils::advance(buffer, size, FlattenableUtils::align<4>(mVendor.length())); + + FlattenableUtils::write(buffer, size, mVersion); + FlattenableUtils::write(buffer, size, mHandle); + FlattenableUtils::write(buffer, size, mType); + FlattenableUtils::write(buffer, size, mMinValue); + FlattenableUtils::write(buffer, size, mMaxValue); + FlattenableUtils::write(buffer, size, mResolution); + FlattenableUtils::write(buffer, size, mPower); + FlattenableUtils::write(buffer, size, mMinDelay); + FlattenableUtils::write(buffer, size, mFifoReservedEventCount); + FlattenableUtils::write(buffer, size, mFifoMaxEventCount); return NO_ERROR; } -static inline -size_t read(void const* buffer, size_t offset, String8* value, int32_t len) { - value->setTo(static_cast<char const*>(buffer) + offset, len); - return (len + 3) & ~3; -} +status_t Sensor::unflatten(void const* buffer, size_t size) { + size_t len; -static inline -size_t read(void const* buffer, size_t offset, float* value) { - *value = *reinterpret_cast<float const*>(static_cast<char const*>(buffer) + offset); - return sizeof(float); -} + if (size < sizeof(size_t)) { + return NO_MEMORY; + } + FlattenableUtils::read(buffer, size, len); + if (size < len) { + return NO_MEMORY; + } + mName.setTo(static_cast<char const*>(buffer), len); + FlattenableUtils::advance(buffer, size, FlattenableUtils::align<4>(len)); -static inline -size_t read(void const* buffer, size_t offset, int32_t* value) { - *value = *reinterpret_cast<int32_t const*>(static_cast<char const*>(buffer) + offset); - return sizeof(int32_t); -} -status_t Sensor::unflatten(void const* buffer, size_t size) -{ - int32_t len; - size_t offset = 0; - offset += read(buffer, offset, &len); - offset += read(buffer, offset, &mName, len); - offset += read(buffer, offset, &len); - offset += read(buffer, offset, &mVendor, len); - offset += read(buffer, offset, &mVersion); - offset += read(buffer, offset, &mHandle); - offset += read(buffer, offset, &mType); - offset += read(buffer, offset, &mMinValue); - offset += read(buffer, offset, &mMaxValue); - offset += read(buffer, offset, &mResolution); - offset += read(buffer, offset, &mPower); - offset += read(buffer, offset, &mMinDelay); + if (size < sizeof(size_t)) { + return NO_MEMORY; + } + FlattenableUtils::read(buffer, size, len); + if (size < len) { + return NO_MEMORY; + } + mVendor.setTo(static_cast<char const*>(buffer), len); + FlattenableUtils::advance(buffer, size, FlattenableUtils::align<4>(len)); + + size_t fixedSize = + sizeof(int32_t) * 3 + + sizeof(float) * 4 + + sizeof(int32_t) * 3; + + if (size < fixedSize) { + return NO_MEMORY; + } + + FlattenableUtils::read(buffer, size, mVersion); + FlattenableUtils::read(buffer, size, mHandle); + FlattenableUtils::read(buffer, size, mType); + FlattenableUtils::read(buffer, size, mMinValue); + FlattenableUtils::read(buffer, size, mMaxValue); + FlattenableUtils::read(buffer, size, mResolution); + FlattenableUtils::read(buffer, size, mPower); + FlattenableUtils::read(buffer, size, mMinDelay); + FlattenableUtils::read(buffer, size, mFifoReservedEventCount); + FlattenableUtils::read(buffer, size, mFifoMaxEventCount); return NO_ERROR; } diff --git a/libs/gui/SensorEventQueue.cpp b/libs/gui/SensorEventQueue.cpp index 8a1bf41..c365671 100644 --- a/libs/gui/SensorEventQueue.cpp +++ b/libs/gui/SensorEventQueue.cpp @@ -35,12 +35,12 @@ namespace android { // ---------------------------------------------------------------------------- SensorEventQueue::SensorEventQueue(const sp<ISensorEventConnection>& connection) - : mSensorEventConnection(connection) -{ + : mSensorEventConnection(connection), mRecBuffer(NULL), mAvailable(0), mConsumed(0) { + mRecBuffer = new ASensorEvent[MAX_RECEIVE_BUFFER_EVENT_COUNT]; } -SensorEventQueue::~SensorEventQueue() -{ +SensorEventQueue::~SensorEventQueue() { + delete [] mRecBuffer; } void SensorEventQueue::onFirstRef() @@ -59,9 +59,21 @@ ssize_t SensorEventQueue::write(const sp<BitTube>& tube, return BitTube::sendObjects(tube, events, numEvents); } -ssize_t SensorEventQueue::read(ASensorEvent* events, size_t numEvents) -{ - return BitTube::recvObjects(mSensorChannel, events, numEvents); +ssize_t SensorEventQueue::read(ASensorEvent* events, size_t numEvents) { + if (mAvailable == 0) { + ssize_t err = BitTube::recvObjects(mSensorChannel, + mRecBuffer, MAX_RECEIVE_BUFFER_EVENT_COUNT); + if (err < 0) { + return err; + } + mAvailable = err; + mConsumed = 0; + } + size_t count = numEvents < mAvailable ? numEvents : mAvailable; + memcpy(events, mRecBuffer + mConsumed, count*sizeof(ASensorEvent)); + mAvailable -= count; + mConsumed += count; + return count; } sp<Looper> SensorEventQueue::getLooper() const @@ -107,23 +119,25 @@ status_t SensorEventQueue::wake() const } status_t SensorEventQueue::enableSensor(Sensor const* sensor) const { - return mSensorEventConnection->enableDisable(sensor->getHandle(), true); + return mSensorEventConnection->enableDisable(sensor->getHandle(), true, 0, 0, false); } status_t SensorEventQueue::disableSensor(Sensor const* sensor) const { - return mSensorEventConnection->enableDisable(sensor->getHandle(), false); + return mSensorEventConnection->enableDisable(sensor->getHandle(), false, 0, 0, false); } -status_t SensorEventQueue::enableSensor(int32_t handle, int32_t us) const { - status_t err = mSensorEventConnection->enableDisable(handle, true); - if (err == NO_ERROR) { - mSensorEventConnection->setEventRate(handle, us2ns(us)); - } - return err; +status_t SensorEventQueue::enableSensor(int32_t handle, int32_t samplingPeriodUs, + int maxBatchReportLatencyUs, int reservedFlags) const { + return mSensorEventConnection->enableDisable(handle, true, us2ns(samplingPeriodUs), + us2ns(maxBatchReportLatencyUs), reservedFlags); +} + +status_t SensorEventQueue::flush() const { + return mSensorEventConnection->flush(); } status_t SensorEventQueue::disableSensor(int32_t handle) const { - return mSensorEventConnection->enableDisable(handle, false); + return mSensorEventConnection->enableDisable(handle, false, 0, 0, false); } status_t SensorEventQueue::setEventRate(Sensor const* sensor, nsecs_t ns) const { diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index a616c1e..27dbc4e 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -37,7 +37,8 @@ namespace android { Surface::Surface( - const sp<IGraphicBufferProducer>& bufferProducer) + const sp<IGraphicBufferProducer>& bufferProducer, + bool controlledByApp) : mGraphicBufferProducer(bufferProducer) { // Initialize the ANativeWindow function pointers. @@ -71,6 +72,8 @@ Surface::Surface( mTransformHint = 0; mConsumerRunningBehind = false; mConnectedToCpu = false; + mProducerControlledByApp = controlledByApp; + mSwapIntervalZero = false; } Surface::~Surface() { @@ -160,7 +163,6 @@ int Surface::setSwapInterval(int interval) { // EGL specification states: // interval is silently clamped to minimum and maximum implementation // dependent values before being stored. - // Although we don't have to, we apply the same logic here. if (interval < minSwapInterval) interval = minSwapInterval; @@ -168,13 +170,12 @@ int Surface::setSwapInterval(int interval) { if (interval > maxSwapInterval) interval = maxSwapInterval; - status_t res = mGraphicBufferProducer->setSynchronousMode(interval ? true : false); + mSwapIntervalZero = (interval == 0); - return res; + return NO_ERROR; } -int Surface::dequeueBuffer(android_native_buffer_t** buffer, - int* fenceFd) { +int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) { ATRACE_CALL(); ALOGV("Surface::dequeueBuffer"); Mutex::Autolock lock(mMutex); @@ -182,7 +183,7 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer, int reqW = mReqWidth ? mReqWidth : mUserWidth; int reqH = mReqHeight ? mReqHeight : mUserHeight; sp<Fence> fence; - status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, + status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, mSwapIntervalZero, reqW, reqH, mReqFormat, mReqUsage); if (result < 0) { ALOGV("dequeueBuffer: IGraphicBufferProducer::dequeueBuffer(%d, %d, %d, %d)" @@ -191,6 +192,10 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer, return result; } sp<GraphicBuffer>& gbuf(mSlots[buf].buffer); + + // this should never happen + ALOGE_IF(fence == NULL, "Surface::dequeueBuffer: received null Fence! buf=%d", buf); + if (result & IGraphicBufferProducer::RELEASE_ALL_BUFFERS) { freeAllBuffers(); } @@ -198,8 +203,7 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer, if ((result & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) || gbuf == 0) { result = mGraphicBufferProducer->requestBuffer(buf, &gbuf); if (result != NO_ERROR) { - ALOGE("dequeueBuffer: IGraphicBufferProducer::requestBuffer failed: %d", - result); + ALOGE("dequeueBuffer: IGraphicBufferProducer::requestBuffer failed: %d", result); return result; } } @@ -258,10 +262,12 @@ int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) { ALOGV("Surface::queueBuffer"); Mutex::Autolock lock(mMutex); int64_t timestamp; + bool isAutoTimestamp = false; if (mTimestamp == NATIVE_WINDOW_TIMESTAMP_AUTO) { timestamp = systemTime(SYSTEM_TIME_MONOTONIC); + isAutoTimestamp = true; ALOGV("Surface::queueBuffer making up timestamp: %.2f ms", - timestamp / 1000000.f); + timestamp / 1000000.f); } else { timestamp = mTimestamp; } @@ -277,8 +283,8 @@ int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) { sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE); IGraphicBufferProducer::QueueBufferOutput output; - IGraphicBufferProducer::QueueBufferInput input(timestamp, crop, mScalingMode, - mTransform, fence); + IGraphicBufferProducer::QueueBufferInput input(timestamp, isAutoTimestamp, + crop, mScalingMode, mTransform, mSwapIntervalZero, fence); status_t err = mGraphicBufferProducer->queueBuffer(i, input, &output); if (err != OK) { ALOGE("queueBuffer: error queuing buffer to SurfaceTexture, %d", err); @@ -484,9 +490,10 @@ int Surface::dispatchUnlockAndPost(va_list args) { int Surface::connect(int api) { ATRACE_CALL(); ALOGV("Surface::connect"); + static sp<BBinder> sLife = new BBinder(); Mutex::Autolock lock(mMutex); IGraphicBufferProducer::QueueBufferOutput output; - int err = mGraphicBufferProducer->connect(api, &output); + int err = mGraphicBufferProducer->connect(sLife, api, mProducerControlledByApp, &output); if (err == NO_ERROR) { uint32_t numPendingBuffers = 0; output.deflate(&mDefaultWidth, &mDefaultHeight, &mTransformHint, @@ -499,6 +506,7 @@ int Surface::connect(int api) { return err; } + int Surface::disconnect(int api) { ATRACE_CALL(); ALOGV("Surface::disconnect"); diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index f345df8..aafc4d2 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -135,6 +135,7 @@ class Composer : public Singleton<Composer> public: sp<IBinder> createDisplay(const String8& displayName, bool secure); + void destroyDisplay(const sp<IBinder>& display); sp<IBinder> getBuiltInDisplay(int32_t id); status_t setPosition(const sp<SurfaceComposerClient>& client, const sp<IBinder>& id, @@ -188,6 +189,10 @@ sp<IBinder> Composer::createDisplay(const String8& displayName, bool secure) { secure); } +void Composer::destroyDisplay(const sp<IBinder>& display) { + return ComposerService::getComposerService()->destroyDisplay(display); +} + sp<IBinder> Composer::getBuiltInDisplay(int32_t id) { return ComposerService::getComposerService()->getBuiltInDisplay(id); } @@ -490,6 +495,10 @@ sp<IBinder> SurfaceComposerClient::createDisplay(const String8& displayName, return Composer::getInstance().createDisplay(displayName, secure); } +void SurfaceComposerClient::destroyDisplay(const sp<IBinder>& display) { + Composer::getInstance().destroyDisplay(display); +} + sp<IBinder> SurfaceComposerClient::getBuiltInDisplay(int32_t id) { return Composer::getInstance().getBuiltInDisplay(id); } @@ -618,8 +627,7 @@ status_t ScreenshotClient::capture( sp<ISurfaceComposer> s(ComposerService::getComposerService()); if (s == NULL) return NO_INIT; return s->captureScreen(display, producer, - reqWidth, reqHeight, minLayerZ, maxLayerZ, - false); + reqWidth, reqHeight, minLayerZ, maxLayerZ); } ScreenshotClient::ScreenshotClient() @@ -633,7 +641,8 @@ ScreenshotClient::~ScreenshotClient() { sp<CpuConsumer> ScreenshotClient::getCpuConsumer() const { if (mCpuConsumer == NULL) { - mCpuConsumer = new CpuConsumer(1); + mBufferQueue = new BufferQueue(); + mCpuConsumer = new CpuConsumer(mBufferQueue, 1); mCpuConsumer->setName(String8("ScreenshotClient")); } return mCpuConsumer; @@ -652,8 +661,8 @@ status_t ScreenshotClient::update(const sp<IBinder>& display, mHaveBuffer = false; } - status_t err = s->captureScreen(display,cpuConsumer->getBufferQueue(), - reqWidth, reqHeight, minLayerZ, maxLayerZ, true); + status_t err = s->captureScreen(display, mBufferQueue, + reqWidth, reqHeight, minLayerZ, maxLayerZ); if (err == NO_ERROR) { err = mCpuConsumer->lockNextBuffer(&mBuffer); diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp index f4e88f5..16e533c 100644 --- a/libs/gui/SurfaceControl.cpp +++ b/libs/gui/SurfaceControl.cpp @@ -46,13 +46,13 @@ namespace android { // ============================================================================ SurfaceControl::SurfaceControl( - const sp<SurfaceComposerClient>& client, + const sp<SurfaceComposerClient>& client, const sp<IBinder>& handle, const sp<IGraphicBufferProducer>& gbp) : mClient(client), mHandle(handle), mGraphicBufferProducer(gbp) { } - + SurfaceControl::~SurfaceControl() { destroy(); @@ -71,7 +71,7 @@ void SurfaceControl::destroy() IPCThreadState::self()->flushCommands(); } -void SurfaceControl::clear() +void SurfaceControl::clear() { // here, the window manager tells us explicitly that we should destroy // the surface's resource. Soon after this call, it will also release @@ -83,7 +83,7 @@ void SurfaceControl::clear() } bool SurfaceControl::isSameSurface( - const sp<SurfaceControl>& lhs, const sp<SurfaceControl>& rhs) + const sp<SurfaceControl>& lhs, const sp<SurfaceControl>& rhs) { if (lhs == 0 || rhs == 0) return false; @@ -181,7 +181,9 @@ sp<Surface> SurfaceControl::getSurface() const { Mutex::Autolock _l(mLock); if (mSurfaceData == 0) { - mSurfaceData = new Surface(mGraphicBufferProducer); + // This surface is always consumed by SurfaceFlinger, so the + // producerControlledByApp value doesn't matter; using false. + mSurfaceData = new Surface(mGraphicBufferProducer, false); } return mSurfaceData; } diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp index 62d215b..03c1a29 100644 --- a/libs/gui/tests/BufferQueue_test.cpp +++ b/libs/gui/tests/BufferQueue_test.cpp @@ -55,47 +55,47 @@ protected: sp<BufferQueue> mBQ; }; -struct DummyConsumer : public BufferQueue::ConsumerListener { +struct DummyConsumer : public BnConsumerListener { virtual void onFrameAvailable() {} virtual void onBuffersReleased() {} }; TEST_F(BufferQueueTest, AcquireBuffer_ExceedsMaxAcquireCount_Fails) { sp<DummyConsumer> dc(new DummyConsumer); - mBQ->consumerConnect(dc); + mBQ->consumerConnect(dc, false); IGraphicBufferProducer::QueueBufferOutput qbo; - mBQ->connect(NATIVE_WINDOW_API_CPU, &qbo); + mBQ->connect(NULL, NATIVE_WINDOW_API_CPU, false, &qbo); mBQ->setBufferCount(4); int slot; sp<Fence> fence; sp<GraphicBuffer> buf; - IGraphicBufferProducer::QueueBufferInput qbi(0, Rect(0, 0, 1, 1), - NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE); + IGraphicBufferProducer::QueueBufferInput qbi(0, false, Rect(0, 0, 1, 1), + NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, false, Fence::NO_FENCE); BufferQueue::BufferItem item; for (int i = 0; i < 2; i++) { ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, - mBQ->dequeueBuffer(&slot, &fence, 1, 1, 0, + mBQ->dequeueBuffer(&slot, &fence, false, 1, 1, 0, GRALLOC_USAGE_SW_READ_OFTEN)); ASSERT_EQ(OK, mBQ->requestBuffer(slot, &buf)); ASSERT_EQ(OK, mBQ->queueBuffer(slot, qbi, &qbo)); - ASSERT_EQ(OK, mBQ->acquireBuffer(&item)); + ASSERT_EQ(OK, mBQ->acquireBuffer(&item, 0)); } ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, - mBQ->dequeueBuffer(&slot, &fence, 1, 1, 0, + mBQ->dequeueBuffer(&slot, &fence, false, 1, 1, 0, GRALLOC_USAGE_SW_READ_OFTEN)); ASSERT_EQ(OK, mBQ->requestBuffer(slot, &buf)); ASSERT_EQ(OK, mBQ->queueBuffer(slot, qbi, &qbo)); // Acquire the third buffer, which should fail. - ASSERT_EQ(INVALID_OPERATION, mBQ->acquireBuffer(&item)); + ASSERT_EQ(INVALID_OPERATION, mBQ->acquireBuffer(&item, 0)); } TEST_F(BufferQueueTest, SetMaxAcquiredBufferCountWithIllegalValues_ReturnsError) { sp<DummyConsumer> dc(new DummyConsumer); - mBQ->consumerConnect(dc); + mBQ->consumerConnect(dc, false); ASSERT_EQ(BAD_VALUE, mBQ->setMaxAcquiredBufferCount(0)); ASSERT_EQ(BAD_VALUE, mBQ->setMaxAcquiredBufferCount(-3)); @@ -106,7 +106,7 @@ TEST_F(BufferQueueTest, SetMaxAcquiredBufferCountWithIllegalValues_ReturnsError) TEST_F(BufferQueueTest, SetMaxAcquiredBufferCountWithLegalValues_Succeeds) { sp<DummyConsumer> dc(new DummyConsumer); - mBQ->consumerConnect(dc); + mBQ->consumerConnect(dc, false); ASSERT_EQ(OK, mBQ->setMaxAcquiredBufferCount(1)); ASSERT_EQ(OK, mBQ->setMaxAcquiredBufferCount(2)); diff --git a/libs/gui/tests/CpuConsumer_test.cpp b/libs/gui/tests/CpuConsumer_test.cpp index 73fdd04..afbc026 100644 --- a/libs/gui/tests/CpuConsumer_test.cpp +++ b/libs/gui/tests/CpuConsumer_test.cpp @@ -66,10 +66,11 @@ protected: test_info->name(), params.width, params.height, params.maxLockedBuffers, params.format); - mCC = new CpuConsumer(params.maxLockedBuffers); + sp<BufferQueue> bq = new BufferQueue(); + mCC = new CpuConsumer(bq, params.maxLockedBuffers); String8 name("CpuConsumer_Under_Test"); mCC->setName(name); - mSTC = new Surface(mCC->getProducerInterface()); + mSTC = new Surface(bq); mANW = mSTC; } diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp index 7376b4c..989fcef 100644 --- a/libs/gui/tests/SurfaceTextureClient_test.cpp +++ b/libs/gui/tests/SurfaceTextureClient_test.cpp @@ -18,7 +18,10 @@ //#define LOG_NDEBUG 0 #include <EGL/egl.h> +#include <GLES2/gl2.h> + #include <gtest/gtest.h> +#include <gui/GLConsumer.h> #include <gui/Surface.h> #include <system/graphics.h> #include <utils/Log.h> @@ -40,8 +43,9 @@ protected: ALOGV("Begin test: %s.%s", testInfo->test_case_name(), testInfo->name()); - mST = new GLConsumer(123); - mSTC = new Surface(mST->getBufferQueue()); + sp<BufferQueue> bq = new BufferQueue(); + mST = new GLConsumer(bq, 123); + mSTC = new Surface(bq); mANW = mSTC; // We need a valid GL context so we can test updateTexImage() @@ -337,7 +341,7 @@ TEST_F(SurfaceTextureClientTest, SurfaceTextureSetDefaultSizeVsGeometry) { TEST_F(SurfaceTextureClientTest, SurfaceTextureTooManyUpdateTexImage) { android_native_buffer_t* buf[3]; - ASSERT_EQ(OK, mST->setSynchronousMode(false)); + ASSERT_EQ(OK, mANW->setSwapInterval(mANW.get(), 0)); ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 4)); ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[0])); @@ -345,7 +349,7 @@ TEST_F(SurfaceTextureClientTest, SurfaceTextureTooManyUpdateTexImage) { EXPECT_EQ(OK, mST->updateTexImage()); EXPECT_EQ(OK, mST->updateTexImage()); - ASSERT_EQ(OK, mST->setSynchronousMode(true)); + ASSERT_EQ(OK, mANW->setSwapInterval(mANW.get(), 1)); ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 3)); ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[0])); @@ -360,7 +364,6 @@ TEST_F(SurfaceTextureClientTest, SurfaceTextureTooManyUpdateTexImage) { TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeSlowRetire) { android_native_buffer_t* buf[3]; - ASSERT_EQ(OK, mST->setSynchronousMode(true)); ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 4)); ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[0])); ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[1])); @@ -381,7 +384,6 @@ TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeSlowRetire) { TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeFastRetire) { android_native_buffer_t* buf[3]; - ASSERT_EQ(OK, mST->setSynchronousMode(true)); ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 4)); ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[0])); ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[1])); @@ -402,7 +404,6 @@ TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeFastRetire) { TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeDQQR) { android_native_buffer_t* buf[3]; - ASSERT_EQ(OK, mST->setSynchronousMode(true)); ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 3)); ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[0])); @@ -428,7 +429,6 @@ TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeDQQR) { TEST_F(SurfaceTextureClientTest, DISABLED_SurfaceTextureSyncModeDequeueCurrent) { android_native_buffer_t* buf[3]; android_native_buffer_t* firstBuf; - ASSERT_EQ(OK, mST->setSynchronousMode(true)); ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 3)); ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &firstBuf)); ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), firstBuf, -1)); @@ -448,7 +448,6 @@ TEST_F(SurfaceTextureClientTest, DISABLED_SurfaceTextureSyncModeDequeueCurrent) TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeMinUndequeued) { android_native_buffer_t* buf[3]; - ASSERT_EQ(OK, mST->setSynchronousMode(true)); ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 3)); // We should be able to dequeue all the buffers before we've queued mANWy. @@ -527,7 +526,6 @@ TEST_F(SurfaceTextureClientTest, DISABLED_SurfaceTextureSyncModeWaitRetire) { }; android_native_buffer_t* buf[3]; - ASSERT_EQ(OK, mST->setSynchronousMode(true)); ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 3)); // dequeue/queue/update so we have a current buffer ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[0])); @@ -661,8 +659,6 @@ TEST_F(SurfaceTextureClientTest, QueryFormatAfterSettingWorks) { HAL_PIXEL_FORMAT_RGB_888, HAL_PIXEL_FORMAT_RGB_565, HAL_PIXEL_FORMAT_BGRA_8888, - HAL_PIXEL_FORMAT_RGBA_5551, - HAL_PIXEL_FORMAT_RGBA_4444, HAL_PIXEL_FORMAT_YV12, }; @@ -715,8 +711,9 @@ protected: ASSERT_NE(EGL_NO_CONTEXT, mEglContext); for (int i = 0; i < NUM_SURFACE_TEXTURES; i++) { - sp<GLConsumer> st(new GLConsumer(i)); - sp<Surface> stc(new Surface(st->getBufferQueue())); + sp<BufferQueue> bq = new BufferQueue(); + sp<GLConsumer> st(new GLConsumer(bq, i)); + sp<Surface> stc(new Surface(bq)); mEglSurfaces[i] = eglCreateWindowSurface(mEglDisplay, myConfig, static_cast<ANativeWindow*>(stc.get()), NULL); ASSERT_EQ(EGL_SUCCESS, eglGetError()); diff --git a/libs/gui/tests/SurfaceTexture_test.cpp b/libs/gui/tests/SurfaceTexture_test.cpp index dd6c435..e4fba15 100644 --- a/libs/gui/tests/SurfaceTexture_test.cpp +++ b/libs/gui/tests/SurfaceTexture_test.cpp @@ -35,7 +35,6 @@ #include <GLES2/gl2ext.h> #include <ui/FramebufferNativeWindow.h> -#include <utils/UniquePtr.h> #include <android/native_window.h> namespace android { @@ -385,8 +384,9 @@ protected: virtual void SetUp() { GLTest::SetUp(); - mGlConsumer = new GLConsumer(TEX_ID); - mSurface = new Surface(mGlConsumer->getBufferQueue()); + sp<BufferQueue> bq = new BufferQueue(); + mGlConsumer = new GLConsumer(bq, TEX_ID); + mSurface = new Surface(bq); mANW = mSurface.get(); } @@ -479,8 +479,10 @@ protected: virtual void SetUp() { GLTest::SetUp(); - mST = new GLConsumer(TEX_ID); - mSTC = new Surface(mST->getBufferQueue()); + sp<BufferQueue> bq = new BufferQueue(); + mBQ = bq; + mST = new GLConsumer(bq, TEX_ID); + mSTC = new Surface(bq); mANW = mSTC; mTextureRenderer = new TextureRenderer(TEX_ID, mST); ASSERT_NO_FATAL_FAILURE(mTextureRenderer->SetUp()); @@ -626,7 +628,7 @@ protected: // no way to forward the events. This DisconnectWaiter will not let the // disconnect finish until finishDisconnect() is called. It will // also block until a disconnect is called - class DisconnectWaiter : public BufferQueue::ConsumerListener { + class DisconnectWaiter : public BnConsumerListener { public: DisconnectWaiter () : mWaitForDisconnect(false), @@ -670,6 +672,7 @@ protected: Condition mFrameCondition; }; + sp<BufferQueue> mBQ; sp<GLConsumer> mST; sp<Surface> mSTC; sp<ANativeWindow> mANW; @@ -942,7 +945,6 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BuffersRepeatedly) { enum { texHeight = 16 }; enum { numFrames = 1024 }; - ASSERT_EQ(NO_ERROR, mST->setSynchronousMode(true)); ASSERT_EQ(NO_ERROR, mST->setDefaultMaxBufferCount(2)); ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(), texWidth, texHeight, HAL_PIXEL_FORMAT_YV12)); @@ -1209,10 +1211,8 @@ TEST_F(SurfaceTextureGLTest, DisconnectStressTest) { sp<ANativeWindow> mANW; }; - ASSERT_EQ(OK, mST->setSynchronousMode(true)); - sp<DisconnectWaiter> dw(new DisconnectWaiter()); - mST->getBufferQueue()->consumerConnect(dw); + mBQ->consumerConnect(dw, false); sp<Thread> pt(new ProducerThread(mANW)); @@ -1235,8 +1235,6 @@ TEST_F(SurfaceTextureGLTest, DisconnectStressTest) { // when it is disconnected and reconnected. Otherwise it will // attempt to release a buffer that it does not owned TEST_F(SurfaceTextureGLTest, DisconnectClearsCurrentTexture) { - ASSERT_EQ(OK, mST->setSynchronousMode(true)); - ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_EGL)); @@ -1256,8 +1254,6 @@ TEST_F(SurfaceTextureGLTest, DisconnectClearsCurrentTexture) { ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_EGL)); - ASSERT_EQ(OK, mST->setSynchronousMode(true)); - EXPECT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); EXPECT_EQ(OK, mANW->queueBuffer(mANW.get(), anb, -1)); @@ -1270,8 +1266,6 @@ TEST_F(SurfaceTextureGLTest, DisconnectClearsCurrentTexture) { } TEST_F(SurfaceTextureGLTest, ScaleToWindowMode) { - ASSERT_EQ(OK, mST->setSynchronousMode(true)); - ASSERT_EQ(OK, native_window_set_scaling_mode(mANW.get(), NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW)); @@ -1304,8 +1298,6 @@ TEST_F(SurfaceTextureGLTest, ScaleToWindowMode) { // the image such that it has the same aspect ratio as the // default buffer size TEST_F(SurfaceTextureGLTest, CroppedScalingMode) { - ASSERT_EQ(OK, mST->setSynchronousMode(true)); - ASSERT_EQ(OK, native_window_set_scaling_mode(mANW.get(), NATIVE_WINDOW_SCALING_MODE_SCALE_CROP)); @@ -1415,7 +1407,6 @@ TEST_F(SurfaceTextureGLTest, AbandonUnblocksDequeueBuffer) { Mutex mMutex; }; - ASSERT_EQ(OK, mST->setSynchronousMode(true)); ASSERT_EQ(OK, mST->setDefaultMaxBufferCount(2)); sp<Thread> pt(new ProducerThread(mANW)); @@ -1808,32 +1799,6 @@ TEST_F(SurfaceTextureGLToGLTest, EglMakeCurrentAfterConsumerDeathUnrefsBuffers) EXPECT_EQ(1, buffer->getStrongCount()); } - -TEST_F(SurfaceTextureGLToGLTest, EglSurfaceDefaultsToSynchronousMode) { - // This test requires 3 buffers to run on a single thread. - mST->setDefaultMaxBufferCount(3); - - ASSERT_TRUE(mST->isSynchronousMode()); - - for (int i = 0; i < 10; i++) { - // Produce a frame - EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface, - mProducerEglSurface, mProducerEglContext)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - glClear(GL_COLOR_BUFFER_BIT); - EXPECT_TRUE(eglSwapBuffers(mEglDisplay, mProducerEglSurface)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - - // Consume a frame - EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, - mEglContext)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - ASSERT_EQ(NO_ERROR, mST->updateTexImage()); - } - - ASSERT_TRUE(mST->isSynchronousMode()); -} - TEST_F(SurfaceTextureGLToGLTest, TexturingFromUserSizedGLFilledBuffer) { enum { texWidth = 64 }; enum { texHeight = 64 }; @@ -2283,7 +2248,6 @@ TEST_F(SurfaceTextureGLThreadToGLTest, } }; - ASSERT_EQ(OK, mST->setSynchronousMode(true)); ASSERT_EQ(OK, mST->setDefaultMaxBufferCount(2)); runProducerThread(new PT()); @@ -2824,7 +2788,6 @@ TEST_F(SurfaceTextureMultiContextGLTest, TEST_F(SurfaceTextureMultiContextGLTest, UpdateTexImageSucceedsForBufferConsumedBeforeDetach) { - ASSERT_EQ(NO_ERROR, mST->setSynchronousMode(true)); ASSERT_EQ(NO_ERROR, mST->setDefaultMaxBufferCount(2)); // produce two frames and consume them both on the primary context diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index 429becf..e0272ba 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -20,6 +20,7 @@ #include <gui/ISurfaceComposer.h> #include <gui/Surface.h> #include <gui/SurfaceComposerClient.h> +#include <gui/BufferItemConsumer.h> #include <utils/String8.h> #include <private/gui/ComposerService.h> @@ -87,11 +88,12 @@ TEST_F(SurfaceTest, ScreenshotsOfProtectedBuffersSucceed) { sp<ANativeWindow> anw(mSurface); // Verify the screenshot works with no protected buffers. - sp<CpuConsumer> consumer = new CpuConsumer(1); + sp<BufferQueue> bq = new BufferQueue(); + sp<CpuConsumer> consumer = new CpuConsumer(bq, 1); sp<ISurfaceComposer> sf(ComposerService::getComposerService()); sp<IBinder> display(sf->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain)); - ASSERT_EQ(NO_ERROR, sf->captureScreen(display, consumer->getBufferQueue(), - 64, 64, 0, 0x7fffffff, true)); + ASSERT_EQ(NO_ERROR, sf->captureScreen(display, bq, + 64, 64, 0, 0x7fffffff)); // Set the PROTECTED usage bit and verify that the screenshot fails. Note // that we need to dequeue a buffer in order for it to actually get @@ -119,8 +121,8 @@ TEST_F(SurfaceTest, ScreenshotsOfProtectedBuffersSucceed) { &buf)); ASSERT_EQ(NO_ERROR, anw->queueBuffer(anw.get(), buf, -1)); } - ASSERT_EQ(NO_ERROR, sf->captureScreen(display, consumer->getBufferQueue(), - 64, 64, 0, 0x7fffffff, true)); + ASSERT_EQ(NO_ERROR, sf->captureScreen(display, bq, + 64, 64, 0, 0x7fffffff)); } TEST_F(SurfaceTest, ConcreteTypeIsSurface) { @@ -131,4 +133,21 @@ TEST_F(SurfaceTest, ConcreteTypeIsSurface) { EXPECT_EQ(NATIVE_WINDOW_SURFACE, result); } +TEST_F(SurfaceTest, QueryConsumerUsage) { + const int TEST_USAGE_FLAGS = + GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER; + sp<BufferQueue> bq = new BufferQueue(); + sp<BufferItemConsumer> c = new BufferItemConsumer(bq, + TEST_USAGE_FLAGS); + sp<Surface> s = new Surface(bq); + + sp<ANativeWindow> anw(s); + + int flags = -1; + int err = anw->query(anw.get(), NATIVE_WINDOW_CONSUMER_USAGE_BITS, &flags); + + ASSERT_EQ(NO_ERROR, err); + ASSERT_EQ(TEST_USAGE_FLAGS, flags); +} + } diff --git a/libs/input/Android.mk b/libs/input/Android.mk new file mode 100644 index 0000000..f1921a4 --- /dev/null +++ b/libs/input/Android.mk @@ -0,0 +1,78 @@ +# Copyright (C) 2013 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +LOCAL_PATH:= $(call my-dir) + +# libinput is partially built for the host (used by build time keymap validation tool) +# These files are common to host and target builds. + +commonSources := \ + Input.cpp \ + InputDevice.cpp \ + Keyboard.cpp \ + KeyCharacterMap.cpp \ + KeyLayoutMap.cpp \ + VirtualKeyMap.cpp + +deviceSources := \ + $(commonSources) \ + InputTransport.cpp \ + VelocityControl.cpp \ + VelocityTracker.cpp + +hostSources := \ + $(commonSources) + +# For the host +# ===================================================== + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= $(hostSources) + +LOCAL_MODULE:= libinput + +LOCAL_MODULE_TAGS := optional + +include $(BUILD_HOST_STATIC_LIBRARY) + + +# For the device +# ===================================================== + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= $(deviceSources) + +LOCAL_SHARED_LIBRARIES := \ + liblog \ + libcutils \ + libutils \ + libbinder + +LOCAL_MODULE:= libinput + +LOCAL_MODULE_TAGS := optional + +include $(BUILD_SHARED_LIBRARY) + + +# Include subdirectory makefiles +# ============================================================ + +# If we're building with ONE_SHOT_MAKEFILE (mm, mmm), then what the framework +# team really wants is to build the stuff defined by this makefile. +ifeq (,$(ONE_SHOT_MAKEFILE)) +include $(call first-makefiles-under,$(LOCAL_PATH)) +endif diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp new file mode 100644 index 0000000..6f53996 --- /dev/null +++ b/libs/input/Input.cpp @@ -0,0 +1,646 @@ +/* + * 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. + */ + +#define LOG_TAG "Input" +//#define LOG_NDEBUG 0 + +#include <math.h> +#include <limits.h> + +#include <input/Input.h> + +#ifdef HAVE_ANDROID_OS +#include <binder/Parcel.h> +#endif + +namespace android { + +// --- InputEvent --- + +void InputEvent::initialize(int32_t deviceId, int32_t source) { + mDeviceId = deviceId; + mSource = source; +} + +void InputEvent::initialize(const InputEvent& from) { + mDeviceId = from.mDeviceId; + mSource = from.mSource; +} + +// --- KeyEvent --- + +bool KeyEvent::hasDefaultAction(int32_t keyCode) { + switch (keyCode) { + case AKEYCODE_HOME: + case AKEYCODE_BACK: + case AKEYCODE_CALL: + case AKEYCODE_ENDCALL: + case AKEYCODE_VOLUME_UP: + case AKEYCODE_VOLUME_DOWN: + case AKEYCODE_VOLUME_MUTE: + case AKEYCODE_POWER: + case AKEYCODE_CAMERA: + case AKEYCODE_HEADSETHOOK: + case AKEYCODE_MENU: + case AKEYCODE_NOTIFICATION: + case AKEYCODE_FOCUS: + case AKEYCODE_SEARCH: + case AKEYCODE_MEDIA_PLAY: + case AKEYCODE_MEDIA_PAUSE: + case AKEYCODE_MEDIA_PLAY_PAUSE: + case AKEYCODE_MEDIA_STOP: + case AKEYCODE_MEDIA_NEXT: + case AKEYCODE_MEDIA_PREVIOUS: + case AKEYCODE_MEDIA_REWIND: + case AKEYCODE_MEDIA_RECORD: + case AKEYCODE_MEDIA_FAST_FORWARD: + case AKEYCODE_MUTE: + case AKEYCODE_BRIGHTNESS_DOWN: + case AKEYCODE_BRIGHTNESS_UP: + case AKEYCODE_MEDIA_AUDIO_TRACK: + return true; + } + + return false; +} + +bool KeyEvent::hasDefaultAction() const { + return hasDefaultAction(getKeyCode()); +} + +bool KeyEvent::isSystemKey(int32_t keyCode) { + switch (keyCode) { + case AKEYCODE_MENU: + case AKEYCODE_SOFT_RIGHT: + case AKEYCODE_HOME: + case AKEYCODE_BACK: + case AKEYCODE_CALL: + case AKEYCODE_ENDCALL: + case AKEYCODE_VOLUME_UP: + case AKEYCODE_VOLUME_DOWN: + case AKEYCODE_VOLUME_MUTE: + case AKEYCODE_MUTE: + case AKEYCODE_POWER: + case AKEYCODE_HEADSETHOOK: + case AKEYCODE_MEDIA_PLAY: + case AKEYCODE_MEDIA_PAUSE: + case AKEYCODE_MEDIA_PLAY_PAUSE: + case AKEYCODE_MEDIA_STOP: + case AKEYCODE_MEDIA_NEXT: + case AKEYCODE_MEDIA_PREVIOUS: + case AKEYCODE_MEDIA_REWIND: + case AKEYCODE_MEDIA_RECORD: + case AKEYCODE_MEDIA_FAST_FORWARD: + case AKEYCODE_CAMERA: + case AKEYCODE_FOCUS: + case AKEYCODE_SEARCH: + case AKEYCODE_BRIGHTNESS_DOWN: + case AKEYCODE_BRIGHTNESS_UP: + case AKEYCODE_MEDIA_AUDIO_TRACK: + return true; + } + + return false; +} + +bool KeyEvent::isSystemKey() const { + return isSystemKey(getKeyCode()); +} + +void KeyEvent::initialize( + int32_t deviceId, + int32_t source, + int32_t action, + int32_t flags, + int32_t keyCode, + int32_t scanCode, + int32_t metaState, + int32_t repeatCount, + nsecs_t downTime, + nsecs_t eventTime) { + InputEvent::initialize(deviceId, source); + mAction = action; + mFlags = flags; + mKeyCode = keyCode; + mScanCode = scanCode; + mMetaState = metaState; + mRepeatCount = repeatCount; + mDownTime = downTime; + mEventTime = eventTime; +} + +void KeyEvent::initialize(const KeyEvent& from) { + InputEvent::initialize(from); + mAction = from.mAction; + mFlags = from.mFlags; + mKeyCode = from.mKeyCode; + mScanCode = from.mScanCode; + mMetaState = from.mMetaState; + mRepeatCount = from.mRepeatCount; + mDownTime = from.mDownTime; + mEventTime = from.mEventTime; +} + + +// --- PointerCoords --- + +float PointerCoords::getAxisValue(int32_t axis) const { + if (axis < 0 || axis > 63) { + return 0; + } + + uint64_t axisBit = 1LL << axis; + if (!(bits & axisBit)) { + return 0; + } + uint32_t index = __builtin_popcountll(bits & (axisBit - 1LL)); + return values[index]; +} + +status_t PointerCoords::setAxisValue(int32_t axis, float value) { + if (axis < 0 || axis > 63) { + return NAME_NOT_FOUND; + } + + uint64_t axisBit = 1LL << axis; + uint32_t index = __builtin_popcountll(bits & (axisBit - 1LL)); + if (!(bits & axisBit)) { + if (value == 0) { + return OK; // axes with value 0 do not need to be stored + } + uint32_t count = __builtin_popcountll(bits); + if (count >= MAX_AXES) { + tooManyAxes(axis); + return NO_MEMORY; + } + bits |= axisBit; + for (uint32_t i = count; i > index; i--) { + values[i] = values[i - 1]; + } + } + values[index] = value; + return OK; +} + +static inline void scaleAxisValue(PointerCoords& c, int axis, float scaleFactor) { + float value = c.getAxisValue(axis); + if (value != 0) { + c.setAxisValue(axis, value * scaleFactor); + } +} + +void PointerCoords::scale(float scaleFactor) { + // No need to scale pressure or size since they are normalized. + // No need to scale orientation since it is meaningless to do so. + scaleAxisValue(*this, AMOTION_EVENT_AXIS_X, scaleFactor); + scaleAxisValue(*this, AMOTION_EVENT_AXIS_Y, scaleFactor); + scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOUCH_MAJOR, scaleFactor); + scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOUCH_MINOR, scaleFactor); + scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOOL_MAJOR, scaleFactor); + scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOOL_MINOR, scaleFactor); +} + +#ifdef HAVE_ANDROID_OS +status_t PointerCoords::readFromParcel(Parcel* parcel) { + bits = parcel->readInt64(); + + uint32_t count = __builtin_popcountll(bits); + if (count > MAX_AXES) { + return BAD_VALUE; + } + + for (uint32_t i = 0; i < count; i++) { + values[i] = parcel->readFloat(); + } + return OK; +} + +status_t PointerCoords::writeToParcel(Parcel* parcel) const { + parcel->writeInt64(bits); + + uint32_t count = __builtin_popcountll(bits); + for (uint32_t i = 0; i < count; i++) { + parcel->writeFloat(values[i]); + } + return OK; +} +#endif + +void PointerCoords::tooManyAxes(int axis) { + ALOGW("Could not set value for axis %d because the PointerCoords structure is full and " + "cannot contain more than %d axis values.", axis, int(MAX_AXES)); +} + +bool PointerCoords::operator==(const PointerCoords& other) const { + if (bits != other.bits) { + return false; + } + uint32_t count = __builtin_popcountll(bits); + for (uint32_t i = 0; i < count; i++) { + if (values[i] != other.values[i]) { + return false; + } + } + return true; +} + +void PointerCoords::copyFrom(const PointerCoords& other) { + bits = other.bits; + uint32_t count = __builtin_popcountll(bits); + for (uint32_t i = 0; i < count; i++) { + values[i] = other.values[i]; + } +} + + +// --- PointerProperties --- + +bool PointerProperties::operator==(const PointerProperties& other) const { + return id == other.id + && toolType == other.toolType; +} + +void PointerProperties::copyFrom(const PointerProperties& other) { + id = other.id; + toolType = other.toolType; +} + + +// --- MotionEvent --- + +void MotionEvent::initialize( + int32_t deviceId, + int32_t source, + int32_t action, + int32_t flags, + int32_t edgeFlags, + int32_t metaState, + int32_t buttonState, + float xOffset, + float yOffset, + float xPrecision, + float yPrecision, + nsecs_t downTime, + nsecs_t eventTime, + size_t pointerCount, + const PointerProperties* pointerProperties, + const PointerCoords* pointerCoords) { + InputEvent::initialize(deviceId, source); + mAction = action; + mFlags = flags; + mEdgeFlags = edgeFlags; + mMetaState = metaState; + mButtonState = buttonState; + mXOffset = xOffset; + mYOffset = yOffset; + mXPrecision = xPrecision; + mYPrecision = yPrecision; + mDownTime = downTime; + mPointerProperties.clear(); + mPointerProperties.appendArray(pointerProperties, pointerCount); + mSampleEventTimes.clear(); + mSamplePointerCoords.clear(); + addSample(eventTime, pointerCoords); +} + +void MotionEvent::copyFrom(const MotionEvent* other, bool keepHistory) { + InputEvent::initialize(other->mDeviceId, other->mSource); + mAction = other->mAction; + mFlags = other->mFlags; + mEdgeFlags = other->mEdgeFlags; + mMetaState = other->mMetaState; + mButtonState = other->mButtonState; + mXOffset = other->mXOffset; + mYOffset = other->mYOffset; + mXPrecision = other->mXPrecision; + mYPrecision = other->mYPrecision; + mDownTime = other->mDownTime; + mPointerProperties = other->mPointerProperties; + + if (keepHistory) { + mSampleEventTimes = other->mSampleEventTimes; + mSamplePointerCoords = other->mSamplePointerCoords; + } else { + mSampleEventTimes.clear(); + mSampleEventTimes.push(other->getEventTime()); + mSamplePointerCoords.clear(); + size_t pointerCount = other->getPointerCount(); + size_t historySize = other->getHistorySize(); + mSamplePointerCoords.appendArray(other->mSamplePointerCoords.array() + + (historySize * pointerCount), pointerCount); + } +} + +void MotionEvent::addSample( + int64_t eventTime, + const PointerCoords* pointerCoords) { + mSampleEventTimes.push(eventTime); + mSamplePointerCoords.appendArray(pointerCoords, getPointerCount()); +} + +const PointerCoords* MotionEvent::getRawPointerCoords(size_t pointerIndex) const { + return &mSamplePointerCoords[getHistorySize() * getPointerCount() + pointerIndex]; +} + +float MotionEvent::getRawAxisValue(int32_t axis, size_t pointerIndex) const { + return getRawPointerCoords(pointerIndex)->getAxisValue(axis); +} + +float MotionEvent::getAxisValue(int32_t axis, size_t pointerIndex) const { + float value = getRawPointerCoords(pointerIndex)->getAxisValue(axis); + switch (axis) { + case AMOTION_EVENT_AXIS_X: + return value + mXOffset; + case AMOTION_EVENT_AXIS_Y: + return value + mYOffset; + } + return value; +} + +const PointerCoords* MotionEvent::getHistoricalRawPointerCoords( + size_t pointerIndex, size_t historicalIndex) const { + return &mSamplePointerCoords[historicalIndex * getPointerCount() + pointerIndex]; +} + +float MotionEvent::getHistoricalRawAxisValue(int32_t axis, size_t pointerIndex, + size_t historicalIndex) const { + return getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis); +} + +float MotionEvent::getHistoricalAxisValue(int32_t axis, size_t pointerIndex, + size_t historicalIndex) const { + float value = getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis); + switch (axis) { + case AMOTION_EVENT_AXIS_X: + return value + mXOffset; + case AMOTION_EVENT_AXIS_Y: + return value + mYOffset; + } + return value; +} + +ssize_t MotionEvent::findPointerIndex(int32_t pointerId) const { + size_t pointerCount = mPointerProperties.size(); + for (size_t i = 0; i < pointerCount; i++) { + if (mPointerProperties.itemAt(i).id == pointerId) { + return i; + } + } + return -1; +} + +void MotionEvent::offsetLocation(float xOffset, float yOffset) { + mXOffset += xOffset; + mYOffset += yOffset; +} + +void MotionEvent::scale(float scaleFactor) { + mXOffset *= scaleFactor; + mYOffset *= scaleFactor; + mXPrecision *= scaleFactor; + mYPrecision *= scaleFactor; + + size_t numSamples = mSamplePointerCoords.size(); + for (size_t i = 0; i < numSamples; i++) { + mSamplePointerCoords.editItemAt(i).scale(scaleFactor); + } +} + +static void transformPoint(const float matrix[9], float x, float y, float *outX, float *outY) { + // Apply perspective transform like Skia. + float newX = matrix[0] * x + matrix[1] * y + matrix[2]; + float newY = matrix[3] * x + matrix[4] * y + matrix[5]; + float newZ = matrix[6] * x + matrix[7] * y + matrix[8]; + if (newZ) { + newZ = 1.0f / newZ; + } + *outX = newX * newZ; + *outY = newY * newZ; +} + +static float transformAngle(const float matrix[9], float angleRadians, + float originX, float originY) { + // Construct and transform a vector oriented at the specified clockwise angle from vertical. + // Coordinate system: down is increasing Y, right is increasing X. + float x = sinf(angleRadians); + float y = -cosf(angleRadians); + transformPoint(matrix, x, y, &x, &y); + x -= originX; + y -= originY; + + // Derive the transformed vector's clockwise angle from vertical. + float result = atan2f(x, -y); + if (result < - M_PI_2) { + result += M_PI; + } else if (result > M_PI_2) { + result -= M_PI; + } + return result; +} + +void MotionEvent::transform(const float matrix[9]) { + // The tricky part of this implementation is to preserve the value of + // rawX and rawY. So we apply the transformation to the first point + // then derive an appropriate new X/Y offset that will preserve rawX + // and rawY for that point. + float oldXOffset = mXOffset; + float oldYOffset = mYOffset; + float newX, newY; + float rawX = getRawX(0); + float rawY = getRawY(0); + transformPoint(matrix, rawX + oldXOffset, rawY + oldYOffset, &newX, &newY); + mXOffset = newX - rawX; + mYOffset = newY - rawY; + + // Determine how the origin is transformed by the matrix so that we + // can transform orientation vectors. + float originX, originY; + transformPoint(matrix, 0, 0, &originX, &originY); + + // Apply the transformation to all samples. + size_t numSamples = mSamplePointerCoords.size(); + for (size_t i = 0; i < numSamples; i++) { + PointerCoords& c = mSamplePointerCoords.editItemAt(i); + float x = c.getAxisValue(AMOTION_EVENT_AXIS_X) + oldXOffset; + float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y) + oldYOffset; + transformPoint(matrix, x, y, &x, &y); + c.setAxisValue(AMOTION_EVENT_AXIS_X, x - mXOffset); + c.setAxisValue(AMOTION_EVENT_AXIS_Y, y - mYOffset); + + float orientation = c.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION); + c.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, + transformAngle(matrix, orientation, originX, originY)); + } +} + +#ifdef HAVE_ANDROID_OS +status_t MotionEvent::readFromParcel(Parcel* parcel) { + size_t pointerCount = parcel->readInt32(); + size_t sampleCount = parcel->readInt32(); + if (pointerCount == 0 || pointerCount > MAX_POINTERS || sampleCount == 0) { + return BAD_VALUE; + } + + mDeviceId = parcel->readInt32(); + mSource = parcel->readInt32(); + mAction = parcel->readInt32(); + mFlags = parcel->readInt32(); + mEdgeFlags = parcel->readInt32(); + mMetaState = parcel->readInt32(); + mButtonState = parcel->readInt32(); + mXOffset = parcel->readFloat(); + mYOffset = parcel->readFloat(); + mXPrecision = parcel->readFloat(); + mYPrecision = parcel->readFloat(); + mDownTime = parcel->readInt64(); + + mPointerProperties.clear(); + mPointerProperties.setCapacity(pointerCount); + mSampleEventTimes.clear(); + mSampleEventTimes.setCapacity(sampleCount); + mSamplePointerCoords.clear(); + mSamplePointerCoords.setCapacity(sampleCount * pointerCount); + + for (size_t i = 0; i < pointerCount; i++) { + mPointerProperties.push(); + PointerProperties& properties = mPointerProperties.editTop(); + properties.id = parcel->readInt32(); + properties.toolType = parcel->readInt32(); + } + + while (sampleCount-- > 0) { + mSampleEventTimes.push(parcel->readInt64()); + for (size_t i = 0; i < pointerCount; i++) { + mSamplePointerCoords.push(); + status_t status = mSamplePointerCoords.editTop().readFromParcel(parcel); + if (status) { + return status; + } + } + } + return OK; +} + +status_t MotionEvent::writeToParcel(Parcel* parcel) const { + size_t pointerCount = mPointerProperties.size(); + size_t sampleCount = mSampleEventTimes.size(); + + parcel->writeInt32(pointerCount); + parcel->writeInt32(sampleCount); + + parcel->writeInt32(mDeviceId); + parcel->writeInt32(mSource); + parcel->writeInt32(mAction); + parcel->writeInt32(mFlags); + parcel->writeInt32(mEdgeFlags); + parcel->writeInt32(mMetaState); + parcel->writeInt32(mButtonState); + parcel->writeFloat(mXOffset); + parcel->writeFloat(mYOffset); + parcel->writeFloat(mXPrecision); + parcel->writeFloat(mYPrecision); + parcel->writeInt64(mDownTime); + + for (size_t i = 0; i < pointerCount; i++) { + const PointerProperties& properties = mPointerProperties.itemAt(i); + parcel->writeInt32(properties.id); + parcel->writeInt32(properties.toolType); + } + + const PointerCoords* pc = mSamplePointerCoords.array(); + for (size_t h = 0; h < sampleCount; h++) { + parcel->writeInt64(mSampleEventTimes.itemAt(h)); + for (size_t i = 0; i < pointerCount; i++) { + status_t status = (pc++)->writeToParcel(parcel); + if (status) { + return status; + } + } + } + return OK; +} +#endif + +bool MotionEvent::isTouchEvent(int32_t source, int32_t action) { + if (source & AINPUT_SOURCE_CLASS_POINTER) { + // Specifically excludes HOVER_MOVE and SCROLL. + switch (action & AMOTION_EVENT_ACTION_MASK) { + case AMOTION_EVENT_ACTION_DOWN: + case AMOTION_EVENT_ACTION_MOVE: + case AMOTION_EVENT_ACTION_UP: + case AMOTION_EVENT_ACTION_POINTER_DOWN: + case AMOTION_EVENT_ACTION_POINTER_UP: + case AMOTION_EVENT_ACTION_CANCEL: + case AMOTION_EVENT_ACTION_OUTSIDE: + return true; + } + } + return false; +} + + +// --- PooledInputEventFactory --- + +PooledInputEventFactory::PooledInputEventFactory(size_t maxPoolSize) : + mMaxPoolSize(maxPoolSize) { +} + +PooledInputEventFactory::~PooledInputEventFactory() { + for (size_t i = 0; i < mKeyEventPool.size(); i++) { + delete mKeyEventPool.itemAt(i); + } + for (size_t i = 0; i < mMotionEventPool.size(); i++) { + delete mMotionEventPool.itemAt(i); + } +} + +KeyEvent* PooledInputEventFactory::createKeyEvent() { + if (!mKeyEventPool.isEmpty()) { + KeyEvent* event = mKeyEventPool.top(); + mKeyEventPool.pop(); + return event; + } + return new KeyEvent(); +} + +MotionEvent* PooledInputEventFactory::createMotionEvent() { + if (!mMotionEventPool.isEmpty()) { + MotionEvent* event = mMotionEventPool.top(); + mMotionEventPool.pop(); + return event; + } + return new MotionEvent(); +} + +void PooledInputEventFactory::recycle(InputEvent* event) { + switch (event->getType()) { + case AINPUT_EVENT_TYPE_KEY: + if (mKeyEventPool.size() < mMaxPoolSize) { + mKeyEventPool.push(static_cast<KeyEvent*>(event)); + return; + } + break; + case AINPUT_EVENT_TYPE_MOTION: + if (mMotionEventPool.size() < mMaxPoolSize) { + mMotionEventPool.push(static_cast<MotionEvent*>(event)); + return; + } + break; + } + delete event; +} + +} // namespace android diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp new file mode 100644 index 0000000..b11110a --- /dev/null +++ b/libs/input/InputDevice.cpp @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "InputDevice" + +#include <stdlib.h> +#include <unistd.h> +#include <ctype.h> + +#include <input/InputDevice.h> + +namespace android { + +static const char* CONFIGURATION_FILE_DIR[] = { + "idc/", + "keylayout/", + "keychars/", +}; + +static const char* CONFIGURATION_FILE_EXTENSION[] = { + ".idc", + ".kl", + ".kcm", +}; + +static bool isValidNameChar(char ch) { + return isascii(ch) && (isdigit(ch) || isalpha(ch) || ch == '-' || ch == '_'); +} + +static void appendInputDeviceConfigurationFileRelativePath(String8& path, + const String8& name, InputDeviceConfigurationFileType type) { + path.append(CONFIGURATION_FILE_DIR[type]); + for (size_t i = 0; i < name.length(); i++) { + char ch = name[i]; + if (!isValidNameChar(ch)) { + ch = '_'; + } + path.append(&ch, 1); + } + path.append(CONFIGURATION_FILE_EXTENSION[type]); +} + +String8 getInputDeviceConfigurationFilePathByDeviceIdentifier( + const InputDeviceIdentifier& deviceIdentifier, + InputDeviceConfigurationFileType type) { + if (deviceIdentifier.vendor !=0 && deviceIdentifier.product != 0) { + if (deviceIdentifier.version != 0) { + // Try vendor product version. + String8 versionPath(getInputDeviceConfigurationFilePathByName( + String8::format("Vendor_%04x_Product_%04x_Version_%04x", + deviceIdentifier.vendor, deviceIdentifier.product, + deviceIdentifier.version), + type)); + if (!versionPath.isEmpty()) { + return versionPath; + } + } + + // Try vendor product. + String8 productPath(getInputDeviceConfigurationFilePathByName( + String8::format("Vendor_%04x_Product_%04x", + deviceIdentifier.vendor, deviceIdentifier.product), + type)); + if (!productPath.isEmpty()) { + return productPath; + } + } + + // Try device name. + return getInputDeviceConfigurationFilePathByName(deviceIdentifier.name, type); +} + +String8 getInputDeviceConfigurationFilePathByName( + const String8& name, InputDeviceConfigurationFileType type) { + // Search system repository. + String8 path; + path.setTo(getenv("ANDROID_ROOT")); + path.append("/usr/"); + appendInputDeviceConfigurationFileRelativePath(path, name, type); +#if DEBUG_PROBE + ALOGD("Probing for system provided input device configuration file: path='%s'", path.string()); +#endif + if (!access(path.string(), R_OK)) { +#if DEBUG_PROBE + ALOGD("Found"); +#endif + return path; + } + + // Search user repository. + // TODO Should only look here if not in safe mode. + path.setTo(getenv("ANDROID_DATA")); + path.append("/system/devices/"); + appendInputDeviceConfigurationFileRelativePath(path, name, type); +#if DEBUG_PROBE + ALOGD("Probing for system user input device configuration file: path='%s'", path.string()); +#endif + if (!access(path.string(), R_OK)) { +#if DEBUG_PROBE + ALOGD("Found"); +#endif + return path; + } + + // Not found. +#if DEBUG_PROBE + ALOGD("Probe failed to find input device configuration file: name='%s', type=%d", + name.string(), type); +#endif + return String8(); +} + + +// --- InputDeviceInfo --- + +InputDeviceInfo::InputDeviceInfo() { + initialize(-1, 0, -1, InputDeviceIdentifier(), String8(), false); +} + +InputDeviceInfo::InputDeviceInfo(const InputDeviceInfo& other) : + mId(other.mId), mGeneration(other.mGeneration), mControllerNumber(other.mControllerNumber), + mIdentifier(other.mIdentifier), mAlias(other.mAlias), mIsExternal(other.mIsExternal), + mSources(other.mSources), mKeyboardType(other.mKeyboardType), + mKeyCharacterMap(other.mKeyCharacterMap), mHasVibrator(other.mHasVibrator), + mHasButtonUnderPad(other.mHasButtonUnderPad), mMotionRanges(other.mMotionRanges) { +} + +InputDeviceInfo::~InputDeviceInfo() { +} + +void InputDeviceInfo::initialize(int32_t id, int32_t generation, int32_t controllerNumber, + const InputDeviceIdentifier& identifier, const String8& alias, bool isExternal) { + mId = id; + mGeneration = generation; + mControllerNumber = controllerNumber; + mIdentifier = identifier; + mAlias = alias; + mIsExternal = isExternal; + mSources = 0; + mKeyboardType = AINPUT_KEYBOARD_TYPE_NONE; + mHasVibrator = false; + mHasButtonUnderPad = false; + mMotionRanges.clear(); +} + +const InputDeviceInfo::MotionRange* InputDeviceInfo::getMotionRange( + int32_t axis, uint32_t source) const { + size_t numRanges = mMotionRanges.size(); + for (size_t i = 0; i < numRanges; i++) { + const MotionRange& range = mMotionRanges.itemAt(i); + if (range.axis == axis && range.source == source) { + return ⦥ + } + } + return NULL; +} + +void InputDeviceInfo::addSource(uint32_t source) { + mSources |= source; +} + +void InputDeviceInfo::addMotionRange(int32_t axis, uint32_t source, float min, float max, + float flat, float fuzz, float resolution) { + MotionRange range = { axis, source, min, max, flat, fuzz, resolution }; + mMotionRanges.add(range); +} + +void InputDeviceInfo::addMotionRange(const MotionRange& range) { + mMotionRanges.add(range); +} + +} // namespace android diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp new file mode 100644 index 0000000..9bd7fc6 --- /dev/null +++ b/libs/input/InputTransport.cpp @@ -0,0 +1,958 @@ +// +// Copyright 2010 The Android Open Source Project +// +// Provides a shared memory transport for input events. +// +#define LOG_TAG "InputTransport" + +//#define LOG_NDEBUG 0 + +// Log debug messages about channel messages (send message, receive message) +#define DEBUG_CHANNEL_MESSAGES 0 + +// Log debug messages whenever InputChannel objects are created/destroyed +#define DEBUG_CHANNEL_LIFECYCLE 0 + +// Log debug messages about transport actions +#define DEBUG_TRANSPORT_ACTIONS 0 + +// Log debug messages about touch event resampling +#define DEBUG_RESAMPLING 0 + + +#include <errno.h> +#include <fcntl.h> +#include <math.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <unistd.h> + +#include <cutils/log.h> +#include <cutils/properties.h> +#include <input/InputTransport.h> + + +namespace android { + +// Socket buffer size. The default is typically about 128KB, which is much larger than +// we really need. So we make it smaller. It just needs to be big enough to hold +// a few dozen large multi-finger motion events in the case where an application gets +// behind processing touches. +static const size_t SOCKET_BUFFER_SIZE = 32 * 1024; + +// Nanoseconds per milliseconds. +static const nsecs_t NANOS_PER_MS = 1000000; + +// Latency added during resampling. A few milliseconds doesn't hurt much but +// reduces the impact of mispredicted touch positions. +static const nsecs_t RESAMPLE_LATENCY = 5 * NANOS_PER_MS; + +// Minimum time difference between consecutive samples before attempting to resample. +static const nsecs_t RESAMPLE_MIN_DELTA = 2 * NANOS_PER_MS; + +// Maximum time to predict forward from the last known state, to avoid predicting too +// far into the future. This time is further bounded by 50% of the last time delta. +static const nsecs_t RESAMPLE_MAX_PREDICTION = 8 * NANOS_PER_MS; + +template<typename T> +inline static T min(const T& a, const T& b) { + return a < b ? a : b; +} + +inline static float lerp(float a, float b, float alpha) { + return a + alpha * (b - a); +} + +// --- InputMessage --- + +bool InputMessage::isValid(size_t actualSize) const { + if (size() == actualSize) { + switch (header.type) { + case TYPE_KEY: + return true; + case TYPE_MOTION: + return body.motion.pointerCount > 0 + && body.motion.pointerCount <= MAX_POINTERS; + case TYPE_FINISHED: + return true; + } + } + return false; +} + +size_t InputMessage::size() const { + switch (header.type) { + case TYPE_KEY: + return sizeof(Header) + body.key.size(); + case TYPE_MOTION: + return sizeof(Header) + body.motion.size(); + case TYPE_FINISHED: + return sizeof(Header) + body.finished.size(); + } + return sizeof(Header); +} + + +// --- InputChannel --- + +InputChannel::InputChannel(const String8& name, int fd) : + mName(name), mFd(fd) { +#if DEBUG_CHANNEL_LIFECYCLE + ALOGD("Input channel constructed: name='%s', fd=%d", + mName.string(), fd); +#endif + + int result = fcntl(mFd, F_SETFL, O_NONBLOCK); + LOG_ALWAYS_FATAL_IF(result != 0, "channel '%s' ~ Could not make socket " + "non-blocking. errno=%d", mName.string(), errno); +} + +InputChannel::~InputChannel() { +#if DEBUG_CHANNEL_LIFECYCLE + ALOGD("Input channel destroyed: name='%s', fd=%d", + mName.string(), mFd); +#endif + + ::close(mFd); +} + +status_t InputChannel::openInputChannelPair(const String8& name, + sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) { + int sockets[2]; + if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) { + status_t result = -errno; + ALOGE("channel '%s' ~ Could not create socket pair. errno=%d", + name.string(), errno); + outServerChannel.clear(); + outClientChannel.clear(); + return result; + } + + int bufferSize = SOCKET_BUFFER_SIZE; + setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize)); + setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize)); + setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize)); + setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize)); + + String8 serverChannelName = name; + serverChannelName.append(" (server)"); + outServerChannel = new InputChannel(serverChannelName, sockets[0]); + + String8 clientChannelName = name; + clientChannelName.append(" (client)"); + outClientChannel = new InputChannel(clientChannelName, sockets[1]); + return OK; +} + +status_t InputChannel::sendMessage(const InputMessage* msg) { + size_t msgLength = msg->size(); + ssize_t nWrite; + do { + nWrite = ::send(mFd, msg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL); + } while (nWrite == -1 && errno == EINTR); + + if (nWrite < 0) { + int error = errno; +#if DEBUG_CHANNEL_MESSAGES + ALOGD("channel '%s' ~ error sending message of type %d, errno=%d", mName.string(), + msg->header.type, error); +#endif + if (error == EAGAIN || error == EWOULDBLOCK) { + return WOULD_BLOCK; + } + if (error == EPIPE || error == ENOTCONN || error == ECONNREFUSED || error == ECONNRESET) { + return DEAD_OBJECT; + } + return -error; + } + + if (size_t(nWrite) != msgLength) { +#if DEBUG_CHANNEL_MESSAGES + ALOGD("channel '%s' ~ error sending message type %d, send was incomplete", + mName.string(), msg->header.type); +#endif + return DEAD_OBJECT; + } + +#if DEBUG_CHANNEL_MESSAGES + ALOGD("channel '%s' ~ sent message of type %d", mName.string(), msg->header.type); +#endif + return OK; +} + +status_t InputChannel::receiveMessage(InputMessage* msg) { + ssize_t nRead; + do { + nRead = ::recv(mFd, msg, sizeof(InputMessage), MSG_DONTWAIT); + } while (nRead == -1 && errno == EINTR); + + if (nRead < 0) { + int error = errno; +#if DEBUG_CHANNEL_MESSAGES + ALOGD("channel '%s' ~ receive message failed, errno=%d", mName.string(), errno); +#endif + if (error == EAGAIN || error == EWOULDBLOCK) { + return WOULD_BLOCK; + } + if (error == EPIPE || error == ENOTCONN || error == ECONNREFUSED) { + return DEAD_OBJECT; + } + return -error; + } + + if (nRead == 0) { // check for EOF +#if DEBUG_CHANNEL_MESSAGES + ALOGD("channel '%s' ~ receive message failed because peer was closed", mName.string()); +#endif + return DEAD_OBJECT; + } + + if (!msg->isValid(nRead)) { +#if DEBUG_CHANNEL_MESSAGES + ALOGD("channel '%s' ~ received invalid message", mName.string()); +#endif + return BAD_VALUE; + } + +#if DEBUG_CHANNEL_MESSAGES + ALOGD("channel '%s' ~ received message of type %d", mName.string(), msg->header.type); +#endif + return OK; +} + +sp<InputChannel> InputChannel::dup() const { + int fd = ::dup(getFd()); + return fd >= 0 ? new InputChannel(getName(), fd) : NULL; +} + + +// --- InputPublisher --- + +InputPublisher::InputPublisher(const sp<InputChannel>& channel) : + mChannel(channel) { +} + +InputPublisher::~InputPublisher() { +} + +status_t InputPublisher::publishKeyEvent( + uint32_t seq, + int32_t deviceId, + int32_t source, + int32_t action, + int32_t flags, + int32_t keyCode, + int32_t scanCode, + int32_t metaState, + int32_t repeatCount, + nsecs_t downTime, + nsecs_t eventTime) { +#if DEBUG_TRANSPORT_ACTIONS + ALOGD("channel '%s' publisher ~ publishKeyEvent: seq=%u, deviceId=%d, source=0x%x, " + "action=0x%x, flags=0x%x, keyCode=%d, scanCode=%d, metaState=0x%x, repeatCount=%d," + "downTime=%lld, eventTime=%lld", + mChannel->getName().string(), seq, + deviceId, source, action, flags, keyCode, scanCode, metaState, repeatCount, + downTime, eventTime); +#endif + + if (!seq) { + ALOGE("Attempted to publish a key event with sequence number 0."); + return BAD_VALUE; + } + + InputMessage msg; + msg.header.type = InputMessage::TYPE_KEY; + msg.body.key.seq = seq; + msg.body.key.deviceId = deviceId; + msg.body.key.source = source; + msg.body.key.action = action; + msg.body.key.flags = flags; + msg.body.key.keyCode = keyCode; + msg.body.key.scanCode = scanCode; + msg.body.key.metaState = metaState; + msg.body.key.repeatCount = repeatCount; + msg.body.key.downTime = downTime; + msg.body.key.eventTime = eventTime; + return mChannel->sendMessage(&msg); +} + +status_t InputPublisher::publishMotionEvent( + uint32_t seq, + int32_t deviceId, + int32_t source, + int32_t action, + int32_t flags, + int32_t edgeFlags, + int32_t metaState, + int32_t buttonState, + float xOffset, + float yOffset, + float xPrecision, + float yPrecision, + nsecs_t downTime, + nsecs_t eventTime, + size_t pointerCount, + const PointerProperties* pointerProperties, + const PointerCoords* pointerCoords) { +#if DEBUG_TRANSPORT_ACTIONS + ALOGD("channel '%s' publisher ~ publishMotionEvent: seq=%u, deviceId=%d, source=0x%x, " + "action=0x%x, flags=0x%x, edgeFlags=0x%x, metaState=0x%x, buttonState=0x%x, " + "xOffset=%f, yOffset=%f, " + "xPrecision=%f, yPrecision=%f, downTime=%lld, eventTime=%lld, " + "pointerCount=%d", + mChannel->getName().string(), seq, + deviceId, source, action, flags, edgeFlags, metaState, buttonState, + xOffset, yOffset, xPrecision, yPrecision, downTime, eventTime, pointerCount); +#endif + + if (!seq) { + ALOGE("Attempted to publish a motion event with sequence number 0."); + return BAD_VALUE; + } + + if (pointerCount > MAX_POINTERS || pointerCount < 1) { + ALOGE("channel '%s' publisher ~ Invalid number of pointers provided: %d.", + mChannel->getName().string(), pointerCount); + return BAD_VALUE; + } + + InputMessage msg; + msg.header.type = InputMessage::TYPE_MOTION; + msg.body.motion.seq = seq; + msg.body.motion.deviceId = deviceId; + msg.body.motion.source = source; + msg.body.motion.action = action; + msg.body.motion.flags = flags; + msg.body.motion.edgeFlags = edgeFlags; + msg.body.motion.metaState = metaState; + msg.body.motion.buttonState = buttonState; + msg.body.motion.xOffset = xOffset; + msg.body.motion.yOffset = yOffset; + msg.body.motion.xPrecision = xPrecision; + msg.body.motion.yPrecision = yPrecision; + msg.body.motion.downTime = downTime; + msg.body.motion.eventTime = eventTime; + msg.body.motion.pointerCount = pointerCount; + for (size_t i = 0; i < pointerCount; i++) { + msg.body.motion.pointers[i].properties.copyFrom(pointerProperties[i]); + msg.body.motion.pointers[i].coords.copyFrom(pointerCoords[i]); + } + return mChannel->sendMessage(&msg); +} + +status_t InputPublisher::receiveFinishedSignal(uint32_t* outSeq, bool* outHandled) { +#if DEBUG_TRANSPORT_ACTIONS + ALOGD("channel '%s' publisher ~ receiveFinishedSignal", + mChannel->getName().string()); +#endif + + InputMessage msg; + status_t result = mChannel->receiveMessage(&msg); + if (result) { + *outSeq = 0; + *outHandled = false; + return result; + } + if (msg.header.type != InputMessage::TYPE_FINISHED) { + ALOGE("channel '%s' publisher ~ Received unexpected message of type %d from consumer", + mChannel->getName().string(), msg.header.type); + return UNKNOWN_ERROR; + } + *outSeq = msg.body.finished.seq; + *outHandled = msg.body.finished.handled; + return OK; +} + +// --- InputConsumer --- + +InputConsumer::InputConsumer(const sp<InputChannel>& channel) : + mResampleTouch(isTouchResamplingEnabled()), + mChannel(channel), mMsgDeferred(false) { +} + +InputConsumer::~InputConsumer() { +} + +bool InputConsumer::isTouchResamplingEnabled() { + char value[PROPERTY_VALUE_MAX]; + int length = property_get("ro.input.noresample", value, NULL); + if (length > 0) { + if (!strcmp("1", value)) { + return false; + } + if (strcmp("0", value)) { + ALOGD("Unrecognized property value for 'ro.input.noresample'. " + "Use '1' or '0'."); + } + } + return true; +} + +status_t InputConsumer::consume(InputEventFactoryInterface* factory, + bool consumeBatches, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) { +#if DEBUG_TRANSPORT_ACTIONS + ALOGD("channel '%s' consumer ~ consume: consumeBatches=%s, frameTime=%lld", + mChannel->getName().string(), consumeBatches ? "true" : "false", frameTime); +#endif + + *outSeq = 0; + *outEvent = NULL; + + // Fetch the next input message. + // Loop until an event can be returned or no additional events are received. + while (!*outEvent) { + if (mMsgDeferred) { + // mMsg contains a valid input message from the previous call to consume + // that has not yet been processed. + mMsgDeferred = false; + } else { + // Receive a fresh message. + status_t result = mChannel->receiveMessage(&mMsg); + if (result) { + // Consume the next batched event unless batches are being held for later. + if (consumeBatches || result != WOULD_BLOCK) { + result = consumeBatch(factory, frameTime, outSeq, outEvent); + if (*outEvent) { +#if DEBUG_TRANSPORT_ACTIONS + ALOGD("channel '%s' consumer ~ consumed batch event, seq=%u", + mChannel->getName().string(), *outSeq); +#endif + break; + } + } + return result; + } + } + + switch (mMsg.header.type) { + case InputMessage::TYPE_KEY: { + KeyEvent* keyEvent = factory->createKeyEvent(); + if (!keyEvent) return NO_MEMORY; + + initializeKeyEvent(keyEvent, &mMsg); + *outSeq = mMsg.body.key.seq; + *outEvent = keyEvent; +#if DEBUG_TRANSPORT_ACTIONS + ALOGD("channel '%s' consumer ~ consumed key event, seq=%u", + mChannel->getName().string(), *outSeq); +#endif + break; + } + + case AINPUT_EVENT_TYPE_MOTION: { + ssize_t batchIndex = findBatch(mMsg.body.motion.deviceId, mMsg.body.motion.source); + if (batchIndex >= 0) { + Batch& batch = mBatches.editItemAt(batchIndex); + if (canAddSample(batch, &mMsg)) { + batch.samples.push(mMsg); +#if DEBUG_TRANSPORT_ACTIONS + ALOGD("channel '%s' consumer ~ appended to batch event", + mChannel->getName().string()); +#endif + break; + } else { + // We cannot append to the batch in progress, so we need to consume + // the previous batch right now and defer the new message until later. + mMsgDeferred = true; + status_t result = consumeSamples(factory, + batch, batch.samples.size(), outSeq, outEvent); + mBatches.removeAt(batchIndex); + if (result) { + return result; + } +#if DEBUG_TRANSPORT_ACTIONS + ALOGD("channel '%s' consumer ~ consumed batch event and " + "deferred current event, seq=%u", + mChannel->getName().string(), *outSeq); +#endif + break; + } + } + + // Start a new batch if needed. + if (mMsg.body.motion.action == AMOTION_EVENT_ACTION_MOVE + || mMsg.body.motion.action == AMOTION_EVENT_ACTION_HOVER_MOVE) { + mBatches.push(); + Batch& batch = mBatches.editTop(); + batch.samples.push(mMsg); +#if DEBUG_TRANSPORT_ACTIONS + ALOGD("channel '%s' consumer ~ started batch event", + mChannel->getName().string()); +#endif + break; + } + + MotionEvent* motionEvent = factory->createMotionEvent(); + if (! motionEvent) return NO_MEMORY; + + updateTouchState(&mMsg); + initializeMotionEvent(motionEvent, &mMsg); + *outSeq = mMsg.body.motion.seq; + *outEvent = motionEvent; +#if DEBUG_TRANSPORT_ACTIONS + ALOGD("channel '%s' consumer ~ consumed motion event, seq=%u", + mChannel->getName().string(), *outSeq); +#endif + break; + } + + default: + ALOGE("channel '%s' consumer ~ Received unexpected message of type %d", + mChannel->getName().string(), mMsg.header.type); + return UNKNOWN_ERROR; + } + } + return OK; +} + +status_t InputConsumer::consumeBatch(InputEventFactoryInterface* factory, + nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) { + status_t result; + for (size_t i = mBatches.size(); i-- > 0; ) { + Batch& batch = mBatches.editItemAt(i); + if (frameTime < 0 || !mResampleTouch) { + result = consumeSamples(factory, batch, batch.samples.size(), + outSeq, outEvent); + mBatches.removeAt(i); + return result; + } + + nsecs_t sampleTime = frameTime - RESAMPLE_LATENCY; + ssize_t split = findSampleNoLaterThan(batch, sampleTime); + if (split < 0) { + continue; + } + + result = consumeSamples(factory, batch, split + 1, outSeq, outEvent); + const InputMessage* next; + if (batch.samples.isEmpty()) { + mBatches.removeAt(i); + next = NULL; + } else { + next = &batch.samples.itemAt(0); + } + if (!result) { + resampleTouchState(sampleTime, static_cast<MotionEvent*>(*outEvent), next); + } + return result; + } + + return WOULD_BLOCK; +} + +status_t InputConsumer::consumeSamples(InputEventFactoryInterface* factory, + Batch& batch, size_t count, uint32_t* outSeq, InputEvent** outEvent) { + MotionEvent* motionEvent = factory->createMotionEvent(); + if (! motionEvent) return NO_MEMORY; + + uint32_t chain = 0; + for (size_t i = 0; i < count; i++) { + InputMessage& msg = batch.samples.editItemAt(i); + updateTouchState(&msg); + if (i) { + SeqChain seqChain; + seqChain.seq = msg.body.motion.seq; + seqChain.chain = chain; + mSeqChains.push(seqChain); + addSample(motionEvent, &msg); + } else { + initializeMotionEvent(motionEvent, &msg); + } + chain = msg.body.motion.seq; + } + batch.samples.removeItemsAt(0, count); + + *outSeq = chain; + *outEvent = motionEvent; + return OK; +} + +void InputConsumer::updateTouchState(InputMessage* msg) { + if (!mResampleTouch || + !(msg->body.motion.source & AINPUT_SOURCE_CLASS_POINTER)) { + return; + } + + int32_t deviceId = msg->body.motion.deviceId; + int32_t source = msg->body.motion.source; + nsecs_t eventTime = msg->body.motion.eventTime; + + // Update the touch state history to incorporate the new input message. + // If the message is in the past relative to the most recently produced resampled + // touch, then use the resampled time and coordinates instead. + switch (msg->body.motion.action & AMOTION_EVENT_ACTION_MASK) { + case AMOTION_EVENT_ACTION_DOWN: { + ssize_t index = findTouchState(deviceId, source); + if (index < 0) { + mTouchStates.push(); + index = mTouchStates.size() - 1; + } + TouchState& touchState = mTouchStates.editItemAt(index); + touchState.initialize(deviceId, source); + touchState.addHistory(msg); + break; + } + + case AMOTION_EVENT_ACTION_MOVE: { + ssize_t index = findTouchState(deviceId, source); + if (index >= 0) { + TouchState& touchState = mTouchStates.editItemAt(index); + touchState.addHistory(msg); + if (eventTime < touchState.lastResample.eventTime) { + rewriteMessage(touchState, msg); + } else { + touchState.lastResample.idBits.clear(); + } + } + break; + } + + case AMOTION_EVENT_ACTION_POINTER_DOWN: { + ssize_t index = findTouchState(deviceId, source); + if (index >= 0) { + TouchState& touchState = mTouchStates.editItemAt(index); + touchState.lastResample.idBits.clearBit(msg->body.motion.getActionId()); + rewriteMessage(touchState, msg); + } + break; + } + + case AMOTION_EVENT_ACTION_POINTER_UP: { + ssize_t index = findTouchState(deviceId, source); + if (index >= 0) { + TouchState& touchState = mTouchStates.editItemAt(index); + rewriteMessage(touchState, msg); + touchState.lastResample.idBits.clearBit(msg->body.motion.getActionId()); + } + break; + } + + case AMOTION_EVENT_ACTION_SCROLL: { + ssize_t index = findTouchState(deviceId, source); + if (index >= 0) { + const TouchState& touchState = mTouchStates.itemAt(index); + rewriteMessage(touchState, msg); + } + break; + } + + case AMOTION_EVENT_ACTION_UP: + case AMOTION_EVENT_ACTION_CANCEL: { + ssize_t index = findTouchState(deviceId, source); + if (index >= 0) { + const TouchState& touchState = mTouchStates.itemAt(index); + rewriteMessage(touchState, msg); + mTouchStates.removeAt(index); + } + break; + } + } +} + +void InputConsumer::rewriteMessage(const TouchState& state, InputMessage* msg) { + for (size_t i = 0; i < msg->body.motion.pointerCount; i++) { + uint32_t id = msg->body.motion.pointers[i].properties.id; + if (state.lastResample.idBits.hasBit(id)) { + PointerCoords& msgCoords = msg->body.motion.pointers[i].coords; + const PointerCoords& resampleCoords = state.lastResample.getPointerById(id); +#if DEBUG_RESAMPLING + ALOGD("[%d] - rewrite (%0.3f, %0.3f), old (%0.3f, %0.3f)", id, + resampleCoords.getAxisValue(AMOTION_EVENT_AXIS_X), + resampleCoords.getAxisValue(AMOTION_EVENT_AXIS_Y), + msgCoords.getAxisValue(AMOTION_EVENT_AXIS_X), + msgCoords.getAxisValue(AMOTION_EVENT_AXIS_Y)); +#endif + msgCoords.setAxisValue(AMOTION_EVENT_AXIS_X, resampleCoords.getX()); + msgCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, resampleCoords.getY()); + } + } +} + +void InputConsumer::resampleTouchState(nsecs_t sampleTime, MotionEvent* event, + const InputMessage* next) { + if (!mResampleTouch + || !(event->getSource() & AINPUT_SOURCE_CLASS_POINTER) + || event->getAction() != AMOTION_EVENT_ACTION_MOVE) { + return; + } + + ssize_t index = findTouchState(event->getDeviceId(), event->getSource()); + if (index < 0) { +#if DEBUG_RESAMPLING + ALOGD("Not resampled, no touch state for device."); +#endif + return; + } + + TouchState& touchState = mTouchStates.editItemAt(index); + if (touchState.historySize < 1) { +#if DEBUG_RESAMPLING + ALOGD("Not resampled, no history for device."); +#endif + return; + } + + // Ensure that the current sample has all of the pointers that need to be reported. + const History* current = touchState.getHistory(0); + size_t pointerCount = event->getPointerCount(); + for (size_t i = 0; i < pointerCount; i++) { + uint32_t id = event->getPointerId(i); + if (!current->idBits.hasBit(id)) { +#if DEBUG_RESAMPLING + ALOGD("Not resampled, missing id %d", id); +#endif + return; + } + } + + // Find the data to use for resampling. + const History* other; + History future; + float alpha; + if (next) { + // Interpolate between current sample and future sample. + // So current->eventTime <= sampleTime <= future.eventTime. + future.initializeFrom(next); + other = &future; + nsecs_t delta = future.eventTime - current->eventTime; + if (delta < RESAMPLE_MIN_DELTA) { +#if DEBUG_RESAMPLING + ALOGD("Not resampled, delta time is %lld ns.", delta); +#endif + return; + } + alpha = float(sampleTime - current->eventTime) / delta; + } else if (touchState.historySize >= 2) { + // Extrapolate future sample using current sample and past sample. + // So other->eventTime <= current->eventTime <= sampleTime. + other = touchState.getHistory(1); + nsecs_t delta = current->eventTime - other->eventTime; + if (delta < RESAMPLE_MIN_DELTA) { +#if DEBUG_RESAMPLING + ALOGD("Not resampled, delta time is %lld ns.", delta); +#endif + return; + } + nsecs_t maxPredict = current->eventTime + min(delta / 2, RESAMPLE_MAX_PREDICTION); + if (sampleTime > maxPredict) { +#if DEBUG_RESAMPLING + ALOGD("Sample time is too far in the future, adjusting prediction " + "from %lld to %lld ns.", + sampleTime - current->eventTime, maxPredict - current->eventTime); +#endif + sampleTime = maxPredict; + } + alpha = float(current->eventTime - sampleTime) / delta; + } else { +#if DEBUG_RESAMPLING + ALOGD("Not resampled, insufficient data."); +#endif + return; + } + + // Resample touch coordinates. + touchState.lastResample.eventTime = sampleTime; + touchState.lastResample.idBits.clear(); + for (size_t i = 0; i < pointerCount; i++) { + uint32_t id = event->getPointerId(i); + touchState.lastResample.idToIndex[id] = i; + touchState.lastResample.idBits.markBit(id); + PointerCoords& resampledCoords = touchState.lastResample.pointers[i]; + const PointerCoords& currentCoords = current->getPointerById(id); + if (other->idBits.hasBit(id) + && shouldResampleTool(event->getToolType(i))) { + const PointerCoords& otherCoords = other->getPointerById(id); + resampledCoords.copyFrom(currentCoords); + resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_X, + lerp(currentCoords.getX(), otherCoords.getX(), alpha)); + resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, + lerp(currentCoords.getY(), otherCoords.getY(), alpha)); +#if DEBUG_RESAMPLING + ALOGD("[%d] - out (%0.3f, %0.3f), cur (%0.3f, %0.3f), " + "other (%0.3f, %0.3f), alpha %0.3f", + id, resampledCoords.getX(), resampledCoords.getY(), + currentCoords.getX(), currentCoords.getY(), + otherCoords.getX(), otherCoords.getY(), + alpha); +#endif + } else { + resampledCoords.copyFrom(currentCoords); +#if DEBUG_RESAMPLING + ALOGD("[%d] - out (%0.3f, %0.3f), cur (%0.3f, %0.3f)", + id, resampledCoords.getX(), resampledCoords.getY(), + currentCoords.getX(), currentCoords.getY()); +#endif + } + } + + event->addSample(sampleTime, touchState.lastResample.pointers); +} + +bool InputConsumer::shouldResampleTool(int32_t toolType) { + return toolType == AMOTION_EVENT_TOOL_TYPE_FINGER + || toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN; +} + +status_t InputConsumer::sendFinishedSignal(uint32_t seq, bool handled) { +#if DEBUG_TRANSPORT_ACTIONS + ALOGD("channel '%s' consumer ~ sendFinishedSignal: seq=%u, handled=%s", + mChannel->getName().string(), seq, handled ? "true" : "false"); +#endif + + if (!seq) { + ALOGE("Attempted to send a finished signal with sequence number 0."); + return BAD_VALUE; + } + + // Send finished signals for the batch sequence chain first. + size_t seqChainCount = mSeqChains.size(); + if (seqChainCount) { + uint32_t currentSeq = seq; + uint32_t chainSeqs[seqChainCount]; + size_t chainIndex = 0; + for (size_t i = seqChainCount; i-- > 0; ) { + const SeqChain& seqChain = mSeqChains.itemAt(i); + if (seqChain.seq == currentSeq) { + currentSeq = seqChain.chain; + chainSeqs[chainIndex++] = currentSeq; + mSeqChains.removeAt(i); + } + } + status_t status = OK; + while (!status && chainIndex-- > 0) { + status = sendUnchainedFinishedSignal(chainSeqs[chainIndex], handled); + } + if (status) { + // An error occurred so at least one signal was not sent, reconstruct the chain. + do { + SeqChain seqChain; + seqChain.seq = chainIndex != 0 ? chainSeqs[chainIndex - 1] : seq; + seqChain.chain = chainSeqs[chainIndex]; + mSeqChains.push(seqChain); + } while (chainIndex-- > 0); + return status; + } + } + + // Send finished signal for the last message in the batch. + return sendUnchainedFinishedSignal(seq, handled); +} + +status_t InputConsumer::sendUnchainedFinishedSignal(uint32_t seq, bool handled) { + InputMessage msg; + msg.header.type = InputMessage::TYPE_FINISHED; + msg.body.finished.seq = seq; + msg.body.finished.handled = handled; + return mChannel->sendMessage(&msg); +} + +bool InputConsumer::hasDeferredEvent() const { + return mMsgDeferred; +} + +bool InputConsumer::hasPendingBatch() const { + return !mBatches.isEmpty(); +} + +ssize_t InputConsumer::findBatch(int32_t deviceId, int32_t source) const { + for (size_t i = 0; i < mBatches.size(); i++) { + const Batch& batch = mBatches.itemAt(i); + const InputMessage& head = batch.samples.itemAt(0); + if (head.body.motion.deviceId == deviceId && head.body.motion.source == source) { + return i; + } + } + return -1; +} + +ssize_t InputConsumer::findTouchState(int32_t deviceId, int32_t source) const { + for (size_t i = 0; i < mTouchStates.size(); i++) { + const TouchState& touchState = mTouchStates.itemAt(i); + if (touchState.deviceId == deviceId && touchState.source == source) { + return i; + } + } + return -1; +} + +void InputConsumer::initializeKeyEvent(KeyEvent* event, const InputMessage* msg) { + event->initialize( + msg->body.key.deviceId, + msg->body.key.source, + msg->body.key.action, + msg->body.key.flags, + msg->body.key.keyCode, + msg->body.key.scanCode, + msg->body.key.metaState, + msg->body.key.repeatCount, + msg->body.key.downTime, + msg->body.key.eventTime); +} + +void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage* msg) { + size_t pointerCount = msg->body.motion.pointerCount; + PointerProperties pointerProperties[pointerCount]; + PointerCoords pointerCoords[pointerCount]; + for (size_t i = 0; i < pointerCount; i++) { + pointerProperties[i].copyFrom(msg->body.motion.pointers[i].properties); + pointerCoords[i].copyFrom(msg->body.motion.pointers[i].coords); + } + + event->initialize( + msg->body.motion.deviceId, + msg->body.motion.source, + msg->body.motion.action, + msg->body.motion.flags, + msg->body.motion.edgeFlags, + msg->body.motion.metaState, + msg->body.motion.buttonState, + msg->body.motion.xOffset, + msg->body.motion.yOffset, + msg->body.motion.xPrecision, + msg->body.motion.yPrecision, + msg->body.motion.downTime, + msg->body.motion.eventTime, + pointerCount, + pointerProperties, + pointerCoords); +} + +void InputConsumer::addSample(MotionEvent* event, const InputMessage* msg) { + size_t pointerCount = msg->body.motion.pointerCount; + PointerCoords pointerCoords[pointerCount]; + for (size_t i = 0; i < pointerCount; i++) { + pointerCoords[i].copyFrom(msg->body.motion.pointers[i].coords); + } + + event->setMetaState(event->getMetaState() | msg->body.motion.metaState); + event->addSample(msg->body.motion.eventTime, pointerCoords); +} + +bool InputConsumer::canAddSample(const Batch& batch, const InputMessage *msg) { + const InputMessage& head = batch.samples.itemAt(0); + size_t pointerCount = msg->body.motion.pointerCount; + if (head.body.motion.pointerCount != pointerCount + || head.body.motion.action != msg->body.motion.action) { + return false; + } + for (size_t i = 0; i < pointerCount; i++) { + if (head.body.motion.pointers[i].properties + != msg->body.motion.pointers[i].properties) { + return false; + } + } + return true; +} + +ssize_t InputConsumer::findSampleNoLaterThan(const Batch& batch, nsecs_t time) { + size_t numSamples = batch.samples.size(); + size_t index = 0; + while (index < numSamples + && batch.samples.itemAt(index).body.motion.eventTime <= time) { + index += 1; + } + return ssize_t(index) - 1; +} + +} // namespace android diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp new file mode 100644 index 0000000..15a8774 --- /dev/null +++ b/libs/input/KeyCharacterMap.cpp @@ -0,0 +1,1154 @@ +/* + * 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. + */ + +#define LOG_TAG "KeyCharacterMap" + +#include <stdlib.h> +#include <string.h> + +#if HAVE_ANDROID_OS +#include <binder/Parcel.h> +#endif + +#include <android/keycodes.h> +#include <input/Keyboard.h> +#include <input/KeyCharacterMap.h> + +#include <utils/Log.h> +#include <utils/Errors.h> +#include <utils/Tokenizer.h> +#include <utils/Timers.h> + +// Enables debug output for the parser. +#define DEBUG_PARSER 0 + +// Enables debug output for parser performance. +#define DEBUG_PARSER_PERFORMANCE 0 + +// Enables debug output for mapping. +#define DEBUG_MAPPING 0 + + +namespace android { + +static const char* WHITESPACE = " \t\r"; +static const char* WHITESPACE_OR_PROPERTY_DELIMITER = " \t\r,:"; + +struct Modifier { + const char* label; + int32_t metaState; +}; +static const Modifier modifiers[] = { + { "shift", AMETA_SHIFT_ON }, + { "lshift", AMETA_SHIFT_LEFT_ON }, + { "rshift", AMETA_SHIFT_RIGHT_ON }, + { "alt", AMETA_ALT_ON }, + { "lalt", AMETA_ALT_LEFT_ON }, + { "ralt", AMETA_ALT_RIGHT_ON }, + { "ctrl", AMETA_CTRL_ON }, + { "lctrl", AMETA_CTRL_LEFT_ON }, + { "rctrl", AMETA_CTRL_RIGHT_ON }, + { "meta", AMETA_META_ON }, + { "lmeta", AMETA_META_LEFT_ON }, + { "rmeta", AMETA_META_RIGHT_ON }, + { "sym", AMETA_SYM_ON }, + { "fn", AMETA_FUNCTION_ON }, + { "capslock", AMETA_CAPS_LOCK_ON }, + { "numlock", AMETA_NUM_LOCK_ON }, + { "scrolllock", AMETA_SCROLL_LOCK_ON }, +}; + +#if DEBUG_MAPPING +static String8 toString(const char16_t* chars, size_t numChars) { + String8 result; + for (size_t i = 0; i < numChars; i++) { + result.appendFormat(i == 0 ? "%d" : ", %d", chars[i]); + } + return result; +} +#endif + + +// --- KeyCharacterMap --- + +sp<KeyCharacterMap> KeyCharacterMap::sEmpty = new KeyCharacterMap(); + +KeyCharacterMap::KeyCharacterMap() : + mType(KEYBOARD_TYPE_UNKNOWN) { +} + +KeyCharacterMap::KeyCharacterMap(const KeyCharacterMap& other) : + RefBase(), mType(other.mType), mKeysByScanCode(other.mKeysByScanCode), + mKeysByUsageCode(other.mKeysByUsageCode) { + for (size_t i = 0; i < other.mKeys.size(); i++) { + mKeys.add(other.mKeys.keyAt(i), new Key(*other.mKeys.valueAt(i))); + } +} + +KeyCharacterMap::~KeyCharacterMap() { + for (size_t i = 0; i < mKeys.size(); i++) { + Key* key = mKeys.editValueAt(i); + delete key; + } +} + +status_t KeyCharacterMap::load(const String8& filename, + Format format, sp<KeyCharacterMap>* outMap) { + outMap->clear(); + + Tokenizer* tokenizer; + status_t status = Tokenizer::open(filename, &tokenizer); + if (status) { + ALOGE("Error %d opening key character map file %s.", status, filename.string()); + } else { + status = load(tokenizer, format, outMap); + delete tokenizer; + } + return status; +} + +status_t KeyCharacterMap::loadContents(const String8& filename, const char* contents, + Format format, sp<KeyCharacterMap>* outMap) { + outMap->clear(); + + Tokenizer* tokenizer; + status_t status = Tokenizer::fromContents(filename, contents, &tokenizer); + if (status) { + ALOGE("Error %d opening key character map.", status); + } else { + status = load(tokenizer, format, outMap); + delete tokenizer; + } + return status; +} + +status_t KeyCharacterMap::load(Tokenizer* tokenizer, + Format format, sp<KeyCharacterMap>* outMap) { + status_t status = OK; + sp<KeyCharacterMap> map = new KeyCharacterMap(); + if (!map.get()) { + ALOGE("Error allocating key character map."); + status = NO_MEMORY; + } else { +#if DEBUG_PARSER_PERFORMANCE + nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); +#endif + Parser parser(map.get(), tokenizer, format); + status = parser.parse(); +#if DEBUG_PARSER_PERFORMANCE + nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; + ALOGD("Parsed key character map file '%s' %d lines in %0.3fms.", + tokenizer->getFilename().string(), tokenizer->getLineNumber(), + elapsedTime / 1000000.0); +#endif + if (!status) { + *outMap = map; + } + } + return status; +} + +sp<KeyCharacterMap> KeyCharacterMap::combine(const sp<KeyCharacterMap>& base, + const sp<KeyCharacterMap>& overlay) { + if (overlay == NULL) { + return base; + } + if (base == NULL) { + return overlay; + } + + sp<KeyCharacterMap> map = new KeyCharacterMap(*base.get()); + for (size_t i = 0; i < overlay->mKeys.size(); i++) { + int32_t keyCode = overlay->mKeys.keyAt(i); + Key* key = overlay->mKeys.valueAt(i); + ssize_t oldIndex = map->mKeys.indexOfKey(keyCode); + if (oldIndex >= 0) { + delete map->mKeys.valueAt(oldIndex); + map->mKeys.editValueAt(oldIndex) = new Key(*key); + } else { + map->mKeys.add(keyCode, new Key(*key)); + } + } + + for (size_t i = 0; i < overlay->mKeysByScanCode.size(); i++) { + map->mKeysByScanCode.replaceValueFor(overlay->mKeysByScanCode.keyAt(i), + overlay->mKeysByScanCode.valueAt(i)); + } + + for (size_t i = 0; i < overlay->mKeysByUsageCode.size(); i++) { + map->mKeysByUsageCode.replaceValueFor(overlay->mKeysByUsageCode.keyAt(i), + overlay->mKeysByUsageCode.valueAt(i)); + } + return map; +} + +sp<KeyCharacterMap> KeyCharacterMap::empty() { + return sEmpty; +} + +int32_t KeyCharacterMap::getKeyboardType() const { + return mType; +} + +char16_t KeyCharacterMap::getDisplayLabel(int32_t keyCode) const { + char16_t result = 0; + const Key* key; + if (getKey(keyCode, &key)) { + result = key->label; + } +#if DEBUG_MAPPING + ALOGD("getDisplayLabel: keyCode=%d ~ Result %d.", keyCode, result); +#endif + return result; +} + +char16_t KeyCharacterMap::getNumber(int32_t keyCode) const { + char16_t result = 0; + const Key* key; + if (getKey(keyCode, &key)) { + result = key->number; + } +#if DEBUG_MAPPING + ALOGD("getNumber: keyCode=%d ~ Result %d.", keyCode, result); +#endif + return result; +} + +char16_t KeyCharacterMap::getCharacter(int32_t keyCode, int32_t metaState) const { + char16_t result = 0; + const Key* key; + const Behavior* behavior; + if (getKeyBehavior(keyCode, metaState, &key, &behavior)) { + result = behavior->character; + } +#if DEBUG_MAPPING + ALOGD("getCharacter: keyCode=%d, metaState=0x%08x ~ Result %d.", keyCode, metaState, result); +#endif + return result; +} + +bool KeyCharacterMap::getFallbackAction(int32_t keyCode, int32_t metaState, + FallbackAction* outFallbackAction) const { + outFallbackAction->keyCode = 0; + outFallbackAction->metaState = 0; + + bool result = false; + const Key* key; + const Behavior* behavior; + if (getKeyBehavior(keyCode, metaState, &key, &behavior)) { + if (behavior->fallbackKeyCode) { + outFallbackAction->keyCode = behavior->fallbackKeyCode; + outFallbackAction->metaState = metaState & ~behavior->metaState; + result = true; + } + } +#if DEBUG_MAPPING + ALOGD("getFallbackKeyCode: keyCode=%d, metaState=0x%08x ~ Result %s, " + "fallback keyCode=%d, fallback metaState=0x%08x.", + keyCode, metaState, result ? "true" : "false", + outFallbackAction->keyCode, outFallbackAction->metaState); +#endif + return result; +} + +char16_t KeyCharacterMap::getMatch(int32_t keyCode, const char16_t* chars, size_t numChars, + int32_t metaState) const { + char16_t result = 0; + const Key* key; + if (getKey(keyCode, &key)) { + // Try to find the most general behavior that maps to this character. + // For example, the base key behavior will usually be last in the list. + // However, if we find a perfect meta state match for one behavior then use that one. + for (const Behavior* behavior = key->firstBehavior; behavior; behavior = behavior->next) { + if (behavior->character) { + for (size_t i = 0; i < numChars; i++) { + if (behavior->character == chars[i]) { + result = behavior->character; + if ((behavior->metaState & metaState) == behavior->metaState) { + goto ExactMatch; + } + break; + } + } + } + } + ExactMatch: ; + } +#if DEBUG_MAPPING + ALOGD("getMatch: keyCode=%d, chars=[%s], metaState=0x%08x ~ Result %d.", + keyCode, toString(chars, numChars).string(), metaState, result); +#endif + return result; +} + +bool KeyCharacterMap::getEvents(int32_t deviceId, const char16_t* chars, size_t numChars, + Vector<KeyEvent>& outEvents) const { + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + + for (size_t i = 0; i < numChars; i++) { + int32_t keyCode, metaState; + char16_t ch = chars[i]; + if (!findKey(ch, &keyCode, &metaState)) { +#if DEBUG_MAPPING + ALOGD("getEvents: deviceId=%d, chars=[%s] ~ Failed to find mapping for character %d.", + deviceId, toString(chars, numChars).string(), ch); +#endif + return false; + } + + int32_t currentMetaState = 0; + addMetaKeys(outEvents, deviceId, metaState, true, now, ¤tMetaState); + addKey(outEvents, deviceId, keyCode, currentMetaState, true, now); + addKey(outEvents, deviceId, keyCode, currentMetaState, false, now); + addMetaKeys(outEvents, deviceId, metaState, false, now, ¤tMetaState); + } +#if DEBUG_MAPPING + ALOGD("getEvents: deviceId=%d, chars=[%s] ~ Generated %d events.", + deviceId, toString(chars, numChars).string(), int32_t(outEvents.size())); + for (size_t i = 0; i < outEvents.size(); i++) { + ALOGD(" Key: keyCode=%d, metaState=0x%08x, %s.", + outEvents[i].getKeyCode(), outEvents[i].getMetaState(), + outEvents[i].getAction() == AKEY_EVENT_ACTION_DOWN ? "down" : "up"); + } +#endif + return true; +} + +status_t KeyCharacterMap::mapKey(int32_t scanCode, int32_t usageCode, int32_t* outKeyCode) const { + if (usageCode) { + ssize_t index = mKeysByUsageCode.indexOfKey(usageCode); + if (index >= 0) { +#if DEBUG_MAPPING + ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Result keyCode=%d.", + scanCode, usageCode, *outKeyCode); +#endif + *outKeyCode = mKeysByUsageCode.valueAt(index); + return OK; + } + } + if (scanCode) { + ssize_t index = mKeysByScanCode.indexOfKey(scanCode); + if (index >= 0) { +#if DEBUG_MAPPING + ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Result keyCode=%d.", + scanCode, usageCode, *outKeyCode); +#endif + *outKeyCode = mKeysByScanCode.valueAt(index); + return OK; + } + } + +#if DEBUG_MAPPING + ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Failed.", scanCode, usageCode); +#endif + *outKeyCode = AKEYCODE_UNKNOWN; + return NAME_NOT_FOUND; +} + +bool KeyCharacterMap::getKey(int32_t keyCode, const Key** outKey) const { + ssize_t index = mKeys.indexOfKey(keyCode); + if (index >= 0) { + *outKey = mKeys.valueAt(index); + return true; + } + return false; +} + +bool KeyCharacterMap::getKeyBehavior(int32_t keyCode, int32_t metaState, + const Key** outKey, const Behavior** outBehavior) const { + const Key* key; + if (getKey(keyCode, &key)) { + const Behavior* behavior = key->firstBehavior; + while (behavior) { + if (matchesMetaState(metaState, behavior->metaState)) { + *outKey = key; + *outBehavior = behavior; + return true; + } + behavior = behavior->next; + } + } + return false; +} + +bool KeyCharacterMap::matchesMetaState(int32_t eventMetaState, int32_t behaviorMetaState) { + // Behavior must have at least the set of meta states specified. + // And if the key event has CTRL, ALT or META then the behavior must exactly + // match those, taking into account that a behavior can specify that it handles + // one, both or either of a left/right modifier pair. + if ((eventMetaState & behaviorMetaState) == behaviorMetaState) { + const int32_t EXACT_META_STATES = + AMETA_CTRL_ON | AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON + | AMETA_ALT_ON | AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON + | AMETA_META_ON | AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON; + int32_t unmatchedMetaState = eventMetaState & ~behaviorMetaState & EXACT_META_STATES; + if (behaviorMetaState & AMETA_CTRL_ON) { + unmatchedMetaState &= ~(AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON); + } else if (behaviorMetaState & (AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON)) { + unmatchedMetaState &= ~AMETA_CTRL_ON; + } + if (behaviorMetaState & AMETA_ALT_ON) { + unmatchedMetaState &= ~(AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON); + } else if (behaviorMetaState & (AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON)) { + unmatchedMetaState &= ~AMETA_ALT_ON; + } + if (behaviorMetaState & AMETA_META_ON) { + unmatchedMetaState &= ~(AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON); + } else if (behaviorMetaState & (AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON)) { + unmatchedMetaState &= ~AMETA_META_ON; + } + return !unmatchedMetaState; + } + return false; +} + +bool KeyCharacterMap::findKey(char16_t ch, int32_t* outKeyCode, int32_t* outMetaState) const { + if (!ch) { + return false; + } + + for (size_t i = 0; i < mKeys.size(); i++) { + const Key* key = mKeys.valueAt(i); + + // Try to find the most general behavior that maps to this character. + // For example, the base key behavior will usually be last in the list. + const Behavior* found = NULL; + for (const Behavior* behavior = key->firstBehavior; behavior; behavior = behavior->next) { + if (behavior->character == ch) { + found = behavior; + } + } + if (found) { + *outKeyCode = mKeys.keyAt(i); + *outMetaState = found->metaState; + return true; + } + } + return false; +} + +void KeyCharacterMap::addKey(Vector<KeyEvent>& outEvents, + int32_t deviceId, int32_t keyCode, int32_t metaState, bool down, nsecs_t time) { + outEvents.push(); + KeyEvent& event = outEvents.editTop(); + event.initialize(deviceId, AINPUT_SOURCE_KEYBOARD, + down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, + 0, keyCode, 0, metaState, 0, time, time); +} + +void KeyCharacterMap::addMetaKeys(Vector<KeyEvent>& outEvents, + int32_t deviceId, int32_t metaState, bool down, nsecs_t time, + int32_t* currentMetaState) { + // Add and remove meta keys symmetrically. + if (down) { + addLockedMetaKey(outEvents, deviceId, metaState, time, + AKEYCODE_CAPS_LOCK, AMETA_CAPS_LOCK_ON, currentMetaState); + addLockedMetaKey(outEvents, deviceId, metaState, time, + AKEYCODE_NUM_LOCK, AMETA_NUM_LOCK_ON, currentMetaState); + addLockedMetaKey(outEvents, deviceId, metaState, time, + AKEYCODE_SCROLL_LOCK, AMETA_SCROLL_LOCK_ON, currentMetaState); + + addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time, + AKEYCODE_SHIFT_LEFT, AMETA_SHIFT_LEFT_ON, + AKEYCODE_SHIFT_RIGHT, AMETA_SHIFT_RIGHT_ON, + AMETA_SHIFT_ON, currentMetaState); + addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time, + AKEYCODE_ALT_LEFT, AMETA_ALT_LEFT_ON, + AKEYCODE_ALT_RIGHT, AMETA_ALT_RIGHT_ON, + AMETA_ALT_ON, currentMetaState); + addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time, + AKEYCODE_CTRL_LEFT, AMETA_CTRL_LEFT_ON, + AKEYCODE_CTRL_RIGHT, AMETA_CTRL_RIGHT_ON, + AMETA_CTRL_ON, currentMetaState); + addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time, + AKEYCODE_META_LEFT, AMETA_META_LEFT_ON, + AKEYCODE_META_RIGHT, AMETA_META_RIGHT_ON, + AMETA_META_ON, currentMetaState); + + addSingleEphemeralMetaKey(outEvents, deviceId, metaState, true, time, + AKEYCODE_SYM, AMETA_SYM_ON, currentMetaState); + addSingleEphemeralMetaKey(outEvents, deviceId, metaState, true, time, + AKEYCODE_FUNCTION, AMETA_FUNCTION_ON, currentMetaState); + } else { + addSingleEphemeralMetaKey(outEvents, deviceId, metaState, false, time, + AKEYCODE_FUNCTION, AMETA_FUNCTION_ON, currentMetaState); + addSingleEphemeralMetaKey(outEvents, deviceId, metaState, false, time, + AKEYCODE_SYM, AMETA_SYM_ON, currentMetaState); + + addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time, + AKEYCODE_META_LEFT, AMETA_META_LEFT_ON, + AKEYCODE_META_RIGHT, AMETA_META_RIGHT_ON, + AMETA_META_ON, currentMetaState); + addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time, + AKEYCODE_CTRL_LEFT, AMETA_CTRL_LEFT_ON, + AKEYCODE_CTRL_RIGHT, AMETA_CTRL_RIGHT_ON, + AMETA_CTRL_ON, currentMetaState); + addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time, + AKEYCODE_ALT_LEFT, AMETA_ALT_LEFT_ON, + AKEYCODE_ALT_RIGHT, AMETA_ALT_RIGHT_ON, + AMETA_ALT_ON, currentMetaState); + addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time, + AKEYCODE_SHIFT_LEFT, AMETA_SHIFT_LEFT_ON, + AKEYCODE_SHIFT_RIGHT, AMETA_SHIFT_RIGHT_ON, + AMETA_SHIFT_ON, currentMetaState); + + addLockedMetaKey(outEvents, deviceId, metaState, time, + AKEYCODE_SCROLL_LOCK, AMETA_SCROLL_LOCK_ON, currentMetaState); + addLockedMetaKey(outEvents, deviceId, metaState, time, + AKEYCODE_NUM_LOCK, AMETA_NUM_LOCK_ON, currentMetaState); + addLockedMetaKey(outEvents, deviceId, metaState, time, + AKEYCODE_CAPS_LOCK, AMETA_CAPS_LOCK_ON, currentMetaState); + } +} + +bool KeyCharacterMap::addSingleEphemeralMetaKey(Vector<KeyEvent>& outEvents, + int32_t deviceId, int32_t metaState, bool down, nsecs_t time, + int32_t keyCode, int32_t keyMetaState, + int32_t* currentMetaState) { + if ((metaState & keyMetaState) == keyMetaState) { + *currentMetaState = updateMetaState(keyCode, down, *currentMetaState); + addKey(outEvents, deviceId, keyCode, *currentMetaState, down, time); + return true; + } + return false; +} + +void KeyCharacterMap::addDoubleEphemeralMetaKey(Vector<KeyEvent>& outEvents, + int32_t deviceId, int32_t metaState, bool down, nsecs_t time, + int32_t leftKeyCode, int32_t leftKeyMetaState, + int32_t rightKeyCode, int32_t rightKeyMetaState, + int32_t eitherKeyMetaState, + int32_t* currentMetaState) { + bool specific = false; + specific |= addSingleEphemeralMetaKey(outEvents, deviceId, metaState, down, time, + leftKeyCode, leftKeyMetaState, currentMetaState); + specific |= addSingleEphemeralMetaKey(outEvents, deviceId, metaState, down, time, + rightKeyCode, rightKeyMetaState, currentMetaState); + + if (!specific) { + addSingleEphemeralMetaKey(outEvents, deviceId, metaState, down, time, + leftKeyCode, eitherKeyMetaState, currentMetaState); + } +} + +void KeyCharacterMap::addLockedMetaKey(Vector<KeyEvent>& outEvents, + int32_t deviceId, int32_t metaState, nsecs_t time, + int32_t keyCode, int32_t keyMetaState, + int32_t* currentMetaState) { + if ((metaState & keyMetaState) == keyMetaState) { + *currentMetaState = updateMetaState(keyCode, true, *currentMetaState); + addKey(outEvents, deviceId, keyCode, *currentMetaState, true, time); + *currentMetaState = updateMetaState(keyCode, false, *currentMetaState); + addKey(outEvents, deviceId, keyCode, *currentMetaState, false, time); + } +} + +#if HAVE_ANDROID_OS +sp<KeyCharacterMap> KeyCharacterMap::readFromParcel(Parcel* parcel) { + sp<KeyCharacterMap> map = new KeyCharacterMap(); + map->mType = parcel->readInt32(); + size_t numKeys = parcel->readInt32(); + if (parcel->errorCheck()) { + return NULL; + } + + for (size_t i = 0; i < numKeys; i++) { + int32_t keyCode = parcel->readInt32(); + char16_t label = parcel->readInt32(); + char16_t number = parcel->readInt32(); + if (parcel->errorCheck()) { + return NULL; + } + + Key* key = new Key(); + key->label = label; + key->number = number; + map->mKeys.add(keyCode, key); + + Behavior* lastBehavior = NULL; + while (parcel->readInt32()) { + int32_t metaState = parcel->readInt32(); + char16_t character = parcel->readInt32(); + int32_t fallbackKeyCode = parcel->readInt32(); + if (parcel->errorCheck()) { + return NULL; + } + + Behavior* behavior = new Behavior(); + behavior->metaState = metaState; + behavior->character = character; + behavior->fallbackKeyCode = fallbackKeyCode; + if (lastBehavior) { + lastBehavior->next = behavior; + } else { + key->firstBehavior = behavior; + } + lastBehavior = behavior; + } + + if (parcel->errorCheck()) { + return NULL; + } + } + return map; +} + +void KeyCharacterMap::writeToParcel(Parcel* parcel) const { + parcel->writeInt32(mType); + + size_t numKeys = mKeys.size(); + parcel->writeInt32(numKeys); + for (size_t i = 0; i < numKeys; i++) { + int32_t keyCode = mKeys.keyAt(i); + const Key* key = mKeys.valueAt(i); + parcel->writeInt32(keyCode); + parcel->writeInt32(key->label); + parcel->writeInt32(key->number); + for (const Behavior* behavior = key->firstBehavior; behavior != NULL; + behavior = behavior->next) { + parcel->writeInt32(1); + parcel->writeInt32(behavior->metaState); + parcel->writeInt32(behavior->character); + parcel->writeInt32(behavior->fallbackKeyCode); + } + parcel->writeInt32(0); + } +} +#endif + + +// --- KeyCharacterMap::Key --- + +KeyCharacterMap::Key::Key() : + label(0), number(0), firstBehavior(NULL) { +} + +KeyCharacterMap::Key::Key(const Key& other) : + label(other.label), number(other.number), + firstBehavior(other.firstBehavior ? new Behavior(*other.firstBehavior) : NULL) { +} + +KeyCharacterMap::Key::~Key() { + Behavior* behavior = firstBehavior; + while (behavior) { + Behavior* next = behavior->next; + delete behavior; + behavior = next; + } +} + + +// --- KeyCharacterMap::Behavior --- + +KeyCharacterMap::Behavior::Behavior() : + next(NULL), metaState(0), character(0), fallbackKeyCode(0) { +} + +KeyCharacterMap::Behavior::Behavior(const Behavior& other) : + next(other.next ? new Behavior(*other.next) : NULL), + metaState(other.metaState), character(other.character), + fallbackKeyCode(other.fallbackKeyCode) { +} + + +// --- KeyCharacterMap::Parser --- + +KeyCharacterMap::Parser::Parser(KeyCharacterMap* map, Tokenizer* tokenizer, Format format) : + mMap(map), mTokenizer(tokenizer), mFormat(format), mState(STATE_TOP) { +} + +KeyCharacterMap::Parser::~Parser() { +} + +status_t KeyCharacterMap::Parser::parse() { + while (!mTokenizer->isEof()) { +#if DEBUG_PARSER + ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(), + mTokenizer->peekRemainderOfLine().string()); +#endif + + mTokenizer->skipDelimiters(WHITESPACE); + + if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') { + switch (mState) { + case STATE_TOP: { + String8 keywordToken = mTokenizer->nextToken(WHITESPACE); + if (keywordToken == "type") { + mTokenizer->skipDelimiters(WHITESPACE); + status_t status = parseType(); + if (status) return status; + } else if (keywordToken == "map") { + mTokenizer->skipDelimiters(WHITESPACE); + status_t status = parseMap(); + if (status) return status; + } else if (keywordToken == "key") { + mTokenizer->skipDelimiters(WHITESPACE); + status_t status = parseKey(); + if (status) return status; + } else { + ALOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().string(), + keywordToken.string()); + return BAD_VALUE; + } + break; + } + + case STATE_KEY: { + status_t status = parseKeyProperty(); + if (status) return status; + break; + } + } + + mTokenizer->skipDelimiters(WHITESPACE); + if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') { + ALOGE("%s: Expected end of line or trailing comment, got '%s'.", + mTokenizer->getLocation().string(), + mTokenizer->peekRemainderOfLine().string()); + return BAD_VALUE; + } + } + + mTokenizer->nextLine(); + } + + if (mState != STATE_TOP) { + ALOGE("%s: Unterminated key description at end of file.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + + if (mMap->mType == KEYBOARD_TYPE_UNKNOWN) { + ALOGE("%s: Keyboard layout missing required keyboard 'type' declaration.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + + if (mFormat == FORMAT_BASE) { + if (mMap->mType == KEYBOARD_TYPE_OVERLAY) { + ALOGE("%s: Base keyboard layout must specify a keyboard 'type' other than 'OVERLAY'.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + } else if (mFormat == FORMAT_OVERLAY) { + if (mMap->mType != KEYBOARD_TYPE_OVERLAY) { + ALOGE("%s: Overlay keyboard layout missing required keyboard " + "'type OVERLAY' declaration.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + } + + return NO_ERROR; +} + +status_t KeyCharacterMap::Parser::parseType() { + if (mMap->mType != KEYBOARD_TYPE_UNKNOWN) { + ALOGE("%s: Duplicate keyboard 'type' declaration.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + + KeyboardType type; + String8 typeToken = mTokenizer->nextToken(WHITESPACE); + if (typeToken == "NUMERIC") { + type = KEYBOARD_TYPE_NUMERIC; + } else if (typeToken == "PREDICTIVE") { + type = KEYBOARD_TYPE_PREDICTIVE; + } else if (typeToken == "ALPHA") { + type = KEYBOARD_TYPE_ALPHA; + } else if (typeToken == "FULL") { + type = KEYBOARD_TYPE_FULL; + } else if (typeToken == "SPECIAL_FUNCTION") { + type = KEYBOARD_TYPE_SPECIAL_FUNCTION; + } else if (typeToken == "OVERLAY") { + type = KEYBOARD_TYPE_OVERLAY; + } else { + ALOGE("%s: Expected keyboard type label, got '%s'.", mTokenizer->getLocation().string(), + typeToken.string()); + return BAD_VALUE; + } + +#if DEBUG_PARSER + ALOGD("Parsed type: type=%d.", type); +#endif + mMap->mType = type; + return NO_ERROR; +} + +status_t KeyCharacterMap::Parser::parseMap() { + String8 keywordToken = mTokenizer->nextToken(WHITESPACE); + if (keywordToken == "key") { + mTokenizer->skipDelimiters(WHITESPACE); + return parseMapKey(); + } + ALOGE("%s: Expected keyword after 'map', got '%s'.", mTokenizer->getLocation().string(), + keywordToken.string()); + return BAD_VALUE; +} + +status_t KeyCharacterMap::Parser::parseMapKey() { + String8 codeToken = mTokenizer->nextToken(WHITESPACE); + bool mapUsage = false; + if (codeToken == "usage") { + mapUsage = true; + mTokenizer->skipDelimiters(WHITESPACE); + codeToken = mTokenizer->nextToken(WHITESPACE); + } + + char* end; + int32_t code = int32_t(strtol(codeToken.string(), &end, 0)); + if (*end) { + ALOGE("%s: Expected key %s number, got '%s'.", mTokenizer->getLocation().string(), + mapUsage ? "usage" : "scan code", codeToken.string()); + return BAD_VALUE; + } + KeyedVector<int32_t, int32_t>& map = + mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode; + if (map.indexOfKey(code) >= 0) { + ALOGE("%s: Duplicate entry for key %s '%s'.", mTokenizer->getLocation().string(), + mapUsage ? "usage" : "scan code", codeToken.string()); + return BAD_VALUE; + } + + mTokenizer->skipDelimiters(WHITESPACE); + String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE); + int32_t keyCode = getKeyCodeByLabel(keyCodeToken.string()); + if (!keyCode) { + ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(), + keyCodeToken.string()); + return BAD_VALUE; + } + +#if DEBUG_PARSER + ALOGD("Parsed map key %s: code=%d, keyCode=%d.", + mapUsage ? "usage" : "scan code", code, keyCode); +#endif + map.add(code, keyCode); + return NO_ERROR; +} + +status_t KeyCharacterMap::Parser::parseKey() { + String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE); + int32_t keyCode = getKeyCodeByLabel(keyCodeToken.string()); + if (!keyCode) { + ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(), + keyCodeToken.string()); + return BAD_VALUE; + } + if (mMap->mKeys.indexOfKey(keyCode) >= 0) { + ALOGE("%s: Duplicate entry for key code '%s'.", mTokenizer->getLocation().string(), + keyCodeToken.string()); + return BAD_VALUE; + } + + mTokenizer->skipDelimiters(WHITESPACE); + String8 openBraceToken = mTokenizer->nextToken(WHITESPACE); + if (openBraceToken != "{") { + ALOGE("%s: Expected '{' after key code label, got '%s'.", + mTokenizer->getLocation().string(), openBraceToken.string()); + return BAD_VALUE; + } + +#if DEBUG_PARSER + ALOGD("Parsed beginning of key: keyCode=%d.", keyCode); +#endif + mKeyCode = keyCode; + mMap->mKeys.add(keyCode, new Key()); + mState = STATE_KEY; + return NO_ERROR; +} + +status_t KeyCharacterMap::Parser::parseKeyProperty() { + Key* key = mMap->mKeys.valueFor(mKeyCode); + String8 token = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER); + if (token == "}") { + mState = STATE_TOP; + return finishKey(key); + } + + Vector<Property> properties; + + // Parse all comma-delimited property names up to the first colon. + for (;;) { + if (token == "label") { + properties.add(Property(PROPERTY_LABEL)); + } else if (token == "number") { + properties.add(Property(PROPERTY_NUMBER)); + } else { + int32_t metaState; + status_t status = parseModifier(token, &metaState); + if (status) { + ALOGE("%s: Expected a property name or modifier, got '%s'.", + mTokenizer->getLocation().string(), token.string()); + return status; + } + properties.add(Property(PROPERTY_META, metaState)); + } + + mTokenizer->skipDelimiters(WHITESPACE); + if (!mTokenizer->isEol()) { + char ch = mTokenizer->nextChar(); + if (ch == ':') { + break; + } else if (ch == ',') { + mTokenizer->skipDelimiters(WHITESPACE); + token = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER); + continue; + } + } + + ALOGE("%s: Expected ',' or ':' after property name.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + + // Parse behavior after the colon. + mTokenizer->skipDelimiters(WHITESPACE); + + Behavior behavior; + bool haveCharacter = false; + bool haveFallback = false; + + do { + char ch = mTokenizer->peekChar(); + if (ch == '\'') { + char16_t character; + status_t status = parseCharacterLiteral(&character); + if (status || !character) { + ALOGE("%s: Invalid character literal for key.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + if (haveCharacter) { + ALOGE("%s: Cannot combine multiple character literals or 'none'.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + behavior.character = character; + haveCharacter = true; + } else { + token = mTokenizer->nextToken(WHITESPACE); + if (token == "none") { + if (haveCharacter) { + ALOGE("%s: Cannot combine multiple character literals or 'none'.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + haveCharacter = true; + } else if (token == "fallback") { + mTokenizer->skipDelimiters(WHITESPACE); + token = mTokenizer->nextToken(WHITESPACE); + int32_t keyCode = getKeyCodeByLabel(token.string()); + if (!keyCode) { + ALOGE("%s: Invalid key code label for fallback behavior, got '%s'.", + mTokenizer->getLocation().string(), + token.string()); + return BAD_VALUE; + } + if (haveFallback) { + ALOGE("%s: Cannot combine multiple fallback key codes.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + behavior.fallbackKeyCode = keyCode; + haveFallback = true; + } else { + ALOGE("%s: Expected a key behavior after ':'.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + } + + mTokenizer->skipDelimiters(WHITESPACE); + } while (!mTokenizer->isEol() && mTokenizer->peekChar() != '#'); + + // Add the behavior. + for (size_t i = 0; i < properties.size(); i++) { + const Property& property = properties.itemAt(i); + switch (property.property) { + case PROPERTY_LABEL: + if (key->label) { + ALOGE("%s: Duplicate label for key.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + key->label = behavior.character; +#if DEBUG_PARSER + ALOGD("Parsed key label: keyCode=%d, label=%d.", mKeyCode, key->label); +#endif + break; + case PROPERTY_NUMBER: + if (key->number) { + ALOGE("%s: Duplicate number for key.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + key->number = behavior.character; +#if DEBUG_PARSER + ALOGD("Parsed key number: keyCode=%d, number=%d.", mKeyCode, key->number); +#endif + break; + case PROPERTY_META: { + for (Behavior* b = key->firstBehavior; b; b = b->next) { + if (b->metaState == property.metaState) { + ALOGE("%s: Duplicate key behavior for modifier.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + } + Behavior* newBehavior = new Behavior(behavior); + newBehavior->metaState = property.metaState; + newBehavior->next = key->firstBehavior; + key->firstBehavior = newBehavior; +#if DEBUG_PARSER + ALOGD("Parsed key meta: keyCode=%d, meta=0x%x, char=%d, fallback=%d.", mKeyCode, + newBehavior->metaState, newBehavior->character, newBehavior->fallbackKeyCode); +#endif + break; + } + } + } + return NO_ERROR; +} + +status_t KeyCharacterMap::Parser::finishKey(Key* key) { + // Fill in default number property. + if (!key->number) { + char16_t digit = 0; + char16_t symbol = 0; + for (Behavior* b = key->firstBehavior; b; b = b->next) { + char16_t ch = b->character; + if (ch) { + if (ch >= '0' && ch <= '9') { + digit = ch; + } else if (ch == '(' || ch == ')' || ch == '#' || ch == '*' + || ch == '-' || ch == '+' || ch == ',' || ch == '.' + || ch == '\'' || ch == ':' || ch == ';' || ch == '/') { + symbol = ch; + } + } + } + key->number = digit ? digit : symbol; + } + return NO_ERROR; +} + +status_t KeyCharacterMap::Parser::parseModifier(const String8& token, int32_t* outMetaState) { + if (token == "base") { + *outMetaState = 0; + return NO_ERROR; + } + + int32_t combinedMeta = 0; + + const char* str = token.string(); + const char* start = str; + for (const char* cur = str; ; cur++) { + char ch = *cur; + if (ch == '+' || ch == '\0') { + size_t len = cur - start; + int32_t metaState = 0; + for (size_t i = 0; i < sizeof(modifiers) / sizeof(Modifier); i++) { + if (strlen(modifiers[i].label) == len + && strncmp(modifiers[i].label, start, len) == 0) { + metaState = modifiers[i].metaState; + break; + } + } + if (!metaState) { + return BAD_VALUE; + } + if (combinedMeta & metaState) { + ALOGE("%s: Duplicate modifier combination '%s'.", + mTokenizer->getLocation().string(), token.string()); + return BAD_VALUE; + } + + combinedMeta |= metaState; + start = cur + 1; + + if (ch == '\0') { + break; + } + } + } + *outMetaState = combinedMeta; + return NO_ERROR; +} + +status_t KeyCharacterMap::Parser::parseCharacterLiteral(char16_t* outCharacter) { + char ch = mTokenizer->nextChar(); + if (ch != '\'') { + goto Error; + } + + ch = mTokenizer->nextChar(); + if (ch == '\\') { + // Escape sequence. + ch = mTokenizer->nextChar(); + if (ch == 'n') { + *outCharacter = '\n'; + } else if (ch == 't') { + *outCharacter = '\t'; + } else if (ch == '\\') { + *outCharacter = '\\'; + } else if (ch == '\'') { + *outCharacter = '\''; + } else if (ch == '"') { + *outCharacter = '"'; + } else if (ch == 'u') { + *outCharacter = 0; + for (int i = 0; i < 4; i++) { + ch = mTokenizer->nextChar(); + int digit; + if (ch >= '0' && ch <= '9') { + digit = ch - '0'; + } else if (ch >= 'A' && ch <= 'F') { + digit = ch - 'A' + 10; + } else if (ch >= 'a' && ch <= 'f') { + digit = ch - 'a' + 10; + } else { + goto Error; + } + *outCharacter = (*outCharacter << 4) | digit; + } + } else { + goto Error; + } + } else if (ch >= 32 && ch <= 126 && ch != '\'') { + // ASCII literal character. + *outCharacter = ch; + } else { + goto Error; + } + + ch = mTokenizer->nextChar(); + if (ch != '\'') { + goto Error; + } + + // Ensure that we consumed the entire token. + if (mTokenizer->nextToken(WHITESPACE).isEmpty()) { + return NO_ERROR; + } + +Error: + ALOGE("%s: Malformed character literal.", mTokenizer->getLocation().string()); + return BAD_VALUE; +} + +} // namespace android diff --git a/libs/input/KeyLayoutMap.cpp b/libs/input/KeyLayoutMap.cpp new file mode 100644 index 0000000..2f5494b --- /dev/null +++ b/libs/input/KeyLayoutMap.cpp @@ -0,0 +1,367 @@ +/* + * 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. + */ + +#define LOG_TAG "KeyLayoutMap" + +#include <stdlib.h> + +#include <android/keycodes.h> +#include <input/Keyboard.h> +#include <input/KeyLayoutMap.h> +#include <utils/Log.h> +#include <utils/Errors.h> +#include <utils/Tokenizer.h> +#include <utils/Timers.h> + +// Enables debug output for the parser. +#define DEBUG_PARSER 0 + +// Enables debug output for parser performance. +#define DEBUG_PARSER_PERFORMANCE 0 + +// Enables debug output for mapping. +#define DEBUG_MAPPING 0 + + +namespace android { + +static const char* WHITESPACE = " \t\r"; + +// --- KeyLayoutMap --- + +KeyLayoutMap::KeyLayoutMap() { +} + +KeyLayoutMap::~KeyLayoutMap() { +} + +status_t KeyLayoutMap::load(const String8& filename, sp<KeyLayoutMap>* outMap) { + outMap->clear(); + + Tokenizer* tokenizer; + status_t status = Tokenizer::open(filename, &tokenizer); + if (status) { + ALOGE("Error %d opening key layout map file %s.", status, filename.string()); + } else { + sp<KeyLayoutMap> map = new KeyLayoutMap(); + if (!map.get()) { + ALOGE("Error allocating key layout map."); + status = NO_MEMORY; + } else { +#if DEBUG_PARSER_PERFORMANCE + nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); +#endif + Parser parser(map.get(), tokenizer); + status = parser.parse(); +#if DEBUG_PARSER_PERFORMANCE + nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; + ALOGD("Parsed key layout map file '%s' %d lines in %0.3fms.", + tokenizer->getFilename().string(), tokenizer->getLineNumber(), + elapsedTime / 1000000.0); +#endif + if (!status) { + *outMap = map; + } + } + delete tokenizer; + } + return status; +} + +status_t KeyLayoutMap::mapKey(int32_t scanCode, int32_t usageCode, + int32_t* outKeyCode, uint32_t* outFlags) const { + const Key* key = getKey(scanCode, usageCode); + if (!key) { +#if DEBUG_MAPPING + ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Failed.", scanCode, usageCode); +#endif + *outKeyCode = AKEYCODE_UNKNOWN; + *outFlags = 0; + return NAME_NOT_FOUND; + } + + *outKeyCode = key->keyCode; + *outFlags = key->flags; + +#if DEBUG_MAPPING + ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Result keyCode=%d, outFlags=0x%08x.", + scanCode, usageCode, *outKeyCode, *outFlags); +#endif + return NO_ERROR; +} + +const KeyLayoutMap::Key* KeyLayoutMap::getKey(int32_t scanCode, int32_t usageCode) const { + if (usageCode) { + ssize_t index = mKeysByUsageCode.indexOfKey(usageCode); + if (index >= 0) { + return &mKeysByUsageCode.valueAt(index); + } + } + if (scanCode) { + ssize_t index = mKeysByScanCode.indexOfKey(scanCode); + if (index >= 0) { + return &mKeysByScanCode.valueAt(index); + } + } + return NULL; +} + +status_t KeyLayoutMap::findScanCodesForKey(int32_t keyCode, Vector<int32_t>* outScanCodes) const { + const size_t N = mKeysByScanCode.size(); + for (size_t i=0; i<N; i++) { + if (mKeysByScanCode.valueAt(i).keyCode == keyCode) { + outScanCodes->add(mKeysByScanCode.keyAt(i)); + } + } + return NO_ERROR; +} + +status_t KeyLayoutMap::mapAxis(int32_t scanCode, AxisInfo* outAxisInfo) const { + ssize_t index = mAxes.indexOfKey(scanCode); + if (index < 0) { +#if DEBUG_MAPPING + ALOGD("mapAxis: scanCode=%d ~ Failed.", scanCode); +#endif + return NAME_NOT_FOUND; + } + + *outAxisInfo = mAxes.valueAt(index); + +#if DEBUG_MAPPING + ALOGD("mapAxis: scanCode=%d ~ Result mode=%d, axis=%d, highAxis=%d, " + "splitValue=%d, flatOverride=%d.", + scanCode, + outAxisInfo->mode, outAxisInfo->axis, outAxisInfo->highAxis, + outAxisInfo->splitValue, outAxisInfo->flatOverride); +#endif + return NO_ERROR; +} + + +// --- KeyLayoutMap::Parser --- + +KeyLayoutMap::Parser::Parser(KeyLayoutMap* map, Tokenizer* tokenizer) : + mMap(map), mTokenizer(tokenizer) { +} + +KeyLayoutMap::Parser::~Parser() { +} + +status_t KeyLayoutMap::Parser::parse() { + while (!mTokenizer->isEof()) { +#if DEBUG_PARSER + ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(), + mTokenizer->peekRemainderOfLine().string()); +#endif + + mTokenizer->skipDelimiters(WHITESPACE); + + if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') { + String8 keywordToken = mTokenizer->nextToken(WHITESPACE); + if (keywordToken == "key") { + mTokenizer->skipDelimiters(WHITESPACE); + status_t status = parseKey(); + if (status) return status; + } else if (keywordToken == "axis") { + mTokenizer->skipDelimiters(WHITESPACE); + status_t status = parseAxis(); + if (status) return status; + } else { + ALOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().string(), + keywordToken.string()); + return BAD_VALUE; + } + + mTokenizer->skipDelimiters(WHITESPACE); + if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') { + ALOGE("%s: Expected end of line or trailing comment, got '%s'.", + mTokenizer->getLocation().string(), + mTokenizer->peekRemainderOfLine().string()); + return BAD_VALUE; + } + } + + mTokenizer->nextLine(); + } + return NO_ERROR; +} + +status_t KeyLayoutMap::Parser::parseKey() { + String8 codeToken = mTokenizer->nextToken(WHITESPACE); + bool mapUsage = false; + if (codeToken == "usage") { + mapUsage = true; + mTokenizer->skipDelimiters(WHITESPACE); + codeToken = mTokenizer->nextToken(WHITESPACE); + } + + char* end; + int32_t code = int32_t(strtol(codeToken.string(), &end, 0)); + if (*end) { + ALOGE("%s: Expected key %s number, got '%s'.", mTokenizer->getLocation().string(), + mapUsage ? "usage" : "scan code", codeToken.string()); + return BAD_VALUE; + } + KeyedVector<int32_t, Key>& map = + mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode; + if (map.indexOfKey(code) >= 0) { + ALOGE("%s: Duplicate entry for key %s '%s'.", mTokenizer->getLocation().string(), + mapUsage ? "usage" : "scan code", codeToken.string()); + return BAD_VALUE; + } + + mTokenizer->skipDelimiters(WHITESPACE); + String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE); + int32_t keyCode = getKeyCodeByLabel(keyCodeToken.string()); + if (!keyCode) { + ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(), + keyCodeToken.string()); + return BAD_VALUE; + } + + uint32_t flags = 0; + for (;;) { + mTokenizer->skipDelimiters(WHITESPACE); + if (mTokenizer->isEol() || mTokenizer->peekChar() == '#') break; + + String8 flagToken = mTokenizer->nextToken(WHITESPACE); + uint32_t flag = getKeyFlagByLabel(flagToken.string()); + if (!flag) { + ALOGE("%s: Expected key flag label, got '%s'.", mTokenizer->getLocation().string(), + flagToken.string()); + return BAD_VALUE; + } + if (flags & flag) { + ALOGE("%s: Duplicate key flag '%s'.", mTokenizer->getLocation().string(), + flagToken.string()); + return BAD_VALUE; + } + flags |= flag; + } + +#if DEBUG_PARSER + ALOGD("Parsed key %s: code=%d, keyCode=%d, flags=0x%08x.", + mapUsage ? "usage" : "scan code", code, keyCode, flags); +#endif + Key key; + key.keyCode = keyCode; + key.flags = flags; + map.add(code, key); + return NO_ERROR; +} + +status_t KeyLayoutMap::Parser::parseAxis() { + String8 scanCodeToken = mTokenizer->nextToken(WHITESPACE); + char* end; + int32_t scanCode = int32_t(strtol(scanCodeToken.string(), &end, 0)); + if (*end) { + ALOGE("%s: Expected axis scan code number, got '%s'.", mTokenizer->getLocation().string(), + scanCodeToken.string()); + return BAD_VALUE; + } + if (mMap->mAxes.indexOfKey(scanCode) >= 0) { + ALOGE("%s: Duplicate entry for axis scan code '%s'.", mTokenizer->getLocation().string(), + scanCodeToken.string()); + return BAD_VALUE; + } + + AxisInfo axisInfo; + + mTokenizer->skipDelimiters(WHITESPACE); + String8 token = mTokenizer->nextToken(WHITESPACE); + if (token == "invert") { + axisInfo.mode = AxisInfo::MODE_INVERT; + + mTokenizer->skipDelimiters(WHITESPACE); + String8 axisToken = mTokenizer->nextToken(WHITESPACE); + axisInfo.axis = getAxisByLabel(axisToken.string()); + if (axisInfo.axis < 0) { + ALOGE("%s: Expected inverted axis label, got '%s'.", + mTokenizer->getLocation().string(), axisToken.string()); + return BAD_VALUE; + } + } else if (token == "split") { + axisInfo.mode = AxisInfo::MODE_SPLIT; + + mTokenizer->skipDelimiters(WHITESPACE); + String8 splitToken = mTokenizer->nextToken(WHITESPACE); + axisInfo.splitValue = int32_t(strtol(splitToken.string(), &end, 0)); + if (*end) { + ALOGE("%s: Expected split value, got '%s'.", + mTokenizer->getLocation().string(), splitToken.string()); + return BAD_VALUE; + } + + mTokenizer->skipDelimiters(WHITESPACE); + String8 lowAxisToken = mTokenizer->nextToken(WHITESPACE); + axisInfo.axis = getAxisByLabel(lowAxisToken.string()); + if (axisInfo.axis < 0) { + ALOGE("%s: Expected low axis label, got '%s'.", + mTokenizer->getLocation().string(), lowAxisToken.string()); + return BAD_VALUE; + } + + mTokenizer->skipDelimiters(WHITESPACE); + String8 highAxisToken = mTokenizer->nextToken(WHITESPACE); + axisInfo.highAxis = getAxisByLabel(highAxisToken.string()); + if (axisInfo.highAxis < 0) { + ALOGE("%s: Expected high axis label, got '%s'.", + mTokenizer->getLocation().string(), highAxisToken.string()); + return BAD_VALUE; + } + } else { + axisInfo.axis = getAxisByLabel(token.string()); + if (axisInfo.axis < 0) { + ALOGE("%s: Expected axis label, 'split' or 'invert', got '%s'.", + mTokenizer->getLocation().string(), token.string()); + return BAD_VALUE; + } + } + + for (;;) { + mTokenizer->skipDelimiters(WHITESPACE); + if (mTokenizer->isEol() || mTokenizer->peekChar() == '#') { + break; + } + String8 keywordToken = mTokenizer->nextToken(WHITESPACE); + if (keywordToken == "flat") { + mTokenizer->skipDelimiters(WHITESPACE); + String8 flatToken = mTokenizer->nextToken(WHITESPACE); + axisInfo.flatOverride = int32_t(strtol(flatToken.string(), &end, 0)); + if (*end) { + ALOGE("%s: Expected flat value, got '%s'.", + mTokenizer->getLocation().string(), flatToken.string()); + return BAD_VALUE; + } + } else { + ALOGE("%s: Expected keyword 'flat', got '%s'.", + mTokenizer->getLocation().string(), keywordToken.string()); + return BAD_VALUE; + } + } + +#if DEBUG_PARSER + ALOGD("Parsed axis: scanCode=%d, mode=%d, axis=%d, highAxis=%d, " + "splitValue=%d, flatOverride=%d.", + scanCode, + axisInfo.mode, axisInfo.axis, axisInfo.highAxis, + axisInfo.splitValue, axisInfo.flatOverride); +#endif + mMap->mAxes.add(scanCode, axisInfo); + return NO_ERROR; +} + +}; diff --git a/libs/input/Keyboard.cpp b/libs/input/Keyboard.cpp new file mode 100644 index 0000000..b6551ee --- /dev/null +++ b/libs/input/Keyboard.cpp @@ -0,0 +1,297 @@ +/* + * 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. + */ + +#define LOG_TAG "Keyboard" + +#include <stdlib.h> +#include <unistd.h> +#include <limits.h> + +#include <input/Keyboard.h> +#include <input/KeycodeLabels.h> +#include <input/KeyLayoutMap.h> +#include <input/KeyCharacterMap.h> +#include <input/InputDevice.h> +#include <utils/Errors.h> +#include <utils/Log.h> + +namespace android { + +// --- KeyMap --- + +KeyMap::KeyMap() { +} + +KeyMap::~KeyMap() { +} + +status_t KeyMap::load(const InputDeviceIdentifier& deviceIdenfifier, + const PropertyMap* deviceConfiguration) { + // Use the configured key layout if available. + if (deviceConfiguration) { + String8 keyLayoutName; + if (deviceConfiguration->tryGetProperty(String8("keyboard.layout"), + keyLayoutName)) { + status_t status = loadKeyLayout(deviceIdenfifier, keyLayoutName); + if (status == NAME_NOT_FOUND) { + ALOGE("Configuration for keyboard device '%s' requested keyboard layout '%s' but " + "it was not found.", + deviceIdenfifier.name.string(), keyLayoutName.string()); + } + } + + String8 keyCharacterMapName; + if (deviceConfiguration->tryGetProperty(String8("keyboard.characterMap"), + keyCharacterMapName)) { + status_t status = loadKeyCharacterMap(deviceIdenfifier, keyCharacterMapName); + if (status == NAME_NOT_FOUND) { + ALOGE("Configuration for keyboard device '%s' requested keyboard character " + "map '%s' but it was not found.", + deviceIdenfifier.name.string(), keyLayoutName.string()); + } + } + + if (isComplete()) { + return OK; + } + } + + // Try searching by device identifier. + if (probeKeyMap(deviceIdenfifier, String8::empty())) { + return OK; + } + + // Fall back on the Generic key map. + // TODO Apply some additional heuristics here to figure out what kind of + // generic key map to use (US English, etc.) for typical external keyboards. + if (probeKeyMap(deviceIdenfifier, String8("Generic"))) { + return OK; + } + + // Try the Virtual key map as a last resort. + if (probeKeyMap(deviceIdenfifier, String8("Virtual"))) { + return OK; + } + + // Give up! + ALOGE("Could not determine key map for device '%s' and no default key maps were found!", + deviceIdenfifier.name.string()); + return NAME_NOT_FOUND; +} + +bool KeyMap::probeKeyMap(const InputDeviceIdentifier& deviceIdentifier, + const String8& keyMapName) { + if (!haveKeyLayout()) { + loadKeyLayout(deviceIdentifier, keyMapName); + } + if (!haveKeyCharacterMap()) { + loadKeyCharacterMap(deviceIdentifier, keyMapName); + } + return isComplete(); +} + +status_t KeyMap::loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier, + const String8& name) { + String8 path(getPath(deviceIdentifier, name, + INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT)); + if (path.isEmpty()) { + return NAME_NOT_FOUND; + } + + status_t status = KeyLayoutMap::load(path, &keyLayoutMap); + if (status) { + return status; + } + + keyLayoutFile.setTo(path); + return OK; +} + +status_t KeyMap::loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifier, + const String8& name) { + String8 path(getPath(deviceIdentifier, name, + INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP)); + if (path.isEmpty()) { + return NAME_NOT_FOUND; + } + + status_t status = KeyCharacterMap::load(path, + KeyCharacterMap::FORMAT_BASE, &keyCharacterMap); + if (status) { + return status; + } + + keyCharacterMapFile.setTo(path); + return OK; +} + +String8 KeyMap::getPath(const InputDeviceIdentifier& deviceIdentifier, + const String8& name, InputDeviceConfigurationFileType type) { + return name.isEmpty() + ? getInputDeviceConfigurationFilePathByDeviceIdentifier(deviceIdentifier, type) + : getInputDeviceConfigurationFilePathByName(name, type); +} + + +// --- Global functions --- + +bool isEligibleBuiltInKeyboard(const InputDeviceIdentifier& deviceIdentifier, + const PropertyMap* deviceConfiguration, const KeyMap* keyMap) { + if (!keyMap->haveKeyCharacterMap() + || keyMap->keyCharacterMap->getKeyboardType() + == KeyCharacterMap::KEYBOARD_TYPE_SPECIAL_FUNCTION) { + return false; + } + + if (deviceConfiguration) { + bool builtIn = false; + if (deviceConfiguration->tryGetProperty(String8("keyboard.builtIn"), builtIn) + && builtIn) { + return true; + } + } + + return strstr(deviceIdentifier.name.string(), "-keypad"); +} + +static int lookupValueByLabel(const char* literal, const KeycodeLabel *list) { + while (list->literal) { + if (strcmp(literal, list->literal) == 0) { + return list->value; + } + list++; + } + return list->value; +} + +static const char* lookupLabelByValue(int value, const KeycodeLabel *list) { + while (list->literal) { + if (list->value == value) { + return list->literal; + } + list++; + } + return NULL; +} + +int32_t getKeyCodeByLabel(const char* label) { + return int32_t(lookupValueByLabel(label, KEYCODES)); +} + +uint32_t getKeyFlagByLabel(const char* label) { + return uint32_t(lookupValueByLabel(label, FLAGS)); +} + +int32_t getAxisByLabel(const char* label) { + return int32_t(lookupValueByLabel(label, AXES)); +} + +const char* getAxisLabel(int32_t axisId) { + return lookupLabelByValue(axisId, AXES); +} + +static int32_t setEphemeralMetaState(int32_t mask, bool down, int32_t oldMetaState) { + int32_t newMetaState; + if (down) { + newMetaState = oldMetaState | mask; + } else { + newMetaState = oldMetaState & + ~(mask | AMETA_ALT_ON | AMETA_SHIFT_ON | AMETA_CTRL_ON | AMETA_META_ON); + } + + if (newMetaState & (AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON)) { + newMetaState |= AMETA_ALT_ON; + } + + if (newMetaState & (AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON)) { + newMetaState |= AMETA_SHIFT_ON; + } + + if (newMetaState & (AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON)) { + newMetaState |= AMETA_CTRL_ON; + } + + if (newMetaState & (AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON)) { + newMetaState |= AMETA_META_ON; + } + return newMetaState; +} + +static int32_t toggleLockedMetaState(int32_t mask, bool down, int32_t oldMetaState) { + if (down) { + return oldMetaState; + } else { + return oldMetaState ^ mask; + } +} + +int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState) { + int32_t mask; + switch (keyCode) { + case AKEYCODE_ALT_LEFT: + return setEphemeralMetaState(AMETA_ALT_LEFT_ON, down, oldMetaState); + case AKEYCODE_ALT_RIGHT: + return setEphemeralMetaState(AMETA_ALT_RIGHT_ON, down, oldMetaState); + case AKEYCODE_SHIFT_LEFT: + return setEphemeralMetaState(AMETA_SHIFT_LEFT_ON, down, oldMetaState); + case AKEYCODE_SHIFT_RIGHT: + return setEphemeralMetaState(AMETA_SHIFT_RIGHT_ON, down, oldMetaState); + case AKEYCODE_SYM: + return setEphemeralMetaState(AMETA_SYM_ON, down, oldMetaState); + case AKEYCODE_FUNCTION: + return setEphemeralMetaState(AMETA_FUNCTION_ON, down, oldMetaState); + case AKEYCODE_CTRL_LEFT: + return setEphemeralMetaState(AMETA_CTRL_LEFT_ON, down, oldMetaState); + case AKEYCODE_CTRL_RIGHT: + return setEphemeralMetaState(AMETA_CTRL_RIGHT_ON, down, oldMetaState); + case AKEYCODE_META_LEFT: + return setEphemeralMetaState(AMETA_META_LEFT_ON, down, oldMetaState); + case AKEYCODE_META_RIGHT: + return setEphemeralMetaState(AMETA_META_RIGHT_ON, down, oldMetaState); + case AKEYCODE_CAPS_LOCK: + return toggleLockedMetaState(AMETA_CAPS_LOCK_ON, down, oldMetaState); + case AKEYCODE_NUM_LOCK: + return toggleLockedMetaState(AMETA_NUM_LOCK_ON, down, oldMetaState); + case AKEYCODE_SCROLL_LOCK: + return toggleLockedMetaState(AMETA_SCROLL_LOCK_ON, down, oldMetaState); + default: + return oldMetaState; + } +} + +bool isMetaKey(int32_t keyCode) { + switch (keyCode) { + case AKEYCODE_ALT_LEFT: + case AKEYCODE_ALT_RIGHT: + case AKEYCODE_SHIFT_LEFT: + case AKEYCODE_SHIFT_RIGHT: + case AKEYCODE_SYM: + case AKEYCODE_FUNCTION: + case AKEYCODE_CTRL_LEFT: + case AKEYCODE_CTRL_RIGHT: + case AKEYCODE_META_LEFT: + case AKEYCODE_META_RIGHT: + case AKEYCODE_CAPS_LOCK: + case AKEYCODE_NUM_LOCK: + case AKEYCODE_SCROLL_LOCK: + return true; + default: + return false; + } +} + + +} // namespace android diff --git a/libs/input/VelocityControl.cpp b/libs/input/VelocityControl.cpp new file mode 100644 index 0000000..bcf55b0 --- /dev/null +++ b/libs/input/VelocityControl.cpp @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "VelocityControl" +//#define LOG_NDEBUG 0 + +// Log debug messages about acceleration. +#define DEBUG_ACCELERATION 0 + +#include <math.h> +#include <limits.h> + +#include <input/VelocityControl.h> +#include <utils/BitSet.h> +#include <utils/Timers.h> + +namespace android { + +// --- VelocityControl --- + +const nsecs_t VelocityControl::STOP_TIME; + +VelocityControl::VelocityControl() { + reset(); +} + +void VelocityControl::setParameters(const VelocityControlParameters& parameters) { + mParameters = parameters; + reset(); +} + +void VelocityControl::reset() { + mLastMovementTime = LLONG_MIN; + mRawPosition.x = 0; + mRawPosition.y = 0; + mVelocityTracker.clear(); +} + +void VelocityControl::move(nsecs_t eventTime, float* deltaX, float* deltaY) { + if ((deltaX && *deltaX) || (deltaY && *deltaY)) { + if (eventTime >= mLastMovementTime + STOP_TIME) { +#if DEBUG_ACCELERATION + ALOGD("VelocityControl: stopped, last movement was %0.3fms ago", + (eventTime - mLastMovementTime) * 0.000001f); +#endif + reset(); + } + + mLastMovementTime = eventTime; + if (deltaX) { + mRawPosition.x += *deltaX; + } + if (deltaY) { + mRawPosition.y += *deltaY; + } + mVelocityTracker.addMovement(eventTime, BitSet32(BitSet32::valueForBit(0)), &mRawPosition); + + float vx, vy; + float scale = mParameters.scale; + if (mVelocityTracker.getVelocity(0, &vx, &vy)) { + float speed = hypotf(vx, vy) * scale; + if (speed >= mParameters.highThreshold) { + // Apply full acceleration above the high speed threshold. + scale *= mParameters.acceleration; + } else if (speed > mParameters.lowThreshold) { + // Linearly interpolate the acceleration to apply between the low and high + // speed thresholds. + scale *= 1 + (speed - mParameters.lowThreshold) + / (mParameters.highThreshold - mParameters.lowThreshold) + * (mParameters.acceleration - 1); + } + +#if DEBUG_ACCELERATION + ALOGD("VelocityControl(%0.3f, %0.3f, %0.3f, %0.3f): " + "vx=%0.3f, vy=%0.3f, speed=%0.3f, accel=%0.3f", + mParameters.scale, mParameters.lowThreshold, mParameters.highThreshold, + mParameters.acceleration, + vx, vy, speed, scale / mParameters.scale); +#endif + } else { +#if DEBUG_ACCELERATION + ALOGD("VelocityControl(%0.3f, %0.3f, %0.3f, %0.3f): unknown velocity", + mParameters.scale, mParameters.lowThreshold, mParameters.highThreshold, + mParameters.acceleration); +#endif + } + + if (deltaX) { + *deltaX *= scale; + } + if (deltaY) { + *deltaY *= scale; + } + } +} + +} // namespace android diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp new file mode 100644 index 0000000..6c70c3c --- /dev/null +++ b/libs/input/VelocityTracker.cpp @@ -0,0 +1,927 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "VelocityTracker" +//#define LOG_NDEBUG 0 + +// Log debug messages about velocity tracking. +#define DEBUG_VELOCITY 0 + +// Log debug messages about the progress of the algorithm itself. +#define DEBUG_STRATEGY 0 + +#include <math.h> +#include <limits.h> + +#include <cutils/properties.h> +#include <input/VelocityTracker.h> +#include <utils/BitSet.h> +#include <utils/String8.h> +#include <utils/Timers.h> + +namespace android { + +// Nanoseconds per milliseconds. +static const nsecs_t NANOS_PER_MS = 1000000; + +// Threshold for determining that a pointer has stopped moving. +// Some input devices do not send ACTION_MOVE events in the case where a pointer has +// stopped. We need to detect this case so that we can accurately predict the +// velocity after the pointer starts moving again. +static const nsecs_t ASSUME_POINTER_STOPPED_TIME = 40 * NANOS_PER_MS; + + +static float vectorDot(const float* a, const float* b, uint32_t m) { + float r = 0; + while (m--) { + r += *(a++) * *(b++); + } + return r; +} + +static float vectorNorm(const float* a, uint32_t m) { + float r = 0; + while (m--) { + float t = *(a++); + r += t * t; + } + return sqrtf(r); +} + +#if DEBUG_STRATEGY || DEBUG_VELOCITY +static String8 vectorToString(const float* a, uint32_t m) { + String8 str; + str.append("["); + while (m--) { + str.appendFormat(" %f", *(a++)); + if (m) { + str.append(","); + } + } + str.append(" ]"); + return str; +} + +static String8 matrixToString(const float* a, uint32_t m, uint32_t n, bool rowMajor) { + String8 str; + str.append("["); + for (size_t i = 0; i < m; i++) { + if (i) { + str.append(","); + } + str.append(" ["); + for (size_t j = 0; j < n; j++) { + if (j) { + str.append(","); + } + str.appendFormat(" %f", a[rowMajor ? i * n + j : j * m + i]); + } + str.append(" ]"); + } + str.append(" ]"); + return str; +} +#endif + + +// --- VelocityTracker --- + +// The default velocity tracker strategy. +// Although other strategies are available for testing and comparison purposes, +// this is the strategy that applications will actually use. Be very careful +// when adjusting the default strategy because it can dramatically affect +// (often in a bad way) the user experience. +const char* VelocityTracker::DEFAULT_STRATEGY = "lsq2"; + +VelocityTracker::VelocityTracker(const char* strategy) : + mLastEventTime(0), mCurrentPointerIdBits(0), mActivePointerId(-1) { + char value[PROPERTY_VALUE_MAX]; + + // Allow the default strategy to be overridden using a system property for debugging. + if (!strategy) { + int length = property_get("debug.velocitytracker.strategy", value, NULL); + if (length > 0) { + strategy = value; + } else { + strategy = DEFAULT_STRATEGY; + } + } + + // Configure the strategy. + if (!configureStrategy(strategy)) { + ALOGD("Unrecognized velocity tracker strategy name '%s'.", strategy); + if (!configureStrategy(DEFAULT_STRATEGY)) { + LOG_ALWAYS_FATAL("Could not create the default velocity tracker strategy '%s'!", + strategy); + } + } +} + +VelocityTracker::~VelocityTracker() { + delete mStrategy; +} + +bool VelocityTracker::configureStrategy(const char* strategy) { + mStrategy = createStrategy(strategy); + return mStrategy != NULL; +} + +VelocityTrackerStrategy* VelocityTracker::createStrategy(const char* strategy) { + if (!strcmp("lsq1", strategy)) { + // 1st order least squares. Quality: POOR. + // Frequently underfits the touch data especially when the finger accelerates + // or changes direction. Often underestimates velocity. The direction + // is overly influenced by historical touch points. + return new LeastSquaresVelocityTrackerStrategy(1); + } + if (!strcmp("lsq2", strategy)) { + // 2nd order least squares. Quality: VERY GOOD. + // Pretty much ideal, but can be confused by certain kinds of touch data, + // particularly if the panel has a tendency to generate delayed, + // duplicate or jittery touch coordinates when the finger is released. + return new LeastSquaresVelocityTrackerStrategy(2); + } + if (!strcmp("lsq3", strategy)) { + // 3rd order least squares. Quality: UNUSABLE. + // Frequently overfits the touch data yielding wildly divergent estimates + // of the velocity when the finger is released. + return new LeastSquaresVelocityTrackerStrategy(3); + } + if (!strcmp("wlsq2-delta", strategy)) { + // 2nd order weighted least squares, delta weighting. Quality: EXPERIMENTAL + return new LeastSquaresVelocityTrackerStrategy(2, + LeastSquaresVelocityTrackerStrategy::WEIGHTING_DELTA); + } + if (!strcmp("wlsq2-central", strategy)) { + // 2nd order weighted least squares, central weighting. Quality: EXPERIMENTAL + return new LeastSquaresVelocityTrackerStrategy(2, + LeastSquaresVelocityTrackerStrategy::WEIGHTING_CENTRAL); + } + if (!strcmp("wlsq2-recent", strategy)) { + // 2nd order weighted least squares, recent weighting. Quality: EXPERIMENTAL + return new LeastSquaresVelocityTrackerStrategy(2, + LeastSquaresVelocityTrackerStrategy::WEIGHTING_RECENT); + } + if (!strcmp("int1", strategy)) { + // 1st order integrating filter. Quality: GOOD. + // Not as good as 'lsq2' because it cannot estimate acceleration but it is + // more tolerant of errors. Like 'lsq1', this strategy tends to underestimate + // the velocity of a fling but this strategy tends to respond to changes in + // direction more quickly and accurately. + return new IntegratingVelocityTrackerStrategy(1); + } + if (!strcmp("int2", strategy)) { + // 2nd order integrating filter. Quality: EXPERIMENTAL. + // For comparison purposes only. Unlike 'int1' this strategy can compensate + // for acceleration but it typically overestimates the effect. + return new IntegratingVelocityTrackerStrategy(2); + } + if (!strcmp("legacy", strategy)) { + // Legacy velocity tracker algorithm. Quality: POOR. + // For comparison purposes only. This algorithm is strongly influenced by + // old data points, consistently underestimates velocity and takes a very long + // time to adjust to changes in direction. + return new LegacyVelocityTrackerStrategy(); + } + return NULL; +} + +void VelocityTracker::clear() { + mCurrentPointerIdBits.clear(); + mActivePointerId = -1; + + mStrategy->clear(); +} + +void VelocityTracker::clearPointers(BitSet32 idBits) { + BitSet32 remainingIdBits(mCurrentPointerIdBits.value & ~idBits.value); + mCurrentPointerIdBits = remainingIdBits; + + if (mActivePointerId >= 0 && idBits.hasBit(mActivePointerId)) { + mActivePointerId = !remainingIdBits.isEmpty() ? remainingIdBits.firstMarkedBit() : -1; + } + + mStrategy->clearPointers(idBits); +} + +void VelocityTracker::addMovement(nsecs_t eventTime, BitSet32 idBits, const Position* positions) { + while (idBits.count() > MAX_POINTERS) { + idBits.clearLastMarkedBit(); + } + + if ((mCurrentPointerIdBits.value & idBits.value) + && eventTime >= mLastEventTime + ASSUME_POINTER_STOPPED_TIME) { +#if DEBUG_VELOCITY + ALOGD("VelocityTracker: stopped for %0.3f ms, clearing state.", + (eventTime - mLastEventTime) * 0.000001f); +#endif + // We have not received any movements for too long. Assume that all pointers + // have stopped. + mStrategy->clear(); + } + mLastEventTime = eventTime; + + mCurrentPointerIdBits = idBits; + if (mActivePointerId < 0 || !idBits.hasBit(mActivePointerId)) { + mActivePointerId = idBits.isEmpty() ? -1 : idBits.firstMarkedBit(); + } + + mStrategy->addMovement(eventTime, idBits, positions); + +#if DEBUG_VELOCITY + ALOGD("VelocityTracker: addMovement eventTime=%lld, idBits=0x%08x, activePointerId=%d", + eventTime, idBits.value, mActivePointerId); + for (BitSet32 iterBits(idBits); !iterBits.isEmpty(); ) { + uint32_t id = iterBits.firstMarkedBit(); + uint32_t index = idBits.getIndexOfBit(id); + iterBits.clearBit(id); + Estimator estimator; + getEstimator(id, &estimator); + ALOGD(" %d: position (%0.3f, %0.3f), " + "estimator (degree=%d, xCoeff=%s, yCoeff=%s, confidence=%f)", + id, positions[index].x, positions[index].y, + int(estimator.degree), + vectorToString(estimator.xCoeff, estimator.degree + 1).string(), + vectorToString(estimator.yCoeff, estimator.degree + 1).string(), + estimator.confidence); + } +#endif +} + +void VelocityTracker::addMovement(const MotionEvent* event) { + int32_t actionMasked = event->getActionMasked(); + + switch (actionMasked) { + case AMOTION_EVENT_ACTION_DOWN: + case AMOTION_EVENT_ACTION_HOVER_ENTER: + // Clear all pointers on down before adding the new movement. + clear(); + break; + case AMOTION_EVENT_ACTION_POINTER_DOWN: { + // Start a new movement trace for a pointer that just went down. + // We do this on down instead of on up because the client may want to query the + // final velocity for a pointer that just went up. + BitSet32 downIdBits; + downIdBits.markBit(event->getPointerId(event->getActionIndex())); + clearPointers(downIdBits); + break; + } + case AMOTION_EVENT_ACTION_MOVE: + case AMOTION_EVENT_ACTION_HOVER_MOVE: + break; + default: + // Ignore all other actions because they do not convey any new information about + // pointer movement. We also want to preserve the last known velocity of the pointers. + // Note that ACTION_UP and ACTION_POINTER_UP always report the last known position + // of the pointers that went up. ACTION_POINTER_UP does include the new position of + // pointers that remained down but we will also receive an ACTION_MOVE with this + // information if any of them actually moved. Since we don't know how many pointers + // will be going up at once it makes sense to just wait for the following ACTION_MOVE + // before adding the movement. + return; + } + + size_t pointerCount = event->getPointerCount(); + if (pointerCount > MAX_POINTERS) { + pointerCount = MAX_POINTERS; + } + + BitSet32 idBits; + for (size_t i = 0; i < pointerCount; i++) { + idBits.markBit(event->getPointerId(i)); + } + + uint32_t pointerIndex[MAX_POINTERS]; + for (size_t i = 0; i < pointerCount; i++) { + pointerIndex[i] = idBits.getIndexOfBit(event->getPointerId(i)); + } + + nsecs_t eventTime; + Position positions[pointerCount]; + + size_t historySize = event->getHistorySize(); + for (size_t h = 0; h < historySize; h++) { + eventTime = event->getHistoricalEventTime(h); + for (size_t i = 0; i < pointerCount; i++) { + uint32_t index = pointerIndex[i]; + positions[index].x = event->getHistoricalX(i, h); + positions[index].y = event->getHistoricalY(i, h); + } + addMovement(eventTime, idBits, positions); + } + + eventTime = event->getEventTime(); + for (size_t i = 0; i < pointerCount; i++) { + uint32_t index = pointerIndex[i]; + positions[index].x = event->getX(i); + positions[index].y = event->getY(i); + } + addMovement(eventTime, idBits, positions); +} + +bool VelocityTracker::getVelocity(uint32_t id, float* outVx, float* outVy) const { + Estimator estimator; + if (getEstimator(id, &estimator) && estimator.degree >= 1) { + *outVx = estimator.xCoeff[1]; + *outVy = estimator.yCoeff[1]; + return true; + } + *outVx = 0; + *outVy = 0; + return false; +} + +bool VelocityTracker::getEstimator(uint32_t id, Estimator* outEstimator) const { + return mStrategy->getEstimator(id, outEstimator); +} + + +// --- LeastSquaresVelocityTrackerStrategy --- + +const nsecs_t LeastSquaresVelocityTrackerStrategy::HORIZON; +const uint32_t LeastSquaresVelocityTrackerStrategy::HISTORY_SIZE; + +LeastSquaresVelocityTrackerStrategy::LeastSquaresVelocityTrackerStrategy( + uint32_t degree, Weighting weighting) : + mDegree(degree), mWeighting(weighting) { + clear(); +} + +LeastSquaresVelocityTrackerStrategy::~LeastSquaresVelocityTrackerStrategy() { +} + +void LeastSquaresVelocityTrackerStrategy::clear() { + mIndex = 0; + mMovements[0].idBits.clear(); +} + +void LeastSquaresVelocityTrackerStrategy::clearPointers(BitSet32 idBits) { + BitSet32 remainingIdBits(mMovements[mIndex].idBits.value & ~idBits.value); + mMovements[mIndex].idBits = remainingIdBits; +} + +void LeastSquaresVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits, + const VelocityTracker::Position* positions) { + if (++mIndex == HISTORY_SIZE) { + mIndex = 0; + } + + Movement& movement = mMovements[mIndex]; + movement.eventTime = eventTime; + movement.idBits = idBits; + uint32_t count = idBits.count(); + for (uint32_t i = 0; i < count; i++) { + movement.positions[i] = positions[i]; + } +} + +/** + * Solves a linear least squares problem to obtain a N degree polynomial that fits + * the specified input data as nearly as possible. + * + * Returns true if a solution is found, false otherwise. + * + * The input consists of two vectors of data points X and Y with indices 0..m-1 + * along with a weight vector W of the same size. + * + * The output is a vector B with indices 0..n that describes a polynomial + * that fits the data, such the sum of W[i] * W[i] * abs(Y[i] - (B[0] + B[1] X[i] + * + B[2] X[i]^2 ... B[n] X[i]^n)) for all i between 0 and m-1 is minimized. + * + * Accordingly, the weight vector W should be initialized by the caller with the + * reciprocal square root of the variance of the error in each input data point. + * In other words, an ideal choice for W would be W[i] = 1 / var(Y[i]) = 1 / stddev(Y[i]). + * The weights express the relative importance of each data point. If the weights are + * all 1, then the data points are considered to be of equal importance when fitting + * the polynomial. It is a good idea to choose weights that diminish the importance + * of data points that may have higher than usual error margins. + * + * Errors among data points are assumed to be independent. W is represented here + * as a vector although in the literature it is typically taken to be a diagonal matrix. + * + * That is to say, the function that generated the input data can be approximated + * by y(x) ~= B[0] + B[1] x + B[2] x^2 + ... + B[n] x^n. + * + * The coefficient of determination (R^2) is also returned to describe the goodness + * of fit of the model for the given data. It is a value between 0 and 1, where 1 + * indicates perfect correspondence. + * + * This function first expands the X vector to a m by n matrix A such that + * A[i][0] = 1, A[i][1] = X[i], A[i][2] = X[i]^2, ..., A[i][n] = X[i]^n, then + * multiplies it by w[i]./ + * + * Then it calculates the QR decomposition of A yielding an m by m orthonormal matrix Q + * and an m by n upper triangular matrix R. Because R is upper triangular (lower + * part is all zeroes), we can simplify the decomposition into an m by n matrix + * Q1 and a n by n matrix R1 such that A = Q1 R1. + * + * Finally we solve the system of linear equations given by R1 B = (Qtranspose W Y) + * to find B. + * + * For efficiency, we lay out A and Q column-wise in memory because we frequently + * operate on the column vectors. Conversely, we lay out R row-wise. + * + * http://en.wikipedia.org/wiki/Numerical_methods_for_linear_least_squares + * http://en.wikipedia.org/wiki/Gram-Schmidt + */ +static bool solveLeastSquares(const float* x, const float* y, + const float* w, uint32_t m, uint32_t n, float* outB, float* outDet) { +#if DEBUG_STRATEGY + ALOGD("solveLeastSquares: m=%d, n=%d, x=%s, y=%s, w=%s", int(m), int(n), + vectorToString(x, m).string(), vectorToString(y, m).string(), + vectorToString(w, m).string()); +#endif + + // Expand the X vector to a matrix A, pre-multiplied by the weights. + float a[n][m]; // column-major order + for (uint32_t h = 0; h < m; h++) { + a[0][h] = w[h]; + for (uint32_t i = 1; i < n; i++) { + a[i][h] = a[i - 1][h] * x[h]; + } + } +#if DEBUG_STRATEGY + ALOGD(" - a=%s", matrixToString(&a[0][0], m, n, false /*rowMajor*/).string()); +#endif + + // Apply the Gram-Schmidt process to A to obtain its QR decomposition. + float q[n][m]; // orthonormal basis, column-major order + float r[n][n]; // upper triangular matrix, row-major order + for (uint32_t j = 0; j < n; j++) { + for (uint32_t h = 0; h < m; h++) { + q[j][h] = a[j][h]; + } + for (uint32_t i = 0; i < j; i++) { + float dot = vectorDot(&q[j][0], &q[i][0], m); + for (uint32_t h = 0; h < m; h++) { + q[j][h] -= dot * q[i][h]; + } + } + + float norm = vectorNorm(&q[j][0], m); + if (norm < 0.000001f) { + // vectors are linearly dependent or zero so no solution +#if DEBUG_STRATEGY + ALOGD(" - no solution, norm=%f", norm); +#endif + return false; + } + + float invNorm = 1.0f / norm; + for (uint32_t h = 0; h < m; h++) { + q[j][h] *= invNorm; + } + for (uint32_t i = 0; i < n; i++) { + r[j][i] = i < j ? 0 : vectorDot(&q[j][0], &a[i][0], m); + } + } +#if DEBUG_STRATEGY + ALOGD(" - q=%s", matrixToString(&q[0][0], m, n, false /*rowMajor*/).string()); + ALOGD(" - r=%s", matrixToString(&r[0][0], n, n, true /*rowMajor*/).string()); + + // calculate QR, if we factored A correctly then QR should equal A + float qr[n][m]; + for (uint32_t h = 0; h < m; h++) { + for (uint32_t i = 0; i < n; i++) { + qr[i][h] = 0; + for (uint32_t j = 0; j < n; j++) { + qr[i][h] += q[j][h] * r[j][i]; + } + } + } + ALOGD(" - qr=%s", matrixToString(&qr[0][0], m, n, false /*rowMajor*/).string()); +#endif + + // Solve R B = Qt W Y to find B. This is easy because R is upper triangular. + // We just work from bottom-right to top-left calculating B's coefficients. + float wy[m]; + for (uint32_t h = 0; h < m; h++) { + wy[h] = y[h] * w[h]; + } + for (uint32_t i = n; i-- != 0; ) { + outB[i] = vectorDot(&q[i][0], wy, m); + for (uint32_t j = n - 1; j > i; j--) { + outB[i] -= r[i][j] * outB[j]; + } + outB[i] /= r[i][i]; + } +#if DEBUG_STRATEGY + ALOGD(" - b=%s", vectorToString(outB, n).string()); +#endif + + // Calculate the coefficient of determination as 1 - (SSerr / SStot) where + // SSerr is the residual sum of squares (variance of the error), + // and SStot is the total sum of squares (variance of the data) where each + // has been weighted. + float ymean = 0; + for (uint32_t h = 0; h < m; h++) { + ymean += y[h]; + } + ymean /= m; + + float sserr = 0; + float sstot = 0; + for (uint32_t h = 0; h < m; h++) { + float err = y[h] - outB[0]; + float term = 1; + for (uint32_t i = 1; i < n; i++) { + term *= x[h]; + err -= term * outB[i]; + } + sserr += w[h] * w[h] * err * err; + float var = y[h] - ymean; + sstot += w[h] * w[h] * var * var; + } + *outDet = sstot > 0.000001f ? 1.0f - (sserr / sstot) : 1; +#if DEBUG_STRATEGY + ALOGD(" - sserr=%f", sserr); + ALOGD(" - sstot=%f", sstot); + ALOGD(" - det=%f", *outDet); +#endif + return true; +} + +bool LeastSquaresVelocityTrackerStrategy::getEstimator(uint32_t id, + VelocityTracker::Estimator* outEstimator) const { + outEstimator->clear(); + + // Iterate over movement samples in reverse time order and collect samples. + float x[HISTORY_SIZE]; + float y[HISTORY_SIZE]; + float w[HISTORY_SIZE]; + float time[HISTORY_SIZE]; + uint32_t m = 0; + uint32_t index = mIndex; + const Movement& newestMovement = mMovements[mIndex]; + do { + const Movement& movement = mMovements[index]; + if (!movement.idBits.hasBit(id)) { + break; + } + + nsecs_t age = newestMovement.eventTime - movement.eventTime; + if (age > HORIZON) { + break; + } + + const VelocityTracker::Position& position = movement.getPosition(id); + x[m] = position.x; + y[m] = position.y; + w[m] = chooseWeight(index); + time[m] = -age * 0.000000001f; + index = (index == 0 ? HISTORY_SIZE : index) - 1; + } while (++m < HISTORY_SIZE); + + if (m == 0) { + return false; // no data + } + + // Calculate a least squares polynomial fit. + uint32_t degree = mDegree; + if (degree > m - 1) { + degree = m - 1; + } + if (degree >= 1) { + float xdet, ydet; + uint32_t n = degree + 1; + if (solveLeastSquares(time, x, w, m, n, outEstimator->xCoeff, &xdet) + && solveLeastSquares(time, y, w, m, n, outEstimator->yCoeff, &ydet)) { + outEstimator->time = newestMovement.eventTime; + outEstimator->degree = degree; + outEstimator->confidence = xdet * ydet; +#if DEBUG_STRATEGY + ALOGD("estimate: degree=%d, xCoeff=%s, yCoeff=%s, confidence=%f", + int(outEstimator->degree), + vectorToString(outEstimator->xCoeff, n).string(), + vectorToString(outEstimator->yCoeff, n).string(), + outEstimator->confidence); +#endif + return true; + } + } + + // No velocity data available for this pointer, but we do have its current position. + outEstimator->xCoeff[0] = x[0]; + outEstimator->yCoeff[0] = y[0]; + outEstimator->time = newestMovement.eventTime; + outEstimator->degree = 0; + outEstimator->confidence = 1; + return true; +} + +float LeastSquaresVelocityTrackerStrategy::chooseWeight(uint32_t index) const { + switch (mWeighting) { + case WEIGHTING_DELTA: { + // Weight points based on how much time elapsed between them and the next + // point so that points that "cover" a shorter time span are weighed less. + // delta 0ms: 0.5 + // delta 10ms: 1.0 + if (index == mIndex) { + return 1.0f; + } + uint32_t nextIndex = (index + 1) % HISTORY_SIZE; + float deltaMillis = (mMovements[nextIndex].eventTime- mMovements[index].eventTime) + * 0.000001f; + if (deltaMillis < 0) { + return 0.5f; + } + if (deltaMillis < 10) { + return 0.5f + deltaMillis * 0.05; + } + return 1.0f; + } + + case WEIGHTING_CENTRAL: { + // Weight points based on their age, weighing very recent and very old points less. + // age 0ms: 0.5 + // age 10ms: 1.0 + // age 50ms: 1.0 + // age 60ms: 0.5 + float ageMillis = (mMovements[mIndex].eventTime - mMovements[index].eventTime) + * 0.000001f; + if (ageMillis < 0) { + return 0.5f; + } + if (ageMillis < 10) { + return 0.5f + ageMillis * 0.05; + } + if (ageMillis < 50) { + return 1.0f; + } + if (ageMillis < 60) { + return 0.5f + (60 - ageMillis) * 0.05; + } + return 0.5f; + } + + case WEIGHTING_RECENT: { + // Weight points based on their age, weighing older points less. + // age 0ms: 1.0 + // age 50ms: 1.0 + // age 100ms: 0.5 + float ageMillis = (mMovements[mIndex].eventTime - mMovements[index].eventTime) + * 0.000001f; + if (ageMillis < 50) { + return 1.0f; + } + if (ageMillis < 100) { + return 0.5f + (100 - ageMillis) * 0.01f; + } + return 0.5f; + } + + case WEIGHTING_NONE: + default: + return 1.0f; + } +} + + +// --- IntegratingVelocityTrackerStrategy --- + +IntegratingVelocityTrackerStrategy::IntegratingVelocityTrackerStrategy(uint32_t degree) : + mDegree(degree) { +} + +IntegratingVelocityTrackerStrategy::~IntegratingVelocityTrackerStrategy() { +} + +void IntegratingVelocityTrackerStrategy::clear() { + mPointerIdBits.clear(); +} + +void IntegratingVelocityTrackerStrategy::clearPointers(BitSet32 idBits) { + mPointerIdBits.value &= ~idBits.value; +} + +void IntegratingVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits, + const VelocityTracker::Position* positions) { + uint32_t index = 0; + for (BitSet32 iterIdBits(idBits); !iterIdBits.isEmpty();) { + uint32_t id = iterIdBits.clearFirstMarkedBit(); + State& state = mPointerState[id]; + const VelocityTracker::Position& position = positions[index++]; + if (mPointerIdBits.hasBit(id)) { + updateState(state, eventTime, position.x, position.y); + } else { + initState(state, eventTime, position.x, position.y); + } + } + + mPointerIdBits = idBits; +} + +bool IntegratingVelocityTrackerStrategy::getEstimator(uint32_t id, + VelocityTracker::Estimator* outEstimator) const { + outEstimator->clear(); + + if (mPointerIdBits.hasBit(id)) { + const State& state = mPointerState[id]; + populateEstimator(state, outEstimator); + return true; + } + + return false; +} + +void IntegratingVelocityTrackerStrategy::initState(State& state, + nsecs_t eventTime, float xpos, float ypos) const { + state.updateTime = eventTime; + state.degree = 0; + + state.xpos = xpos; + state.xvel = 0; + state.xaccel = 0; + state.ypos = ypos; + state.yvel = 0; + state.yaccel = 0; +} + +void IntegratingVelocityTrackerStrategy::updateState(State& state, + nsecs_t eventTime, float xpos, float ypos) const { + const nsecs_t MIN_TIME_DELTA = 2 * NANOS_PER_MS; + const float FILTER_TIME_CONSTANT = 0.010f; // 10 milliseconds + + if (eventTime <= state.updateTime + MIN_TIME_DELTA) { + return; + } + + float dt = (eventTime - state.updateTime) * 0.000000001f; + state.updateTime = eventTime; + + float xvel = (xpos - state.xpos) / dt; + float yvel = (ypos - state.ypos) / dt; + if (state.degree == 0) { + state.xvel = xvel; + state.yvel = yvel; + state.degree = 1; + } else { + float alpha = dt / (FILTER_TIME_CONSTANT + dt); + if (mDegree == 1) { + state.xvel += (xvel - state.xvel) * alpha; + state.yvel += (yvel - state.yvel) * alpha; + } else { + float xaccel = (xvel - state.xvel) / dt; + float yaccel = (yvel - state.yvel) / dt; + if (state.degree == 1) { + state.xaccel = xaccel; + state.yaccel = yaccel; + state.degree = 2; + } else { + state.xaccel += (xaccel - state.xaccel) * alpha; + state.yaccel += (yaccel - state.yaccel) * alpha; + } + state.xvel += (state.xaccel * dt) * alpha; + state.yvel += (state.yaccel * dt) * alpha; + } + } + state.xpos = xpos; + state.ypos = ypos; +} + +void IntegratingVelocityTrackerStrategy::populateEstimator(const State& state, + VelocityTracker::Estimator* outEstimator) const { + outEstimator->time = state.updateTime; + outEstimator->confidence = 1.0f; + outEstimator->degree = state.degree; + outEstimator->xCoeff[0] = state.xpos; + outEstimator->xCoeff[1] = state.xvel; + outEstimator->xCoeff[2] = state.xaccel / 2; + outEstimator->yCoeff[0] = state.ypos; + outEstimator->yCoeff[1] = state.yvel; + outEstimator->yCoeff[2] = state.yaccel / 2; +} + + +// --- LegacyVelocityTrackerStrategy --- + +const nsecs_t LegacyVelocityTrackerStrategy::HORIZON; +const uint32_t LegacyVelocityTrackerStrategy::HISTORY_SIZE; +const nsecs_t LegacyVelocityTrackerStrategy::MIN_DURATION; + +LegacyVelocityTrackerStrategy::LegacyVelocityTrackerStrategy() { + clear(); +} + +LegacyVelocityTrackerStrategy::~LegacyVelocityTrackerStrategy() { +} + +void LegacyVelocityTrackerStrategy::clear() { + mIndex = 0; + mMovements[0].idBits.clear(); +} + +void LegacyVelocityTrackerStrategy::clearPointers(BitSet32 idBits) { + BitSet32 remainingIdBits(mMovements[mIndex].idBits.value & ~idBits.value); + mMovements[mIndex].idBits = remainingIdBits; +} + +void LegacyVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits, + const VelocityTracker::Position* positions) { + if (++mIndex == HISTORY_SIZE) { + mIndex = 0; + } + + Movement& movement = mMovements[mIndex]; + movement.eventTime = eventTime; + movement.idBits = idBits; + uint32_t count = idBits.count(); + for (uint32_t i = 0; i < count; i++) { + movement.positions[i] = positions[i]; + } +} + +bool LegacyVelocityTrackerStrategy::getEstimator(uint32_t id, + VelocityTracker::Estimator* outEstimator) const { + outEstimator->clear(); + + const Movement& newestMovement = mMovements[mIndex]; + if (!newestMovement.idBits.hasBit(id)) { + return false; // no data + } + + // Find the oldest sample that contains the pointer and that is not older than HORIZON. + nsecs_t minTime = newestMovement.eventTime - HORIZON; + uint32_t oldestIndex = mIndex; + uint32_t numTouches = 1; + do { + uint32_t nextOldestIndex = (oldestIndex == 0 ? HISTORY_SIZE : oldestIndex) - 1; + const Movement& nextOldestMovement = mMovements[nextOldestIndex]; + if (!nextOldestMovement.idBits.hasBit(id) + || nextOldestMovement.eventTime < minTime) { + break; + } + oldestIndex = nextOldestIndex; + } while (++numTouches < HISTORY_SIZE); + + // Calculate an exponentially weighted moving average of the velocity estimate + // at different points in time measured relative to the oldest sample. + // This is essentially an IIR filter. Newer samples are weighted more heavily + // than older samples. Samples at equal time points are weighted more or less + // equally. + // + // One tricky problem is that the sample data may be poorly conditioned. + // Sometimes samples arrive very close together in time which can cause us to + // overestimate the velocity at that time point. Most samples might be measured + // 16ms apart but some consecutive samples could be only 0.5sm apart because + // the hardware or driver reports them irregularly or in bursts. + float accumVx = 0; + float accumVy = 0; + uint32_t index = oldestIndex; + uint32_t samplesUsed = 0; + const Movement& oldestMovement = mMovements[oldestIndex]; + const VelocityTracker::Position& oldestPosition = oldestMovement.getPosition(id); + nsecs_t lastDuration = 0; + + while (numTouches-- > 1) { + if (++index == HISTORY_SIZE) { + index = 0; + } + const Movement& movement = mMovements[index]; + nsecs_t duration = movement.eventTime - oldestMovement.eventTime; + + // If the duration between samples is small, we may significantly overestimate + // the velocity. Consequently, we impose a minimum duration constraint on the + // samples that we include in the calculation. + if (duration >= MIN_DURATION) { + const VelocityTracker::Position& position = movement.getPosition(id); + float scale = 1000000000.0f / duration; // one over time delta in seconds + float vx = (position.x - oldestPosition.x) * scale; + float vy = (position.y - oldestPosition.y) * scale; + accumVx = (accumVx * lastDuration + vx * duration) / (duration + lastDuration); + accumVy = (accumVy * lastDuration + vy * duration) / (duration + lastDuration); + lastDuration = duration; + samplesUsed += 1; + } + } + + // Report velocity. + const VelocityTracker::Position& newestPosition = newestMovement.getPosition(id); + outEstimator->time = newestMovement.eventTime; + outEstimator->confidence = 1; + outEstimator->xCoeff[0] = newestPosition.x; + outEstimator->yCoeff[0] = newestPosition.y; + if (samplesUsed) { + outEstimator->xCoeff[1] = accumVx; + outEstimator->yCoeff[1] = accumVy; + outEstimator->degree = 1; + } else { + outEstimator->degree = 0; + } + return true; +} + +} // namespace android diff --git a/libs/input/VirtualKeyMap.cpp b/libs/input/VirtualKeyMap.cpp new file mode 100644 index 0000000..28ea717 --- /dev/null +++ b/libs/input/VirtualKeyMap.cpp @@ -0,0 +1,172 @@ +/* + * 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. + */ + +#define LOG_TAG "VirtualKeyMap" + +#include <stdlib.h> +#include <string.h> + +#include <input/VirtualKeyMap.h> +#include <utils/Log.h> +#include <utils/Errors.h> +#include <utils/Tokenizer.h> +#include <utils/Timers.h> + +// Enables debug output for the parser. +#define DEBUG_PARSER 0 + +// Enables debug output for parser performance. +#define DEBUG_PARSER_PERFORMANCE 0 + + +namespace android { + +static const char* WHITESPACE = " \t\r"; +static const char* WHITESPACE_OR_FIELD_DELIMITER = " \t\r:"; + + +// --- VirtualKeyMap --- + +VirtualKeyMap::VirtualKeyMap() { +} + +VirtualKeyMap::~VirtualKeyMap() { +} + +status_t VirtualKeyMap::load(const String8& filename, VirtualKeyMap** outMap) { + *outMap = NULL; + + Tokenizer* tokenizer; + status_t status = Tokenizer::open(filename, &tokenizer); + if (status) { + ALOGE("Error %d opening virtual key map file %s.", status, filename.string()); + } else { + VirtualKeyMap* map = new VirtualKeyMap(); + if (!map) { + ALOGE("Error allocating virtual key map."); + status = NO_MEMORY; + } else { +#if DEBUG_PARSER_PERFORMANCE + nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); +#endif + Parser parser(map, tokenizer); + status = parser.parse(); +#if DEBUG_PARSER_PERFORMANCE + nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; + ALOGD("Parsed key character map file '%s' %d lines in %0.3fms.", + tokenizer->getFilename().string(), tokenizer->getLineNumber(), + elapsedTime / 1000000.0); +#endif + if (status) { + delete map; + } else { + *outMap = map; + } + } + delete tokenizer; + } + return status; +} + + +// --- VirtualKeyMap::Parser --- + +VirtualKeyMap::Parser::Parser(VirtualKeyMap* map, Tokenizer* tokenizer) : + mMap(map), mTokenizer(tokenizer) { +} + +VirtualKeyMap::Parser::~Parser() { +} + +status_t VirtualKeyMap::Parser::parse() { + while (!mTokenizer->isEof()) { +#if DEBUG_PARSER + ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(), + mTokenizer->peekRemainderOfLine().string()); +#endif + + mTokenizer->skipDelimiters(WHITESPACE); + + if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') { + // Multiple keys can appear on one line or they can be broken up across multiple lines. + do { + String8 token = mTokenizer->nextToken(WHITESPACE_OR_FIELD_DELIMITER); + if (token != "0x01") { + ALOGE("%s: Unknown virtual key type, expected 0x01.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + + VirtualKeyDefinition defn; + bool success = parseNextIntField(&defn.scanCode) + && parseNextIntField(&defn.centerX) + && parseNextIntField(&defn.centerY) + && parseNextIntField(&defn.width) + && parseNextIntField(&defn.height); + if (!success) { + ALOGE("%s: Expected 5 colon-delimited integers in virtual key definition.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + +#if DEBUG_PARSER + ALOGD("Parsed virtual key: scanCode=%d, centerX=%d, centerY=%d, " + "width=%d, height=%d", + defn.scanCode, defn.centerX, defn.centerY, defn.width, defn.height); +#endif + mMap->mVirtualKeys.push(defn); + } while (consumeFieldDelimiterAndSkipWhitespace()); + + if (!mTokenizer->isEol()) { + ALOGE("%s: Expected end of line, got '%s'.", + mTokenizer->getLocation().string(), + mTokenizer->peekRemainderOfLine().string()); + return BAD_VALUE; + } + } + + mTokenizer->nextLine(); + } + + return NO_ERROR; +} + +bool VirtualKeyMap::Parser::consumeFieldDelimiterAndSkipWhitespace() { + mTokenizer->skipDelimiters(WHITESPACE); + if (mTokenizer->peekChar() == ':') { + mTokenizer->nextChar(); + mTokenizer->skipDelimiters(WHITESPACE); + return true; + } + return false; +} + +bool VirtualKeyMap::Parser::parseNextIntField(int32_t* outValue) { + if (!consumeFieldDelimiterAndSkipWhitespace()) { + return false; + } + + String8 token = mTokenizer->nextToken(WHITESPACE_OR_FIELD_DELIMITER); + char* end; + *outValue = strtol(token.string(), &end, 0); + if (token.isEmpty() || *end != '\0') { + ALOGE("Expected an integer, got '%s'.", token.string()); + return false; + } + return true; +} + +} // namespace android diff --git a/libs/utils/tests/Android.mk b/libs/input/tests/Android.mk index a2ca9c8..c62dff1 100644 --- a/libs/utils/tests/Android.mk +++ b/libs/input/tests/Android.mk @@ -1,23 +1,19 @@ # Build the unit tests. -LOCAL_PATH := $(call my-dir) +LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) # Build the unit tests. test_src_files := \ - BasicHashtable_test.cpp \ - BlobCache_test.cpp \ - Looper_test.cpp \ - LruCache_test.cpp \ - String8_test.cpp \ - Unicode_test.cpp \ - Vector_test.cpp \ - ZipFileRO_test.cpp + InputChannel_test.cpp \ + InputEvent_test.cpp \ + InputPublisherAndConsumer_test.cpp shared_libraries := \ - libz \ - liblog \ + libinput \ libcutils \ libutils \ + libbinder \ + libui \ libstlport static_libraries := \ @@ -32,3 +28,6 @@ $(foreach file,$(test_src_files), \ $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \ $(eval include $(BUILD_NATIVE_TEST)) \ ) + +# Build the manual test programs. +include $(call all-makefiles-under, $(LOCAL_PATH)) diff --git a/libs/input/tests/InputChannel_test.cpp b/libs/input/tests/InputChannel_test.cpp new file mode 100644 index 0000000..e71ebe2 --- /dev/null +++ b/libs/input/tests/InputChannel_test.cpp @@ -0,0 +1,159 @@ +/* + * 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. + */ + +#include "TestHelpers.h" + +#include <unistd.h> +#include <time.h> +#include <errno.h> + +#include <gtest/gtest.h> +#include <input/InputTransport.h> +#include <utils/Timers.h> +#include <utils/StopWatch.h> +#include <utils/StrongPointer.h> + +namespace android { + +class InputChannelTest : public testing::Test { +protected: + virtual void SetUp() { } + virtual void TearDown() { } +}; + + +TEST_F(InputChannelTest, ConstructorAndDestructor_TakesOwnershipOfFileDescriptors) { + // Our purpose here is to verify that the input channel destructor closes the + // file descriptor provided to it. One easy way is to provide it with one end + // of a pipe and to check for EPIPE on the other end after the channel is destroyed. + Pipe pipe; + + sp<InputChannel> inputChannel = new InputChannel(String8("channel name"), pipe.sendFd); + + EXPECT_STREQ("channel name", inputChannel->getName().string()) + << "channel should have provided name"; + EXPECT_EQ(pipe.sendFd, inputChannel->getFd()) + << "channel should have provided fd"; + + inputChannel.clear(); // destroys input channel + + EXPECT_EQ(-EPIPE, pipe.readSignal()) + << "channel should have closed fd when destroyed"; + + // clean up fds of Pipe endpoints that were closed so we don't try to close them again + pipe.sendFd = -1; +} + +TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) { + sp<InputChannel> serverChannel, clientChannel; + + status_t result = InputChannel::openInputChannelPair(String8("channel name"), + serverChannel, clientChannel); + + ASSERT_EQ(OK, result) + << "should have successfully opened a channel pair"; + + // Name + EXPECT_STREQ("channel name (server)", serverChannel->getName().string()) + << "server channel should have suffixed name"; + EXPECT_STREQ("channel name (client)", clientChannel->getName().string()) + << "client channel should have suffixed name"; + + // Server->Client communication + InputMessage serverMsg; + memset(&serverMsg, 0, sizeof(InputMessage)); + serverMsg.header.type = InputMessage::TYPE_KEY; + serverMsg.body.key.action = AKEY_EVENT_ACTION_DOWN; + EXPECT_EQ(OK, serverChannel->sendMessage(&serverMsg)) + << "server channel should be able to send message to client channel"; + + InputMessage clientMsg; + EXPECT_EQ(OK, clientChannel->receiveMessage(&clientMsg)) + << "client channel should be able to receive message from server channel"; + EXPECT_EQ(serverMsg.header.type, clientMsg.header.type) + << "client channel should receive the correct message from server channel"; + EXPECT_EQ(serverMsg.body.key.action, clientMsg.body.key.action) + << "client channel should receive the correct message from server channel"; + + // Client->Server communication + InputMessage clientReply; + memset(&clientReply, 0, sizeof(InputMessage)); + clientReply.header.type = InputMessage::TYPE_FINISHED; + clientReply.body.finished.seq = 0x11223344; + clientReply.body.finished.handled = true; + EXPECT_EQ(OK, clientChannel->sendMessage(&clientReply)) + << "client channel should be able to send message to server channel"; + + InputMessage serverReply; + EXPECT_EQ(OK, serverChannel->receiveMessage(&serverReply)) + << "server channel should be able to receive message from client channel"; + EXPECT_EQ(clientReply.header.type, serverReply.header.type) + << "server channel should receive the correct message from client channel"; + EXPECT_EQ(clientReply.body.finished.seq, serverReply.body.finished.seq) + << "server channel should receive the correct message from client channel"; + EXPECT_EQ(clientReply.body.finished.handled, serverReply.body.finished.handled) + << "server channel should receive the correct message from client channel"; +} + +TEST_F(InputChannelTest, ReceiveSignal_WhenNoSignalPresent_ReturnsAnError) { + sp<InputChannel> serverChannel, clientChannel; + + status_t result = InputChannel::openInputChannelPair(String8("channel name"), + serverChannel, clientChannel); + + ASSERT_EQ(OK, result) + << "should have successfully opened a channel pair"; + + InputMessage msg; + EXPECT_EQ(WOULD_BLOCK, clientChannel->receiveMessage(&msg)) + << "receiveMessage should have returned WOULD_BLOCK"; +} + +TEST_F(InputChannelTest, ReceiveSignal_WhenPeerClosed_ReturnsAnError) { + sp<InputChannel> serverChannel, clientChannel; + + status_t result = InputChannel::openInputChannelPair(String8("channel name"), + serverChannel, clientChannel); + + ASSERT_EQ(OK, result) + << "should have successfully opened a channel pair"; + + serverChannel.clear(); // close server channel + + InputMessage msg; + EXPECT_EQ(DEAD_OBJECT, clientChannel->receiveMessage(&msg)) + << "receiveMessage should have returned DEAD_OBJECT"; +} + +TEST_F(InputChannelTest, SendSignal_WhenPeerClosed_ReturnsAnError) { + sp<InputChannel> serverChannel, clientChannel; + + status_t result = InputChannel::openInputChannelPair(String8("channel name"), + serverChannel, clientChannel); + + ASSERT_EQ(OK, result) + << "should have successfully opened a channel pair"; + + serverChannel.clear(); // close server channel + + InputMessage msg; + msg.header.type = InputMessage::TYPE_KEY; + EXPECT_EQ(DEAD_OBJECT, clientChannel->sendMessage(&msg)) + << "sendMessage should have returned DEAD_OBJECT"; +} + + +} // namespace android diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp new file mode 100644 index 0000000..78ea98e --- /dev/null +++ b/libs/input/tests/InputEvent_test.cpp @@ -0,0 +1,594 @@ +/* + * 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 <math.h> + +#include <binder/Parcel.h> +#include <gtest/gtest.h> +#include <input/Input.h> + +namespace android { + +class BaseTest : public testing::Test { +protected: + virtual void SetUp() { } + virtual void TearDown() { } +}; + +// --- PointerCoordsTest --- + +class PointerCoordsTest : public BaseTest { +}; + +TEST_F(PointerCoordsTest, ClearSetsBitsToZero) { + PointerCoords coords; + coords.clear(); + + ASSERT_EQ(0ULL, coords.bits); +} + +TEST_F(PointerCoordsTest, AxisValues) { + float* valuePtr; + PointerCoords coords; + coords.clear(); + + // Check invariants when no axes are present. + ASSERT_EQ(0, coords.getAxisValue(0)) + << "getAxisValue should return zero because axis is not present"; + ASSERT_EQ(0, coords.getAxisValue(1)) + << "getAxisValue should return zero because axis is not present"; + + // Set first axis. + ASSERT_EQ(OK, coords.setAxisValue(1, 5)); + ASSERT_EQ(0x00000002ULL, coords.bits); + ASSERT_EQ(5, coords.values[0]); + + ASSERT_EQ(0, coords.getAxisValue(0)) + << "getAxisValue should return zero because axis is not present"; + ASSERT_EQ(5, coords.getAxisValue(1)) + << "getAxisValue should return value of axis"; + + // Set an axis with a higher id than all others. (appending value at the end) + ASSERT_EQ(OK, coords.setAxisValue(3, 2)); + ASSERT_EQ(0x0000000aULL, coords.bits); + ASSERT_EQ(5, coords.values[0]); + ASSERT_EQ(2, coords.values[1]); + + ASSERT_EQ(0, coords.getAxisValue(0)) + << "getAxisValue should return zero because axis is not present"; + ASSERT_EQ(5, coords.getAxisValue(1)) + << "getAxisValue should return value of axis"; + ASSERT_EQ(0, coords.getAxisValue(2)) + << "getAxisValue should return zero because axis is not present"; + ASSERT_EQ(2, coords.getAxisValue(3)) + << "getAxisValue should return value of axis"; + + // Set an axis with an id lower than all others. (prepending value at beginning) + ASSERT_EQ(OK, coords.setAxisValue(0, 4)); + ASSERT_EQ(0x0000000bULL, coords.bits); + ASSERT_EQ(4, coords.values[0]); + ASSERT_EQ(5, coords.values[1]); + ASSERT_EQ(2, coords.values[2]); + + ASSERT_EQ(4, coords.getAxisValue(0)) + << "getAxisValue should return value of axis"; + ASSERT_EQ(5, coords.getAxisValue(1)) + << "getAxisValue should return value of axis"; + ASSERT_EQ(0, coords.getAxisValue(2)) + << "getAxisValue should return zero because axis is not present"; + ASSERT_EQ(2, coords.getAxisValue(3)) + << "getAxisValue should return value of axis"; + + // Set an axis with an id between the others. (inserting value in the middle) + ASSERT_EQ(OK, coords.setAxisValue(2, 1)); + ASSERT_EQ(0x0000000fULL, coords.bits); + ASSERT_EQ(4, coords.values[0]); + ASSERT_EQ(5, coords.values[1]); + ASSERT_EQ(1, coords.values[2]); + ASSERT_EQ(2, coords.values[3]); + + ASSERT_EQ(4, coords.getAxisValue(0)) + << "getAxisValue should return value of axis"; + ASSERT_EQ(5, coords.getAxisValue(1)) + << "getAxisValue should return value of axis"; + ASSERT_EQ(1, coords.getAxisValue(2)) + << "getAxisValue should return value of axis"; + ASSERT_EQ(2, coords.getAxisValue(3)) + << "getAxisValue should return value of axis"; + + // Set an existing axis value in place. + ASSERT_EQ(OK, coords.setAxisValue(1, 6)); + ASSERT_EQ(0x0000000fULL, coords.bits); + ASSERT_EQ(4, coords.values[0]); + ASSERT_EQ(6, coords.values[1]); + ASSERT_EQ(1, coords.values[2]); + ASSERT_EQ(2, coords.values[3]); + + ASSERT_EQ(4, coords.getAxisValue(0)) + << "getAxisValue should return value of axis"; + ASSERT_EQ(6, coords.getAxisValue(1)) + << "getAxisValue should return value of axis"; + ASSERT_EQ(1, coords.getAxisValue(2)) + << "getAxisValue should return value of axis"; + ASSERT_EQ(2, coords.getAxisValue(3)) + << "getAxisValue should return value of axis"; + + // Set maximum number of axes. + for (size_t axis = 4; axis < PointerCoords::MAX_AXES; axis++) { + ASSERT_EQ(OK, coords.setAxisValue(axis, axis)); + } + ASSERT_EQ(PointerCoords::MAX_AXES, __builtin_popcountll(coords.bits)); + + // Try to set one more axis beyond maximum number. + // Ensure bits are unchanged. + ASSERT_EQ(NO_MEMORY, coords.setAxisValue(PointerCoords::MAX_AXES, 100)); + ASSERT_EQ(PointerCoords::MAX_AXES, __builtin_popcountll(coords.bits)); +} + +TEST_F(PointerCoordsTest, Parcel) { + Parcel parcel; + + PointerCoords inCoords; + inCoords.clear(); + PointerCoords outCoords; + + // Round trip with empty coords. + inCoords.writeToParcel(&parcel); + parcel.setDataPosition(0); + outCoords.readFromParcel(&parcel); + + ASSERT_EQ(0ULL, outCoords.bits); + + // Round trip with some values. + parcel.freeData(); + inCoords.setAxisValue(2, 5); + inCoords.setAxisValue(5, 8); + + inCoords.writeToParcel(&parcel); + parcel.setDataPosition(0); + outCoords.readFromParcel(&parcel); + + ASSERT_EQ(outCoords.bits, inCoords.bits); + ASSERT_EQ(outCoords.values[0], inCoords.values[0]); + ASSERT_EQ(outCoords.values[1], inCoords.values[1]); +} + + +// --- KeyEventTest --- + +class KeyEventTest : public BaseTest { +}; + +TEST_F(KeyEventTest, Properties) { + KeyEvent event; + + // Initialize and get properties. + const nsecs_t ARBITRARY_DOWN_TIME = 1; + const nsecs_t ARBITRARY_EVENT_TIME = 2; + event.initialize(2, AINPUT_SOURCE_GAMEPAD, AKEY_EVENT_ACTION_DOWN, + AKEY_EVENT_FLAG_FROM_SYSTEM, AKEYCODE_BUTTON_X, 121, + AMETA_ALT_ON, 1, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME); + + ASSERT_EQ(AINPUT_EVENT_TYPE_KEY, event.getType()); + ASSERT_EQ(2, event.getDeviceId()); + ASSERT_EQ(AINPUT_SOURCE_GAMEPAD, event.getSource()); + ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, event.getAction()); + ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, event.getFlags()); + ASSERT_EQ(AKEYCODE_BUTTON_X, event.getKeyCode()); + ASSERT_EQ(121, event.getScanCode()); + ASSERT_EQ(AMETA_ALT_ON, event.getMetaState()); + ASSERT_EQ(1, event.getRepeatCount()); + ASSERT_EQ(ARBITRARY_DOWN_TIME, event.getDownTime()); + ASSERT_EQ(ARBITRARY_EVENT_TIME, event.getEventTime()); + + // Set source. + event.setSource(AINPUT_SOURCE_JOYSTICK); + ASSERT_EQ(AINPUT_SOURCE_JOYSTICK, event.getSource()); +} + + +// --- MotionEventTest --- + +class MotionEventTest : public BaseTest { +protected: + static const nsecs_t ARBITRARY_DOWN_TIME; + static const nsecs_t ARBITRARY_EVENT_TIME; + static const float X_OFFSET; + static const float Y_OFFSET; + + void initializeEventWithHistory(MotionEvent* event); + void assertEqualsEventWithHistory(const MotionEvent* event); +}; + +const nsecs_t MotionEventTest::ARBITRARY_DOWN_TIME = 1; +const nsecs_t MotionEventTest::ARBITRARY_EVENT_TIME = 2; +const float MotionEventTest::X_OFFSET = 1.0f; +const float MotionEventTest::Y_OFFSET = 1.1f; + +void MotionEventTest::initializeEventWithHistory(MotionEvent* event) { + PointerProperties pointerProperties[2]; + pointerProperties[0].clear(); + pointerProperties[0].id = 1; + pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + pointerProperties[1].clear(); + pointerProperties[1].id = 2; + pointerProperties[1].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS; + + PointerCoords pointerCoords[2]; + pointerCoords[0].clear(); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 10); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 11); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 12); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 13); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 14); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 15); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 16); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 17); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 18); + pointerCoords[1].clear(); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 20); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 21); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 22); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 23); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 24); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 25); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 26); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 27); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 28); + event->initialize(2, AINPUT_SOURCE_TOUCHSCREEN, AMOTION_EVENT_ACTION_MOVE, + AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, + AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY, + X_OFFSET, Y_OFFSET, 2.0f, 2.1f, + ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, + 2, pointerProperties, pointerCoords); + + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 110); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 111); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 112); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 113); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 114); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 115); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 116); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 117); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 118); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 120); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 121); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 122); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 123); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 124); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 125); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 126); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 127); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 128); + event->addSample(ARBITRARY_EVENT_TIME + 1, pointerCoords); + + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 210); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 211); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 212); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 213); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 214); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 215); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 216); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 217); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 218); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 220); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 221); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 222); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 223); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 224); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 225); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 226); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 227); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 228); + event->addSample(ARBITRARY_EVENT_TIME + 2, pointerCoords); +} + +void MotionEventTest::assertEqualsEventWithHistory(const MotionEvent* event) { + // Check properties. + ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType()); + ASSERT_EQ(2, event->getDeviceId()); + ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, event->getSource()); + ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, event->getAction()); + ASSERT_EQ(AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, event->getFlags()); + ASSERT_EQ(AMOTION_EVENT_EDGE_FLAG_TOP, event->getEdgeFlags()); + ASSERT_EQ(AMETA_ALT_ON, event->getMetaState()); + ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, event->getButtonState()); + ASSERT_EQ(X_OFFSET, event->getXOffset()); + ASSERT_EQ(Y_OFFSET, event->getYOffset()); + ASSERT_EQ(2.0f, event->getXPrecision()); + ASSERT_EQ(2.1f, event->getYPrecision()); + ASSERT_EQ(ARBITRARY_DOWN_TIME, event->getDownTime()); + + ASSERT_EQ(2U, event->getPointerCount()); + ASSERT_EQ(1, event->getPointerId(0)); + ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, event->getToolType(0)); + ASSERT_EQ(2, event->getPointerId(1)); + ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_STYLUS, event->getToolType(1)); + + ASSERT_EQ(2U, event->getHistorySize()); + + // Check data. + ASSERT_EQ(ARBITRARY_EVENT_TIME, event->getHistoricalEventTime(0)); + ASSERT_EQ(ARBITRARY_EVENT_TIME + 1, event->getHistoricalEventTime(1)); + ASSERT_EQ(ARBITRARY_EVENT_TIME + 2, event->getEventTime()); + + ASSERT_EQ(11, event->getHistoricalRawPointerCoords(0, 0)-> + getAxisValue(AMOTION_EVENT_AXIS_Y)); + ASSERT_EQ(21, event->getHistoricalRawPointerCoords(1, 0)-> + getAxisValue(AMOTION_EVENT_AXIS_Y)); + ASSERT_EQ(111, event->getHistoricalRawPointerCoords(0, 1)-> + getAxisValue(AMOTION_EVENT_AXIS_Y)); + ASSERT_EQ(121, event->getHistoricalRawPointerCoords(1, 1)-> + getAxisValue(AMOTION_EVENT_AXIS_Y)); + ASSERT_EQ(211, event->getRawPointerCoords(0)-> + getAxisValue(AMOTION_EVENT_AXIS_Y)); + ASSERT_EQ(221, event->getRawPointerCoords(1)-> + getAxisValue(AMOTION_EVENT_AXIS_Y)); + + ASSERT_EQ(11, event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 0)); + ASSERT_EQ(21, event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 0)); + ASSERT_EQ(111, event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 1)); + ASSERT_EQ(121, event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 1)); + ASSERT_EQ(211, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 0)); + ASSERT_EQ(221, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 1)); + + ASSERT_EQ(10, event->getHistoricalRawX(0, 0)); + ASSERT_EQ(20, event->getHistoricalRawX(1, 0)); + ASSERT_EQ(110, event->getHistoricalRawX(0, 1)); + ASSERT_EQ(120, event->getHistoricalRawX(1, 1)); + ASSERT_EQ(210, event->getRawX(0)); + ASSERT_EQ(220, event->getRawX(1)); + + ASSERT_EQ(11, event->getHistoricalRawY(0, 0)); + ASSERT_EQ(21, event->getHistoricalRawY(1, 0)); + ASSERT_EQ(111, event->getHistoricalRawY(0, 1)); + ASSERT_EQ(121, event->getHistoricalRawY(1, 1)); + ASSERT_EQ(211, event->getRawY(0)); + ASSERT_EQ(221, event->getRawY(1)); + + ASSERT_EQ(X_OFFSET + 10, event->getHistoricalX(0, 0)); + ASSERT_EQ(X_OFFSET + 20, event->getHistoricalX(1, 0)); + ASSERT_EQ(X_OFFSET + 110, event->getHistoricalX(0, 1)); + ASSERT_EQ(X_OFFSET + 120, event->getHistoricalX(1, 1)); + ASSERT_EQ(X_OFFSET + 210, event->getX(0)); + ASSERT_EQ(X_OFFSET + 220, event->getX(1)); + + ASSERT_EQ(Y_OFFSET + 11, event->getHistoricalY(0, 0)); + ASSERT_EQ(Y_OFFSET + 21, event->getHistoricalY(1, 0)); + ASSERT_EQ(Y_OFFSET + 111, event->getHistoricalY(0, 1)); + ASSERT_EQ(Y_OFFSET + 121, event->getHistoricalY(1, 1)); + ASSERT_EQ(Y_OFFSET + 211, event->getY(0)); + ASSERT_EQ(Y_OFFSET + 221, event->getY(1)); + + ASSERT_EQ(12, event->getHistoricalPressure(0, 0)); + ASSERT_EQ(22, event->getHistoricalPressure(1, 0)); + ASSERT_EQ(112, event->getHistoricalPressure(0, 1)); + ASSERT_EQ(122, event->getHistoricalPressure(1, 1)); + ASSERT_EQ(212, event->getPressure(0)); + ASSERT_EQ(222, event->getPressure(1)); + + ASSERT_EQ(13, event->getHistoricalSize(0, 0)); + ASSERT_EQ(23, event->getHistoricalSize(1, 0)); + ASSERT_EQ(113, event->getHistoricalSize(0, 1)); + ASSERT_EQ(123, event->getHistoricalSize(1, 1)); + ASSERT_EQ(213, event->getSize(0)); + ASSERT_EQ(223, event->getSize(1)); + + ASSERT_EQ(14, event->getHistoricalTouchMajor(0, 0)); + ASSERT_EQ(24, event->getHistoricalTouchMajor(1, 0)); + ASSERT_EQ(114, event->getHistoricalTouchMajor(0, 1)); + ASSERT_EQ(124, event->getHistoricalTouchMajor(1, 1)); + ASSERT_EQ(214, event->getTouchMajor(0)); + ASSERT_EQ(224, event->getTouchMajor(1)); + + ASSERT_EQ(15, event->getHistoricalTouchMinor(0, 0)); + ASSERT_EQ(25, event->getHistoricalTouchMinor(1, 0)); + ASSERT_EQ(115, event->getHistoricalTouchMinor(0, 1)); + ASSERT_EQ(125, event->getHistoricalTouchMinor(1, 1)); + ASSERT_EQ(215, event->getTouchMinor(0)); + ASSERT_EQ(225, event->getTouchMinor(1)); + + ASSERT_EQ(16, event->getHistoricalToolMajor(0, 0)); + ASSERT_EQ(26, event->getHistoricalToolMajor(1, 0)); + ASSERT_EQ(116, event->getHistoricalToolMajor(0, 1)); + ASSERT_EQ(126, event->getHistoricalToolMajor(1, 1)); + ASSERT_EQ(216, event->getToolMajor(0)); + ASSERT_EQ(226, event->getToolMajor(1)); + + ASSERT_EQ(17, event->getHistoricalToolMinor(0, 0)); + ASSERT_EQ(27, event->getHistoricalToolMinor(1, 0)); + ASSERT_EQ(117, event->getHistoricalToolMinor(0, 1)); + ASSERT_EQ(127, event->getHistoricalToolMinor(1, 1)); + ASSERT_EQ(217, event->getToolMinor(0)); + ASSERT_EQ(227, event->getToolMinor(1)); + + ASSERT_EQ(18, event->getHistoricalOrientation(0, 0)); + ASSERT_EQ(28, event->getHistoricalOrientation(1, 0)); + ASSERT_EQ(118, event->getHistoricalOrientation(0, 1)); + ASSERT_EQ(128, event->getHistoricalOrientation(1, 1)); + ASSERT_EQ(218, event->getOrientation(0)); + ASSERT_EQ(228, event->getOrientation(1)); +} + +TEST_F(MotionEventTest, Properties) { + MotionEvent event; + + // Initialize, add samples and check properties. + initializeEventWithHistory(&event); + ASSERT_NO_FATAL_FAILURE(assertEqualsEventWithHistory(&event)); + + // Set source. + event.setSource(AINPUT_SOURCE_JOYSTICK); + ASSERT_EQ(AINPUT_SOURCE_JOYSTICK, event.getSource()); + + // Set action. + event.setAction(AMOTION_EVENT_ACTION_CANCEL); + ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, event.getAction()); + + // Set meta state. + event.setMetaState(AMETA_CTRL_ON); + ASSERT_EQ(AMETA_CTRL_ON, event.getMetaState()); +} + +TEST_F(MotionEventTest, CopyFrom_KeepHistory) { + MotionEvent event; + initializeEventWithHistory(&event); + + MotionEvent copy; + copy.copyFrom(&event, true /*keepHistory*/); + + ASSERT_NO_FATAL_FAILURE(assertEqualsEventWithHistory(&event)); +} + +TEST_F(MotionEventTest, CopyFrom_DoNotKeepHistory) { + MotionEvent event; + initializeEventWithHistory(&event); + + MotionEvent copy; + copy.copyFrom(&event, false /*keepHistory*/); + + ASSERT_EQ(event.getPointerCount(), copy.getPointerCount()); + ASSERT_EQ(0U, copy.getHistorySize()); + + ASSERT_EQ(event.getPointerId(0), copy.getPointerId(0)); + ASSERT_EQ(event.getPointerId(1), copy.getPointerId(1)); + + ASSERT_EQ(event.getEventTime(), copy.getEventTime()); + + ASSERT_EQ(event.getX(0), copy.getX(0)); +} + +TEST_F(MotionEventTest, OffsetLocation) { + MotionEvent event; + initializeEventWithHistory(&event); + + event.offsetLocation(5.0f, -2.0f); + + ASSERT_EQ(X_OFFSET + 5.0f, event.getXOffset()); + ASSERT_EQ(Y_OFFSET - 2.0f, event.getYOffset()); +} + +TEST_F(MotionEventTest, Scale) { + MotionEvent event; + initializeEventWithHistory(&event); + + event.scale(2.0f); + + ASSERT_EQ(X_OFFSET * 2, event.getXOffset()); + ASSERT_EQ(Y_OFFSET * 2, event.getYOffset()); + + ASSERT_EQ(210 * 2, event.getRawX(0)); + ASSERT_EQ(211 * 2, event.getRawY(0)); + ASSERT_EQ((X_OFFSET + 210) * 2, event.getX(0)); + ASSERT_EQ((Y_OFFSET + 211) * 2, event.getY(0)); + ASSERT_EQ(212, event.getPressure(0)); + ASSERT_EQ(213, event.getSize(0)); + ASSERT_EQ(214 * 2, event.getTouchMajor(0)); + ASSERT_EQ(215 * 2, event.getTouchMinor(0)); + ASSERT_EQ(216 * 2, event.getToolMajor(0)); + ASSERT_EQ(217 * 2, event.getToolMinor(0)); + ASSERT_EQ(218, event.getOrientation(0)); +} + +TEST_F(MotionEventTest, Parcel) { + Parcel parcel; + + MotionEvent inEvent; + initializeEventWithHistory(&inEvent); + MotionEvent outEvent; + + // Round trip. + inEvent.writeToParcel(&parcel); + parcel.setDataPosition(0); + outEvent.readFromParcel(&parcel); + + ASSERT_NO_FATAL_FAILURE(assertEqualsEventWithHistory(&outEvent)); +} + +static void setRotationMatrix(float matrix[9], float angle) { + float sin = sinf(angle); + float cos = cosf(angle); + matrix[0] = cos; + matrix[1] = -sin; + matrix[2] = 0; + matrix[3] = sin; + matrix[4] = cos; + matrix[5] = 0; + matrix[6] = 0; + matrix[7] = 0; + matrix[8] = 1.0f; +} + +TEST_F(MotionEventTest, Transform) { + // Generate some points on a circle. + // Each point 'i' is a point on a circle of radius ROTATION centered at (3,2) at an angle + // of ARC * i degrees clockwise relative to the Y axis. + // The geometrical representation is irrelevant to the test, it's just easy to generate + // and check rotation. We set the orientation to the same angle. + // Coordinate system: down is increasing Y, right is increasing X. + const float PI_180 = float(M_PI / 180); + const float RADIUS = 10; + const float ARC = 36; + const float ROTATION = ARC * 2; + + const size_t pointerCount = 11; + PointerProperties pointerProperties[pointerCount]; + PointerCoords pointerCoords[pointerCount]; + for (size_t i = 0; i < pointerCount; i++) { + float angle = float(i * ARC * PI_180); + pointerProperties[i].clear(); + pointerProperties[i].id = i; + pointerCoords[i].clear(); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X, sinf(angle) * RADIUS + 3); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y, -cosf(angle) * RADIUS + 2); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, angle); + } + MotionEvent event; + event.initialize(0, 0, AMOTION_EVENT_ACTION_MOVE, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, pointerCount, pointerProperties, pointerCoords); + float originalRawX = 0 + 3; + float originalRawY = -RADIUS + 2; + + // Check original raw X and Y assumption. + ASSERT_NEAR(originalRawX, event.getRawX(0), 0.001); + ASSERT_NEAR(originalRawY, event.getRawY(0), 0.001); + + // Now translate the motion event so the circle's origin is at (0,0). + event.offsetLocation(-3, -2); + + // Offsetting the location should preserve the raw X and Y of the first point. + ASSERT_NEAR(originalRawX, event.getRawX(0), 0.001); + ASSERT_NEAR(originalRawY, event.getRawY(0), 0.001); + + // Apply a rotation about the origin by ROTATION degrees clockwise. + float matrix[9]; + setRotationMatrix(matrix, ROTATION * PI_180); + event.transform(matrix); + + // Check the points. + for (size_t i = 0; i < pointerCount; i++) { + float angle = float((i * ARC + ROTATION) * PI_180); + ASSERT_NEAR(sinf(angle) * RADIUS, event.getX(i), 0.001); + ASSERT_NEAR(-cosf(angle) * RADIUS, event.getY(i), 0.001); + ASSERT_NEAR(tanf(angle), tanf(event.getOrientation(i)), 0.1); + } + + // Applying the transformation should preserve the raw X and Y of the first point. + ASSERT_NEAR(originalRawX, event.getRawX(0), 0.001); + ASSERT_NEAR(originalRawY, event.getRawY(0), 0.001); +} + +} // namespace android diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp new file mode 100644 index 0000000..de192f1 --- /dev/null +++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp @@ -0,0 +1,288 @@ +/* + * 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. + */ + +#include "TestHelpers.h" + +#include <unistd.h> +#include <sys/mman.h> +#include <time.h> + +#include <cutils/ashmem.h> +#include <gtest/gtest.h> +#include <input/InputTransport.h> +#include <utils/Timers.h> +#include <utils/StopWatch.h> + +namespace android { + +class InputPublisherAndConsumerTest : public testing::Test { +protected: + sp<InputChannel> serverChannel, clientChannel; + InputPublisher* mPublisher; + InputConsumer* mConsumer; + PreallocatedInputEventFactory mEventFactory; + + virtual void SetUp() { + status_t result = InputChannel::openInputChannelPair(String8("channel name"), + serverChannel, clientChannel); + + mPublisher = new InputPublisher(serverChannel); + mConsumer = new InputConsumer(clientChannel); + } + + virtual void TearDown() { + if (mPublisher) { + delete mPublisher; + mPublisher = NULL; + } + + if (mConsumer) { + delete mConsumer; + mConsumer = NULL; + } + + serverChannel.clear(); + clientChannel.clear(); + } + + void PublishAndConsumeKeyEvent(); + void PublishAndConsumeMotionEvent(); +}; + +TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) { + EXPECT_EQ(serverChannel.get(), mPublisher->getChannel().get()); + EXPECT_EQ(clientChannel.get(), mConsumer->getChannel().get()); +} + +void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() { + status_t status; + + const uint32_t seq = 15; + const int32_t deviceId = 1; + const int32_t source = AINPUT_SOURCE_KEYBOARD; + const int32_t action = AKEY_EVENT_ACTION_DOWN; + const int32_t flags = AKEY_EVENT_FLAG_FROM_SYSTEM; + const int32_t keyCode = AKEYCODE_ENTER; + const int32_t scanCode = 13; + const int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON; + const int32_t repeatCount = 1; + const nsecs_t downTime = 3; + const nsecs_t eventTime = 4; + + status = mPublisher->publishKeyEvent(seq, deviceId, source, action, flags, + keyCode, scanCode, metaState, repeatCount, downTime, eventTime); + ASSERT_EQ(OK, status) + << "publisher publishKeyEvent should return OK"; + + uint32_t consumeSeq; + InputEvent* event; + status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event); + ASSERT_EQ(OK, status) + << "consumer consume should return OK"; + + ASSERT_TRUE(event != NULL) + << "consumer should have returned non-NULL event"; + ASSERT_EQ(AINPUT_EVENT_TYPE_KEY, event->getType()) + << "consumer should have returned a key event"; + + KeyEvent* keyEvent = static_cast<KeyEvent*>(event); + EXPECT_EQ(seq, consumeSeq); + EXPECT_EQ(deviceId, keyEvent->getDeviceId()); + EXPECT_EQ(source, keyEvent->getSource()); + EXPECT_EQ(action, keyEvent->getAction()); + EXPECT_EQ(flags, keyEvent->getFlags()); + EXPECT_EQ(keyCode, keyEvent->getKeyCode()); + EXPECT_EQ(scanCode, keyEvent->getScanCode()); + EXPECT_EQ(metaState, keyEvent->getMetaState()); + EXPECT_EQ(repeatCount, keyEvent->getRepeatCount()); + EXPECT_EQ(downTime, keyEvent->getDownTime()); + EXPECT_EQ(eventTime, keyEvent->getEventTime()); + + status = mConsumer->sendFinishedSignal(seq, true); + ASSERT_EQ(OK, status) + << "consumer sendFinishedSignal should return OK"; + + uint32_t finishedSeq = 0; + bool handled = false; + status = mPublisher->receiveFinishedSignal(&finishedSeq, &handled); + ASSERT_EQ(OK, status) + << "publisher receiveFinishedSignal should return OK"; + ASSERT_EQ(seq, finishedSeq) + << "publisher receiveFinishedSignal should have returned the original sequence number"; + ASSERT_TRUE(handled) + << "publisher receiveFinishedSignal should have set handled to consumer's reply"; +} + +void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { + status_t status; + + const uint32_t seq = 15; + const int32_t deviceId = 1; + const int32_t source = AINPUT_SOURCE_TOUCHSCREEN; + const int32_t action = AMOTION_EVENT_ACTION_MOVE; + const int32_t flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED; + const int32_t edgeFlags = AMOTION_EVENT_EDGE_FLAG_TOP; + const int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON; + const int32_t buttonState = AMOTION_EVENT_BUTTON_PRIMARY; + const float xOffset = -10; + const float yOffset = -20; + const float xPrecision = 0.25; + const float yPrecision = 0.5; + const nsecs_t downTime = 3; + const size_t pointerCount = 3; + const nsecs_t eventTime = 4; + PointerProperties pointerProperties[pointerCount]; + PointerCoords pointerCoords[pointerCount]; + for (size_t i = 0; i < pointerCount; i++) { + pointerProperties[i].clear(); + pointerProperties[i].id = (i + 2) % pointerCount; + pointerProperties[i].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + + pointerCoords[i].clear(); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X, 100 * i); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y, 200 * i); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.5 * i); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 0.7 * i); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 1.5 * i); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 1.7 * i); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 2.5 * i); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 2.7 * i); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 3.5 * i); + } + + status = mPublisher->publishMotionEvent(seq, deviceId, source, action, flags, edgeFlags, + metaState, buttonState, xOffset, yOffset, xPrecision, yPrecision, + downTime, eventTime, pointerCount, + pointerProperties, pointerCoords); + ASSERT_EQ(OK, status) + << "publisher publishMotionEvent should return OK"; + + uint32_t consumeSeq; + InputEvent* event; + status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event); + ASSERT_EQ(OK, status) + << "consumer consume should return OK"; + + ASSERT_TRUE(event != NULL) + << "consumer should have returned non-NULL event"; + ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType()) + << "consumer should have returned a motion event"; + + MotionEvent* motionEvent = static_cast<MotionEvent*>(event); + EXPECT_EQ(seq, consumeSeq); + EXPECT_EQ(deviceId, motionEvent->getDeviceId()); + EXPECT_EQ(source, motionEvent->getSource()); + EXPECT_EQ(action, motionEvent->getAction()); + EXPECT_EQ(flags, motionEvent->getFlags()); + EXPECT_EQ(edgeFlags, motionEvent->getEdgeFlags()); + EXPECT_EQ(metaState, motionEvent->getMetaState()); + EXPECT_EQ(buttonState, motionEvent->getButtonState()); + EXPECT_EQ(xPrecision, motionEvent->getXPrecision()); + EXPECT_EQ(yPrecision, motionEvent->getYPrecision()); + EXPECT_EQ(downTime, motionEvent->getDownTime()); + EXPECT_EQ(eventTime, motionEvent->getEventTime()); + EXPECT_EQ(pointerCount, motionEvent->getPointerCount()); + EXPECT_EQ(0U, motionEvent->getHistorySize()); + + for (size_t i = 0; i < pointerCount; i++) { + SCOPED_TRACE(i); + EXPECT_EQ(pointerProperties[i].id, motionEvent->getPointerId(i)); + EXPECT_EQ(pointerProperties[i].toolType, motionEvent->getToolType(i)); + + EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X), + motionEvent->getRawX(i)); + EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y), + motionEvent->getRawY(i)); + EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X) + xOffset, + motionEvent->getX(i)); + EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y) + yOffset, + motionEvent->getY(i)); + EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), + motionEvent->getPressure(i)); + EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE), + motionEvent->getSize(i)); + EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), + motionEvent->getTouchMajor(i)); + EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), + motionEvent->getTouchMinor(i)); + EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), + motionEvent->getToolMajor(i)); + EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), + motionEvent->getToolMinor(i)); + EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION), + motionEvent->getOrientation(i)); + } + + status = mConsumer->sendFinishedSignal(seq, false); + ASSERT_EQ(OK, status) + << "consumer sendFinishedSignal should return OK"; + + uint32_t finishedSeq = 0; + bool handled = true; + status = mPublisher->receiveFinishedSignal(&finishedSeq, &handled); + ASSERT_EQ(OK, status) + << "publisher receiveFinishedSignal should return OK"; + ASSERT_EQ(seq, finishedSeq) + << "publisher receiveFinishedSignal should have returned the original sequence number"; + ASSERT_FALSE(handled) + << "publisher receiveFinishedSignal should have set handled to consumer's reply"; +} + +TEST_F(InputPublisherAndConsumerTest, PublishKeyEvent_EndToEnd) { + ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent()); +} + +TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_EndToEnd) { + ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent()); +} + +TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenPointerCountLessThan1_ReturnsError) { + status_t status; + const size_t pointerCount = 0; + PointerProperties pointerProperties[pointerCount]; + PointerCoords pointerCoords[pointerCount]; + + status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + pointerCount, pointerProperties, pointerCoords); + ASSERT_EQ(BAD_VALUE, status) + << "publisher publishMotionEvent should return BAD_VALUE"; +} + +TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenPointerCountGreaterThanMax_ReturnsError) { + status_t status; + const size_t pointerCount = MAX_POINTERS + 1; + PointerProperties pointerProperties[pointerCount]; + PointerCoords pointerCoords[pointerCount]; + for (size_t i = 0; i < pointerCount; i++) { + pointerProperties[i].clear(); + pointerCoords[i].clear(); + } + + status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + pointerCount, pointerProperties, pointerCoords); + ASSERT_EQ(BAD_VALUE, status) + << "publisher publishMotionEvent should return BAD_VALUE"; +} + +TEST_F(InputPublisherAndConsumerTest, PublishMultipleEvents_EndToEnd) { + ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent()); + ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent()); + ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent()); + ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent()); + ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent()); +} + +} // namespace android diff --git a/libs/utils/tests/TestHelpers.h b/libs/input/tests/TestHelpers.h index d8e985e..fe87bb9 100644 --- a/libs/utils/tests/TestHelpers.h +++ b/libs/input/tests/TestHelpers.h @@ -17,6 +17,8 @@ #ifndef TESTHELPERS_H #define TESTHELPERS_H +#include <unistd.h> + #include <utils/threads.h> namespace android { diff --git a/libs/ui/Fence.cpp b/libs/ui/Fence.cpp index 464ee86..93ec0ce 100644 --- a/libs/ui/Fence.cpp +++ b/libs/ui/Fence.cpp @@ -127,37 +127,49 @@ nsecs_t Fence::getSignalTime() const { } size_t Fence::getFlattenedSize() const { - return 0; + return 1; } size_t Fence::getFdCount() const { return isValid() ? 1 : 0; } -status_t Fence::flatten(void* buffer, size_t size, int fds[], - size_t count) const { - if (size != getFlattenedSize() || count != getFdCount()) { - return BAD_VALUE; +status_t Fence::flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const { + if (size < getFlattenedSize() || count < getFdCount()) { + return NO_MEMORY; } - + FlattenableUtils::write(buffer, size, getFdCount()); if (isValid()) { - fds[0] = mFenceFd; + *fds++ = mFenceFd; + count--; } return NO_ERROR; } -status_t Fence::unflatten(void const* buffer, size_t size, int fds[], - size_t count) { - if (size != 0 || (count != 0 && count != 1)) { - return BAD_VALUE; - } +status_t Fence::unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count) { if (mFenceFd != -1) { // Don't unflatten if we already have a valid fd. return INVALID_OPERATION; } - if (count == 1) { - mFenceFd = fds[0]; + if (size < 1) { + return NO_MEMORY; + } + + size_t numFds; + FlattenableUtils::read(buffer, size, numFds); + + if (numFds > 1) { + return BAD_VALUE; + } + + if (count < numFds) { + return NO_MEMORY; + } + + if (numFds) { + mFenceFd = *fds++; + count--; } return NO_ERROR; diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp index 580788d..96a7188 100644 --- a/libs/ui/GraphicBuffer.cpp +++ b/libs/ui/GraphicBuffer.cpp @@ -36,8 +36,7 @@ namespace android { GraphicBuffer::GraphicBuffer() : BASE(), mOwner(ownData), mBufferMapper(GraphicBufferMapper::get()), - mInitCheck(NO_ERROR), mIndex(-1) -{ + mInitCheck(NO_ERROR) { width = height = stride = @@ -49,7 +48,7 @@ GraphicBuffer::GraphicBuffer() GraphicBuffer::GraphicBuffer(uint32_t w, uint32_t h, PixelFormat reqFormat, uint32_t reqUsage) : BASE(), mOwner(ownData), mBufferMapper(GraphicBufferMapper::get()), - mInitCheck(NO_ERROR), mIndex(-1) + mInitCheck(NO_ERROR) { width = height = @@ -65,7 +64,7 @@ GraphicBuffer::GraphicBuffer(uint32_t w, uint32_t h, uint32_t inStride, native_handle_t* inHandle, bool keepOwnership) : BASE(), mOwner(keepOwnership ? ownHandle : ownNone), mBufferMapper(GraphicBufferMapper::get()), - mInitCheck(NO_ERROR), mIndex(-1) + mInitCheck(NO_ERROR) { width = w; height = h; @@ -78,7 +77,7 @@ GraphicBuffer::GraphicBuffer(uint32_t w, uint32_t h, GraphicBuffer::GraphicBuffer(ANativeWindowBuffer* buffer, bool keepOwnership) : BASE(), mOwner(keepOwnership ? ownHandle : ownNone), mBufferMapper(GraphicBufferMapper::get()), - mInitCheck(NO_ERROR), mIndex(-1), mWrappedBuffer(buffer) + mInitCheck(NO_ERROR), mWrappedBuffer(buffer) { width = buffer->width; height = buffer->height; @@ -209,9 +208,7 @@ size_t GraphicBuffer::getFdCount() const { return handle ? handle->numFds : 0; } -status_t GraphicBuffer::flatten(void* buffer, size_t size, - int fds[], size_t count) const -{ +status_t GraphicBuffer::flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const { size_t sizeNeeded = GraphicBuffer::getFlattenedSize(); if (size < sizeNeeded) return NO_MEMORY; @@ -236,12 +233,16 @@ status_t GraphicBuffer::flatten(void* buffer, size_t size, memcpy(&buf[8], h->data + h->numFds, h->numInts*sizeof(int)); } + buffer = reinterpret_cast<void*>(static_cast<int*>(buffer) + sizeNeeded); + size -= sizeNeeded; + fds += handle->numFds; + count -= handle->numFds; + return NO_ERROR; } -status_t GraphicBuffer::unflatten(void const* buffer, size_t size, - int fds[], size_t count) -{ +status_t GraphicBuffer::unflatten( + void const*& buffer, size_t& size, int const*& fds, size_t& count) { if (size < 8*sizeof(int)) return NO_MEMORY; int const* buf = static_cast<int const*>(buffer); @@ -281,22 +282,20 @@ status_t GraphicBuffer::unflatten(void const* buffer, size_t size, if (handle != 0) { status_t err = mBufferMapper.registerBuffer(handle); if (err != NO_ERROR) { + width = height = stride = format = usage = 0; + handle = NULL; ALOGE("unflatten: registerBuffer failed: %s (%d)", strerror(-err), err); return err; } } - return NO_ERROR; -} - + buffer = reinterpret_cast<void const*>(static_cast<int const*>(buffer) + sizeNeeded); + size -= sizeNeeded; + fds += numFds; + count -= numFds; -void GraphicBuffer::setIndex(int index) { - mIndex = index; -} - -int GraphicBuffer::getIndex() const { - return mIndex; + return NO_ERROR; } // --------------------------------------------------------------------------- diff --git a/libs/ui/PixelFormat.cpp b/libs/ui/PixelFormat.cpp index 3ced41d..d2d103a 100644 --- a/libs/ui/PixelFormat.cpp +++ b/libs/ui/PixelFormat.cpp @@ -21,123 +21,36 @@ namespace android { // ---------------------------------------------------------------------------- -static const int COMPONENT_YUV = 0xFF; - -struct Info { - size_t size; - size_t bitsPerPixel; - struct { - uint8_t ah; - uint8_t al; - uint8_t rh; - uint8_t rl; - uint8_t gh; - uint8_t gl; - uint8_t bh; - uint8_t bl; - }; - uint8_t components; -}; - -static Info const sPixelFormatInfos[] = { - { 0, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0 }, - { 4, 32, {32,24, 8, 0, 16, 8, 24,16 }, PixelFormatInfo::RGBA }, - { 4, 24, { 0, 0, 8, 0, 16, 8, 24,16 }, PixelFormatInfo::RGB }, - { 3, 24, { 0, 0, 8, 0, 16, 8, 24,16 }, PixelFormatInfo::RGB }, - { 2, 16, { 0, 0, 16,11, 11, 5, 5, 0 }, PixelFormatInfo::RGB }, - { 4, 32, {32,24, 24,16, 16, 8, 8, 0 }, PixelFormatInfo::RGBA }, - { 2, 16, { 1, 0, 16,11, 11, 6, 6, 1 }, PixelFormatInfo::RGBA }, - { 2, 16, { 4, 0, 16,12, 12, 8, 8, 4 }, PixelFormatInfo::RGBA }, - { 1, 8, { 8, 0, 0, 0, 0, 0, 0, 0 }, PixelFormatInfo::ALPHA}, - { 1, 8, { 0, 0, 8, 0, 8, 0, 8, 0 }, PixelFormatInfo::L }, - { 2, 16, {16, 8, 8, 0, 8, 0, 8, 0 }, PixelFormatInfo::LA }, - { 1, 8, { 0, 0, 8, 5, 5, 2, 2, 0 }, PixelFormatInfo::RGB }, -}; - -static const Info* gGetPixelFormatTable(size_t* numEntries) { - if (numEntries) { - *numEntries = sizeof(sPixelFormatInfos)/sizeof(Info); - } - return sPixelFormatInfos; -} - -// ---------------------------------------------------------------------------- - -size_t PixelFormatInfo::getScanlineSize(unsigned int width) const -{ - size_t size; - if (components == COMPONENT_YUV) { - // YCbCr formats are different. - size = (width * bitsPerPixel)>>3; - } else { - size = width * bytesPerPixel; +ssize_t bytesPerPixel(PixelFormat format) { + switch (format) { + case PIXEL_FORMAT_RGBA_8888: + case PIXEL_FORMAT_RGBX_8888: + case PIXEL_FORMAT_BGRA_8888: + return 4; + case PIXEL_FORMAT_RGB_888: + return 3; + case PIXEL_FORMAT_RGB_565: + case PIXEL_FORMAT_RGBA_5551: + case PIXEL_FORMAT_RGBA_4444: + return 2; } - return size; + return BAD_VALUE; } -ssize_t bytesPerPixel(PixelFormat format) -{ - PixelFormatInfo info; - status_t err = getPixelFormatInfo(format, &info); - return (err < 0) ? err : info.bytesPerPixel; -} - -ssize_t bitsPerPixel(PixelFormat format) -{ - PixelFormatInfo info; - status_t err = getPixelFormatInfo(format, &info); - return (err < 0) ? err : info.bitsPerPixel; -} - -status_t getPixelFormatInfo(PixelFormat format, PixelFormatInfo* info) -{ - if (format <= 0) - return BAD_VALUE; - - if (info->version != sizeof(PixelFormatInfo)) - return INVALID_OPERATION; - - // YUV format from the HAL are handled here +ssize_t bitsPerPixel(PixelFormat format) { switch (format) { - case HAL_PIXEL_FORMAT_YCbCr_422_SP: - case HAL_PIXEL_FORMAT_YCbCr_422_I: - info->bitsPerPixel = 16; - goto done; - case HAL_PIXEL_FORMAT_YCrCb_420_SP: - case HAL_PIXEL_FORMAT_YV12: - info->bitsPerPixel = 12; - done: - info->format = format; - info->components = COMPONENT_YUV; - info->bytesPerPixel = 1; - info->h_alpha = 0; - info->l_alpha = 0; - info->h_red = info->h_green = info->h_blue = 8; - info->l_red = info->l_green = info->l_blue = 0; - return NO_ERROR; + case PIXEL_FORMAT_RGBA_8888: + case PIXEL_FORMAT_RGBX_8888: + case PIXEL_FORMAT_BGRA_8888: + return 32; + case PIXEL_FORMAT_RGB_888: + return 24; + case PIXEL_FORMAT_RGB_565: + case PIXEL_FORMAT_RGBA_5551: + case PIXEL_FORMAT_RGBA_4444: + return 16; } - - size_t numEntries; - const Info *i = gGetPixelFormatTable(&numEntries) + format; - bool valid = uint32_t(format) < numEntries; - if (!valid) { - return BAD_INDEX; - } - - info->format = format; - info->bytesPerPixel = i->size; - info->bitsPerPixel = i->bitsPerPixel; - info->h_alpha = i->ah; - info->l_alpha = i->al; - info->h_red = i->rh; - info->l_red = i->rl; - info->h_green = i->gh; - info->l_green = i->gl; - info->h_blue = i->bh; - info->l_blue = i->bl; - info->components = i->components; - - return NO_ERROR; + return BAD_VALUE; } // ---------------------------------------------------------------------------- diff --git a/libs/ui/Rect.cpp b/libs/ui/Rect.cpp index c4dd55b..b480f3a 100644 --- a/libs/ui/Rect.cpp +++ b/libs/ui/Rect.cpp @@ -20,11 +20,11 @@ namespace android { static inline int32_t min(int32_t a, int32_t b) { - return (a<b) ? a : b; + return (a < b) ? a : b; } static inline int32_t max(int32_t a, int32_t b) { - return (a>b) ? a : b; + return (a > b) ? a : b; } void Rect::makeInvalid() { @@ -34,18 +34,17 @@ void Rect::makeInvalid() { bottom = -1; } -bool Rect::operator < (const Rect& rhs) const -{ - if (top<rhs.top) { +bool Rect::operator <(const Rect& rhs) const { + if (top < rhs.top) { return true; } else if (top == rhs.top) { if (left < rhs.left) { return true; } else if (left == rhs.left) { - if (bottom<rhs.bottom) { + if (bottom < rhs.bottom) { return true; } else if (bottom == rhs.bottom) { - if (right<rhs.right) { + if (right < rhs.right) { return true; } } @@ -54,8 +53,7 @@ bool Rect::operator < (const Rect& rhs) const return false; } -Rect& Rect::offsetTo(int32_t x, int32_t y) -{ +Rect& Rect::offsetTo(int32_t x, int32_t y) { right -= left - x; bottom -= top - y; left = x; @@ -63,45 +61,41 @@ Rect& Rect::offsetTo(int32_t x, int32_t y) return *this; } -Rect& Rect::offsetBy(int32_t x, int32_t y) -{ +Rect& Rect::offsetBy(int32_t x, int32_t y) { left += x; - top += y; - right+= x; - bottom+=y; + top += y; + right += x; + bottom += y; return *this; } -const Rect Rect::operator + (const Point& rhs) const -{ - const Rect result(left+rhs.x, top+rhs.y, right+rhs.x, bottom+rhs.y); +const Rect Rect::operator +(const Point& rhs) const { + const Rect result(left + rhs.x, top + rhs.y, right + rhs.x, bottom + rhs.y); return result; } -const Rect Rect::operator - (const Point& rhs) const -{ - const Rect result(left-rhs.x, top-rhs.y, right-rhs.x, bottom-rhs.y); +const Rect Rect::operator -(const Point& rhs) const { + const Rect result(left - rhs.x, top - rhs.y, right - rhs.x, bottom - rhs.y); return result; } -bool Rect::intersect(const Rect& with, Rect* result) const -{ - result->left = max(left, with.left); - result->top = max(top, with.top); - result->right = min(right, with.right); - result->bottom = min(bottom, with.bottom); +bool Rect::intersect(const Rect& with, Rect* result) const { + result->left = max(left, with.left); + result->top = max(top, with.top); + result->right = min(right, with.right); + result->bottom = min(bottom, with.bottom); return !(result->isEmpty()); } Rect Rect::transform(uint32_t xform, int32_t width, int32_t height) const { Rect result(*this); if (xform & HAL_TRANSFORM_FLIP_H) { - result = Rect(width - result.right, result.top, - width - result.left, result.bottom); + result = Rect(width - result.right, result.top, width - result.left, + result.bottom); } if (xform & HAL_TRANSFORM_FLIP_V) { - result = Rect(result.left, height - result.bottom, - result.right, height - result.top); + result = Rect(result.left, height - result.bottom, result.right, + height - result.top); } if (xform & HAL_TRANSFORM_ROT_90) { int left = height - result.bottom; @@ -113,4 +107,35 @@ Rect Rect::transform(uint32_t xform, int32_t width, int32_t height) const { return result; } +Rect Rect::reduce(const Rect& exclude) const { + Rect result; + + uint32_t mask = 0; + mask |= (exclude.left > left) ? 1 : 0; + mask |= (exclude.top > top) ? 2 : 0; + mask |= (exclude.right < right) ? 4 : 0; + mask |= (exclude.bottom < bottom) ? 8 : 0; + + if (mask == 0) { + // crop entirely covers us + result.clear(); + } else { + result = *this; + if (!(mask & (mask - 1))) { + // power-of-2, i.e.: just one bit is set + if (mask & 1) { + result.right = exclude.left; + } else if (mask & 2) { + result.bottom = exclude.top; + } else if (mask & 4) { + result.left = exclude.right; + } else if (mask & 8) { + result.top = exclude.bottom; + } + } + } + + return result; +} + }; // namespace android diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp index bf01488..e5abcf5 100644 --- a/libs/ui/Region.cpp +++ b/libs/ui/Region.cpp @@ -697,7 +697,7 @@ void Region::translate(Region& reg, int dx, int dy) size_t count = reg.mStorage.size(); Rect* rects = reg.mStorage.editArray(); while (count) { - rects->translate(dx, dy); + rects->offsetBy(dx, dy); rects++; count--; } @@ -715,14 +715,17 @@ void Region::translate(Region& dst, const Region& reg, int dx, int dy) // ---------------------------------------------------------------------------- -size_t Region::getSize() const { +size_t Region::getFlattenedSize() const { return mStorage.size() * sizeof(Rect); } -status_t Region::flatten(void* buffer) const { +status_t Region::flatten(void* buffer, size_t size) const { #if VALIDATE_REGIONS validate(*this, "Region::flatten"); #endif + if (size < mStorage.size() * sizeof(Rect)) { + return NO_MEMORY; + } Rect* rects = reinterpret_cast<Rect*>(buffer); memcpy(rects, mStorage.array(), mStorage.size() * sizeof(Rect)); return NO_ERROR; diff --git a/libs/ui/tests/Android.mk b/libs/ui/tests/Android.mk index 8b8e1d8..6f62a55 100644 --- a/libs/ui/tests/Android.mk +++ b/libs/ui/tests/Android.mk @@ -4,9 +4,12 @@ include $(CLEAR_VARS) # Build the unit tests. test_src_files := \ - Region_test.cpp + Region_test.cpp \ + vec_test.cpp \ + mat_test.cpp shared_libraries := \ + libutils \ libui static_libraries := \ diff --git a/libs/ui/tests/mat_test.cpp b/libs/ui/tests/mat_test.cpp new file mode 100644 index 0000000..a2c63ac --- /dev/null +++ b/libs/ui/tests/mat_test.cpp @@ -0,0 +1,139 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "RegionTest" + +#include <stdlib.h> +#include <ui/Region.h> +#include <ui/Rect.h> +#include <gtest/gtest.h> + +#include <ui/mat4.h> + +namespace android { + +class MatTest : public testing::Test { +protected: +}; + +TEST_F(MatTest, Basics) { + mat4 m0; + EXPECT_EQ(sizeof(mat4), sizeof(float)*16); +} + +TEST_F(MatTest, ComparisonOps) { + mat4 m0; + mat4 m1(2); + + EXPECT_TRUE(m0 == m0); + EXPECT_TRUE(m0 != m1); + EXPECT_FALSE(m0 != m0); + EXPECT_FALSE(m0 == m1); +} + +TEST_F(MatTest, Constructors) { + mat4 m0; + ASSERT_EQ(m0[0].x, 1); + ASSERT_EQ(m0[0].y, 0); + ASSERT_EQ(m0[0].z, 0); + ASSERT_EQ(m0[0].w, 0); + ASSERT_EQ(m0[1].x, 0); + ASSERT_EQ(m0[1].y, 1); + ASSERT_EQ(m0[1].z, 0); + ASSERT_EQ(m0[1].w, 0); + ASSERT_EQ(m0[2].x, 0); + ASSERT_EQ(m0[2].y, 0); + ASSERT_EQ(m0[2].z, 1); + ASSERT_EQ(m0[2].w, 0); + ASSERT_EQ(m0[3].x, 0); + ASSERT_EQ(m0[3].y, 0); + ASSERT_EQ(m0[3].z, 0); + ASSERT_EQ(m0[3].w, 1); + + mat4 m1(2); + mat4 m2(vec4(2)); + mat4 m3(m2); + + EXPECT_EQ(m1, m2); + EXPECT_EQ(m2, m3); + EXPECT_EQ(m3, m1); + + mat4 m4(vec4(1), vec4(2), vec4(3), vec4(4)); +} + +TEST_F(MatTest, ArithmeticOps) { + mat4 m0; + mat4 m1(2); + mat4 m2(vec4(2)); + + m1 += m2; + EXPECT_EQ(mat4(4), m1); + + m2 -= m1; + EXPECT_EQ(mat4(-2), m2); + + m1 *= 2; + EXPECT_EQ(mat4(8), m1); + + m1 /= 2; + EXPECT_EQ(mat4(4), m1); + + m0 = -m0; + EXPECT_EQ(mat4(-1), m0); +} + +TEST_F(MatTest, UnaryOps) { + const mat4 identity; + mat4 m0; + + ++m0; + EXPECT_EQ(mat4( vec4(2,1,1,1), vec4(1,2,1,1), vec4(1,1,2,1), vec4(1,1,1,2) ), m0); + EXPECT_EQ(mat4( -vec4(2,1,1,1), -vec4(1,2,1,1), -vec4(1,1,2,1), -vec4(1,1,1,2) ), -m0); + + --m0; + EXPECT_EQ(identity, m0); +} + +TEST_F(MatTest, MiscOps) { + const mat4 identity; + mat4 m0; + EXPECT_EQ(4, trace(m0)); + + mat4 m1(vec4(1,2,3,4), vec4(5,6,7,8), vec4(9,10,11,12), vec4(13,14,15,16)); + mat4 m2(vec4(1,5,9,13), vec4(2,6,10,14), vec4(3,7,11,15), vec4(4,8,12,16)); + EXPECT_EQ(m1, transpose(m2)); + EXPECT_EQ(m2, transpose(m1)); + EXPECT_EQ(vec4(1,6,11,16), diag(m1)); + + EXPECT_EQ(identity, inverse(identity)); + + mat4 m3(vec4(4,3,0,0), vec4(3,2,0,0), vec4(0,0,1,0), vec4(0,0,0,1)); + mat4 m3i(inverse(m3)); + EXPECT_FLOAT_EQ(-2, m3i[0][0]); + EXPECT_FLOAT_EQ( 3, m3i[0][1]); + EXPECT_FLOAT_EQ( 3, m3i[1][0]); + EXPECT_FLOAT_EQ(-4, m3i[1][1]); + + mat4 m3ii(inverse(m3i)); + EXPECT_FLOAT_EQ(m3[0][0], m3ii[0][0]); + EXPECT_FLOAT_EQ(m3[0][1], m3ii[0][1]); + EXPECT_FLOAT_EQ(m3[1][0], m3ii[1][0]); + EXPECT_FLOAT_EQ(m3[1][1], m3ii[1][1]); + + EXPECT_EQ(m1, m1*identity); +} + +}; // namespace android diff --git a/libs/ui/tests/vec_test.cpp b/libs/ui/tests/vec_test.cpp new file mode 100644 index 0000000..00f737e --- /dev/null +++ b/libs/ui/tests/vec_test.cpp @@ -0,0 +1,256 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "RegionTest" + +#include <stdlib.h> +#include <ui/Region.h> +#include <ui/Rect.h> +#include <gtest/gtest.h> + +#include <ui/vec4.h> + +namespace android { + +class VecTest : public testing::Test { +protected: +}; + +TEST_F(VecTest, Basics) { + vec4 v4; + vec3& v3(v4.xyz); + + EXPECT_EQ(sizeof(vec4), sizeof(float)*4); + EXPECT_EQ(sizeof(vec3), sizeof(float)*3); + EXPECT_EQ(sizeof(vec2), sizeof(float)*2); + EXPECT_EQ((void*)&v3, (void*)&v4); +} + +TEST_F(VecTest, Constructors) { + vec4 v0; + EXPECT_EQ(v0.x, 0); + EXPECT_EQ(v0.y, 0); + EXPECT_EQ(v0.z, 0); + EXPECT_EQ(v0.w, 0); + + vec4 v1(1); + EXPECT_EQ(v1.x, 1); + EXPECT_EQ(v1.y, 1); + EXPECT_EQ(v1.z, 1); + EXPECT_EQ(v1.w, 1); + + vec4 v2(1,2,3,4); + EXPECT_EQ(v2.x, 1); + EXPECT_EQ(v2.y, 2); + EXPECT_EQ(v2.z, 3); + EXPECT_EQ(v2.w, 4); + + vec4 v3(v2); + EXPECT_EQ(v3.x, 1); + EXPECT_EQ(v3.y, 2); + EXPECT_EQ(v3.z, 3); + EXPECT_EQ(v3.w, 4); + + vec4 v4(v3.xyz, 42); + EXPECT_EQ(v4.x, 1); + EXPECT_EQ(v4.y, 2); + EXPECT_EQ(v4.z, 3); + EXPECT_EQ(v4.w, 42); + + vec4 v5(vec3(v2.xy, 42), 24); + EXPECT_EQ(v5.x, 1); + EXPECT_EQ(v5.y, 2); + EXPECT_EQ(v5.z, 42); + EXPECT_EQ(v5.w, 24); + + tvec4<double> vd(2); + EXPECT_EQ(vd.x, 2); + EXPECT_EQ(vd.y, 2); + EXPECT_EQ(vd.z, 2); + EXPECT_EQ(vd.w, 2); +} + +TEST_F(VecTest, Access) { + vec4 v0(1,2,3,4); + v0.x = 10; + v0.y = 20; + v0.z = 30; + v0.w = 40; + EXPECT_EQ(v0.x, 10); + EXPECT_EQ(v0.y, 20); + EXPECT_EQ(v0.z, 30); + EXPECT_EQ(v0.w, 40); + + v0[0] = 100; + v0[1] = 200; + v0[2] = 300; + v0[3] = 400; + EXPECT_EQ(v0.x, 100); + EXPECT_EQ(v0.y, 200); + EXPECT_EQ(v0.z, 300); + EXPECT_EQ(v0.w, 400); + + v0.xyz = vec3(1,2,3); + EXPECT_EQ(v0.x, 1); + EXPECT_EQ(v0.y, 2); + EXPECT_EQ(v0.z, 3); + EXPECT_EQ(v0.w, 400); +} + +TEST_F(VecTest, UnaryOps) { + vec4 v0(1,2,3,4); + + v0 += 1; + EXPECT_EQ(v0.x, 2); + EXPECT_EQ(v0.y, 3); + EXPECT_EQ(v0.z, 4); + EXPECT_EQ(v0.w, 5); + + v0 -= 1; + EXPECT_EQ(v0.x, 1); + EXPECT_EQ(v0.y, 2); + EXPECT_EQ(v0.z, 3); + EXPECT_EQ(v0.w, 4); + + v0 *= 2; + EXPECT_EQ(v0.x, 2); + EXPECT_EQ(v0.y, 4); + EXPECT_EQ(v0.z, 6); + EXPECT_EQ(v0.w, 8); + + v0 /= 2; + EXPECT_EQ(v0.x, 1); + EXPECT_EQ(v0.y, 2); + EXPECT_EQ(v0.z, 3); + EXPECT_EQ(v0.w, 4); + + vec4 v1(10, 20, 30, 40); + + v0 += v1; + EXPECT_EQ(v0.x, 11); + EXPECT_EQ(v0.y, 22); + EXPECT_EQ(v0.z, 33); + EXPECT_EQ(v0.w, 44); + + v0 -= v1; + EXPECT_EQ(v0.x, 1); + EXPECT_EQ(v0.y, 2); + EXPECT_EQ(v0.z, 3); + EXPECT_EQ(v0.w, 4); + + v0 *= v1; + EXPECT_EQ(v0.x, 10); + EXPECT_EQ(v0.y, 40); + EXPECT_EQ(v0.z, 90); + EXPECT_EQ(v0.w, 160); + + v0 /= v1; + EXPECT_EQ(v0.x, 1); + EXPECT_EQ(v0.y, 2); + EXPECT_EQ(v0.z, 3); + EXPECT_EQ(v0.w, 4); + + ++v0; + EXPECT_EQ(v0.x, 2); + EXPECT_EQ(v0.y, 3); + EXPECT_EQ(v0.z, 4); + EXPECT_EQ(v0.w, 5); + + ++++v0; + EXPECT_EQ(v0.x, 4); + EXPECT_EQ(v0.y, 5); + EXPECT_EQ(v0.z, 6); + EXPECT_EQ(v0.w, 7); + + --v1; + EXPECT_EQ(v1.x, 9); + EXPECT_EQ(v1.y, 19); + EXPECT_EQ(v1.z, 29); + EXPECT_EQ(v1.w, 39); + + v1 = -v1; + EXPECT_EQ(v1.x, -9); + EXPECT_EQ(v1.y, -19); + EXPECT_EQ(v1.z, -29); + EXPECT_EQ(v1.w, -39); + + tvec4<double> dv(1,2,3,4); + v1 += dv; + EXPECT_EQ(v1.x, -8); + EXPECT_EQ(v1.y, -17); + EXPECT_EQ(v1.z, -26); + EXPECT_EQ(v1.w, -35); +} + +TEST_F(VecTest, ComparisonOps) { + vec4 v0(1,2,3,4); + vec4 v1(10,20,30,40); + + EXPECT_TRUE(v0 == v0); + EXPECT_TRUE(v0 != v1); + EXPECT_FALSE(v0 != v0); + EXPECT_FALSE(v0 == v1); +} + +TEST_F(VecTest, ArithmeticOps) { + vec4 v0(1,2,3,4); + vec4 v1(10,20,30,40); + + vec4 v2(v0 + v1); + EXPECT_EQ(v2.x, 11); + EXPECT_EQ(v2.y, 22); + EXPECT_EQ(v2.z, 33); + EXPECT_EQ(v2.w, 44); + + v0 = v1 * 2; + EXPECT_EQ(v0.x, 20); + EXPECT_EQ(v0.y, 40); + EXPECT_EQ(v0.z, 60); + EXPECT_EQ(v0.w, 80); + + v0 = 2 * v1; + EXPECT_EQ(v0.x, 20); + EXPECT_EQ(v0.y, 40); + EXPECT_EQ(v0.z, 60); + EXPECT_EQ(v0.w, 80); + + tvec4<double> vd(2); + v0 = v1 * vd; + EXPECT_EQ(v0.x, 20); + EXPECT_EQ(v0.y, 40); + EXPECT_EQ(v0.z, 60); + EXPECT_EQ(v0.w, 80); +} + +TEST_F(VecTest, ArithmeticFunc) { + vec3 east(1, 0, 0); + vec3 north(0, 1, 0); + vec3 up( cross(east, north) ); + EXPECT_EQ(up, vec3(0,0,1)); + EXPECT_EQ(dot(east, north), 0); + EXPECT_EQ(length(east), 1); + EXPECT_EQ(distance(east, north), sqrtf(2)); + + vec3 v0(1,2,3); + vec3 vn(normalize(v0)); + EXPECT_FLOAT_EQ(1, length(vn)); + EXPECT_FLOAT_EQ(length(v0), dot(v0, vn)); + + tvec3<double> vd(east); + EXPECT_EQ(length(vd), 1); +} + +}; // namespace android diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk deleted file mode 100644 index cbfe7bd..0000000 --- a/libs/utils/Android.mk +++ /dev/null @@ -1,143 +0,0 @@ -# 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. - -LOCAL_PATH:= $(call my-dir) - -# libutils is a little unique: It's built twice, once for the host -# and once for the device. - -commonSources:= \ - BasicHashtable.cpp \ - BlobCache.cpp \ - BufferedTextOutput.cpp \ - CallStack.cpp \ - Debug.cpp \ - FileMap.cpp \ - Flattenable.cpp \ - JenkinsHash.cpp \ - LinearAllocator.cpp \ - LinearTransform.cpp \ - Log.cpp \ - PropertyMap.cpp \ - RefBase.cpp \ - SharedBuffer.cpp \ - Static.cpp \ - StopWatch.cpp \ - String8.cpp \ - String16.cpp \ - StringArray.cpp \ - SystemClock.cpp \ - TextOutput.cpp \ - Threads.cpp \ - Timers.cpp \ - Tokenizer.cpp \ - Unicode.cpp \ - VectorImpl.cpp \ - WorkQueue.cpp \ - ZipFileCRO.cpp \ - ZipFileRO.cpp \ - ZipUtils.cpp \ - misc.cpp - -host_commonCflags := -DLIBUTILS_NATIVE=1 $(TOOL_CFLAGS) - -ifeq ($(HOST_OS),windows) -ifeq ($(strip $(USE_CYGWIN),),) -# Under MinGW, ctype.h doesn't need multi-byte support -host_commonCflags += -DMB_CUR_MAX=1 -endif -endif - -host_commonLdlibs := - -ifeq ($(TARGET_OS),linux) -host_commonLdlibs += -lrt -ldl -endif - - -# For the host -# ===================================================== -include $(CLEAR_VARS) -LOCAL_SRC_FILES:= $(commonSources) -ifeq ($(HOST_OS), linux) -LOCAL_SRC_FILES += Looper.cpp -endif -LOCAL_MODULE:= libutils -LOCAL_STATIC_LIBRARIES := libz -LOCAL_C_INCLUDES := \ - external/zlib -LOCAL_CFLAGS += $(host_commonCflags) -LOCAL_LDLIBS += $(host_commonLdlibs) -include $(BUILD_HOST_STATIC_LIBRARY) - - -# For the host, 64-bit -# ===================================================== -include $(CLEAR_VARS) -LOCAL_SRC_FILES:= $(commonSources) -ifeq ($(HOST_OS), linux) -LOCAL_SRC_FILES += Looper.cpp -endif -LOCAL_MODULE:= lib64utils -LOCAL_STATIC_LIBRARIES := libz -LOCAL_C_INCLUDES := \ - external/zlib -LOCAL_CFLAGS += $(host_commonCflags) -m64 -LOCAL_LDLIBS += $(host_commonLdlibs) -include $(BUILD_HOST_STATIC_LIBRARY) - - -# For the device -# ===================================================== -include $(CLEAR_VARS) - - -# we have the common sources, plus some device-specific stuff -LOCAL_SRC_FILES:= \ - $(commonSources) \ - Looper.cpp \ - Trace.cpp - -ifeq ($(TARGET_OS),linux) -LOCAL_LDLIBS += -lrt -ldl -endif - -ifeq ($(TARGET_ARCH),mips) -LOCAL_CFLAGS += -DALIGN_DOUBLE -endif - -LOCAL_C_INCLUDES += \ - bionic/libc/private \ - external/zlib - -LOCAL_LDLIBS += -lpthread - -LOCAL_SHARED_LIBRARIES := \ - liblog \ - libcutils \ - libdl \ - libcorkscrew \ - libz - -LOCAL_MODULE:= libutils -include $(BUILD_SHARED_LIBRARY) - -# Include subdirectory makefiles -# ============================================================ - -# If we're building with ONE_SHOT_MAKEFILE (mm, mmm), then what the framework -# team really wants is to build the stuff defined by this makefile. -ifeq (,$(ONE_SHOT_MAKEFILE)) -include $(call first-makefiles-under,$(LOCAL_PATH)) -endif diff --git a/libs/utils/BasicHashtable.cpp b/libs/utils/BasicHashtable.cpp deleted file mode 100644 index fd51b7b..0000000 --- a/libs/utils/BasicHashtable.cpp +++ /dev/null @@ -1,338 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "BasicHashtable" - -#include <math.h> - -#include <utils/Log.h> -#include <utils/BasicHashtable.h> -#include <utils/misc.h> - -namespace android { - -BasicHashtableImpl::BasicHashtableImpl(size_t entrySize, bool hasTrivialDestructor, - size_t minimumInitialCapacity, float loadFactor) : - mBucketSize(entrySize + sizeof(Bucket)), mHasTrivialDestructor(hasTrivialDestructor), - mLoadFactor(loadFactor), mSize(0), - mFilledBuckets(0), mBuckets(NULL) { - determineCapacity(minimumInitialCapacity, mLoadFactor, &mBucketCount, &mCapacity); -} - -BasicHashtableImpl::BasicHashtableImpl(const BasicHashtableImpl& other) : - mBucketSize(other.mBucketSize), mHasTrivialDestructor(other.mHasTrivialDestructor), - mCapacity(other.mCapacity), mLoadFactor(other.mLoadFactor), - mSize(other.mSize), mFilledBuckets(other.mFilledBuckets), - mBucketCount(other.mBucketCount), mBuckets(other.mBuckets) { - if (mBuckets) { - SharedBuffer::bufferFromData(mBuckets)->acquire(); - } -} - -void BasicHashtableImpl::dispose() { - if (mBuckets) { - releaseBuckets(mBuckets, mBucketCount); - } -} - -void BasicHashtableImpl::clone() { - if (mBuckets) { - void* newBuckets = allocateBuckets(mBucketCount); - copyBuckets(mBuckets, newBuckets, mBucketCount); - releaseBuckets(mBuckets, mBucketCount); - mBuckets = newBuckets; - } -} - -void BasicHashtableImpl::setTo(const BasicHashtableImpl& other) { - if (mBuckets) { - releaseBuckets(mBuckets, mBucketCount); - } - - mCapacity = other.mCapacity; - mLoadFactor = other.mLoadFactor; - mSize = other.mSize; - mFilledBuckets = other.mFilledBuckets; - mBucketCount = other.mBucketCount; - mBuckets = other.mBuckets; - - if (mBuckets) { - SharedBuffer::bufferFromData(mBuckets)->acquire(); - } -} - -void BasicHashtableImpl::clear() { - if (mBuckets) { - if (mFilledBuckets) { - SharedBuffer* sb = SharedBuffer::bufferFromData(mBuckets); - if (sb->onlyOwner()) { - destroyBuckets(mBuckets, mBucketCount); - for (size_t i = 0; i < mBucketCount; i++) { - Bucket& bucket = bucketAt(mBuckets, i); - bucket.cookie = 0; - } - } else { - releaseBuckets(mBuckets, mBucketCount); - mBuckets = NULL; - } - mFilledBuckets = 0; - } - mSize = 0; - } -} - -ssize_t BasicHashtableImpl::next(ssize_t index) const { - if (mSize) { - while (size_t(++index) < mBucketCount) { - const Bucket& bucket = bucketAt(mBuckets, index); - if (bucket.cookie & Bucket::PRESENT) { - return index; - } - } - } - return -1; -} - -ssize_t BasicHashtableImpl::find(ssize_t index, hash_t hash, - const void* __restrict__ key) const { - if (!mSize) { - return -1; - } - - hash = trimHash(hash); - if (index < 0) { - index = chainStart(hash, mBucketCount); - - const Bucket& bucket = bucketAt(mBuckets, size_t(index)); - if (bucket.cookie & Bucket::PRESENT) { - if (compareBucketKey(bucket, key)) { - return index; - } - } else { - if (!(bucket.cookie & Bucket::COLLISION)) { - return -1; - } - } - } - - size_t inc = chainIncrement(hash, mBucketCount); - for (;;) { - index = chainSeek(index, inc, mBucketCount); - - const Bucket& bucket = bucketAt(mBuckets, size_t(index)); - if (bucket.cookie & Bucket::PRESENT) { - if ((bucket.cookie & Bucket::HASH_MASK) == hash - && compareBucketKey(bucket, key)) { - return index; - } - } - if (!(bucket.cookie & Bucket::COLLISION)) { - return -1; - } - } -} - -size_t BasicHashtableImpl::add(hash_t hash, const void* entry) { - if (!mBuckets) { - mBuckets = allocateBuckets(mBucketCount); - } else { - edit(); - } - - hash = trimHash(hash); - for (;;) { - size_t index = chainStart(hash, mBucketCount); - Bucket* bucket = &bucketAt(mBuckets, size_t(index)); - if (bucket->cookie & Bucket::PRESENT) { - size_t inc = chainIncrement(hash, mBucketCount); - do { - bucket->cookie |= Bucket::COLLISION; - index = chainSeek(index, inc, mBucketCount); - bucket = &bucketAt(mBuckets, size_t(index)); - } while (bucket->cookie & Bucket::PRESENT); - } - - uint32_t collision = bucket->cookie & Bucket::COLLISION; - if (!collision) { - if (mFilledBuckets >= mCapacity) { - rehash(mCapacity * 2, mLoadFactor); - continue; - } - mFilledBuckets += 1; - } - - bucket->cookie = collision | Bucket::PRESENT | hash; - mSize += 1; - initializeBucketEntry(*bucket, entry); - return index; - } -} - -void BasicHashtableImpl::removeAt(size_t index) { - edit(); - - Bucket& bucket = bucketAt(mBuckets, index); - bucket.cookie &= ~Bucket::PRESENT; - if (!(bucket.cookie & Bucket::COLLISION)) { - mFilledBuckets -= 1; - } - mSize -= 1; - if (!mHasTrivialDestructor) { - destroyBucketEntry(bucket); - } -} - -void BasicHashtableImpl::rehash(size_t minimumCapacity, float loadFactor) { - if (minimumCapacity < mSize) { - minimumCapacity = mSize; - } - size_t newBucketCount, newCapacity; - determineCapacity(minimumCapacity, loadFactor, &newBucketCount, &newCapacity); - - if (newBucketCount != mBucketCount || newCapacity != mCapacity) { - if (mBuckets) { - void* newBuckets; - if (mSize) { - newBuckets = allocateBuckets(newBucketCount); - for (size_t i = 0; i < mBucketCount; i++) { - const Bucket& fromBucket = bucketAt(mBuckets, i); - if (fromBucket.cookie & Bucket::PRESENT) { - hash_t hash = fromBucket.cookie & Bucket::HASH_MASK; - size_t index = chainStart(hash, newBucketCount); - Bucket* toBucket = &bucketAt(newBuckets, size_t(index)); - if (toBucket->cookie & Bucket::PRESENT) { - size_t inc = chainIncrement(hash, newBucketCount); - do { - toBucket->cookie |= Bucket::COLLISION; - index = chainSeek(index, inc, newBucketCount); - toBucket = &bucketAt(newBuckets, size_t(index)); - } while (toBucket->cookie & Bucket::PRESENT); - } - toBucket->cookie = Bucket::PRESENT | hash; - initializeBucketEntry(*toBucket, fromBucket.entry); - } - } - } else { - newBuckets = NULL; - } - releaseBuckets(mBuckets, mBucketCount); - mBuckets = newBuckets; - mFilledBuckets = mSize; - } - mBucketCount = newBucketCount; - mCapacity = newCapacity; - } - mLoadFactor = loadFactor; -} - -void* BasicHashtableImpl::allocateBuckets(size_t count) const { - size_t bytes = count * mBucketSize; - SharedBuffer* sb = SharedBuffer::alloc(bytes); - LOG_ALWAYS_FATAL_IF(!sb, "Could not allocate %u bytes for hashtable with %u buckets.", - uint32_t(bytes), uint32_t(count)); - void* buckets = sb->data(); - for (size_t i = 0; i < count; i++) { - Bucket& bucket = bucketAt(buckets, i); - bucket.cookie = 0; - } - return buckets; -} - -void BasicHashtableImpl::releaseBuckets(void* __restrict__ buckets, size_t count) const { - SharedBuffer* sb = SharedBuffer::bufferFromData(buckets); - if (sb->release(SharedBuffer::eKeepStorage) == 1) { - destroyBuckets(buckets, count); - SharedBuffer::dealloc(sb); - } -} - -void BasicHashtableImpl::destroyBuckets(void* __restrict__ buckets, size_t count) const { - if (!mHasTrivialDestructor) { - for (size_t i = 0; i < count; i++) { - Bucket& bucket = bucketAt(buckets, i); - if (bucket.cookie & Bucket::PRESENT) { - destroyBucketEntry(bucket); - } - } - } -} - -void BasicHashtableImpl::copyBuckets(const void* __restrict__ fromBuckets, - void* __restrict__ toBuckets, size_t count) const { - for (size_t i = 0; i < count; i++) { - const Bucket& fromBucket = bucketAt(fromBuckets, i); - Bucket& toBucket = bucketAt(toBuckets, i); - toBucket.cookie = fromBucket.cookie; - if (fromBucket.cookie & Bucket::PRESENT) { - initializeBucketEntry(toBucket, fromBucket.entry); - } - } -} - -// Table of 31-bit primes where each prime is no less than twice as large -// as the previous one. Generated by "primes.py". -static size_t PRIMES[] = { - 5, - 11, - 23, - 47, - 97, - 197, - 397, - 797, - 1597, - 3203, - 6421, - 12853, - 25717, - 51437, - 102877, - 205759, - 411527, - 823117, - 1646237, - 3292489, - 6584983, - 13169977, - 26339969, - 52679969, - 105359939, - 210719881, - 421439783, - 842879579, - 1685759167, - 0, -}; - -void BasicHashtableImpl::determineCapacity(size_t minimumCapacity, float loadFactor, - size_t* __restrict__ outBucketCount, size_t* __restrict__ outCapacity) { - LOG_ALWAYS_FATAL_IF(loadFactor <= 0.0f || loadFactor > 1.0f, - "Invalid load factor %0.3f. Must be in the range (0, 1].", loadFactor); - - size_t count = ceilf(minimumCapacity / loadFactor) + 1; - size_t i = 0; - while (count > PRIMES[i] && i < NELEM(PRIMES)) { - i++; - } - count = PRIMES[i]; - LOG_ALWAYS_FATAL_IF(!count, "Could not determine required number of buckets for " - "hashtable with minimum capacity %u and load factor %0.3f.", - uint32_t(minimumCapacity), loadFactor); - *outBucketCount = count; - *outCapacity = ceilf((count - 1) * loadFactor); -} - -}; // namespace android diff --git a/libs/utils/BlobCache.cpp b/libs/utils/BlobCache.cpp deleted file mode 100644 index be398ee..0000000 --- a/libs/utils/BlobCache.cpp +++ /dev/null @@ -1,378 +0,0 @@ -/* - ** 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. - */ - -#define LOG_TAG "BlobCache" -//#define LOG_NDEBUG 0 - -#include <stdlib.h> -#include <string.h> - -#include <utils/BlobCache.h> -#include <utils/Errors.h> -#include <utils/Log.h> - -namespace android { - -// BlobCache::Header::mMagicNumber value -static const uint32_t blobCacheMagic = '_Bb$'; - -// BlobCache::Header::mBlobCacheVersion value -static const uint32_t blobCacheVersion = 1; - -// BlobCache::Header::mDeviceVersion value -static const uint32_t blobCacheDeviceVersion = 1; - -BlobCache::BlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize): - mMaxKeySize(maxKeySize), - mMaxValueSize(maxValueSize), - mMaxTotalSize(maxTotalSize), - mTotalSize(0) { - nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); -#ifdef _WIN32 - srand(now); -#else - mRandState[0] = (now >> 0) & 0xFFFF; - mRandState[1] = (now >> 16) & 0xFFFF; - mRandState[2] = (now >> 32) & 0xFFFF; -#endif - ALOGV("initializing random seed using %lld", now); -} - -void BlobCache::set(const void* key, size_t keySize, const void* value, - size_t valueSize) { - if (mMaxKeySize < keySize) { - ALOGV("set: not caching because the key is too large: %d (limit: %d)", - keySize, mMaxKeySize); - return; - } - if (mMaxValueSize < valueSize) { - ALOGV("set: not caching because the value is too large: %d (limit: %d)", - valueSize, mMaxValueSize); - return; - } - if (mMaxTotalSize < keySize + valueSize) { - ALOGV("set: not caching because the combined key/value size is too " - "large: %d (limit: %d)", keySize + valueSize, mMaxTotalSize); - return; - } - if (keySize == 0) { - ALOGW("set: not caching because keySize is 0"); - return; - } - if (valueSize <= 0) { - ALOGW("set: not caching because valueSize is 0"); - return; - } - - sp<Blob> dummyKey(new Blob(key, keySize, false)); - CacheEntry dummyEntry(dummyKey, NULL); - - while (true) { - ssize_t index = mCacheEntries.indexOf(dummyEntry); - if (index < 0) { - // Create a new cache entry. - sp<Blob> keyBlob(new Blob(key, keySize, true)); - sp<Blob> valueBlob(new Blob(value, valueSize, true)); - size_t newTotalSize = mTotalSize + keySize + valueSize; - if (mMaxTotalSize < newTotalSize) { - if (isCleanable()) { - // Clean the cache and try again. - clean(); - continue; - } else { - ALOGV("set: not caching new key/value pair because the " - "total cache size limit would be exceeded: %d " - "(limit: %d)", - keySize + valueSize, mMaxTotalSize); - break; - } - } - mCacheEntries.add(CacheEntry(keyBlob, valueBlob)); - mTotalSize = newTotalSize; - ALOGV("set: created new cache entry with %d byte key and %d byte value", - keySize, valueSize); - } else { - // Update the existing cache entry. - sp<Blob> valueBlob(new Blob(value, valueSize, true)); - sp<Blob> oldValueBlob(mCacheEntries[index].getValue()); - size_t newTotalSize = mTotalSize + valueSize - oldValueBlob->getSize(); - if (mMaxTotalSize < newTotalSize) { - if (isCleanable()) { - // Clean the cache and try again. - clean(); - continue; - } else { - ALOGV("set: not caching new value because the total cache " - "size limit would be exceeded: %d (limit: %d)", - keySize + valueSize, mMaxTotalSize); - break; - } - } - mCacheEntries.editItemAt(index).setValue(valueBlob); - mTotalSize = newTotalSize; - ALOGV("set: updated existing cache entry with %d byte key and %d byte " - "value", keySize, valueSize); - } - break; - } -} - -size_t BlobCache::get(const void* key, size_t keySize, void* value, - size_t valueSize) { - if (mMaxKeySize < keySize) { - ALOGV("get: not searching because the key is too large: %d (limit %d)", - keySize, mMaxKeySize); - return 0; - } - sp<Blob> dummyKey(new Blob(key, keySize, false)); - CacheEntry dummyEntry(dummyKey, NULL); - ssize_t index = mCacheEntries.indexOf(dummyEntry); - if (index < 0) { - ALOGV("get: no cache entry found for key of size %d", keySize); - return 0; - } - - // The key was found. Return the value if the caller's buffer is large - // enough. - sp<Blob> valueBlob(mCacheEntries[index].getValue()); - size_t valueBlobSize = valueBlob->getSize(); - if (valueBlobSize <= valueSize) { - ALOGV("get: copying %d bytes to caller's buffer", valueBlobSize); - memcpy(value, valueBlob->getData(), valueBlobSize); - } else { - ALOGV("get: caller's buffer is too small for value: %d (needs %d)", - valueSize, valueBlobSize); - } - return valueBlobSize; -} - -static inline size_t align4(size_t size) { - return (size + 3) & ~3; -} - -size_t BlobCache::getFlattenedSize() const { - size_t size = sizeof(Header); - for (size_t i = 0; i < mCacheEntries.size(); i++) { - const CacheEntry& e(mCacheEntries[i]); - sp<Blob> keyBlob = e.getKey(); - sp<Blob> valueBlob = e.getValue(); - size = align4(size); - size += sizeof(EntryHeader) + keyBlob->getSize() + - valueBlob->getSize(); - } - return size; -} - -size_t BlobCache::getFdCount() const { - return 0; -} - -status_t BlobCache::flatten(void* buffer, size_t size, int fds[], size_t count) - const { - if (count != 0) { - ALOGE("flatten: nonzero fd count: %zu", count); - return BAD_VALUE; - } - - // Write the cache header - if (size < sizeof(Header)) { - ALOGE("flatten: not enough room for cache header"); - return BAD_VALUE; - } - Header* header = reinterpret_cast<Header*>(buffer); - header->mMagicNumber = blobCacheMagic; - header->mBlobCacheVersion = blobCacheVersion; - header->mDeviceVersion = blobCacheDeviceVersion; - header->mNumEntries = mCacheEntries.size(); - - // Write cache entries - uint8_t* byteBuffer = reinterpret_cast<uint8_t*>(buffer); - off_t byteOffset = align4(sizeof(Header)); - for (size_t i = 0; i < mCacheEntries.size(); i++) { - const CacheEntry& e(mCacheEntries[i]); - sp<Blob> keyBlob = e.getKey(); - sp<Blob> valueBlob = e.getValue(); - size_t keySize = keyBlob->getSize(); - size_t valueSize = valueBlob->getSize(); - - size_t entrySize = sizeof(EntryHeader) + keySize + valueSize; - if (byteOffset + entrySize > size) { - ALOGE("flatten: not enough room for cache entries"); - return BAD_VALUE; - } - - EntryHeader* eheader = reinterpret_cast<EntryHeader*>( - &byteBuffer[byteOffset]); - eheader->mKeySize = keySize; - eheader->mValueSize = valueSize; - - memcpy(eheader->mData, keyBlob->getData(), keySize); - memcpy(eheader->mData + keySize, valueBlob->getData(), valueSize); - - byteOffset += align4(entrySize); - } - - return OK; -} - -status_t BlobCache::unflatten(void const* buffer, size_t size, int fds[], - size_t count) { - // All errors should result in the BlobCache being in an empty state. - mCacheEntries.clear(); - - if (count != 0) { - ALOGE("unflatten: nonzero fd count: %zu", count); - return BAD_VALUE; - } - - // Read the cache header - if (size < sizeof(Header)) { - ALOGE("unflatten: not enough room for cache header"); - return BAD_VALUE; - } - const Header* header = reinterpret_cast<const Header*>(buffer); - if (header->mMagicNumber != blobCacheMagic) { - ALOGE("unflatten: bad magic number: %d", header->mMagicNumber); - return BAD_VALUE; - } - if (header->mBlobCacheVersion != blobCacheVersion || - header->mDeviceVersion != blobCacheDeviceVersion) { - // We treat version mismatches as an empty cache. - return OK; - } - - // Read cache entries - const uint8_t* byteBuffer = reinterpret_cast<const uint8_t*>(buffer); - off_t byteOffset = align4(sizeof(Header)); - size_t numEntries = header->mNumEntries; - for (size_t i = 0; i < numEntries; i++) { - if (byteOffset + sizeof(EntryHeader) > size) { - mCacheEntries.clear(); - ALOGE("unflatten: not enough room for cache entry headers"); - return BAD_VALUE; - } - - const EntryHeader* eheader = reinterpret_cast<const EntryHeader*>( - &byteBuffer[byteOffset]); - size_t keySize = eheader->mKeySize; - size_t valueSize = eheader->mValueSize; - size_t entrySize = sizeof(EntryHeader) + keySize + valueSize; - - if (byteOffset + entrySize > size) { - mCacheEntries.clear(); - ALOGE("unflatten: not enough room for cache entry headers"); - return BAD_VALUE; - } - - const uint8_t* data = eheader->mData; - set(data, keySize, data + keySize, valueSize); - - byteOffset += align4(entrySize); - } - - return OK; -} - -long int BlobCache::blob_random() { -#ifdef _WIN32 - return rand(); -#else - return nrand48(mRandState); -#endif -} - -void BlobCache::clean() { - // Remove a random cache entry until the total cache size gets below half - // the maximum total cache size. - while (mTotalSize > mMaxTotalSize / 2) { - size_t i = size_t(blob_random() % (mCacheEntries.size())); - const CacheEntry& entry(mCacheEntries[i]); - mTotalSize -= entry.getKey()->getSize() + entry.getValue()->getSize(); - mCacheEntries.removeAt(i); - } -} - -bool BlobCache::isCleanable() const { - return mTotalSize > mMaxTotalSize / 2; -} - -BlobCache::Blob::Blob(const void* data, size_t size, bool copyData): - mData(copyData ? malloc(size) : data), - mSize(size), - mOwnsData(copyData) { - if (data != NULL && copyData) { - memcpy(const_cast<void*>(mData), data, size); - } -} - -BlobCache::Blob::~Blob() { - if (mOwnsData) { - free(const_cast<void*>(mData)); - } -} - -bool BlobCache::Blob::operator<(const Blob& rhs) const { - if (mSize == rhs.mSize) { - return memcmp(mData, rhs.mData, mSize) < 0; - } else { - return mSize < rhs.mSize; - } -} - -const void* BlobCache::Blob::getData() const { - return mData; -} - -size_t BlobCache::Blob::getSize() const { - return mSize; -} - -BlobCache::CacheEntry::CacheEntry() { -} - -BlobCache::CacheEntry::CacheEntry(const sp<Blob>& key, const sp<Blob>& value): - mKey(key), - mValue(value) { -} - -BlobCache::CacheEntry::CacheEntry(const CacheEntry& ce): - mKey(ce.mKey), - mValue(ce.mValue) { -} - -bool BlobCache::CacheEntry::operator<(const CacheEntry& rhs) const { - return *mKey < *rhs.mKey; -} - -const BlobCache::CacheEntry& BlobCache::CacheEntry::operator=(const CacheEntry& rhs) { - mKey = rhs.mKey; - mValue = rhs.mValue; - return *this; -} - -sp<BlobCache::Blob> BlobCache::CacheEntry::getKey() const { - return mKey; -} - -sp<BlobCache::Blob> BlobCache::CacheEntry::getValue() const { - return mValue; -} - -void BlobCache::CacheEntry::setValue(const sp<Blob>& value) { - mValue = value; -} - -} // namespace android diff --git a/libs/utils/CallStack.cpp b/libs/utils/CallStack.cpp deleted file mode 100644 index e60f5d8..0000000 --- a/libs/utils/CallStack.cpp +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "CallStack" - -#include <string.h> - -#include <utils/Log.h> -#include <utils/Errors.h> -#include <utils/CallStack.h> -#include <corkscrew/backtrace.h> - -/*****************************************************************************/ -namespace android { - -CallStack::CallStack() : - mCount(0) { -} - -CallStack::CallStack(const char* logtag, int32_t ignoreDepth, int32_t maxDepth) { - this->update(ignoreDepth+1, maxDepth); - this->dump(logtag); -} - -CallStack::CallStack(const CallStack& rhs) : - mCount(rhs.mCount) { - if (mCount) { - memcpy(mStack, rhs.mStack, mCount * sizeof(backtrace_frame_t)); - } -} - -CallStack::~CallStack() { -} - -CallStack& CallStack::operator = (const CallStack& rhs) { - mCount = rhs.mCount; - if (mCount) { - memcpy(mStack, rhs.mStack, mCount * sizeof(backtrace_frame_t)); - } - return *this; -} - -bool CallStack::operator == (const CallStack& rhs) const { - if (mCount != rhs.mCount) - return false; - return !mCount || memcmp(mStack, rhs.mStack, mCount * sizeof(backtrace_frame_t)) == 0; -} - -bool CallStack::operator != (const CallStack& rhs) const { - return !operator == (rhs); -} - -bool CallStack::operator < (const CallStack& rhs) const { - if (mCount != rhs.mCount) - return mCount < rhs.mCount; - return memcmp(mStack, rhs.mStack, mCount * sizeof(backtrace_frame_t)) < 0; -} - -bool CallStack::operator >= (const CallStack& rhs) const { - return !operator < (rhs); -} - -bool CallStack::operator > (const CallStack& rhs) const { - if (mCount != rhs.mCount) - return mCount > rhs.mCount; - return memcmp(mStack, rhs.mStack, mCount * sizeof(backtrace_frame_t)) > 0; -} - -bool CallStack::operator <= (const CallStack& rhs) const { - return !operator > (rhs); -} - -const void* CallStack::operator [] (int index) const { - if (index >= int(mCount)) - return 0; - return reinterpret_cast<const void*>(mStack[index].absolute_pc); -} - -void CallStack::clear() { - mCount = 0; -} - -void CallStack::update(int32_t ignoreDepth, int32_t maxDepth) { - if (maxDepth > MAX_DEPTH) { - maxDepth = MAX_DEPTH; - } - ssize_t count = unwind_backtrace(mStack, ignoreDepth + 1, maxDepth); - mCount = count > 0 ? count : 0; -} - -void CallStack::dump(const char* logtag, const char* prefix) const { - backtrace_symbol_t symbols[mCount]; - - get_backtrace_symbols(mStack, mCount, symbols); - for (size_t i = 0; i < mCount; i++) { - char line[MAX_BACKTRACE_LINE_LENGTH]; - format_backtrace_line(i, &mStack[i], &symbols[i], - line, MAX_BACKTRACE_LINE_LENGTH); - ALOG(LOG_DEBUG, logtag, "%s%s", - prefix ? prefix : "", - line); - } - free_backtrace_symbols(symbols, mCount); -} - -String8 CallStack::toString(const char* prefix) const { - String8 str; - backtrace_symbol_t symbols[mCount]; - - get_backtrace_symbols(mStack, mCount, symbols); - for (size_t i = 0; i < mCount; i++) { - char line[MAX_BACKTRACE_LINE_LENGTH]; - format_backtrace_line(i, &mStack[i], &symbols[i], - line, MAX_BACKTRACE_LINE_LENGTH); - if (prefix) { - str.append(prefix); - } - str.append(line); - str.append("\n"); - } - free_backtrace_symbols(symbols, mCount); - return str; -} - -}; // namespace android diff --git a/libs/utils/FileMap.cpp b/libs/utils/FileMap.cpp deleted file mode 100644 index 9ce370e..0000000 --- a/libs/utils/FileMap.cpp +++ /dev/null @@ -1,225 +0,0 @@ -/* - * Copyright (C) 2006 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. - */ - -// -// Shared file mapping class. -// - -#define LOG_TAG "filemap" - -#include <utils/FileMap.h> -#include <utils/Log.h> - -#include <stdio.h> -#include <stdlib.h> - -#ifdef HAVE_POSIX_FILEMAP -#include <sys/mman.h> -#endif - -#include <string.h> -#include <memory.h> -#include <errno.h> -#include <assert.h> - -using namespace android; - -/*static*/ long FileMap::mPageSize = -1; - - -/* - * Constructor. Create an empty object. - */ -FileMap::FileMap(void) - : mRefCount(1), mFileName(NULL), mBasePtr(NULL), mBaseLength(0), - mDataPtr(NULL), mDataLength(0) -{ -} - -/* - * Destructor. - */ -FileMap::~FileMap(void) -{ - assert(mRefCount == 0); - - //printf("+++ removing FileMap %p %u\n", mDataPtr, mDataLength); - - mRefCount = -100; // help catch double-free - if (mFileName != NULL) { - free(mFileName); - } -#ifdef HAVE_POSIX_FILEMAP - if (mBasePtr && munmap(mBasePtr, mBaseLength) != 0) { - ALOGD("munmap(%p, %d) failed\n", mBasePtr, (int) mBaseLength); - } -#endif -#ifdef HAVE_WIN32_FILEMAP - if (mBasePtr && UnmapViewOfFile(mBasePtr) == 0) { - ALOGD("UnmapViewOfFile(%p) failed, error = %ld\n", mBasePtr, - GetLastError() ); - } - if (mFileMapping != INVALID_HANDLE_VALUE) { - CloseHandle(mFileMapping); - } - CloseHandle(mFileHandle); -#endif -} - - -/* - * Create a new mapping on an open file. - * - * Closing the file descriptor does not unmap the pages, so we don't - * claim ownership of the fd. - * - * Returns "false" on failure. - */ -bool FileMap::create(const char* origFileName, int fd, off64_t offset, size_t length, - bool readOnly) -{ -#ifdef HAVE_WIN32_FILEMAP - int adjust; - off64_t adjOffset; - size_t adjLength; - - if (mPageSize == -1) { - SYSTEM_INFO si; - - GetSystemInfo( &si ); - mPageSize = si.dwAllocationGranularity; - } - - DWORD protect = readOnly ? PAGE_READONLY : PAGE_READWRITE; - - mFileHandle = (HANDLE) _get_osfhandle(fd); - mFileMapping = CreateFileMapping( mFileHandle, NULL, protect, 0, 0, NULL); - if (mFileMapping == NULL) { - ALOGE("CreateFileMapping(%p, %lx) failed with error %ld\n", - mFileHandle, protect, GetLastError() ); - return false; - } - - adjust = offset % mPageSize; - adjOffset = offset - adjust; - adjLength = length + adjust; - - mBasePtr = MapViewOfFile( mFileMapping, - readOnly ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS, - 0, - (DWORD)(adjOffset), - adjLength ); - if (mBasePtr == NULL) { - ALOGE("MapViewOfFile(%ld, %ld) failed with error %ld\n", - adjOffset, adjLength, GetLastError() ); - CloseHandle(mFileMapping); - mFileMapping = INVALID_HANDLE_VALUE; - return false; - } -#endif -#ifdef HAVE_POSIX_FILEMAP - int prot, flags, adjust; - off64_t adjOffset; - size_t adjLength; - - void* ptr; - - assert(mRefCount == 1); - assert(fd >= 0); - assert(offset >= 0); - assert(length > 0); - - /* init on first use */ - if (mPageSize == -1) { -#if NOT_USING_KLIBC - mPageSize = sysconf(_SC_PAGESIZE); - if (mPageSize == -1) { - ALOGE("could not get _SC_PAGESIZE\n"); - return false; - } -#else - /* this holds for Linux, Darwin, Cygwin, and doesn't pain the ARM */ - mPageSize = 4096; -#endif - } - - adjust = offset % mPageSize; -try_again: - adjOffset = offset - adjust; - adjLength = length + adjust; - - flags = MAP_SHARED; - prot = PROT_READ; - if (!readOnly) - prot |= PROT_WRITE; - - ptr = mmap(NULL, adjLength, prot, flags, fd, adjOffset); - if (ptr == MAP_FAILED) { - // Cygwin does not seem to like file mapping files from an offset. - // So if we fail, try again with offset zero - if (adjOffset > 0) { - adjust = offset; - goto try_again; - } - - ALOGE("mmap(%ld,%ld) failed: %s\n", - (long) adjOffset, (long) adjLength, strerror(errno)); - return false; - } - mBasePtr = ptr; -#endif /* HAVE_POSIX_FILEMAP */ - - mFileName = origFileName != NULL ? strdup(origFileName) : NULL; - mBaseLength = adjLength; - mDataOffset = offset; - mDataPtr = (char*) mBasePtr + adjust; - mDataLength = length; - - assert(mBasePtr != NULL); - - ALOGV("MAP: base %p/%d data %p/%d\n", - mBasePtr, (int) mBaseLength, mDataPtr, (int) mDataLength); - - return true; -} - -/* - * Provide guidance to the system. - */ -int FileMap::advise(MapAdvice advice) -{ -#if HAVE_MADVISE - int cc, sysAdvice; - - switch (advice) { - case NORMAL: sysAdvice = MADV_NORMAL; break; - case RANDOM: sysAdvice = MADV_RANDOM; break; - case SEQUENTIAL: sysAdvice = MADV_SEQUENTIAL; break; - case WILLNEED: sysAdvice = MADV_WILLNEED; break; - case DONTNEED: sysAdvice = MADV_DONTNEED; break; - default: - assert(false); - return -1; - } - - cc = madvise(mBasePtr, mBaseLength, sysAdvice); - if (cc != 0) - ALOGW("madvise(%d) failed: %s\n", sysAdvice, strerror(errno)); - return cc; -#else - return -1; -#endif // HAVE_MADVISE -} diff --git a/libs/utils/JenkinsHash.cpp b/libs/utils/JenkinsHash.cpp deleted file mode 100644 index 52c9bb7..0000000 --- a/libs/utils/JenkinsHash.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2012 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. - */ - -/* Implementation of Jenkins one-at-a-time hash function. These choices are - * optimized for code size and portability, rather than raw speed. But speed - * should still be quite good. - **/ - -#include <utils/JenkinsHash.h> - -namespace android { - -hash_t JenkinsHashWhiten(uint32_t hash) { - hash += (hash << 3); - hash ^= (hash >> 11); - hash += (hash << 15); - return hash; -} - -uint32_t JenkinsHashMixBytes(uint32_t hash, const uint8_t* bytes, size_t size) { - hash = JenkinsHashMix(hash, (uint32_t)size); - size_t i; - for (i = 0; i < (size & -4); i += 4) { - uint32_t data = bytes[i] | (bytes[i+1] << 8) | (bytes[i+2] << 16) | (bytes[i+3] << 24); - hash = JenkinsHashMix(hash, data); - } - if (size & 3) { - uint32_t data = bytes[i]; - data |= ((size & 3) > 1) ? (bytes[i+1] << 8) : 0; - data |= ((size & 3) > 2) ? (bytes[i+2] << 16) : 0; - hash = JenkinsHashMix(hash, data); - } - return hash; -} - -uint32_t JenkinsHashMixShorts(uint32_t hash, const uint16_t* shorts, size_t size) { - hash = JenkinsHashMix(hash, (uint32_t)size); - size_t i; - for (i = 0; i < (size & -2); i += 2) { - uint32_t data = shorts[i] | (shorts[i+1] << 16); - hash = JenkinsHashMix(hash, data); - } - if (size & 1) { - uint32_t data = shorts[i]; - hash = JenkinsHashMix(hash, data); - } - return hash; -} - -} - diff --git a/libs/utils/LinearAllocator.cpp b/libs/utils/LinearAllocator.cpp deleted file mode 100644 index a07a291..0000000 --- a/libs/utils/LinearAllocator.cpp +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Copyright 2012, The Android Open Source Project - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#define LOG_TAG "LinearAllocator" -#define LOG_NDEBUG 1 - -#include <stdlib.h> -#include <utils/LinearAllocator.h> -#include <utils/Log.h> - - -// The ideal size of a page allocation (these need to be multiples of 8) -#define INITIAL_PAGE_SIZE ((size_t)4096) // 4kb -#define MAX_PAGE_SIZE ((size_t)131072) // 128kb - -// The maximum amount of wasted space we can have per page -// Allocations exceeding this will have their own dedicated page -// If this is too low, we will malloc too much -// Too high, and we may waste too much space -// Must be smaller than INITIAL_PAGE_SIZE -#define MAX_WASTE_SIZE ((size_t)1024) - -#if ALIGN_DOUBLE -#define ALIGN_SZ (sizeof(double)) -#else -#define ALIGN_SZ (sizeof(int)) -#endif - -#define ALIGN(x) ((x + ALIGN_SZ - 1 ) & ~(ALIGN_SZ - 1)) -#define ALIGN_PTR(p) ((void*)(ALIGN((size_t)p))) - -#if LOG_NDEBUG -#define ADD_ALLOCATION(size) -#define RM_ALLOCATION(size) -#else -#include <utils/Thread.h> -#include <utils/Timers.h> -static size_t s_totalAllocations = 0; -static nsecs_t s_nextLog = 0; -static android::Mutex s_mutex; - -static void _logUsageLocked() { - nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); - if (now > s_nextLog) { - s_nextLog = now + milliseconds_to_nanoseconds(10); - ALOGV("Total memory usage: %zu kb", s_totalAllocations / 1024); - } -} - -static void _addAllocation(size_t size) { - android::AutoMutex lock(s_mutex); - s_totalAllocations += size; - _logUsageLocked(); -} - -#define ADD_ALLOCATION(size) _addAllocation(size); -#define RM_ALLOCATION(size) _addAllocation(-size); -#endif - -#define min(x,y) (((x) < (y)) ? (x) : (y)) - -namespace android { - -class LinearAllocator::Page { -public: - Page* next() { return mNextPage; } - void setNext(Page* next) { mNextPage = next; } - - Page() - : mNextPage(0) - {} - - void* operator new(size_t size, void* buf) { return buf; } - - void* start() { - return (void*) (((size_t)this) + sizeof(Page)); - } - - void* end(int pageSize) { - return (void*) (((size_t)start()) + pageSize); - } - -private: - Page(const Page& other) {} - Page* mNextPage; -}; - -LinearAllocator::LinearAllocator() - : mPageSize(INITIAL_PAGE_SIZE) - , mMaxAllocSize(MAX_WASTE_SIZE) - , mNext(0) - , mCurrentPage(0) - , mPages(0) - , mTotalAllocated(0) - , mWastedSpace(0) - , mPageCount(0) - , mDedicatedPageCount(0) {} - -LinearAllocator::~LinearAllocator(void) { - Page* p = mPages; - while (p) { - Page* next = p->next(); - p->~Page(); - free(p); - RM_ALLOCATION(mPageSize); - p = next; - } -} - -void* LinearAllocator::start(Page* p) { - return ALIGN_PTR(((size_t*)p) + sizeof(Page)); -} - -void* LinearAllocator::end(Page* p) { - return ((char*)p) + mPageSize; -} - -bool LinearAllocator::fitsInCurrentPage(size_t size) { - return mNext && ((char*)mNext + size) <= end(mCurrentPage); -} - -void LinearAllocator::ensureNext(size_t size) { - if (fitsInCurrentPage(size)) return; - - if (mCurrentPage && mPageSize < MAX_PAGE_SIZE) { - mPageSize = min(MAX_PAGE_SIZE, mPageSize * 2); - mPageSize = ALIGN(mPageSize); - } - mWastedSpace += mPageSize; - Page* p = newPage(mPageSize); - if (mCurrentPage) { - mCurrentPage->setNext(p); - } - mCurrentPage = p; - if (!mPages) { - mPages = mCurrentPage; - } - mNext = start(mCurrentPage); -} - -void* LinearAllocator::alloc(size_t size) { - size = ALIGN(size); - if (size > mMaxAllocSize && !fitsInCurrentPage(size)) { - ALOGV("Exceeded max size %zu > %zu", size, mMaxAllocSize); - // Allocation is too large, create a dedicated page for the allocation - Page* page = newPage(size); - mDedicatedPageCount++; - page->setNext(mPages); - mPages = page; - if (!mCurrentPage) - mCurrentPage = mPages; - return start(page); - } - ensureNext(size); - void* ptr = mNext; - mNext = ((char*)mNext) + size; - mWastedSpace -= size; - return ptr; -} - -void LinearAllocator::rewindIfLastAlloc(void* ptr, size_t allocSize) { - // Don't bother rewinding across pages - allocSize = ALIGN(allocSize); - if (ptr >= start(mCurrentPage) && ptr < end(mCurrentPage) - && ptr == ((char*)mNext - allocSize)) { - mTotalAllocated -= allocSize; - mWastedSpace += allocSize; - mNext = ptr; - } -} - -LinearAllocator::Page* LinearAllocator::newPage(size_t pageSize) { - pageSize = ALIGN(pageSize + sizeof(LinearAllocator::Page)); - ADD_ALLOCATION(pageSize); - mTotalAllocated += pageSize; - mPageCount++; - void* buf = malloc(pageSize); - return new (buf) Page(); -} - -static const char* toSize(size_t value, float& result) { - if (value < 2000) { - result = value; - return "B"; - } - if (value < 2000000) { - result = value / 1024.0f; - return "KB"; - } - result = value / 1048576.0f; - return "MB"; -} - -void LinearAllocator::dumpMemoryStats(const char* prefix) { - float prettySize; - const char* prettySuffix; - prettySuffix = toSize(mTotalAllocated, prettySize); - ALOGD("%sTotal allocated: %.2f%s", prefix, prettySize, prettySuffix); - prettySuffix = toSize(mWastedSpace, prettySize); - ALOGD("%sWasted space: %.2f%s (%.1f%%)", prefix, prettySize, prettySuffix, - (float) mWastedSpace / (float) mTotalAllocated * 100.0f); - ALOGD("%sPages %zu (dedicated %zu)", prefix, mPageCount, mDedicatedPageCount); -} - -}; // namespace android diff --git a/libs/utils/LinearTransform.cpp b/libs/utils/LinearTransform.cpp deleted file mode 100644 index b7d28d4..0000000 --- a/libs/utils/LinearTransform.cpp +++ /dev/null @@ -1,265 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define __STDC_LIMIT_MACROS - -#include <assert.h> -#include <stdint.h> - -#include <utils/LinearTransform.h> - -namespace android { - -template<class T> static inline T ABS(T x) { return (x < 0) ? -x : x; } - -// Static math methods involving linear transformations -static bool scale_u64_to_u64( - uint64_t val, - uint32_t N, - uint32_t D, - uint64_t* res, - bool round_up_not_down) { - uint64_t tmp1, tmp2; - uint32_t r; - - assert(res); - assert(D); - - // Let U32(X) denote a uint32_t containing the upper 32 bits of a 64 bit - // integer X. - // Let L32(X) denote a uint32_t containing the lower 32 bits of a 64 bit - // integer X. - // Let X[A, B] with A <= B denote bits A through B of the integer X. - // Let (A | B) denote the concatination of two 32 bit ints, A and B. - // IOW X = (A | B) => U32(X) == A && L32(X) == B - // - // compute M = val * N (a 96 bit int) - // --------------------------------- - // tmp2 = U32(val) * N (a 64 bit int) - // tmp1 = L32(val) * N (a 64 bit int) - // which means - // M = val * N = (tmp2 << 32) + tmp1 - tmp2 = (val >> 32) * N; - tmp1 = (val & UINT32_MAX) * N; - - // compute M[32, 95] - // tmp2 = tmp2 + U32(tmp1) - // = (U32(val) * N) + U32(L32(val) * N) - // = M[32, 95] - tmp2 += tmp1 >> 32; - - // if M[64, 95] >= D, then M/D has bits > 63 set and we have - // an overflow. - if ((tmp2 >> 32) >= D) { - *res = UINT64_MAX; - return false; - } - - // Divide. Going in we know - // tmp2 = M[32, 95] - // U32(tmp2) < D - r = tmp2 % D; - tmp2 /= D; - - // At this point - // tmp1 = L32(val) * N - // tmp2 = M[32, 95] / D - // = (M / D)[32, 95] - // r = M[32, 95] % D - // U32(tmp2) = 0 - // - // compute tmp1 = (r | M[0, 31]) - tmp1 = (tmp1 & UINT32_MAX) | ((uint64_t)r << 32); - - // Divide again. Keep the remainder around in order to round properly. - r = tmp1 % D; - tmp1 /= D; - - // At this point - // tmp2 = (M / D)[32, 95] - // tmp1 = (M / D)[ 0, 31] - // r = M % D - // U32(tmp1) = 0 - // U32(tmp2) = 0 - - // Pack the result and deal with the round-up case (As well as the - // remote possiblility over overflow in such a case). - *res = (tmp2 << 32) | tmp1; - if (r && round_up_not_down) { - ++(*res); - if (!(*res)) { - *res = UINT64_MAX; - return false; - } - } - - return true; -} - -static bool linear_transform_s64_to_s64( - int64_t val, - int64_t basis1, - int32_t N, - uint32_t D, - bool invert_frac, - int64_t basis2, - int64_t* out) { - uint64_t scaled, res; - uint64_t abs_val; - bool is_neg; - - if (!out) - return false; - - // Compute abs(val - basis_64). Keep track of whether or not this delta - // will be negative after the scale opertaion. - if (val < basis1) { - is_neg = true; - abs_val = basis1 - val; - } else { - is_neg = false; - abs_val = val - basis1; - } - - if (N < 0) - is_neg = !is_neg; - - if (!scale_u64_to_u64(abs_val, - invert_frac ? D : ABS(N), - invert_frac ? ABS(N) : D, - &scaled, - is_neg)) - return false; // overflow/undeflow - - // if scaled is >= 0x8000<etc>, then we are going to overflow or - // underflow unless ABS(basis2) is large enough to pull us back into the - // non-overflow/underflow region. - if (scaled & INT64_MIN) { - if (is_neg && (basis2 < 0)) - return false; // certain underflow - - if (!is_neg && (basis2 >= 0)) - return false; // certain overflow - - if (ABS(basis2) <= static_cast<int64_t>(scaled & INT64_MAX)) - return false; // not enough - - // Looks like we are OK - *out = (is_neg ? (-scaled) : scaled) + basis2; - } else { - // Scaled fits within signed bounds, so we just need to check for - // over/underflow for two signed integers. Basically, if both scaled - // and basis2 have the same sign bit, and the result has a different - // sign bit, then we have under/overflow. An easy way to compute this - // is - // (scaled_signbit XNOR basis_signbit) && - // (scaled_signbit XOR res_signbit) - // == - // (scaled_signbit XOR basis_signbit XOR 1) && - // (scaled_signbit XOR res_signbit) - - if (is_neg) - scaled = -scaled; - res = scaled + basis2; - - if ((scaled ^ basis2 ^ INT64_MIN) & (scaled ^ res) & INT64_MIN) - return false; - - *out = res; - } - - return true; -} - -bool LinearTransform::doForwardTransform(int64_t a_in, int64_t* b_out) const { - if (0 == a_to_b_denom) - return false; - - return linear_transform_s64_to_s64(a_in, - a_zero, - a_to_b_numer, - a_to_b_denom, - false, - b_zero, - b_out); -} - -bool LinearTransform::doReverseTransform(int64_t b_in, int64_t* a_out) const { - if (0 == a_to_b_numer) - return false; - - return linear_transform_s64_to_s64(b_in, - b_zero, - a_to_b_numer, - a_to_b_denom, - true, - a_zero, - a_out); -} - -template <class T> void LinearTransform::reduce(T* N, T* D) { - T a, b; - if (!N || !D || !(*D)) { - assert(false); - return; - } - - a = *N; - b = *D; - - if (a == 0) { - *D = 1; - return; - } - - // This implements Euclid's method to find GCD. - if (a < b) { - T tmp = a; - a = b; - b = tmp; - } - - while (1) { - // a is now the greater of the two. - const T remainder = a % b; - if (remainder == 0) { - *N /= b; - *D /= b; - return; - } - // by swapping remainder and b, we are guaranteeing that a is - // still the greater of the two upon entrance to the loop. - a = b; - b = remainder; - } -}; - -template void LinearTransform::reduce<uint64_t>(uint64_t* N, uint64_t* D); -template void LinearTransform::reduce<uint32_t>(uint32_t* N, uint32_t* D); - -void LinearTransform::reduce(int32_t* N, uint32_t* D) { - if (N && D && *D) { - if (*N < 0) { - *N = -(*N); - reduce(reinterpret_cast<uint32_t*>(N), D); - *N = -(*N); - } else { - reduce(reinterpret_cast<uint32_t*>(N), D); - } - } -} - -} // namespace android diff --git a/libs/utils/Log.cpp b/libs/utils/Log.cpp deleted file mode 100644 index bffb56e..0000000 --- a/libs/utils/Log.cpp +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "Log" - -#include <utils/Log.h> -#include <utils/Timers.h> - -namespace android { - -LogIfSlow::LogIfSlow(const char* tag, android_LogPriority priority, - int timeoutMillis, const char* message) : - mTag(tag), mPriority(priority), mTimeoutMillis(timeoutMillis), mMessage(message), - mStart(systemTime(SYSTEM_TIME_BOOTTIME)) { -} - -LogIfSlow::~LogIfSlow() { - int durationMillis = nanoseconds_to_milliseconds(systemTime(SYSTEM_TIME_BOOTTIME) - mStart); - if (durationMillis > mTimeoutMillis) { - LOG_PRI(mPriority, mTag, "%s: %dms", mMessage, durationMillis); - } -} - -} // namespace android diff --git a/libs/utils/Looper.cpp b/libs/utils/Looper.cpp deleted file mode 100644 index a5e6645..0000000 --- a/libs/utils/Looper.cpp +++ /dev/null @@ -1,561 +0,0 @@ -// -// Copyright 2010 The Android Open Source Project -// -// A looper implementation based on epoll(). -// -#define LOG_TAG "Looper" - -//#define LOG_NDEBUG 0 - -// Debugs poll and wake interactions. -#define DEBUG_POLL_AND_WAKE 0 - -// Debugs callback registration and invocation. -#define DEBUG_CALLBACKS 0 - -#include <cutils/log.h> -#include <utils/Looper.h> -#include <utils/Timers.h> - -#include <unistd.h> -#include <fcntl.h> -#include <limits.h> - - -namespace android { - -// --- WeakMessageHandler --- - -WeakMessageHandler::WeakMessageHandler(const wp<MessageHandler>& handler) : - mHandler(handler) { -} - -WeakMessageHandler::~WeakMessageHandler() { -} - -void WeakMessageHandler::handleMessage(const Message& message) { - sp<MessageHandler> handler = mHandler.promote(); - if (handler != NULL) { - handler->handleMessage(message); - } -} - - -// --- SimpleLooperCallback --- - -SimpleLooperCallback::SimpleLooperCallback(ALooper_callbackFunc callback) : - mCallback(callback) { -} - -SimpleLooperCallback::~SimpleLooperCallback() { -} - -int SimpleLooperCallback::handleEvent(int fd, int events, void* data) { - return mCallback(fd, events, data); -} - - -// --- Looper --- - -// Hint for number of file descriptors to be associated with the epoll instance. -static const int EPOLL_SIZE_HINT = 8; - -// Maximum number of file descriptors for which to retrieve poll events each iteration. -static const int EPOLL_MAX_EVENTS = 16; - -static pthread_once_t gTLSOnce = PTHREAD_ONCE_INIT; -static pthread_key_t gTLSKey = 0; - -Looper::Looper(bool allowNonCallbacks) : - mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false), - mResponseIndex(0), mNextMessageUptime(LLONG_MAX) { - int wakeFds[2]; - int result = pipe(wakeFds); - LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe. errno=%d", errno); - - mWakeReadPipeFd = wakeFds[0]; - mWakeWritePipeFd = wakeFds[1]; - - result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK); - LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking. errno=%d", - errno); - - result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK); - LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking. errno=%d", - errno); - - // Allocate the epoll instance and register the wake pipe. - mEpollFd = epoll_create(EPOLL_SIZE_HINT); - LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance. errno=%d", errno); - - struct epoll_event eventItem; - memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union - eventItem.events = EPOLLIN; - eventItem.data.fd = mWakeReadPipeFd; - result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem); - LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance. errno=%d", - errno); -} - -Looper::~Looper() { - close(mWakeReadPipeFd); - close(mWakeWritePipeFd); - close(mEpollFd); -} - -void Looper::initTLSKey() { - int result = pthread_key_create(& gTLSKey, threadDestructor); - LOG_ALWAYS_FATAL_IF(result != 0, "Could not allocate TLS key."); -} - -void Looper::threadDestructor(void *st) { - Looper* const self = static_cast<Looper*>(st); - if (self != NULL) { - self->decStrong((void*)threadDestructor); - } -} - -void Looper::setForThread(const sp<Looper>& looper) { - sp<Looper> old = getForThread(); // also has side-effect of initializing TLS - - if (looper != NULL) { - looper->incStrong((void*)threadDestructor); - } - - pthread_setspecific(gTLSKey, looper.get()); - - if (old != NULL) { - old->decStrong((void*)threadDestructor); - } -} - -sp<Looper> Looper::getForThread() { - int result = pthread_once(& gTLSOnce, initTLSKey); - LOG_ALWAYS_FATAL_IF(result != 0, "pthread_once failed"); - - return (Looper*)pthread_getspecific(gTLSKey); -} - -sp<Looper> Looper::prepare(int opts) { - bool allowNonCallbacks = opts & ALOOPER_PREPARE_ALLOW_NON_CALLBACKS; - sp<Looper> looper = Looper::getForThread(); - if (looper == NULL) { - looper = new Looper(allowNonCallbacks); - Looper::setForThread(looper); - } - if (looper->getAllowNonCallbacks() != allowNonCallbacks) { - ALOGW("Looper already prepared for this thread with a different value for the " - "ALOOPER_PREPARE_ALLOW_NON_CALLBACKS option."); - } - return looper; -} - -bool Looper::getAllowNonCallbacks() const { - return mAllowNonCallbacks; -} - -int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) { - int result = 0; - for (;;) { - while (mResponseIndex < mResponses.size()) { - const Response& response = mResponses.itemAt(mResponseIndex++); - int ident = response.request.ident; - if (ident >= 0) { - int fd = response.request.fd; - int events = response.events; - void* data = response.request.data; -#if DEBUG_POLL_AND_WAKE - ALOGD("%p ~ pollOnce - returning signalled identifier %d: " - "fd=%d, events=0x%x, data=%p", - this, ident, fd, events, data); -#endif - if (outFd != NULL) *outFd = fd; - if (outEvents != NULL) *outEvents = events; - if (outData != NULL) *outData = data; - return ident; - } - } - - if (result != 0) { -#if DEBUG_POLL_AND_WAKE - ALOGD("%p ~ pollOnce - returning result %d", this, result); -#endif - if (outFd != NULL) *outFd = 0; - if (outEvents != NULL) *outEvents = 0; - if (outData != NULL) *outData = NULL; - return result; - } - - result = pollInner(timeoutMillis); - } -} - -int Looper::pollInner(int timeoutMillis) { -#if DEBUG_POLL_AND_WAKE - ALOGD("%p ~ pollOnce - waiting: timeoutMillis=%d", this, timeoutMillis); -#endif - - // Adjust the timeout based on when the next message is due. - if (timeoutMillis != 0 && mNextMessageUptime != LLONG_MAX) { - nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); - int messageTimeoutMillis = toMillisecondTimeoutDelay(now, mNextMessageUptime); - if (messageTimeoutMillis >= 0 - && (timeoutMillis < 0 || messageTimeoutMillis < timeoutMillis)) { - timeoutMillis = messageTimeoutMillis; - } -#if DEBUG_POLL_AND_WAKE - ALOGD("%p ~ pollOnce - next message in %lldns, adjusted timeout: timeoutMillis=%d", - this, mNextMessageUptime - now, timeoutMillis); -#endif - } - - // Poll. - int result = ALOOPER_POLL_WAKE; - mResponses.clear(); - mResponseIndex = 0; - - struct epoll_event eventItems[EPOLL_MAX_EVENTS]; - int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis); - - // Acquire lock. - mLock.lock(); - - // Check for poll error. - if (eventCount < 0) { - if (errno == EINTR) { - goto Done; - } - ALOGW("Poll failed with an unexpected error, errno=%d", errno); - result = ALOOPER_POLL_ERROR; - goto Done; - } - - // Check for poll timeout. - if (eventCount == 0) { -#if DEBUG_POLL_AND_WAKE - ALOGD("%p ~ pollOnce - timeout", this); -#endif - result = ALOOPER_POLL_TIMEOUT; - goto Done; - } - - // Handle all events. -#if DEBUG_POLL_AND_WAKE - ALOGD("%p ~ pollOnce - handling events from %d fds", this, eventCount); -#endif - - for (int i = 0; i < eventCount; i++) { - int fd = eventItems[i].data.fd; - uint32_t epollEvents = eventItems[i].events; - if (fd == mWakeReadPipeFd) { - if (epollEvents & EPOLLIN) { - awoken(); - } else { - ALOGW("Ignoring unexpected epoll events 0x%x on wake read pipe.", epollEvents); - } - } else { - ssize_t requestIndex = mRequests.indexOfKey(fd); - if (requestIndex >= 0) { - int events = 0; - if (epollEvents & EPOLLIN) events |= ALOOPER_EVENT_INPUT; - if (epollEvents & EPOLLOUT) events |= ALOOPER_EVENT_OUTPUT; - if (epollEvents & EPOLLERR) events |= ALOOPER_EVENT_ERROR; - if (epollEvents & EPOLLHUP) events |= ALOOPER_EVENT_HANGUP; - pushResponse(events, mRequests.valueAt(requestIndex)); - } else { - ALOGW("Ignoring unexpected epoll events 0x%x on fd %d that is " - "no longer registered.", epollEvents, fd); - } - } - } -Done: ; - - // Invoke pending message callbacks. - mNextMessageUptime = LLONG_MAX; - while (mMessageEnvelopes.size() != 0) { - nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); - const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(0); - if (messageEnvelope.uptime <= now) { - // Remove the envelope from the list. - // We keep a strong reference to the handler until the call to handleMessage - // finishes. Then we drop it so that the handler can be deleted *before* - // we reacquire our lock. - { // obtain handler - sp<MessageHandler> handler = messageEnvelope.handler; - Message message = messageEnvelope.message; - mMessageEnvelopes.removeAt(0); - mSendingMessage = true; - mLock.unlock(); - -#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS - ALOGD("%p ~ pollOnce - sending message: handler=%p, what=%d", - this, handler.get(), message.what); -#endif - handler->handleMessage(message); - } // release handler - - mLock.lock(); - mSendingMessage = false; - result = ALOOPER_POLL_CALLBACK; - } else { - // The last message left at the head of the queue determines the next wakeup time. - mNextMessageUptime = messageEnvelope.uptime; - break; - } - } - - // Release lock. - mLock.unlock(); - - // Invoke all response callbacks. - for (size_t i = 0; i < mResponses.size(); i++) { - Response& response = mResponses.editItemAt(i); - if (response.request.ident == ALOOPER_POLL_CALLBACK) { - int fd = response.request.fd; - int events = response.events; - void* data = response.request.data; -#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS - ALOGD("%p ~ pollOnce - invoking fd event callback %p: fd=%d, events=0x%x, data=%p", - this, response.request.callback.get(), fd, events, data); -#endif - int callbackResult = response.request.callback->handleEvent(fd, events, data); - if (callbackResult == 0) { - removeFd(fd); - } - // Clear the callback reference in the response structure promptly because we - // will not clear the response vector itself until the next poll. - response.request.callback.clear(); - result = ALOOPER_POLL_CALLBACK; - } - } - return result; -} - -int Looper::pollAll(int timeoutMillis, int* outFd, int* outEvents, void** outData) { - if (timeoutMillis <= 0) { - int result; - do { - result = pollOnce(timeoutMillis, outFd, outEvents, outData); - } while (result == ALOOPER_POLL_CALLBACK); - return result; - } else { - nsecs_t endTime = systemTime(SYSTEM_TIME_MONOTONIC) - + milliseconds_to_nanoseconds(timeoutMillis); - - for (;;) { - int result = pollOnce(timeoutMillis, outFd, outEvents, outData); - if (result != ALOOPER_POLL_CALLBACK) { - return result; - } - - nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); - timeoutMillis = toMillisecondTimeoutDelay(now, endTime); - if (timeoutMillis == 0) { - return ALOOPER_POLL_TIMEOUT; - } - } - } -} - -void Looper::wake() { -#if DEBUG_POLL_AND_WAKE - ALOGD("%p ~ wake", this); -#endif - - ssize_t nWrite; - do { - nWrite = write(mWakeWritePipeFd, "W", 1); - } while (nWrite == -1 && errno == EINTR); - - if (nWrite != 1) { - if (errno != EAGAIN) { - ALOGW("Could not write wake signal, errno=%d", errno); - } - } -} - -void Looper::awoken() { -#if DEBUG_POLL_AND_WAKE - ALOGD("%p ~ awoken", this); -#endif - - char buffer[16]; - ssize_t nRead; - do { - nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer)); - } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer)); -} - -void Looper::pushResponse(int events, const Request& request) { - Response response; - response.events = events; - response.request = request; - mResponses.push(response); -} - -int Looper::addFd(int fd, int ident, int events, ALooper_callbackFunc callback, void* data) { - return addFd(fd, ident, events, callback ? new SimpleLooperCallback(callback) : NULL, data); -} - -int Looper::addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data) { -#if DEBUG_CALLBACKS - ALOGD("%p ~ addFd - fd=%d, ident=%d, events=0x%x, callback=%p, data=%p", this, fd, ident, - events, callback.get(), data); -#endif - - if (!callback.get()) { - if (! mAllowNonCallbacks) { - ALOGE("Invalid attempt to set NULL callback but not allowed for this looper."); - return -1; - } - - if (ident < 0) { - ALOGE("Invalid attempt to set NULL callback with ident < 0."); - return -1; - } - } else { - ident = ALOOPER_POLL_CALLBACK; - } - - int epollEvents = 0; - if (events & ALOOPER_EVENT_INPUT) epollEvents |= EPOLLIN; - if (events & ALOOPER_EVENT_OUTPUT) epollEvents |= EPOLLOUT; - - { // acquire lock - AutoMutex _l(mLock); - - Request request; - request.fd = fd; - request.ident = ident; - request.callback = callback; - request.data = data; - - struct epoll_event eventItem; - memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union - eventItem.events = epollEvents; - eventItem.data.fd = fd; - - ssize_t requestIndex = mRequests.indexOfKey(fd); - if (requestIndex < 0) { - int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem); - if (epollResult < 0) { - ALOGE("Error adding epoll events for fd %d, errno=%d", fd, errno); - return -1; - } - mRequests.add(fd, request); - } else { - int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_MOD, fd, & eventItem); - if (epollResult < 0) { - ALOGE("Error modifying epoll events for fd %d, errno=%d", fd, errno); - return -1; - } - mRequests.replaceValueAt(requestIndex, request); - } - } // release lock - return 1; -} - -int Looper::removeFd(int fd) { -#if DEBUG_CALLBACKS - ALOGD("%p ~ removeFd - fd=%d", this, fd); -#endif - - { // acquire lock - AutoMutex _l(mLock); - ssize_t requestIndex = mRequests.indexOfKey(fd); - if (requestIndex < 0) { - return 0; - } - - int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_DEL, fd, NULL); - if (epollResult < 0) { - ALOGE("Error removing epoll events for fd %d, errno=%d", fd, errno); - return -1; - } - - mRequests.removeItemsAt(requestIndex); - } // release lock - return 1; -} - -void Looper::sendMessage(const sp<MessageHandler>& handler, const Message& message) { - nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); - sendMessageAtTime(now, handler, message); -} - -void Looper::sendMessageDelayed(nsecs_t uptimeDelay, const sp<MessageHandler>& handler, - const Message& message) { - nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); - sendMessageAtTime(now + uptimeDelay, handler, message); -} - -void Looper::sendMessageAtTime(nsecs_t uptime, const sp<MessageHandler>& handler, - const Message& message) { -#if DEBUG_CALLBACKS - ALOGD("%p ~ sendMessageAtTime - uptime=%lld, handler=%p, what=%d", - this, uptime, handler.get(), message.what); -#endif - - size_t i = 0; - { // acquire lock - AutoMutex _l(mLock); - - size_t messageCount = mMessageEnvelopes.size(); - while (i < messageCount && uptime >= mMessageEnvelopes.itemAt(i).uptime) { - i += 1; - } - - MessageEnvelope messageEnvelope(uptime, handler, message); - mMessageEnvelopes.insertAt(messageEnvelope, i, 1); - - // Optimization: If the Looper is currently sending a message, then we can skip - // the call to wake() because the next thing the Looper will do after processing - // messages is to decide when the next wakeup time should be. In fact, it does - // not even matter whether this code is running on the Looper thread. - if (mSendingMessage) { - return; - } - } // release lock - - // Wake the poll loop only when we enqueue a new message at the head. - if (i == 0) { - wake(); - } -} - -void Looper::removeMessages(const sp<MessageHandler>& handler) { -#if DEBUG_CALLBACKS - ALOGD("%p ~ removeMessages - handler=%p", this, handler.get()); -#endif - - { // acquire lock - AutoMutex _l(mLock); - - for (size_t i = mMessageEnvelopes.size(); i != 0; ) { - const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(--i); - if (messageEnvelope.handler == handler) { - mMessageEnvelopes.removeAt(i); - } - } - } // release lock -} - -void Looper::removeMessages(const sp<MessageHandler>& handler, int what) { -#if DEBUG_CALLBACKS - ALOGD("%p ~ removeMessages - handler=%p, what=%d", this, handler.get(), what); -#endif - - { // acquire lock - AutoMutex _l(mLock); - - for (size_t i = mMessageEnvelopes.size(); i != 0; ) { - const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(--i); - if (messageEnvelope.handler == handler - && messageEnvelope.message.what == what) { - mMessageEnvelopes.removeAt(i); - } - } - } // release lock -} - -} // namespace android diff --git a/libs/utils/MODULE_LICENSE_APACHE2 b/libs/utils/MODULE_LICENSE_APACHE2 deleted file mode 100644 index e69de29..0000000 --- a/libs/utils/MODULE_LICENSE_APACHE2 +++ /dev/null diff --git a/libs/utils/NOTICE b/libs/utils/NOTICE deleted file mode 100644 index c5b1efa..0000000 --- a/libs/utils/NOTICE +++ /dev/null @@ -1,190 +0,0 @@ - - Copyright (c) 2005-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. - - 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. - - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - diff --git a/libs/utils/PropertyMap.cpp b/libs/utils/PropertyMap.cpp deleted file mode 100644 index 5520702..0000000 --- a/libs/utils/PropertyMap.cpp +++ /dev/null @@ -1,218 +0,0 @@ -/* - * 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. - */ - -#define LOG_TAG "PropertyMap" - -#include <stdlib.h> -#include <string.h> - -#include <utils/PropertyMap.h> -#include <utils/Log.h> - -// Enables debug output for the parser. -#define DEBUG_PARSER 0 - -// Enables debug output for parser performance. -#define DEBUG_PARSER_PERFORMANCE 0 - - -namespace android { - -static const char* WHITESPACE = " \t\r"; -static const char* WHITESPACE_OR_PROPERTY_DELIMITER = " \t\r="; - - -// --- PropertyMap --- - -PropertyMap::PropertyMap() { -} - -PropertyMap::~PropertyMap() { -} - -void PropertyMap::clear() { - mProperties.clear(); -} - -void PropertyMap::addProperty(const String8& key, const String8& value) { - mProperties.add(key, value); -} - -bool PropertyMap::hasProperty(const String8& key) const { - return mProperties.indexOfKey(key) >= 0; -} - -bool PropertyMap::tryGetProperty(const String8& key, String8& outValue) const { - ssize_t index = mProperties.indexOfKey(key); - if (index < 0) { - return false; - } - - outValue = mProperties.valueAt(index); - return true; -} - -bool PropertyMap::tryGetProperty(const String8& key, bool& outValue) const { - int32_t intValue; - if (!tryGetProperty(key, intValue)) { - return false; - } - - outValue = intValue; - return true; -} - -bool PropertyMap::tryGetProperty(const String8& key, int32_t& outValue) const { - String8 stringValue; - if (! tryGetProperty(key, stringValue) || stringValue.length() == 0) { - return false; - } - - char* end; - int value = strtol(stringValue.string(), & end, 10); - if (*end != '\0') { - ALOGW("Property key '%s' has invalid value '%s'. Expected an integer.", - key.string(), stringValue.string()); - return false; - } - outValue = value; - return true; -} - -bool PropertyMap::tryGetProperty(const String8& key, float& outValue) const { - String8 stringValue; - if (! tryGetProperty(key, stringValue) || stringValue.length() == 0) { - return false; - } - - char* end; - float value = strtof(stringValue.string(), & end); - if (*end != '\0') { - ALOGW("Property key '%s' has invalid value '%s'. Expected a float.", - key.string(), stringValue.string()); - return false; - } - outValue = value; - return true; -} - -void PropertyMap::addAll(const PropertyMap* map) { - for (size_t i = 0; i < map->mProperties.size(); i++) { - mProperties.add(map->mProperties.keyAt(i), map->mProperties.valueAt(i)); - } -} - -status_t PropertyMap::load(const String8& filename, PropertyMap** outMap) { - *outMap = NULL; - - Tokenizer* tokenizer; - status_t status = Tokenizer::open(filename, &tokenizer); - if (status) { - ALOGE("Error %d opening property file %s.", status, filename.string()); - } else { - PropertyMap* map = new PropertyMap(); - if (!map) { - ALOGE("Error allocating property map."); - status = NO_MEMORY; - } else { -#if DEBUG_PARSER_PERFORMANCE - nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); -#endif - Parser parser(map, tokenizer); - status = parser.parse(); -#if DEBUG_PARSER_PERFORMANCE - nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; - ALOGD("Parsed property file '%s' %d lines in %0.3fms.", - tokenizer->getFilename().string(), tokenizer->getLineNumber(), - elapsedTime / 1000000.0); -#endif - if (status) { - delete map; - } else { - *outMap = map; - } - } - delete tokenizer; - } - return status; -} - - -// --- PropertyMap::Parser --- - -PropertyMap::Parser::Parser(PropertyMap* map, Tokenizer* tokenizer) : - mMap(map), mTokenizer(tokenizer) { -} - -PropertyMap::Parser::~Parser() { -} - -status_t PropertyMap::Parser::parse() { - while (!mTokenizer->isEof()) { -#if DEBUG_PARSER - ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(), - mTokenizer->peekRemainderOfLine().string()); -#endif - - mTokenizer->skipDelimiters(WHITESPACE); - - if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') { - String8 keyToken = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER); - if (keyToken.isEmpty()) { - ALOGE("%s: Expected non-empty property key.", mTokenizer->getLocation().string()); - return BAD_VALUE; - } - - mTokenizer->skipDelimiters(WHITESPACE); - - if (mTokenizer->nextChar() != '=') { - ALOGE("%s: Expected '=' between property key and value.", - mTokenizer->getLocation().string()); - return BAD_VALUE; - } - - mTokenizer->skipDelimiters(WHITESPACE); - - String8 valueToken = mTokenizer->nextToken(WHITESPACE); - if (valueToken.find("\\", 0) >= 0 || valueToken.find("\"", 0) >= 0) { - ALOGE("%s: Found reserved character '\\' or '\"' in property value.", - mTokenizer->getLocation().string()); - return BAD_VALUE; - } - - mTokenizer->skipDelimiters(WHITESPACE); - if (!mTokenizer->isEol()) { - ALOGE("%s: Expected end of line, got '%s'.", - mTokenizer->getLocation().string(), - mTokenizer->peekRemainderOfLine().string()); - return BAD_VALUE; - } - - if (mMap->hasProperty(keyToken)) { - ALOGE("%s: Duplicate property value for key '%s'.", - mTokenizer->getLocation().string(), keyToken.string()); - return BAD_VALUE; - } - - mMap->addProperty(keyToken, valueToken); - } - - mTokenizer->nextLine(); - } - return NO_ERROR; -} - -} // namespace android diff --git a/libs/utils/README b/libs/utils/README deleted file mode 100644 index 01741e0..0000000 --- a/libs/utils/README +++ /dev/null @@ -1,289 +0,0 @@ -Android Utility Function Library -================================ - - -If you need a feature that is native to Linux but not present on other -platforms, construct a platform-dependent implementation that shares -the Linux interface. That way the actual device runs as "light" as -possible. - -If that isn't feasible, create a system-independent interface and hide -the details. - -The ultimate goal is *not* to create a super-duper platform abstraction -layer. The goal is to provide an optimized solution for Linux with -reasonable implementations for other platforms. - - - -Resource overlay -================ - - -Introduction ------------- - -Overlay packages are special .apk files which provide no code but -additional resource values (and possibly new configurations) for -resources in other packages. When an application requests resources, -the system will return values from either the application's original -package or any associated overlay package. Any redirection is completely -transparent to the calling application. - -Resource values have the following precedence table, listed in -descending precedence. - - * overlay package, matching config (eg res/values-en-land) - - * original package, matching config - - * overlay package, no config (eg res/values) - - * original package, no config - -During compilation, overlay packages are differentiated from regular -packages by passing the -o flag to aapt. - - -Background ----------- - -This section provides generic background material on resources in -Android. - - -How resources are bundled in .apk files -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Android .apk files are .zip files, usually housing .dex code, -certificates and resources, though packages containing resources but -no code are possible. Resources can be divided into the following -categories; a `configuration' indicates a set of phone language, display -density, network operator, etc. - - * assets: uncompressed, raw files packaged as part of an .apk and - explicitly referenced by filename. These files are - independent of configuration. - - * res/drawable: bitmap or xml graphics. Each file may have different - values depending on configuration. - - * res/values: integers, strings, etc. Each resource may have different - values depending on configuration. - -Resource meta information and information proper is stored in a binary -format in a named file resources.arsc, bundled as part of the .apk. - -Resource IDs and lookup -~~~~~~~~~~~~~~~~~~~~~~~ -During compilation, the aapt tool gathers application resources and -generates a resources.arsc file. Each resource name is assigned an -integer ID 0xppttiii (translated to a symbolic name via R.java), where - - * pp: corresponds to the package namespace (details below). - - * tt: corresponds to the resource type (string, int, etc). Every - resource of the same type within the same package has the same - tt value, but depending on available types, the actual numerical - value may be different between packages. - - * iiii: sequential number, assigned in the order resources are found. - -Resource values are specified paired with a set of configuration -constraints (the default being the empty set), eg res/values-sv-port -which imposes restrictions on language (Swedish) and display orientation -(portrait). During lookup, every constraint set is matched against the -current configuration, and the value corresponding to the best matching -constraint set is returned (ResourceTypes.{h,cpp}). - -Parsing of resources.arsc is handled by ResourceTypes.cpp; this utility -is governed by AssetManager.cpp, which tracks loaded resources per -process. - -Assets are looked up by path and filename in AssetManager.cpp. The path -to resources in res/drawable are located by ResourceTypes.cpp and then -handled like assets by AssetManager.cpp. Other resources are handled -solely by ResourceTypes.cpp. - -Package ID as namespace -~~~~~~~~~~~~~~~~~~~~~~~ -The pp part of a resource ID defines a namespace. Android currently -defines two namespaces: - - * 0x01: system resources (pre-installed in framework-res.apk) - - * 0x7f: application resources (bundled in the application .apk) - -ResourceTypes.cpp supports package IDs between 0x01 and 0x7f -(inclusive); values outside this range are invalid. - -Each running (Dalvik) process is assigned a unique instance of -AssetManager, which in turn keeps a forest structure of loaded -resource.arsc files. Normally, this forest is structured as follows, -where mPackageMap is the internal vector employed in ResourceTypes.cpp. - -mPackageMap[0x00] -> system package -mPackageMap[0x01] -> NULL -mPackageMap[0x02] -> NULL -... -mPackageMap[0x7f - 2] -> NULL -mPackageMap[0x7f - 1] -> application package - - - -The resource overlay extension ------------------------------- - -The resource overlay mechanism aims to (partly) shadow and extend -existing resources with new values for defined and new configurations. -Technically, this is achieved by adding resource-only packages (called -overlay packages) to existing resource namespaces, like so: - -mPackageMap[0x00] -> system package -> system overlay package -mPackageMap[0x01] -> NULL -mPackageMap[0x02] -> NULL -... -mPackageMap[0x7f - 2] -> NULL -mPackageMap[0x7f - 1] -> application package -> overlay 1 -> overlay 2 - -The use of overlay resources is completely transparent to -applications; no additional resource identifiers are introduced, only -configuration/value pairs. Any number of overlay packages may be loaded -at a time; overlay packages are agnostic to what they target -- both -system and application resources are fair game. - -The package targeted by an overlay package is called the target or -original package. - -Resource overlay operates on symbolic resources names. Hence, to -override the string/str1 resources in a package, the overlay package -would include a resource also named string/str1. The end user does not -have to worry about the numeric resources IDs assigned by aapt, as this -is resolved automatically by the system. - -As of this writing, the use of resource overlay has not been fully -explored. Until it has, only OEMs are trusted to use resource overlay. -For this reason, overlay packages must reside in /system/overlay. - - -Resource ID mapping -~~~~~~~~~~~~~~~~~~~ -Resource identifiers must be coherent within the same namespace (ie -PackageGroup in ResourceTypes.cpp). Calling applications will refer to -resources using the IDs defined in the original package, but there is no -guarantee aapt has assigned the same ID to the corresponding resource in -an overlay package. To translate between the two, a resource ID mapping -{original ID -> overlay ID} is created during package installation -(PackageManagerService.java) and used during resource lookup. The -mapping is stored in /data/resource-cache, with a @idmap file name -suffix. - -The idmap file format is documented in a separate section, below. - - -Package management -~~~~~~~~~~~~~~~~~~ -Packages are managed by the PackageManagerService. Addition and removal -of packages are monitored via the inotify framework, exposed via -android.os.FileObserver. - -During initialization of a Dalvik process, ActivityThread.java requests -the process' AssetManager (by proxy, via AssetManager.java and JNI) -to load a list of packages. This list includes overlay packages, if -present. - -When a target package or a corresponding overlay package is installed, -the target package's process is stopped and a new idmap is generated. -This is similar to how applications are stopped when their packages are -upgraded. - - -Creating overlay packages -------------------------- - -Overlay packages should contain no code, define (some) resources with -the same type and name as in the original package, and be compiled with -the -o flag passed to aapt. - -The aapt -o flag instructs aapt to create an overlay package. -Technically, this means the package will be assigned package id 0x00. - -There are no restrictions on overlay packages names, though the naming -convention <original.package.name>.overlay.<name> is recommended. - - -Example overlay package -~~~~~~~~~~~~~~~~~~~~~~~ - -To overlay the resource bool/b in package com.foo.bar, to be applied -when the display is in landscape mode, create a new package with -no source code and a single .xml file under res/values-land, with -an entry for bool/b. Compile with aapt -o and place the results in -/system/overlay by adding the following to Android.mk: - -LOCAL_AAPT_FLAGS := -o com.foo.bar -LOCAL_MODULE_PATH := $(TARGET_OUT)/overlay - - -The ID map (idmap) file format ------------------------------- - -The idmap format is designed for lookup performance. However, leading -and trailing undefined overlay values are discarded to reduce the memory -footprint. - - -idmap grammar -~~~~~~~~~~~~~ -All atoms (names in square brackets) are uint32_t integers. The -idmap-magic constant spells "idmp" in ASCII. Offsets are given relative -to the data_header, not to the beginning of the file. - -map := header data -header := idmap-magic <crc32-original-pkg> <crc32-overlay-pkg> -idmap-magic := <0x706d6469> -data := data_header type_block+ -data_header := <m> header_block{m} -header_block := <0> | <type_block_offset> -type_block := <n> <id_offset> entry{n} -entry := <resource_id_in_target_package> - - -idmap example -~~~~~~~~~~~~~ -Given a pair of target and overlay packages with CRC sums 0x216a8fe2 -and 0x6b9beaec, each defining the following resources - -Name Target package Overlay package -string/str0 0x7f010000 - -string/str1 0x7f010001 0x7f010000 -string/str2 0x7f010002 - -string/str3 0x7f010003 0x7f010001 -string/str4 0x7f010004 - -bool/bool0 0x7f020000 - -integer/int0 0x7f030000 0x7f020000 -integer/int1 0x7f030001 - - -the corresponding resource map is - -0x706d6469 0x216a8fe2 0x6b9beaec 0x00000003 \ -0x00000004 0x00000000 0x00000009 0x00000003 \ -0x00000001 0x7f010000 0x00000000 0x7f010001 \ -0x00000001 0x00000000 0x7f020000 - -or, formatted differently - -0x706d6469 # magic: all idmap files begin with this constant -0x216a8fe2 # CRC32 of the resources.arsc file in the original package -0x6b9beaec # CRC32 of the resources.arsc file in the overlay package -0x00000003 # header; three types (string, bool, integer) in the target package -0x00000004 # header_block for type 0 (string) is located at offset 4 -0x00000000 # no bool type exists in overlay package -> no header_block -0x00000009 # header_block for type 2 (integer) is located at offset 9 -0x00000003 # header_block for string; overlay IDs span 3 elements -0x00000001 # the first string in target package is entry 1 == offset -0x7f010000 # target 0x7f01001 -> overlay 0x7f010000 -0x00000000 # str2 not defined in overlay package -0x7f010001 # target 0x7f010003 -> overlay 0x7f010001 -0x00000001 # header_block for integer; overlay IDs span 1 element -0x00000000 # offset == 0 -0x7f020000 # target 0x7f030000 -> overlay 0x7f020000 diff --git a/libs/utils/RefBase.cpp b/libs/utils/RefBase.cpp deleted file mode 100644 index e538f68..0000000 --- a/libs/utils/RefBase.cpp +++ /dev/null @@ -1,666 +0,0 @@ -/* - * Copyright (C) 2005 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "RefBase" -// #define LOG_NDEBUG 0 - -#include <utils/RefBase.h> - -#include <utils/Atomic.h> -#include <utils/CallStack.h> -#include <utils/Log.h> -#include <utils/threads.h> -#include <utils/TextOutput.h> - -#include <stdlib.h> -#include <stdio.h> -#include <typeinfo> -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <unistd.h> - -// compile with refcounting debugging enabled -#define DEBUG_REFS 0 - -// whether ref-tracking is enabled by default, if not, trackMe(true, false) -// needs to be called explicitly -#define DEBUG_REFS_ENABLED_BY_DEFAULT 0 - -// whether callstack are collected (significantly slows things down) -#define DEBUG_REFS_CALLSTACK_ENABLED 1 - -// folder where stack traces are saved when DEBUG_REFS is enabled -// this folder needs to exist and be writable -#define DEBUG_REFS_CALLSTACK_PATH "/data/debug" - -// log all reference counting operations -#define PRINT_REFS 0 - -// --------------------------------------------------------------------------- - -namespace android { - -#define INITIAL_STRONG_VALUE (1<<28) - -// --------------------------------------------------------------------------- - -class RefBase::weakref_impl : public RefBase::weakref_type -{ -public: - volatile int32_t mStrong; - volatile int32_t mWeak; - RefBase* const mBase; - volatile int32_t mFlags; - -#if !DEBUG_REFS - - weakref_impl(RefBase* base) - : mStrong(INITIAL_STRONG_VALUE) - , mWeak(0) - , mBase(base) - , mFlags(0) - { - } - - void addStrongRef(const void* /*id*/) { } - void removeStrongRef(const void* /*id*/) { } - void renameStrongRefId(const void* /*old_id*/, const void* /*new_id*/) { } - void addWeakRef(const void* /*id*/) { } - void removeWeakRef(const void* /*id*/) { } - void renameWeakRefId(const void* /*old_id*/, const void* /*new_id*/) { } - void printRefs() const { } - void trackMe(bool, bool) { } - -#else - - weakref_impl(RefBase* base) - : mStrong(INITIAL_STRONG_VALUE) - , mWeak(0) - , mBase(base) - , mFlags(0) - , mStrongRefs(NULL) - , mWeakRefs(NULL) - , mTrackEnabled(!!DEBUG_REFS_ENABLED_BY_DEFAULT) - , mRetain(false) - { - } - - ~weakref_impl() - { - bool dumpStack = false; - if (!mRetain && mStrongRefs != NULL) { - dumpStack = true; - ALOGE("Strong references remain:"); - ref_entry* refs = mStrongRefs; - while (refs) { - char inc = refs->ref >= 0 ? '+' : '-'; - ALOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref); -#if DEBUG_REFS_CALLSTACK_ENABLED - refs->stack.dump(LOG_TAG); -#endif - refs = refs->next; - } - } - - if (!mRetain && mWeakRefs != NULL) { - dumpStack = true; - ALOGE("Weak references remain!"); - ref_entry* refs = mWeakRefs; - while (refs) { - char inc = refs->ref >= 0 ? '+' : '-'; - ALOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref); -#if DEBUG_REFS_CALLSTACK_ENABLED - refs->stack.dump(LOG_TAG); -#endif - refs = refs->next; - } - } - if (dumpStack) { - ALOGE("above errors at:"); - CallStack stack(LOG_TAG); - } - } - - void addStrongRef(const void* id) { - //ALOGD_IF(mTrackEnabled, - // "addStrongRef: RefBase=%p, id=%p", mBase, id); - addRef(&mStrongRefs, id, mStrong); - } - - void removeStrongRef(const void* id) { - //ALOGD_IF(mTrackEnabled, - // "removeStrongRef: RefBase=%p, id=%p", mBase, id); - if (!mRetain) { - removeRef(&mStrongRefs, id); - } else { - addRef(&mStrongRefs, id, -mStrong); - } - } - - void renameStrongRefId(const void* old_id, const void* new_id) { - //ALOGD_IF(mTrackEnabled, - // "renameStrongRefId: RefBase=%p, oid=%p, nid=%p", - // mBase, old_id, new_id); - renameRefsId(mStrongRefs, old_id, new_id); - } - - void addWeakRef(const void* id) { - addRef(&mWeakRefs, id, mWeak); - } - - void removeWeakRef(const void* id) { - if (!mRetain) { - removeRef(&mWeakRefs, id); - } else { - addRef(&mWeakRefs, id, -mWeak); - } - } - - void renameWeakRefId(const void* old_id, const void* new_id) { - renameRefsId(mWeakRefs, old_id, new_id); - } - - void trackMe(bool track, bool retain) - { - mTrackEnabled = track; - mRetain = retain; - } - - void printRefs() const - { - String8 text; - - { - Mutex::Autolock _l(mMutex); - char buf[128]; - sprintf(buf, "Strong references on RefBase %p (weakref_type %p):\n", mBase, this); - text.append(buf); - printRefsLocked(&text, mStrongRefs); - sprintf(buf, "Weak references on RefBase %p (weakref_type %p):\n", mBase, this); - text.append(buf); - printRefsLocked(&text, mWeakRefs); - } - - { - char name[100]; - snprintf(name, 100, DEBUG_REFS_CALLSTACK_PATH "/%p.stack", this); - int rc = open(name, O_RDWR | O_CREAT | O_APPEND, 644); - if (rc >= 0) { - write(rc, text.string(), text.length()); - close(rc); - ALOGD("STACK TRACE for %p saved in %s", this, name); - } - else ALOGE("FAILED TO PRINT STACK TRACE for %p in %s: %s", this, - name, strerror(errno)); - } - } - -private: - struct ref_entry - { - ref_entry* next; - const void* id; -#if DEBUG_REFS_CALLSTACK_ENABLED - CallStack stack; -#endif - int32_t ref; - }; - - void addRef(ref_entry** refs, const void* id, int32_t mRef) - { - if (mTrackEnabled) { - AutoMutex _l(mMutex); - - ref_entry* ref = new ref_entry; - // Reference count at the time of the snapshot, but before the - // update. Positive value means we increment, negative--we - // decrement the reference count. - ref->ref = mRef; - ref->id = id; -#if DEBUG_REFS_CALLSTACK_ENABLED - ref->stack.update(2); -#endif - ref->next = *refs; - *refs = ref; - } - } - - void removeRef(ref_entry** refs, const void* id) - { - if (mTrackEnabled) { - AutoMutex _l(mMutex); - - ref_entry* const head = *refs; - ref_entry* ref = head; - while (ref != NULL) { - if (ref->id == id) { - *refs = ref->next; - delete ref; - return; - } - refs = &ref->next; - ref = *refs; - } - - ALOGE("RefBase: removing id %p on RefBase %p" - "(weakref_type %p) that doesn't exist!", - id, mBase, this); - - ref = head; - while (ref) { - char inc = ref->ref >= 0 ? '+' : '-'; - ALOGD("\t%c ID %p (ref %d):", inc, ref->id, ref->ref); - ref = ref->next; - } - - CallStack stack(LOG_TAG); - } - } - - void renameRefsId(ref_entry* r, const void* old_id, const void* new_id) - { - if (mTrackEnabled) { - AutoMutex _l(mMutex); - ref_entry* ref = r; - while (ref != NULL) { - if (ref->id == old_id) { - ref->id = new_id; - } - ref = ref->next; - } - } - } - - void printRefsLocked(String8* out, const ref_entry* refs) const - { - char buf[128]; - while (refs) { - char inc = refs->ref >= 0 ? '+' : '-'; - sprintf(buf, "\t%c ID %p (ref %d):\n", - inc, refs->id, refs->ref); - out->append(buf); -#if DEBUG_REFS_CALLSTACK_ENABLED - out->append(refs->stack.toString("\t\t")); -#else - out->append("\t\t(call stacks disabled)"); -#endif - refs = refs->next; - } - } - - mutable Mutex mMutex; - ref_entry* mStrongRefs; - ref_entry* mWeakRefs; - - bool mTrackEnabled; - // Collect stack traces on addref and removeref, instead of deleting the stack references - // on removeref that match the address ones. - bool mRetain; - -#endif -}; - -// --------------------------------------------------------------------------- - -void RefBase::incStrong(const void* id) const -{ - weakref_impl* const refs = mRefs; - refs->incWeak(id); - - refs->addStrongRef(id); - const int32_t c = android_atomic_inc(&refs->mStrong); - ALOG_ASSERT(c > 0, "incStrong() called on %p after last strong ref", refs); -#if PRINT_REFS - ALOGD("incStrong of %p from %p: cnt=%d\n", this, id, c); -#endif - if (c != INITIAL_STRONG_VALUE) { - return; - } - - android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong); - refs->mBase->onFirstRef(); -} - -void RefBase::decStrong(const void* id) const -{ - weakref_impl* const refs = mRefs; - refs->removeStrongRef(id); - const int32_t c = android_atomic_dec(&refs->mStrong); -#if PRINT_REFS - ALOGD("decStrong of %p from %p: cnt=%d\n", this, id, c); -#endif - ALOG_ASSERT(c >= 1, "decStrong() called on %p too many times", refs); - if (c == 1) { - refs->mBase->onLastStrongRef(id); - if ((refs->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) { - delete this; - } - } - refs->decWeak(id); -} - -void RefBase::forceIncStrong(const void* id) const -{ - weakref_impl* const refs = mRefs; - refs->incWeak(id); - - refs->addStrongRef(id); - const int32_t c = android_atomic_inc(&refs->mStrong); - ALOG_ASSERT(c >= 0, "forceIncStrong called on %p after ref count underflow", - refs); -#if PRINT_REFS - ALOGD("forceIncStrong of %p from %p: cnt=%d\n", this, id, c); -#endif - - switch (c) { - case INITIAL_STRONG_VALUE: - android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong); - // fall through... - case 0: - refs->mBase->onFirstRef(); - } -} - -int32_t RefBase::getStrongCount() const -{ - return mRefs->mStrong; -} - -RefBase* RefBase::weakref_type::refBase() const -{ - return static_cast<const weakref_impl*>(this)->mBase; -} - -void RefBase::weakref_type::incWeak(const void* id) -{ - weakref_impl* const impl = static_cast<weakref_impl*>(this); - impl->addWeakRef(id); - const int32_t c = android_atomic_inc(&impl->mWeak); - ALOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this); -} - - -void RefBase::weakref_type::decWeak(const void* id) -{ - weakref_impl* const impl = static_cast<weakref_impl*>(this); - impl->removeWeakRef(id); - const int32_t c = android_atomic_dec(&impl->mWeak); - ALOG_ASSERT(c >= 1, "decWeak called on %p too many times", this); - if (c != 1) return; - - if ((impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_STRONG) { - // This is the regular lifetime case. The object is destroyed - // when the last strong reference goes away. Since weakref_impl - // outlive the object, it is not destroyed in the dtor, and - // we'll have to do it here. - if (impl->mStrong == INITIAL_STRONG_VALUE) { - // Special case: we never had a strong reference, so we need to - // destroy the object now. - delete impl->mBase; - } else { - // ALOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase); - delete impl; - } - } else { - // less common case: lifetime is OBJECT_LIFETIME_{WEAK|FOREVER} - impl->mBase->onLastWeakRef(id); - if ((impl->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_WEAK) { - // this is the OBJECT_LIFETIME_WEAK case. The last weak-reference - // is gone, we can destroy the object. - delete impl->mBase; - } - } -} - -bool RefBase::weakref_type::attemptIncStrong(const void* id) -{ - incWeak(id); - - weakref_impl* const impl = static_cast<weakref_impl*>(this); - int32_t curCount = impl->mStrong; - - ALOG_ASSERT(curCount >= 0, - "attemptIncStrong called on %p after underflow", this); - - while (curCount > 0 && curCount != INITIAL_STRONG_VALUE) { - // we're in the easy/common case of promoting a weak-reference - // from an existing strong reference. - if (android_atomic_cmpxchg(curCount, curCount+1, &impl->mStrong) == 0) { - break; - } - // the strong count has changed on us, we need to re-assert our - // situation. - curCount = impl->mStrong; - } - - if (curCount <= 0 || curCount == INITIAL_STRONG_VALUE) { - // we're now in the harder case of either: - // - there never was a strong reference on us - // - or, all strong references have been released - if ((impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_STRONG) { - // this object has a "normal" life-time, i.e.: it gets destroyed - // when the last strong reference goes away - if (curCount <= 0) { - // the last strong-reference got released, the object cannot - // be revived. - decWeak(id); - return false; - } - - // here, curCount == INITIAL_STRONG_VALUE, which means - // there never was a strong-reference, so we can try to - // promote this object; we need to do that atomically. - while (curCount > 0) { - if (android_atomic_cmpxchg(curCount, curCount + 1, - &impl->mStrong) == 0) { - break; - } - // the strong count has changed on us, we need to re-assert our - // situation (e.g.: another thread has inc/decStrong'ed us) - curCount = impl->mStrong; - } - - if (curCount <= 0) { - // promote() failed, some other thread destroyed us in the - // meantime (i.e.: strong count reached zero). - decWeak(id); - return false; - } - } else { - // this object has an "extended" life-time, i.e.: it can be - // revived from a weak-reference only. - // Ask the object's implementation if it agrees to be revived - if (!impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id)) { - // it didn't so give-up. - decWeak(id); - return false; - } - // grab a strong-reference, which is always safe due to the - // extended life-time. - curCount = android_atomic_inc(&impl->mStrong); - } - - // If the strong reference count has already been incremented by - // someone else, the implementor of onIncStrongAttempted() is holding - // an unneeded reference. So call onLastStrongRef() here to remove it. - // (No, this is not pretty.) Note that we MUST NOT do this if we - // are in fact acquiring the first reference. - if (curCount > 0 && curCount < INITIAL_STRONG_VALUE) { - impl->mBase->onLastStrongRef(id); - } - } - - impl->addStrongRef(id); - -#if PRINT_REFS - ALOGD("attemptIncStrong of %p from %p: cnt=%d\n", this, id, curCount); -#endif - - // now we need to fix-up the count if it was INITIAL_STRONG_VALUE - // this must be done safely, i.e.: handle the case where several threads - // were here in attemptIncStrong(). - curCount = impl->mStrong; - while (curCount >= INITIAL_STRONG_VALUE) { - ALOG_ASSERT(curCount > INITIAL_STRONG_VALUE, - "attemptIncStrong in %p underflowed to INITIAL_STRONG_VALUE", - this); - if (android_atomic_cmpxchg(curCount, curCount-INITIAL_STRONG_VALUE, - &impl->mStrong) == 0) { - break; - } - // the strong-count changed on us, we need to re-assert the situation, - // for e.g.: it's possible the fix-up happened in another thread. - curCount = impl->mStrong; - } - - return true; -} - -bool RefBase::weakref_type::attemptIncWeak(const void* id) -{ - weakref_impl* const impl = static_cast<weakref_impl*>(this); - - int32_t curCount = impl->mWeak; - ALOG_ASSERT(curCount >= 0, "attemptIncWeak called on %p after underflow", - this); - while (curCount > 0) { - if (android_atomic_cmpxchg(curCount, curCount+1, &impl->mWeak) == 0) { - break; - } - curCount = impl->mWeak; - } - - if (curCount > 0) { - impl->addWeakRef(id); - } - - return curCount > 0; -} - -int32_t RefBase::weakref_type::getWeakCount() const -{ - return static_cast<const weakref_impl*>(this)->mWeak; -} - -void RefBase::weakref_type::printRefs() const -{ - static_cast<const weakref_impl*>(this)->printRefs(); -} - -void RefBase::weakref_type::trackMe(bool enable, bool retain) -{ - static_cast<weakref_impl*>(this)->trackMe(enable, retain); -} - -RefBase::weakref_type* RefBase::createWeak(const void* id) const -{ - mRefs->incWeak(id); - return mRefs; -} - -RefBase::weakref_type* RefBase::getWeakRefs() const -{ - return mRefs; -} - -RefBase::RefBase() - : mRefs(new weakref_impl(this)) -{ -} - -RefBase::~RefBase() -{ - if (mRefs->mStrong == INITIAL_STRONG_VALUE) { - // we never acquired a strong (and/or weak) reference on this object. - delete mRefs; - } else { - // life-time of this object is extended to WEAK or FOREVER, in - // which case weakref_impl doesn't out-live the object and we - // can free it now. - if ((mRefs->mFlags & OBJECT_LIFETIME_MASK) != OBJECT_LIFETIME_STRONG) { - // It's possible that the weak count is not 0 if the object - // re-acquired a weak reference in its destructor - if (mRefs->mWeak == 0) { - delete mRefs; - } - } - } - // for debugging purposes, clear this. - const_cast<weakref_impl*&>(mRefs) = NULL; -} - -void RefBase::extendObjectLifetime(int32_t mode) -{ - android_atomic_or(mode, &mRefs->mFlags); -} - -void RefBase::onFirstRef() -{ -} - -void RefBase::onLastStrongRef(const void* /*id*/) -{ -} - -bool RefBase::onIncStrongAttempted(uint32_t flags, const void* id) -{ - return (flags&FIRST_INC_STRONG) ? true : false; -} - -void RefBase::onLastWeakRef(const void* /*id*/) -{ -} - -// --------------------------------------------------------------------------- - -void RefBase::renameRefs(size_t n, const ReferenceRenamer& renamer) { -#if DEBUG_REFS - for (size_t i=0 ; i<n ; i++) { - renamer(i); - } -#endif -} - -void RefBase::renameRefId(weakref_type* ref, - const void* old_id, const void* new_id) { - weakref_impl* const impl = static_cast<weakref_impl*>(ref); - impl->renameStrongRefId(old_id, new_id); - impl->renameWeakRefId(old_id, new_id); -} - -void RefBase::renameRefId(RefBase* ref, - const void* old_id, const void* new_id) { - ref->mRefs->renameStrongRefId(old_id, new_id); - ref->mRefs->renameWeakRefId(old_id, new_id); -} - -// --------------------------------------------------------------------------- - -TextOutput& printStrongPointer(TextOutput& to, const void* val) -{ - to << "sp<>(" << val << ")"; - return to; -} - -TextOutput& printWeakPointer(TextOutput& to, const void* val) -{ - to << "wp<>(" << val << ")"; - return to; -} - - -}; // namespace android diff --git a/libs/utils/SharedBuffer.cpp b/libs/utils/SharedBuffer.cpp deleted file mode 100644 index 3555fb7..0000000 --- a/libs/utils/SharedBuffer.cpp +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (C) 2005 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 <stdlib.h> -#include <string.h> - -#include <utils/SharedBuffer.h> -#include <utils/Atomic.h> - -// --------------------------------------------------------------------------- - -namespace android { - -SharedBuffer* SharedBuffer::alloc(size_t size) -{ - SharedBuffer* sb = static_cast<SharedBuffer *>(malloc(sizeof(SharedBuffer) + size)); - if (sb) { - sb->mRefs = 1; - sb->mSize = size; - } - return sb; -} - - -ssize_t SharedBuffer::dealloc(const SharedBuffer* released) -{ - if (released->mRefs != 0) return -1; // XXX: invalid operation - free(const_cast<SharedBuffer*>(released)); - return 0; -} - -SharedBuffer* SharedBuffer::edit() const -{ - if (onlyOwner()) { - return const_cast<SharedBuffer*>(this); - } - SharedBuffer* sb = alloc(mSize); - if (sb) { - memcpy(sb->data(), data(), size()); - release(); - } - return sb; -} - -SharedBuffer* SharedBuffer::editResize(size_t newSize) const -{ - if (onlyOwner()) { - SharedBuffer* buf = const_cast<SharedBuffer*>(this); - if (buf->mSize == newSize) return buf; - buf = (SharedBuffer*)realloc(buf, sizeof(SharedBuffer) + newSize); - if (buf != NULL) { - buf->mSize = newSize; - return buf; - } - } - SharedBuffer* sb = alloc(newSize); - if (sb) { - const size_t mySize = mSize; - memcpy(sb->data(), data(), newSize < mySize ? newSize : mySize); - release(); - } - return sb; -} - -SharedBuffer* SharedBuffer::attemptEdit() const -{ - if (onlyOwner()) { - return const_cast<SharedBuffer*>(this); - } - return 0; -} - -SharedBuffer* SharedBuffer::reset(size_t new_size) const -{ - // cheap-o-reset. - SharedBuffer* sb = alloc(new_size); - if (sb) { - release(); - } - return sb; -} - -void SharedBuffer::acquire() const { - android_atomic_inc(&mRefs); -} - -int32_t SharedBuffer::release(uint32_t flags) const -{ - int32_t prev = 1; - if (onlyOwner() || ((prev = android_atomic_dec(&mRefs)) == 1)) { - mRefs = 0; - if ((flags & eKeepStorage) == 0) { - free(const_cast<SharedBuffer*>(this)); - } - } - return prev; -} - - -}; // namespace android diff --git a/libs/utils/Static.cpp b/libs/utils/Static.cpp deleted file mode 100644 index 624e917..0000000 --- a/libs/utils/Static.cpp +++ /dev/null @@ -1,91 +0,0 @@ -/* - * 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. - */ - -// All static variables go here, to control initialization and -// destruction order in the library. - -#include <private/utils/Static.h> - -#include <utils/BufferedTextOutput.h> -#include <utils/Log.h> - -namespace android { - -class LibUtilsFirstStatics -{ -public: - LibUtilsFirstStatics() - { - initialize_string8(); - initialize_string16(); - } - - ~LibUtilsFirstStatics() - { - terminate_string16(); - terminate_string8(); - } -}; - -static LibUtilsFirstStatics gFirstStatics; -int gDarwinCantLoadAllObjects = 1; - -// ------------ Text output streams - -Vector<int32_t> gTextBuffers; - -class LogTextOutput : public BufferedTextOutput -{ -public: - LogTextOutput() : BufferedTextOutput(MULTITHREADED) { } - virtual ~LogTextOutput() { }; - -protected: - virtual status_t writeLines(const struct iovec& vec, size_t N) - { - //android_writevLog(&vec, N); <-- this is now a no-op - if (N != 1) ALOGI("WARNING: writeLines N=%zu\n", N); - ALOGI("%.*s", (int)vec.iov_len, (const char*) vec.iov_base); - return NO_ERROR; - } -}; - -class FdTextOutput : public BufferedTextOutput -{ -public: - FdTextOutput(int fd) : BufferedTextOutput(MULTITHREADED), mFD(fd) { } - virtual ~FdTextOutput() { }; - -protected: - virtual status_t writeLines(const struct iovec& vec, size_t N) - { - writev(mFD, &vec, N); - return NO_ERROR; - } - -private: - int mFD; -}; - -static LogTextOutput gLogTextOutput; -static FdTextOutput gStdoutTextOutput(STDOUT_FILENO); -static FdTextOutput gStderrTextOutput(STDERR_FILENO); - -TextOutput& alog(gLogTextOutput); -TextOutput& aout(gStdoutTextOutput); -TextOutput& aerr(gStderrTextOutput); - -} // namespace android diff --git a/libs/utils/StopWatch.cpp b/libs/utils/StopWatch.cpp deleted file mode 100644 index b1708d6..0000000 --- a/libs/utils/StopWatch.cpp +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2005 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "StopWatch" - -#include <string.h> -#include <stdlib.h> -#include <stdio.h> - -/* for PRId64 */ -#define __STDC_FORMAT_MACROS 1 -#include <inttypes.h> - -#include <utils/Log.h> -#include <utils/Errors.h> -#include <utils/StopWatch.h> - -/*****************************************************************************/ - -namespace android { - - -StopWatch::StopWatch(const char *name, int clock, uint32_t flags) - : mName(name), mClock(clock), mFlags(flags) -{ - reset(); -} - -StopWatch::~StopWatch() -{ - nsecs_t elapsed = elapsedTime(); - const int n = mNumLaps; - ALOGD("StopWatch %s (us): %" PRId64 " ", mName, ns2us(elapsed)); - for (int i=0 ; i<n ; i++) { - const nsecs_t soFar = mLaps[i].soFar; - const nsecs_t thisLap = mLaps[i].thisLap; - ALOGD(" [%d: %" PRId64 ", %" PRId64, i, ns2us(soFar), ns2us(thisLap)); - } -} - -const char* StopWatch::name() const -{ - return mName; -} - -nsecs_t StopWatch::lap() -{ - nsecs_t elapsed = elapsedTime(); - if (mNumLaps >= 8) { - elapsed = 0; - } else { - const int n = mNumLaps; - mLaps[n].soFar = elapsed; - mLaps[n].thisLap = n ? (elapsed - mLaps[n-1].soFar) : elapsed; - mNumLaps = n+1; - } - return elapsed; -} - -nsecs_t StopWatch::elapsedTime() const -{ - return systemTime(mClock) - mStartTime; -} - -void StopWatch::reset() -{ - mNumLaps = 0; - mStartTime = systemTime(mClock); -} - - -/*****************************************************************************/ - -}; // namespace android - diff --git a/libs/utils/String16.cpp b/libs/utils/String16.cpp deleted file mode 100644 index 94e072f..0000000 --- a/libs/utils/String16.cpp +++ /dev/null @@ -1,418 +0,0 @@ -/* - * Copyright (C) 2005 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <utils/String16.h> - -#include <utils/Debug.h> -#include <utils/Log.h> -#include <utils/Unicode.h> -#include <utils/String8.h> -#include <utils/TextOutput.h> -#include <utils/threads.h> - -#include <private/utils/Static.h> - -#include <memory.h> -#include <stdio.h> -#include <ctype.h> - - -namespace android { - -static SharedBuffer* gEmptyStringBuf = NULL; -static char16_t* gEmptyString = NULL; - -static inline char16_t* getEmptyString() -{ - gEmptyStringBuf->acquire(); - return gEmptyString; -} - -void initialize_string16() -{ - SharedBuffer* buf = SharedBuffer::alloc(sizeof(char16_t)); - char16_t* str = (char16_t*)buf->data(); - *str = 0; - gEmptyStringBuf = buf; - gEmptyString = str; -} - -void terminate_string16() -{ - SharedBuffer::bufferFromData(gEmptyString)->release(); - gEmptyStringBuf = NULL; - gEmptyString = NULL; -} - -// --------------------------------------------------------------------------- - -static char16_t* allocFromUTF8(const char* u8str, size_t u8len) -{ - if (u8len == 0) return getEmptyString(); - - const uint8_t* u8cur = (const uint8_t*) u8str; - - const ssize_t u16len = utf8_to_utf16_length(u8cur, u8len); - if (u16len < 0) { - return getEmptyString(); - } - - const uint8_t* const u8end = u8cur + u8len; - - SharedBuffer* buf = SharedBuffer::alloc(sizeof(char16_t)*(u16len+1)); - if (buf) { - u8cur = (const uint8_t*) u8str; - char16_t* u16str = (char16_t*)buf->data(); - - utf8_to_utf16(u8cur, u8len, u16str); - - //printf("Created UTF-16 string from UTF-8 \"%s\":", in); - //printHexData(1, str, buf->size(), 16, 1); - //printf("\n"); - - return u16str; - } - - return getEmptyString(); -} - -// --------------------------------------------------------------------------- - -String16::String16() - : mString(getEmptyString()) -{ -} - -String16::String16(const String16& o) - : mString(o.mString) -{ - SharedBuffer::bufferFromData(mString)->acquire(); -} - -String16::String16(const String16& o, size_t len, size_t begin) - : mString(getEmptyString()) -{ - setTo(o, len, begin); -} - -String16::String16(const char16_t* o) -{ - size_t len = strlen16(o); - SharedBuffer* buf = SharedBuffer::alloc((len+1)*sizeof(char16_t)); - ALOG_ASSERT(buf, "Unable to allocate shared buffer"); - if (buf) { - char16_t* str = (char16_t*)buf->data(); - strcpy16(str, o); - mString = str; - return; - } - - mString = getEmptyString(); -} - -String16::String16(const char16_t* o, size_t len) -{ - SharedBuffer* buf = SharedBuffer::alloc((len+1)*sizeof(char16_t)); - ALOG_ASSERT(buf, "Unable to allocate shared buffer"); - if (buf) { - char16_t* str = (char16_t*)buf->data(); - memcpy(str, o, len*sizeof(char16_t)); - str[len] = 0; - mString = str; - return; - } - - mString = getEmptyString(); -} - -String16::String16(const String8& o) - : mString(allocFromUTF8(o.string(), o.size())) -{ -} - -String16::String16(const char* o) - : mString(allocFromUTF8(o, strlen(o))) -{ -} - -String16::String16(const char* o, size_t len) - : mString(allocFromUTF8(o, len)) -{ -} - -String16::~String16() -{ - SharedBuffer::bufferFromData(mString)->release(); -} - -void String16::setTo(const String16& other) -{ - SharedBuffer::bufferFromData(other.mString)->acquire(); - SharedBuffer::bufferFromData(mString)->release(); - mString = other.mString; -} - -status_t String16::setTo(const String16& other, size_t len, size_t begin) -{ - const size_t N = other.size(); - if (begin >= N) { - SharedBuffer::bufferFromData(mString)->release(); - mString = getEmptyString(); - return NO_ERROR; - } - if ((begin+len) > N) len = N-begin; - if (begin == 0 && len == N) { - setTo(other); - return NO_ERROR; - } - - if (&other == this) { - LOG_ALWAYS_FATAL("Not implemented"); - } - - return setTo(other.string()+begin, len); -} - -status_t String16::setTo(const char16_t* other) -{ - return setTo(other, strlen16(other)); -} - -status_t String16::setTo(const char16_t* other, size_t len) -{ - SharedBuffer* buf = SharedBuffer::bufferFromData(mString) - ->editResize((len+1)*sizeof(char16_t)); - if (buf) { - char16_t* str = (char16_t*)buf->data(); - memmove(str, other, len*sizeof(char16_t)); - str[len] = 0; - mString = str; - return NO_ERROR; - } - return NO_MEMORY; -} - -status_t String16::append(const String16& other) -{ - const size_t myLen = size(); - const size_t otherLen = other.size(); - if (myLen == 0) { - setTo(other); - return NO_ERROR; - } else if (otherLen == 0) { - return NO_ERROR; - } - - SharedBuffer* buf = SharedBuffer::bufferFromData(mString) - ->editResize((myLen+otherLen+1)*sizeof(char16_t)); - if (buf) { - char16_t* str = (char16_t*)buf->data(); - memcpy(str+myLen, other, (otherLen+1)*sizeof(char16_t)); - mString = str; - return NO_ERROR; - } - return NO_MEMORY; -} - -status_t String16::append(const char16_t* chrs, size_t otherLen) -{ - const size_t myLen = size(); - if (myLen == 0) { - setTo(chrs, otherLen); - return NO_ERROR; - } else if (otherLen == 0) { - return NO_ERROR; - } - - SharedBuffer* buf = SharedBuffer::bufferFromData(mString) - ->editResize((myLen+otherLen+1)*sizeof(char16_t)); - if (buf) { - char16_t* str = (char16_t*)buf->data(); - memcpy(str+myLen, chrs, otherLen*sizeof(char16_t)); - str[myLen+otherLen] = 0; - mString = str; - return NO_ERROR; - } - return NO_MEMORY; -} - -status_t String16::insert(size_t pos, const char16_t* chrs) -{ - return insert(pos, chrs, strlen16(chrs)); -} - -status_t String16::insert(size_t pos, const char16_t* chrs, size_t len) -{ - const size_t myLen = size(); - if (myLen == 0) { - return setTo(chrs, len); - return NO_ERROR; - } else if (len == 0) { - return NO_ERROR; - } - - if (pos > myLen) pos = myLen; - - #if 0 - printf("Insert in to %s: pos=%d, len=%d, myLen=%d, chrs=%s\n", - String8(*this).string(), pos, - len, myLen, String8(chrs, len).string()); - #endif - - SharedBuffer* buf = SharedBuffer::bufferFromData(mString) - ->editResize((myLen+len+1)*sizeof(char16_t)); - if (buf) { - char16_t* str = (char16_t*)buf->data(); - if (pos < myLen) { - memmove(str+pos+len, str+pos, (myLen-pos)*sizeof(char16_t)); - } - memcpy(str+pos, chrs, len*sizeof(char16_t)); - str[myLen+len] = 0; - mString = str; - #if 0 - printf("Result (%d chrs): %s\n", size(), String8(*this).string()); - #endif - return NO_ERROR; - } - return NO_MEMORY; -} - -ssize_t String16::findFirst(char16_t c) const -{ - const char16_t* str = string(); - const char16_t* p = str; - const char16_t* e = p + size(); - while (p < e) { - if (*p == c) { - return p-str; - } - p++; - } - return -1; -} - -ssize_t String16::findLast(char16_t c) const -{ - const char16_t* str = string(); - const char16_t* p = str; - const char16_t* e = p + size(); - while (p < e) { - e--; - if (*e == c) { - return e-str; - } - } - return -1; -} - -bool String16::startsWith(const String16& prefix) const -{ - const size_t ps = prefix.size(); - if (ps > size()) return false; - return strzcmp16(mString, ps, prefix.string(), ps) == 0; -} - -bool String16::startsWith(const char16_t* prefix) const -{ - const size_t ps = strlen16(prefix); - if (ps > size()) return false; - return strncmp16(mString, prefix, ps) == 0; -} - -status_t String16::makeLower() -{ - const size_t N = size(); - const char16_t* str = string(); - char16_t* edit = NULL; - for (size_t i=0; i<N; i++) { - const char16_t v = str[i]; - if (v >= 'A' && v <= 'Z') { - if (!edit) { - SharedBuffer* buf = SharedBuffer::bufferFromData(mString)->edit(); - if (!buf) { - return NO_MEMORY; - } - edit = (char16_t*)buf->data(); - mString = str = edit; - } - edit[i] = tolower((char)v); - } - } - return NO_ERROR; -} - -status_t String16::replaceAll(char16_t replaceThis, char16_t withThis) -{ - const size_t N = size(); - const char16_t* str = string(); - char16_t* edit = NULL; - for (size_t i=0; i<N; i++) { - if (str[i] == replaceThis) { - if (!edit) { - SharedBuffer* buf = SharedBuffer::bufferFromData(mString)->edit(); - if (!buf) { - return NO_MEMORY; - } - edit = (char16_t*)buf->data(); - mString = str = edit; - } - edit[i] = withThis; - } - } - return NO_ERROR; -} - -status_t String16::remove(size_t len, size_t begin) -{ - const size_t N = size(); - if (begin >= N) { - SharedBuffer::bufferFromData(mString)->release(); - mString = getEmptyString(); - return NO_ERROR; - } - if ((begin+len) > N) len = N-begin; - if (begin == 0 && len == N) { - return NO_ERROR; - } - - if (begin > 0) { - SharedBuffer* buf = SharedBuffer::bufferFromData(mString) - ->editResize((N+1)*sizeof(char16_t)); - if (!buf) { - return NO_MEMORY; - } - char16_t* str = (char16_t*)buf->data(); - memmove(str, str+begin, (N-begin+1)*sizeof(char16_t)); - mString = str; - } - SharedBuffer* buf = SharedBuffer::bufferFromData(mString) - ->editResize((len+1)*sizeof(char16_t)); - if (buf) { - char16_t* str = (char16_t*)buf->data(); - str[len] = 0; - mString = str; - return NO_ERROR; - } - return NO_MEMORY; -} - -TextOutput& operator<<(TextOutput& to, const String16& val) -{ - to << String8(val).string(); - return to; -} - -}; // namespace android diff --git a/libs/utils/String8.cpp b/libs/utils/String8.cpp deleted file mode 100644 index 562f026..0000000 --- a/libs/utils/String8.cpp +++ /dev/null @@ -1,634 +0,0 @@ -/* - * Copyright (C) 2005 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <utils/String8.h> - -#include <utils/Log.h> -#include <utils/Unicode.h> -#include <utils/SharedBuffer.h> -#include <utils/String16.h> -#include <utils/TextOutput.h> -#include <utils/threads.h> - -#include <private/utils/Static.h> - -#include <ctype.h> - -/* - * Functions outside android is below the namespace android, since they use - * functions and constants in android namespace. - */ - -// --------------------------------------------------------------------------- - -namespace android { - -// Separator used by resource paths. This is not platform dependent contrary -// to OS_PATH_SEPARATOR. -#define RES_PATH_SEPARATOR '/' - -static SharedBuffer* gEmptyStringBuf = NULL; -static char* gEmptyString = NULL; - -extern int gDarwinCantLoadAllObjects; -int gDarwinIsReallyAnnoying; - -static inline char* getEmptyString() -{ - gEmptyStringBuf->acquire(); - return gEmptyString; -} - -void initialize_string8() -{ - // HACK: This dummy dependency forces linking libutils Static.cpp, - // which is needed to initialize String8/String16 classes. - // These variables are named for Darwin, but are needed elsewhere too, - // including static linking on any platform. - gDarwinIsReallyAnnoying = gDarwinCantLoadAllObjects; - - SharedBuffer* buf = SharedBuffer::alloc(1); - char* str = (char*)buf->data(); - *str = 0; - gEmptyStringBuf = buf; - gEmptyString = str; -} - -void terminate_string8() -{ - SharedBuffer::bufferFromData(gEmptyString)->release(); - gEmptyStringBuf = NULL; - gEmptyString = NULL; -} - -// --------------------------------------------------------------------------- - -static char* allocFromUTF8(const char* in, size_t len) -{ - if (len > 0) { - SharedBuffer* buf = SharedBuffer::alloc(len+1); - ALOG_ASSERT(buf, "Unable to allocate shared buffer"); - if (buf) { - char* str = (char*)buf->data(); - memcpy(str, in, len); - str[len] = 0; - return str; - } - return NULL; - } - - return getEmptyString(); -} - -static char* allocFromUTF16(const char16_t* in, size_t len) -{ - if (len == 0) return getEmptyString(); - - const ssize_t bytes = utf16_to_utf8_length(in, len); - if (bytes < 0) { - return getEmptyString(); - } - - SharedBuffer* buf = SharedBuffer::alloc(bytes+1); - ALOG_ASSERT(buf, "Unable to allocate shared buffer"); - if (!buf) { - return getEmptyString(); - } - - char* str = (char*)buf->data(); - utf16_to_utf8(in, len, str); - return str; -} - -static char* allocFromUTF32(const char32_t* in, size_t len) -{ - if (len == 0) { - return getEmptyString(); - } - - const ssize_t bytes = utf32_to_utf8_length(in, len); - if (bytes < 0) { - return getEmptyString(); - } - - SharedBuffer* buf = SharedBuffer::alloc(bytes+1); - ALOG_ASSERT(buf, "Unable to allocate shared buffer"); - if (!buf) { - return getEmptyString(); - } - - char* str = (char*) buf->data(); - utf32_to_utf8(in, len, str); - - return str; -} - -// --------------------------------------------------------------------------- - -String8::String8() - : mString(getEmptyString()) -{ -} - -String8::String8(const String8& o) - : mString(o.mString) -{ - SharedBuffer::bufferFromData(mString)->acquire(); -} - -String8::String8(const char* o) - : mString(allocFromUTF8(o, strlen(o))) -{ - if (mString == NULL) { - mString = getEmptyString(); - } -} - -String8::String8(const char* o, size_t len) - : mString(allocFromUTF8(o, len)) -{ - if (mString == NULL) { - mString = getEmptyString(); - } -} - -String8::String8(const String16& o) - : mString(allocFromUTF16(o.string(), o.size())) -{ -} - -String8::String8(const char16_t* o) - : mString(allocFromUTF16(o, strlen16(o))) -{ -} - -String8::String8(const char16_t* o, size_t len) - : mString(allocFromUTF16(o, len)) -{ -} - -String8::String8(const char32_t* o) - : mString(allocFromUTF32(o, strlen32(o))) -{ -} - -String8::String8(const char32_t* o, size_t len) - : mString(allocFromUTF32(o, len)) -{ -} - -String8::~String8() -{ - SharedBuffer::bufferFromData(mString)->release(); -} - -String8 String8::format(const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - - String8 result(formatV(fmt, args)); - - va_end(args); - return result; -} - -String8 String8::formatV(const char* fmt, va_list args) -{ - String8 result; - result.appendFormatV(fmt, args); - return result; -} - -void String8::clear() { - SharedBuffer::bufferFromData(mString)->release(); - mString = getEmptyString(); -} - -void String8::setTo(const String8& other) -{ - SharedBuffer::bufferFromData(other.mString)->acquire(); - SharedBuffer::bufferFromData(mString)->release(); - mString = other.mString; -} - -status_t String8::setTo(const char* other) -{ - const char *newString = allocFromUTF8(other, strlen(other)); - SharedBuffer::bufferFromData(mString)->release(); - mString = newString; - if (mString) return NO_ERROR; - - mString = getEmptyString(); - return NO_MEMORY; -} - -status_t String8::setTo(const char* other, size_t len) -{ - const char *newString = allocFromUTF8(other, len); - SharedBuffer::bufferFromData(mString)->release(); - mString = newString; - if (mString) return NO_ERROR; - - mString = getEmptyString(); - return NO_MEMORY; -} - -status_t String8::setTo(const char16_t* other, size_t len) -{ - const char *newString = allocFromUTF16(other, len); - SharedBuffer::bufferFromData(mString)->release(); - mString = newString; - if (mString) return NO_ERROR; - - mString = getEmptyString(); - return NO_MEMORY; -} - -status_t String8::setTo(const char32_t* other, size_t len) -{ - const char *newString = allocFromUTF32(other, len); - SharedBuffer::bufferFromData(mString)->release(); - mString = newString; - if (mString) return NO_ERROR; - - mString = getEmptyString(); - return NO_MEMORY; -} - -status_t String8::append(const String8& other) -{ - const size_t otherLen = other.bytes(); - if (bytes() == 0) { - setTo(other); - return NO_ERROR; - } else if (otherLen == 0) { - return NO_ERROR; - } - - return real_append(other.string(), otherLen); -} - -status_t String8::append(const char* other) -{ - return append(other, strlen(other)); -} - -status_t String8::append(const char* other, size_t otherLen) -{ - if (bytes() == 0) { - return setTo(other, otherLen); - } else if (otherLen == 0) { - return NO_ERROR; - } - - return real_append(other, otherLen); -} - -status_t String8::appendFormat(const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - - status_t result = appendFormatV(fmt, args); - - va_end(args); - return result; -} - -status_t String8::appendFormatV(const char* fmt, va_list args) -{ - int result = NO_ERROR; - int n = vsnprintf(NULL, 0, fmt, args); - if (n != 0) { - size_t oldLength = length(); - char* buf = lockBuffer(oldLength + n); - if (buf) { - vsnprintf(buf + oldLength, n + 1, fmt, args); - } else { - result = NO_MEMORY; - } - } - return result; -} - -status_t String8::real_append(const char* other, size_t otherLen) -{ - const size_t myLen = bytes(); - - SharedBuffer* buf = SharedBuffer::bufferFromData(mString) - ->editResize(myLen+otherLen+1); - if (buf) { - char* str = (char*)buf->data(); - mString = str; - str += myLen; - memcpy(str, other, otherLen); - str[otherLen] = '\0'; - return NO_ERROR; - } - return NO_MEMORY; -} - -char* String8::lockBuffer(size_t size) -{ - SharedBuffer* buf = SharedBuffer::bufferFromData(mString) - ->editResize(size+1); - if (buf) { - char* str = (char*)buf->data(); - mString = str; - return str; - } - return NULL; -} - -void String8::unlockBuffer() -{ - unlockBuffer(strlen(mString)); -} - -status_t String8::unlockBuffer(size_t size) -{ - if (size != this->size()) { - SharedBuffer* buf = SharedBuffer::bufferFromData(mString) - ->editResize(size+1); - if (! buf) { - return NO_MEMORY; - } - - char* str = (char*)buf->data(); - str[size] = 0; - mString = str; - } - - return NO_ERROR; -} - -ssize_t String8::find(const char* other, size_t start) const -{ - size_t len = size(); - if (start >= len) { - return -1; - } - const char* s = mString+start; - const char* p = strstr(s, other); - return p ? p-mString : -1; -} - -void String8::toLower() -{ - toLower(0, size()); -} - -void String8::toLower(size_t start, size_t length) -{ - const size_t len = size(); - if (start >= len) { - return; - } - if (start+length > len) { - length = len-start; - } - char* buf = lockBuffer(len); - buf += start; - while (length > 0) { - *buf = tolower(*buf); - buf++; - length--; - } - unlockBuffer(len); -} - -void String8::toUpper() -{ - toUpper(0, size()); -} - -void String8::toUpper(size_t start, size_t length) -{ - const size_t len = size(); - if (start >= len) { - return; - } - if (start+length > len) { - length = len-start; - } - char* buf = lockBuffer(len); - buf += start; - while (length > 0) { - *buf = toupper(*buf); - buf++; - length--; - } - unlockBuffer(len); -} - -size_t String8::getUtf32Length() const -{ - return utf8_to_utf32_length(mString, length()); -} - -int32_t String8::getUtf32At(size_t index, size_t *next_index) const -{ - return utf32_from_utf8_at(mString, length(), index, next_index); -} - -void String8::getUtf32(char32_t* dst) const -{ - utf8_to_utf32(mString, length(), dst); -} - -TextOutput& operator<<(TextOutput& to, const String8& val) -{ - to << val.string(); - return to; -} - -// --------------------------------------------------------------------------- -// Path functions - -void String8::setPathName(const char* name) -{ - setPathName(name, strlen(name)); -} - -void String8::setPathName(const char* name, size_t len) -{ - char* buf = lockBuffer(len); - - memcpy(buf, name, len); - - // remove trailing path separator, if present - if (len > 0 && buf[len-1] == OS_PATH_SEPARATOR) - len--; - - buf[len] = '\0'; - - unlockBuffer(len); -} - -String8 String8::getPathLeaf(void) const -{ - const char* cp; - const char*const buf = mString; - - cp = strrchr(buf, OS_PATH_SEPARATOR); - if (cp == NULL) - return String8(*this); - else - return String8(cp+1); -} - -String8 String8::getPathDir(void) const -{ - const char* cp; - const char*const str = mString; - - cp = strrchr(str, OS_PATH_SEPARATOR); - if (cp == NULL) - return String8(""); - else - return String8(str, cp - str); -} - -String8 String8::walkPath(String8* outRemains) const -{ - const char* cp; - const char*const str = mString; - const char* buf = str; - - cp = strchr(buf, OS_PATH_SEPARATOR); - if (cp == buf) { - // don't include a leading '/'. - buf = buf+1; - cp = strchr(buf, OS_PATH_SEPARATOR); - } - - if (cp == NULL) { - String8 res = buf != str ? String8(buf) : *this; - if (outRemains) *outRemains = String8(""); - return res; - } - - String8 res(buf, cp-buf); - if (outRemains) *outRemains = String8(cp+1); - return res; -} - -/* - * Helper function for finding the start of an extension in a pathname. - * - * Returns a pointer inside mString, or NULL if no extension was found. - */ -char* String8::find_extension(void) const -{ - const char* lastSlash; - const char* lastDot; - int extLen; - const char* const str = mString; - - // only look at the filename - lastSlash = strrchr(str, OS_PATH_SEPARATOR); - if (lastSlash == NULL) - lastSlash = str; - else - lastSlash++; - - // find the last dot - lastDot = strrchr(lastSlash, '.'); - if (lastDot == NULL) - return NULL; - - // looks good, ship it - return const_cast<char*>(lastDot); -} - -String8 String8::getPathExtension(void) const -{ - char* ext; - - ext = find_extension(); - if (ext != NULL) - return String8(ext); - else - return String8(""); -} - -String8 String8::getBasePath(void) const -{ - char* ext; - const char* const str = mString; - - ext = find_extension(); - if (ext == NULL) - return String8(*this); - else - return String8(str, ext - str); -} - -String8& String8::appendPath(const char* name) -{ - // TODO: The test below will fail for Win32 paths. Fix later or ignore. - if (name[0] != OS_PATH_SEPARATOR) { - if (*name == '\0') { - // nothing to do - return *this; - } - - size_t len = length(); - if (len == 0) { - // no existing filename, just use the new one - setPathName(name); - return *this; - } - - // make room for oldPath + '/' + newPath - int newlen = strlen(name); - - char* buf = lockBuffer(len+1+newlen); - - // insert a '/' if needed - if (buf[len-1] != OS_PATH_SEPARATOR) - buf[len++] = OS_PATH_SEPARATOR; - - memcpy(buf+len, name, newlen+1); - len += newlen; - - unlockBuffer(len); - - return *this; - } else { - setPathName(name); - return *this; - } -} - -String8& String8::convertToResPath() -{ -#if OS_PATH_SEPARATOR != RES_PATH_SEPARATOR - size_t len = length(); - if (len > 0) { - char * buf = lockBuffer(len); - for (char * end = buf + len; buf < end; ++buf) { - if (*buf == OS_PATH_SEPARATOR) - *buf = RES_PATH_SEPARATOR; - } - unlockBuffer(len); - } -#endif - return *this; -} - -}; // namespace android diff --git a/libs/utils/StringArray.cpp b/libs/utils/StringArray.cpp deleted file mode 100644 index aa42d68..0000000 --- a/libs/utils/StringArray.cpp +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (C) 2009 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. - */ - -// -// Sortable array of strings. STL-ish, but STL-free. -// - -#include <stdlib.h> -#include <string.h> - -#include <utils/StringArray.h> - -namespace android { - -// -// An expanding array of strings. Add, get, sort, delete. -// -StringArray::StringArray() - : mMax(0), mCurrent(0), mArray(NULL) -{ -} - -StringArray:: ~StringArray() { - for (int i = 0; i < mCurrent; i++) - delete[] mArray[i]; - delete[] mArray; -} - -// -// Add a string. A copy of the string is made. -// -bool StringArray::push_back(const char* str) { - if (mCurrent >= mMax) { - char** tmp; - - if (mMax == 0) - mMax = 16; // initial storage - else - mMax *= 2; - - tmp = new char*[mMax]; - if (tmp == NULL) - return false; - - memcpy(tmp, mArray, mCurrent * sizeof(char*)); - delete[] mArray; - mArray = tmp; - } - - int len = strlen(str); - mArray[mCurrent] = new char[len+1]; - memcpy(mArray[mCurrent], str, len+1); - mCurrent++; - - return true; -} - -// -// Delete an entry. -// -void StringArray::erase(int idx) { - if (idx < 0 || idx >= mCurrent) - return; - delete[] mArray[idx]; - if (idx < mCurrent-1) { - memmove(&mArray[idx], &mArray[idx+1], - (mCurrent-1 - idx) * sizeof(char*)); - } - mCurrent--; -} - -// -// Sort the array. -// -void StringArray::sort(int (*compare)(const void*, const void*)) { - qsort(mArray, mCurrent, sizeof(char*), compare); -} - -// -// Pass this to the sort routine to do an ascending alphabetical sort. -// -int StringArray::cmpAscendingAlpha(const void* pstr1, const void* pstr2) { - return strcmp(*(const char**)pstr1, *(const char**)pstr2); -} - -// -// Set entry N to specified string. -// [should use operator[] here] -// -void StringArray::setEntry(int idx, const char* str) { - if (idx < 0 || idx >= mCurrent) - return; - delete[] mArray[idx]; - int len = strlen(str); - mArray[idx] = new char[len+1]; - memcpy(mArray[idx], str, len+1); -} - - -}; // namespace android diff --git a/libs/utils/SystemClock.cpp b/libs/utils/SystemClock.cpp deleted file mode 100644 index ec2d82e..0000000 --- a/libs/utils/SystemClock.cpp +++ /dev/null @@ -1,207 +0,0 @@ -/* - * 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. - */ - - -/* - * System clock functions. - */ - -#ifdef HAVE_ANDROID_OS -#include <linux/ioctl.h> -#include <linux/rtc.h> -#include <utils/Atomic.h> -#include <linux/android_alarm.h> -#endif - -#include <sys/time.h> -#include <limits.h> -#include <fcntl.h> -#include <errno.h> -#include <string.h> - -#include <utils/SystemClock.h> -#include <utils/Timers.h> - -#define LOG_TAG "SystemClock" -#include "utils/Log.h" - -namespace android { - -/* - * Set the current time. This only works when running as root. - */ -int setCurrentTimeMillis(int64_t millis) -{ -#if WIN32 - // not implemented - return -1; -#else - struct timeval tv; -#ifdef HAVE_ANDROID_OS - struct timespec ts; - int fd; - int res; -#endif - int ret = 0; - - if (millis <= 0 || millis / 1000LL >= INT_MAX) { - return -1; - } - - tv.tv_sec = (time_t) (millis / 1000LL); - tv.tv_usec = (suseconds_t) ((millis % 1000LL) * 1000LL); - - ALOGD("Setting time of day to sec=%d\n", (int) tv.tv_sec); - -#ifdef HAVE_ANDROID_OS - fd = open("/dev/alarm", O_RDWR); - if(fd < 0) { - ALOGW("Unable to open alarm driver: %s\n", strerror(errno)); - return -1; - } - ts.tv_sec = tv.tv_sec; - ts.tv_nsec = tv.tv_usec * 1000; - res = ioctl(fd, ANDROID_ALARM_SET_RTC, &ts); - if(res < 0) { - ALOGW("Unable to set rtc to %ld: %s\n", tv.tv_sec, strerror(errno)); - ret = -1; - } - close(fd); -#else - if (settimeofday(&tv, NULL) != 0) { - ALOGW("Unable to set clock to %d.%d: %s\n", - (int) tv.tv_sec, (int) tv.tv_usec, strerror(errno)); - ret = -1; - } -#endif - - return ret; -#endif // WIN32 -} - -/* - * native public static long uptimeMillis(); - */ -int64_t uptimeMillis() -{ - int64_t when = systemTime(SYSTEM_TIME_MONOTONIC); - return (int64_t) nanoseconds_to_milliseconds(when); -} - -/* - * native public static long elapsedRealtime(); - */ -int64_t elapsedRealtime() -{ - return nanoseconds_to_milliseconds(elapsedRealtimeNano()); -} - -#define METHOD_CLOCK_GETTIME 0 -#define METHOD_IOCTL 1 -#define METHOD_SYSTEMTIME 2 - -static const char *gettime_method_names[] = { - "clock_gettime", - "ioctl", - "systemTime", -}; - -static inline void checkTimeStamps(int64_t timestamp, - int64_t volatile *prevTimestampPtr, - int volatile *prevMethodPtr, - int curMethod) -{ - /* - * Disable the check for SDK since the prebuilt toolchain doesn't contain - * gettid, and int64_t is different on the ARM platform - * (ie long vs long long). - */ -#ifdef ARCH_ARM - int64_t prevTimestamp = *prevTimestampPtr; - int prevMethod = *prevMethodPtr; - - if (timestamp < prevTimestamp) { - ALOGW("time going backwards: prev %lld(%s) vs now %lld(%s), tid=%d", - prevTimestamp, gettime_method_names[prevMethod], - timestamp, gettime_method_names[curMethod], - gettid()); - } - // NOTE - not atomic and may generate spurious warnings if the 64-bit - // write is interrupted or not observed as a whole. - *prevTimestampPtr = timestamp; - *prevMethodPtr = curMethod; -#endif -} - -/* - * native public static long elapsedRealtimeNano(); - */ -int64_t elapsedRealtimeNano() -{ -#ifdef HAVE_ANDROID_OS - struct timespec ts; - int result; - int64_t timestamp; - static volatile int64_t prevTimestamp; - static volatile int prevMethod; - -#if 0 - /* - * b/7100774 - * clock_gettime appears to have clock skews and can sometimes return - * backwards values. Disable its use until we find out what's wrong. - */ - result = clock_gettime(CLOCK_BOOTTIME, &ts); - if (result == 0) { - timestamp = seconds_to_nanoseconds(ts.tv_sec) + ts.tv_nsec; - checkTimeStamps(timestamp, &prevTimestamp, &prevMethod, - METHOD_CLOCK_GETTIME); - return timestamp; - } -#endif - - // CLOCK_BOOTTIME doesn't exist, fallback to /dev/alarm - static int s_fd = -1; - - if (s_fd == -1) { - int fd = open("/dev/alarm", O_RDONLY); - if (android_atomic_cmpxchg(-1, fd, &s_fd)) { - close(fd); - } - } - - result = ioctl(s_fd, - ANDROID_ALARM_GET_TIME(ANDROID_ALARM_ELAPSED_REALTIME), &ts); - - if (result == 0) { - timestamp = seconds_to_nanoseconds(ts.tv_sec) + ts.tv_nsec; - checkTimeStamps(timestamp, &prevTimestamp, &prevMethod, METHOD_IOCTL); - return timestamp; - } - - // XXX: there was an error, probably because the driver didn't - // exist ... this should return - // a real error, like an exception! - timestamp = systemTime(SYSTEM_TIME_MONOTONIC); - checkTimeStamps(timestamp, &prevTimestamp, &prevMethod, - METHOD_SYSTEMTIME); - return timestamp; -#else - return systemTime(SYSTEM_TIME_MONOTONIC); -#endif -} - -}; // namespace android diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp deleted file mode 100644 index 7b877e0..0000000 --- a/libs/utils/Threads.cpp +++ /dev/null @@ -1,908 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// #define LOG_NDEBUG 0 -#define LOG_TAG "libutils.threads" - -#include <utils/threads.h> -#include <utils/Log.h> - -#include <cutils/sched_policy.h> -#include <cutils/properties.h> - -#include <stdio.h> -#include <stdlib.h> -#include <memory.h> -#include <errno.h> -#include <assert.h> -#include <unistd.h> - -#if defined(HAVE_PTHREADS) -# include <pthread.h> -# include <sched.h> -# include <sys/resource.h> -#ifdef HAVE_ANDROID_OS -# include <bionic_pthread.h> -#endif -#elif defined(HAVE_WIN32_THREADS) -# include <windows.h> -# include <stdint.h> -# include <process.h> -# define HAVE_CREATETHREAD // Cygwin, vs. HAVE__BEGINTHREADEX for MinGW -#endif - -#if defined(HAVE_PRCTL) -#include <sys/prctl.h> -#endif - -/* - * =========================================================================== - * Thread wrappers - * =========================================================================== - */ - -using namespace android; - -// ---------------------------------------------------------------------------- -#if defined(HAVE_PTHREADS) -// ---------------------------------------------------------------------------- - -/* - * Create and run a new thread. - * - * We create it "detached", so it cleans up after itself. - */ - -typedef void* (*android_pthread_entry)(void*); - -static pthread_once_t gDoSchedulingGroupOnce = PTHREAD_ONCE_INIT; -static bool gDoSchedulingGroup = true; - -static void checkDoSchedulingGroup(void) { - char buf[PROPERTY_VALUE_MAX]; - int len = property_get("debug.sys.noschedgroups", buf, ""); - if (len > 0) { - int temp; - if (sscanf(buf, "%d", &temp) == 1) { - gDoSchedulingGroup = temp == 0; - } - } -} - -struct thread_data_t { - thread_func_t entryFunction; - void* userData; - int priority; - char * threadName; - - // we use this trampoline when we need to set the priority with - // nice/setpriority, and name with prctl. - static int trampoline(const thread_data_t* t) { - thread_func_t f = t->entryFunction; - void* u = t->userData; - int prio = t->priority; - char * name = t->threadName; - delete t; - setpriority(PRIO_PROCESS, 0, prio); - pthread_once(&gDoSchedulingGroupOnce, checkDoSchedulingGroup); - if (gDoSchedulingGroup) { - if (prio >= ANDROID_PRIORITY_BACKGROUND) { - set_sched_policy(androidGetTid(), SP_BACKGROUND); - } else if (prio > ANDROID_PRIORITY_AUDIO) { - set_sched_policy(androidGetTid(), SP_FOREGROUND); - } else { - // defaults to that of parent, or as set by requestPriority() - } - } - - if (name) { - androidSetThreadName(name); - free(name); - } - return f(u); - } -}; - -void androidSetThreadName(const char* name) { -#if defined(HAVE_PRCTL) - // Mac OS doesn't have this, and we build libutil for the host too - int hasAt = 0; - int hasDot = 0; - const char *s = name; - while (*s) { - if (*s == '.') hasDot = 1; - else if (*s == '@') hasAt = 1; - s++; - } - int len = s - name; - if (len < 15 || hasAt || !hasDot) { - s = name; - } else { - s = name + len - 15; - } - prctl(PR_SET_NAME, (unsigned long) s, 0, 0, 0); -#endif -} - -int androidCreateRawThreadEtc(android_thread_func_t entryFunction, - void *userData, - const char* threadName, - int32_t threadPriority, - size_t threadStackSize, - android_thread_id_t *threadId) -{ - pthread_attr_t attr; - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - -#ifdef HAVE_ANDROID_OS /* valgrind is rejecting RT-priority create reqs */ - if (threadPriority != PRIORITY_DEFAULT || threadName != NULL) { - // Now that the pthread_t has a method to find the associated - // android_thread_id_t (pid) from pthread_t, it would be possible to avoid - // this trampoline in some cases as the parent could set the properties - // for the child. However, there would be a race condition because the - // child becomes ready immediately, and it doesn't work for the name. - // prctl(PR_SET_NAME) only works for self; prctl(PR_SET_THREAD_NAME) was - // proposed but not yet accepted. - thread_data_t* t = new thread_data_t; - t->priority = threadPriority; - t->threadName = threadName ? strdup(threadName) : NULL; - t->entryFunction = entryFunction; - t->userData = userData; - entryFunction = (android_thread_func_t)&thread_data_t::trampoline; - userData = t; - } -#endif - - if (threadStackSize) { - pthread_attr_setstacksize(&attr, threadStackSize); - } - - errno = 0; - pthread_t thread; - int result = pthread_create(&thread, &attr, - (android_pthread_entry)entryFunction, userData); - pthread_attr_destroy(&attr); - if (result != 0) { - ALOGE("androidCreateRawThreadEtc failed (entry=%p, res=%d, errno=%d)\n" - "(android threadPriority=%d)", - entryFunction, result, errno, threadPriority); - return 0; - } - - // Note that *threadID is directly available to the parent only, as it is - // assigned after the child starts. Use memory barrier / lock if the child - // or other threads also need access. - if (threadId != NULL) { - *threadId = (android_thread_id_t)thread; // XXX: this is not portable - } - return 1; -} - -#ifdef HAVE_ANDROID_OS -static pthread_t android_thread_id_t_to_pthread(android_thread_id_t thread) -{ - return (pthread_t) thread; -} -#endif - -android_thread_id_t androidGetThreadId() -{ - return (android_thread_id_t)pthread_self(); -} - -// ---------------------------------------------------------------------------- -#elif defined(HAVE_WIN32_THREADS) -// ---------------------------------------------------------------------------- - -/* - * Trampoline to make us __stdcall-compliant. - * - * We're expected to delete "vDetails" when we're done. - */ -struct threadDetails { - int (*func)(void*); - void* arg; -}; -static __stdcall unsigned int threadIntermediary(void* vDetails) -{ - struct threadDetails* pDetails = (struct threadDetails*) vDetails; - int result; - - result = (*(pDetails->func))(pDetails->arg); - - delete pDetails; - - ALOG(LOG_VERBOSE, "thread", "thread exiting\n"); - return (unsigned int) result; -} - -/* - * Create and run a new thread. - */ -static bool doCreateThread(android_thread_func_t fn, void* arg, android_thread_id_t *id) -{ - HANDLE hThread; - struct threadDetails* pDetails = new threadDetails; // must be on heap - unsigned int thrdaddr; - - pDetails->func = fn; - pDetails->arg = arg; - -#if defined(HAVE__BEGINTHREADEX) - hThread = (HANDLE) _beginthreadex(NULL, 0, threadIntermediary, pDetails, 0, - &thrdaddr); - if (hThread == 0) -#elif defined(HAVE_CREATETHREAD) - hThread = CreateThread(NULL, 0, - (LPTHREAD_START_ROUTINE) threadIntermediary, - (void*) pDetails, 0, (DWORD*) &thrdaddr); - if (hThread == NULL) -#endif - { - ALOG(LOG_WARN, "thread", "WARNING: thread create failed\n"); - return false; - } - -#if defined(HAVE_CREATETHREAD) - /* close the management handle */ - CloseHandle(hThread); -#endif - - if (id != NULL) { - *id = (android_thread_id_t)thrdaddr; - } - - return true; -} - -int androidCreateRawThreadEtc(android_thread_func_t fn, - void *userData, - const char* threadName, - int32_t threadPriority, - size_t threadStackSize, - android_thread_id_t *threadId) -{ - return doCreateThread( fn, userData, threadId); -} - -android_thread_id_t androidGetThreadId() -{ - return (android_thread_id_t)GetCurrentThreadId(); -} - -// ---------------------------------------------------------------------------- -#else -#error "Threads not supported" -#endif - -// ---------------------------------------------------------------------------- - -int androidCreateThread(android_thread_func_t fn, void* arg) -{ - return createThreadEtc(fn, arg); -} - -int androidCreateThreadGetID(android_thread_func_t fn, void *arg, android_thread_id_t *id) -{ - return createThreadEtc(fn, arg, "android:unnamed_thread", - PRIORITY_DEFAULT, 0, id); -} - -static android_create_thread_fn gCreateThreadFn = androidCreateRawThreadEtc; - -int androidCreateThreadEtc(android_thread_func_t entryFunction, - void *userData, - const char* threadName, - int32_t threadPriority, - size_t threadStackSize, - android_thread_id_t *threadId) -{ - return gCreateThreadFn(entryFunction, userData, threadName, - threadPriority, threadStackSize, threadId); -} - -void androidSetCreateThreadFunc(android_create_thread_fn func) -{ - gCreateThreadFn = func; -} - -pid_t androidGetTid() -{ -#ifdef HAVE_GETTID - return gettid(); -#else - return getpid(); -#endif -} - -#ifdef HAVE_ANDROID_OS -int androidSetThreadPriority(pid_t tid, int pri) -{ - int rc = 0; - -#if defined(HAVE_PTHREADS) - int lasterr = 0; - - pthread_once(&gDoSchedulingGroupOnce, checkDoSchedulingGroup); - if (gDoSchedulingGroup) { - // set_sched_policy does not support tid == 0 - int policy_tid; - if (tid == 0) { - policy_tid = androidGetTid(); - } else { - policy_tid = tid; - } - if (pri >= ANDROID_PRIORITY_BACKGROUND) { - rc = set_sched_policy(policy_tid, SP_BACKGROUND); - } else if (getpriority(PRIO_PROCESS, tid) >= ANDROID_PRIORITY_BACKGROUND) { - rc = set_sched_policy(policy_tid, SP_FOREGROUND); - } - } - - if (rc) { - lasterr = errno; - } - - if (setpriority(PRIO_PROCESS, tid, pri) < 0) { - rc = INVALID_OPERATION; - } else { - errno = lasterr; - } -#endif - - return rc; -} - -int androidGetThreadPriority(pid_t tid) { -#if defined(HAVE_PTHREADS) - return getpriority(PRIO_PROCESS, tid); -#else - return ANDROID_PRIORITY_NORMAL; -#endif -} - -#endif - -namespace android { - -/* - * =========================================================================== - * Mutex class - * =========================================================================== - */ - -#if defined(HAVE_PTHREADS) -// implemented as inlines in threads.h -#elif defined(HAVE_WIN32_THREADS) - -Mutex::Mutex() -{ - HANDLE hMutex; - - assert(sizeof(hMutex) == sizeof(mState)); - - hMutex = CreateMutex(NULL, FALSE, NULL); - mState = (void*) hMutex; -} - -Mutex::Mutex(const char* name) -{ - // XXX: name not used for now - HANDLE hMutex; - - assert(sizeof(hMutex) == sizeof(mState)); - - hMutex = CreateMutex(NULL, FALSE, NULL); - mState = (void*) hMutex; -} - -Mutex::Mutex(int type, const char* name) -{ - // XXX: type and name not used for now - HANDLE hMutex; - - assert(sizeof(hMutex) == sizeof(mState)); - - hMutex = CreateMutex(NULL, FALSE, NULL); - mState = (void*) hMutex; -} - -Mutex::~Mutex() -{ - CloseHandle((HANDLE) mState); -} - -status_t Mutex::lock() -{ - DWORD dwWaitResult; - dwWaitResult = WaitForSingleObject((HANDLE) mState, INFINITE); - return dwWaitResult != WAIT_OBJECT_0 ? -1 : NO_ERROR; -} - -void Mutex::unlock() -{ - if (!ReleaseMutex((HANDLE) mState)) - ALOG(LOG_WARN, "thread", "WARNING: bad result from unlocking mutex\n"); -} - -status_t Mutex::tryLock() -{ - DWORD dwWaitResult; - - dwWaitResult = WaitForSingleObject((HANDLE) mState, 0); - if (dwWaitResult != WAIT_OBJECT_0 && dwWaitResult != WAIT_TIMEOUT) - ALOG(LOG_WARN, "thread", "WARNING: bad result from try-locking mutex\n"); - return (dwWaitResult == WAIT_OBJECT_0) ? 0 : -1; -} - -#else -#error "Somebody forgot to implement threads for this platform." -#endif - - -/* - * =========================================================================== - * Condition class - * =========================================================================== - */ - -#if defined(HAVE_PTHREADS) -// implemented as inlines in threads.h -#elif defined(HAVE_WIN32_THREADS) - -/* - * Windows doesn't have a condition variable solution. It's possible - * to create one, but it's easy to get it wrong. For a discussion, and - * the origin of this implementation, see: - * - * http://www.cs.wustl.edu/~schmidt/win32-cv-1.html - * - * The implementation shown on the page does NOT follow POSIX semantics. - * As an optimization they require acquiring the external mutex before - * calling signal() and broadcast(), whereas POSIX only requires grabbing - * it before calling wait(). The implementation here has been un-optimized - * to have the correct behavior. - */ -typedef struct WinCondition { - // Number of waiting threads. - int waitersCount; - - // Serialize access to waitersCount. - CRITICAL_SECTION waitersCountLock; - - // Semaphore used to queue up threads waiting for the condition to - // become signaled. - HANDLE sema; - - // An auto-reset event used by the broadcast/signal thread to wait - // for all the waiting thread(s) to wake up and be released from - // the semaphore. - HANDLE waitersDone; - - // This mutex wouldn't be necessary if we required that the caller - // lock the external mutex before calling signal() and broadcast(). - // I'm trying to mimic pthread semantics though. - HANDLE internalMutex; - - // Keeps track of whether we were broadcasting or signaling. This - // allows us to optimize the code if we're just signaling. - bool wasBroadcast; - - status_t wait(WinCondition* condState, HANDLE hMutex, nsecs_t* abstime) - { - // Increment the wait count, avoiding race conditions. - EnterCriticalSection(&condState->waitersCountLock); - condState->waitersCount++; - //printf("+++ wait: incr waitersCount to %d (tid=%ld)\n", - // condState->waitersCount, getThreadId()); - LeaveCriticalSection(&condState->waitersCountLock); - - DWORD timeout = INFINITE; - if (abstime) { - nsecs_t reltime = *abstime - systemTime(); - if (reltime < 0) - reltime = 0; - timeout = reltime/1000000; - } - - // Atomically release the external mutex and wait on the semaphore. - DWORD res = - SignalObjectAndWait(hMutex, condState->sema, timeout, FALSE); - - //printf("+++ wait: awake (tid=%ld)\n", getThreadId()); - - // Reacquire lock to avoid race conditions. - EnterCriticalSection(&condState->waitersCountLock); - - // No longer waiting. - condState->waitersCount--; - - // Check to see if we're the last waiter after a broadcast. - bool lastWaiter = (condState->wasBroadcast && condState->waitersCount == 0); - - //printf("+++ wait: lastWaiter=%d (wasBc=%d wc=%d)\n", - // lastWaiter, condState->wasBroadcast, condState->waitersCount); - - LeaveCriticalSection(&condState->waitersCountLock); - - // If we're the last waiter thread during this particular broadcast - // then signal broadcast() that we're all awake. It'll drop the - // internal mutex. - if (lastWaiter) { - // Atomically signal the "waitersDone" event and wait until we - // can acquire the internal mutex. We want to do this in one step - // because it ensures that everybody is in the mutex FIFO before - // any thread has a chance to run. Without it, another thread - // could wake up, do work, and hop back in ahead of us. - SignalObjectAndWait(condState->waitersDone, condState->internalMutex, - INFINITE, FALSE); - } else { - // Grab the internal mutex. - WaitForSingleObject(condState->internalMutex, INFINITE); - } - - // Release the internal and grab the external. - ReleaseMutex(condState->internalMutex); - WaitForSingleObject(hMutex, INFINITE); - - return res == WAIT_OBJECT_0 ? NO_ERROR : -1; - } -} WinCondition; - -/* - * Constructor. Set up the WinCondition stuff. - */ -Condition::Condition() -{ - WinCondition* condState = new WinCondition; - - condState->waitersCount = 0; - condState->wasBroadcast = false; - // semaphore: no security, initial value of 0 - condState->sema = CreateSemaphore(NULL, 0, 0x7fffffff, NULL); - InitializeCriticalSection(&condState->waitersCountLock); - // auto-reset event, not signaled initially - condState->waitersDone = CreateEvent(NULL, FALSE, FALSE, NULL); - // used so we don't have to lock external mutex on signal/broadcast - condState->internalMutex = CreateMutex(NULL, FALSE, NULL); - - mState = condState; -} - -/* - * Destructor. Free Windows resources as well as our allocated storage. - */ -Condition::~Condition() -{ - WinCondition* condState = (WinCondition*) mState; - if (condState != NULL) { - CloseHandle(condState->sema); - CloseHandle(condState->waitersDone); - delete condState; - } -} - - -status_t Condition::wait(Mutex& mutex) -{ - WinCondition* condState = (WinCondition*) mState; - HANDLE hMutex = (HANDLE) mutex.mState; - - return ((WinCondition*)mState)->wait(condState, hMutex, NULL); -} - -status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime) -{ - WinCondition* condState = (WinCondition*) mState; - HANDLE hMutex = (HANDLE) mutex.mState; - nsecs_t absTime = systemTime()+reltime; - - return ((WinCondition*)mState)->wait(condState, hMutex, &absTime); -} - -/* - * Signal the condition variable, allowing one thread to continue. - */ -void Condition::signal() -{ - WinCondition* condState = (WinCondition*) mState; - - // Lock the internal mutex. This ensures that we don't clash with - // broadcast(). - WaitForSingleObject(condState->internalMutex, INFINITE); - - EnterCriticalSection(&condState->waitersCountLock); - bool haveWaiters = (condState->waitersCount > 0); - LeaveCriticalSection(&condState->waitersCountLock); - - // If no waiters, then this is a no-op. Otherwise, knock the semaphore - // down a notch. - if (haveWaiters) - ReleaseSemaphore(condState->sema, 1, 0); - - // Release internal mutex. - ReleaseMutex(condState->internalMutex); -} - -/* - * Signal the condition variable, allowing all threads to continue. - * - * First we have to wake up all threads waiting on the semaphore, then - * we wait until all of the threads have actually been woken before - * releasing the internal mutex. This ensures that all threads are woken. - */ -void Condition::broadcast() -{ - WinCondition* condState = (WinCondition*) mState; - - // Lock the internal mutex. This keeps the guys we're waking up - // from getting too far. - WaitForSingleObject(condState->internalMutex, INFINITE); - - EnterCriticalSection(&condState->waitersCountLock); - bool haveWaiters = false; - - if (condState->waitersCount > 0) { - haveWaiters = true; - condState->wasBroadcast = true; - } - - if (haveWaiters) { - // Wake up all the waiters. - ReleaseSemaphore(condState->sema, condState->waitersCount, 0); - - LeaveCriticalSection(&condState->waitersCountLock); - - // Wait for all awakened threads to acquire the counting semaphore. - // The last guy who was waiting sets this. - WaitForSingleObject(condState->waitersDone, INFINITE); - - // Reset wasBroadcast. (No crit section needed because nobody - // else can wake up to poke at it.) - condState->wasBroadcast = 0; - } else { - // nothing to do - LeaveCriticalSection(&condState->waitersCountLock); - } - - // Release internal mutex. - ReleaseMutex(condState->internalMutex); -} - -#else -#error "condition variables not supported on this platform" -#endif - -// ---------------------------------------------------------------------------- - -/* - * This is our thread object! - */ - -Thread::Thread(bool canCallJava) - : mCanCallJava(canCallJava), - mThread(thread_id_t(-1)), - mLock("Thread::mLock"), - mStatus(NO_ERROR), - mExitPending(false), mRunning(false) -#ifdef HAVE_ANDROID_OS - , mTid(-1) -#endif -{ -} - -Thread::~Thread() -{ -} - -status_t Thread::readyToRun() -{ - return NO_ERROR; -} - -status_t Thread::run(const char* name, int32_t priority, size_t stack) -{ - Mutex::Autolock _l(mLock); - - if (mRunning) { - // thread already started - return INVALID_OPERATION; - } - - // reset status and exitPending to their default value, so we can - // try again after an error happened (either below, or in readyToRun()) - mStatus = NO_ERROR; - mExitPending = false; - mThread = thread_id_t(-1); - - // hold a strong reference on ourself - mHoldSelf = this; - - mRunning = true; - - bool res; - if (mCanCallJava) { - res = createThreadEtc(_threadLoop, - this, name, priority, stack, &mThread); - } else { - res = androidCreateRawThreadEtc(_threadLoop, - this, name, priority, stack, &mThread); - } - - if (res == false) { - mStatus = UNKNOWN_ERROR; // something happened! - mRunning = false; - mThread = thread_id_t(-1); - mHoldSelf.clear(); // "this" may have gone away after this. - - return UNKNOWN_ERROR; - } - - // Do not refer to mStatus here: The thread is already running (may, in fact - // already have exited with a valid mStatus result). The NO_ERROR indication - // here merely indicates successfully starting the thread and does not - // imply successful termination/execution. - return NO_ERROR; - - // Exiting scope of mLock is a memory barrier and allows new thread to run -} - -int Thread::_threadLoop(void* user) -{ - Thread* const self = static_cast<Thread*>(user); - - sp<Thread> strong(self->mHoldSelf); - wp<Thread> weak(strong); - self->mHoldSelf.clear(); - -#ifdef HAVE_ANDROID_OS - // this is very useful for debugging with gdb - self->mTid = gettid(); -#endif - - bool first = true; - - do { - bool result; - if (first) { - first = false; - self->mStatus = self->readyToRun(); - result = (self->mStatus == NO_ERROR); - - if (result && !self->exitPending()) { - // Binder threads (and maybe others) rely on threadLoop - // running at least once after a successful ::readyToRun() - // (unless, of course, the thread has already been asked to exit - // at that point). - // This is because threads are essentially used like this: - // (new ThreadSubclass())->run(); - // The caller therefore does not retain a strong reference to - // the thread and the thread would simply disappear after the - // successful ::readyToRun() call instead of entering the - // threadLoop at least once. - result = self->threadLoop(); - } - } else { - result = self->threadLoop(); - } - - // establish a scope for mLock - { - Mutex::Autolock _l(self->mLock); - if (result == false || self->mExitPending) { - self->mExitPending = true; - self->mRunning = false; - // clear thread ID so that requestExitAndWait() does not exit if - // called by a new thread using the same thread ID as this one. - self->mThread = thread_id_t(-1); - // note that interested observers blocked in requestExitAndWait are - // awoken by broadcast, but blocked on mLock until break exits scope - self->mThreadExitedCondition.broadcast(); - break; - } - } - - // Release our strong reference, to let a chance to the thread - // to die a peaceful death. - strong.clear(); - // And immediately, re-acquire a strong reference for the next loop - strong = weak.promote(); - } while(strong != 0); - - return 0; -} - -void Thread::requestExit() -{ - Mutex::Autolock _l(mLock); - mExitPending = true; -} - -status_t Thread::requestExitAndWait() -{ - Mutex::Autolock _l(mLock); - if (mThread == getThreadId()) { - ALOGW( - "Thread (this=%p): don't call waitForExit() from this " - "Thread object's thread. It's a guaranteed deadlock!", - this); - - return WOULD_BLOCK; - } - - mExitPending = true; - - while (mRunning == true) { - mThreadExitedCondition.wait(mLock); - } - // This next line is probably not needed any more, but is being left for - // historical reference. Note that each interested party will clear flag. - mExitPending = false; - - return mStatus; -} - -status_t Thread::join() -{ - Mutex::Autolock _l(mLock); - if (mThread == getThreadId()) { - ALOGW( - "Thread (this=%p): don't call join() from this " - "Thread object's thread. It's a guaranteed deadlock!", - this); - - return WOULD_BLOCK; - } - - while (mRunning == true) { - mThreadExitedCondition.wait(mLock); - } - - return mStatus; -} - -bool Thread::isRunning() const { - Mutex::Autolock _l(mLock); - return mRunning; -} - -#ifdef HAVE_ANDROID_OS -pid_t Thread::getTid() const -{ - // mTid is not defined until the child initializes it, and the caller may need it earlier - Mutex::Autolock _l(mLock); - pid_t tid; - if (mRunning) { - pthread_t pthread = android_thread_id_t_to_pthread(mThread); - tid = __pthread_gettid(pthread); - } else { - ALOGW("Thread (this=%p): getTid() is undefined before run()", this); - tid = -1; - } - return tid; -} -#endif - -bool Thread::exitPending() const -{ - Mutex::Autolock _l(mLock); - return mExitPending; -} - - - -}; // namespace android diff --git a/libs/utils/Timers.cpp b/libs/utils/Timers.cpp deleted file mode 100644 index d4f8516..0000000 --- a/libs/utils/Timers.cpp +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (C) 2005 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. - */ - -// -// Timer functions. -// -#include <utils/Timers.h> -#include <utils/Log.h> - -#include <stdlib.h> -#include <stdio.h> -#include <unistd.h> -#include <sys/time.h> -#include <time.h> -#include <errno.h> -#include <limits.h> - -#ifdef HAVE_WIN32_THREADS -#include <windows.h> -#endif - -nsecs_t systemTime(int clock) -{ -#if defined(HAVE_POSIX_CLOCKS) - static const clockid_t clocks[] = { - CLOCK_REALTIME, - CLOCK_MONOTONIC, - CLOCK_PROCESS_CPUTIME_ID, - CLOCK_THREAD_CPUTIME_ID, - CLOCK_BOOTTIME - }; - struct timespec t; - t.tv_sec = t.tv_nsec = 0; - clock_gettime(clocks[clock], &t); - return nsecs_t(t.tv_sec)*1000000000LL + t.tv_nsec; -#else - // we don't support the clocks here. - struct timeval t; - t.tv_sec = t.tv_usec = 0; - gettimeofday(&t, NULL); - return nsecs_t(t.tv_sec)*1000000000LL + nsecs_t(t.tv_usec)*1000LL; -#endif -} - -int toMillisecondTimeoutDelay(nsecs_t referenceTime, nsecs_t timeoutTime) -{ - int timeoutDelayMillis; - if (timeoutTime > referenceTime) { - uint64_t timeoutDelay = uint64_t(timeoutTime - referenceTime); - if (timeoutDelay > uint64_t((INT_MAX - 1) * 1000000LL)) { - timeoutDelayMillis = -1; - } else { - timeoutDelayMillis = (timeoutDelay + 999999LL) / 1000000LL; - } - } else { - timeoutDelayMillis = 0; - } - return timeoutDelayMillis; -} - - -/* - * =========================================================================== - * DurationTimer - * =========================================================================== - */ - -using namespace android; - -// Start the timer. -void DurationTimer::start(void) -{ - gettimeofday(&mStartWhen, NULL); -} - -// Stop the timer. -void DurationTimer::stop(void) -{ - gettimeofday(&mStopWhen, NULL); -} - -// Get the duration in microseconds. -long long DurationTimer::durationUsecs(void) const -{ - return (long) subtractTimevals(&mStopWhen, &mStartWhen); -} - -// Subtract two timevals. Returns the difference (ptv1-ptv2) in -// microseconds. -/*static*/ long long DurationTimer::subtractTimevals(const struct timeval* ptv1, - const struct timeval* ptv2) -{ - long long stop = ((long long) ptv1->tv_sec) * 1000000LL + - ((long long) ptv1->tv_usec); - long long start = ((long long) ptv2->tv_sec) * 1000000LL + - ((long long) ptv2->tv_usec); - return stop - start; -} - -// Add the specified amount of time to the timeval. -/*static*/ void DurationTimer::addToTimeval(struct timeval* ptv, long usec) -{ - if (usec < 0) { - ALOG(LOG_WARN, "", "Negative values not supported in addToTimeval\n"); - return; - } - - // normalize tv_usec if necessary - if (ptv->tv_usec >= 1000000) { - ptv->tv_sec += ptv->tv_usec / 1000000; - ptv->tv_usec %= 1000000; - } - - ptv->tv_usec += usec % 1000000; - if (ptv->tv_usec >= 1000000) { - ptv->tv_usec -= 1000000; - ptv->tv_sec++; - } - ptv->tv_sec += usec / 1000000; -} - diff --git a/libs/utils/Tokenizer.cpp b/libs/utils/Tokenizer.cpp deleted file mode 100644 index 7067533..0000000 --- a/libs/utils/Tokenizer.cpp +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "Tokenizer" - -#include <stdlib.h> -#include <unistd.h> -#include <fcntl.h> -#include <errno.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <utils/Log.h> -#include <utils/Tokenizer.h> - -// Enables debug output for the tokenizer. -#define DEBUG_TOKENIZER 0 - - -namespace android { - -static inline bool isDelimiter(char ch, const char* delimiters) { - return strchr(delimiters, ch) != NULL; -} - -Tokenizer::Tokenizer(const String8& filename, FileMap* fileMap, char* buffer, - bool ownBuffer, size_t length) : - mFilename(filename), mFileMap(fileMap), - mBuffer(buffer), mOwnBuffer(ownBuffer), mLength(length), - mCurrent(buffer), mLineNumber(1) { -} - -Tokenizer::~Tokenizer() { - if (mFileMap) { - mFileMap->release(); - } - if (mOwnBuffer) { - delete[] mBuffer; - } -} - -status_t Tokenizer::open(const String8& filename, Tokenizer** outTokenizer) { - *outTokenizer = NULL; - - int result = NO_ERROR; - int fd = ::open(filename.string(), O_RDONLY); - if (fd < 0) { - result = -errno; - ALOGE("Error opening file '%s', %s.", filename.string(), strerror(errno)); - } else { - struct stat stat; - if (fstat(fd, &stat)) { - result = -errno; - ALOGE("Error getting size of file '%s', %s.", filename.string(), strerror(errno)); - } else { - size_t length = size_t(stat.st_size); - - FileMap* fileMap = new FileMap(); - bool ownBuffer = false; - char* buffer; - if (fileMap->create(NULL, fd, 0, length, true)) { - fileMap->advise(FileMap::SEQUENTIAL); - buffer = static_cast<char*>(fileMap->getDataPtr()); - } else { - fileMap->release(); - fileMap = NULL; - - // Fall back to reading into a buffer since we can't mmap files in sysfs. - // The length we obtained from stat is wrong too (it will always be 4096) - // so we must trust that read will read the entire file. - buffer = new char[length]; - ownBuffer = true; - ssize_t nrd = read(fd, buffer, length); - if (nrd < 0) { - result = -errno; - ALOGE("Error reading file '%s', %s.", filename.string(), strerror(errno)); - delete[] buffer; - buffer = NULL; - } else { - length = size_t(nrd); - } - } - - if (!result) { - *outTokenizer = new Tokenizer(filename, fileMap, buffer, ownBuffer, length); - } - } - close(fd); - } - return result; -} - -status_t Tokenizer::fromContents(const String8& filename, - const char* contents, Tokenizer** outTokenizer) { - *outTokenizer = new Tokenizer(filename, NULL, - const_cast<char*>(contents), false, strlen(contents)); - return OK; -} - -String8 Tokenizer::getLocation() const { - String8 result; - result.appendFormat("%s:%d", mFilename.string(), mLineNumber); - return result; -} - -String8 Tokenizer::peekRemainderOfLine() const { - const char* end = getEnd(); - const char* eol = mCurrent; - while (eol != end) { - char ch = *eol; - if (ch == '\n') { - break; - } - eol += 1; - } - return String8(mCurrent, eol - mCurrent); -} - -String8 Tokenizer::nextToken(const char* delimiters) { -#if DEBUG_TOKENIZER - ALOGD("nextToken"); -#endif - const char* end = getEnd(); - const char* tokenStart = mCurrent; - while (mCurrent != end) { - char ch = *mCurrent; - if (ch == '\n' || isDelimiter(ch, delimiters)) { - break; - } - mCurrent += 1; - } - return String8(tokenStart, mCurrent - tokenStart); -} - -void Tokenizer::nextLine() { -#if DEBUG_TOKENIZER - ALOGD("nextLine"); -#endif - const char* end = getEnd(); - while (mCurrent != end) { - char ch = *(mCurrent++); - if (ch == '\n') { - mLineNumber += 1; - break; - } - } -} - -void Tokenizer::skipDelimiters(const char* delimiters) { -#if DEBUG_TOKENIZER - ALOGD("skipDelimiters"); -#endif - const char* end = getEnd(); - while (mCurrent != end) { - char ch = *mCurrent; - if (ch == '\n' || !isDelimiter(ch, delimiters)) { - break; - } - mCurrent += 1; - } -} - -} // namespace android diff --git a/libs/utils/Trace.cpp b/libs/utils/Trace.cpp deleted file mode 100644 index 36fd802..0000000 --- a/libs/utils/Trace.cpp +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <utils/misc.h> -#include <utils/Trace.h> - -static void traceInit() __attribute__((constructor)); - -static void traceInit() -{ - ::android::add_sysprop_change_callback(atrace_update_tags, 0); -} diff --git a/libs/utils/Unicode.cpp b/libs/utils/Unicode.cpp deleted file mode 100644 index 41cbf03..0000000 --- a/libs/utils/Unicode.cpp +++ /dev/null @@ -1,576 +0,0 @@ -/* - * Copyright (C) 2005 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <utils/Unicode.h> - -#include <stddef.h> - -#ifdef HAVE_WINSOCK -# undef nhtol -# undef htonl -# undef nhtos -# undef htons - -# ifdef HAVE_LITTLE_ENDIAN -# define ntohl(x) ( ((x) << 24) | (((x) >> 24) & 255) | (((x) << 8) & 0xff0000) | (((x) >> 8) & 0xff00) ) -# define htonl(x) ntohl(x) -# define ntohs(x) ( (((x) << 8) & 0xff00) | (((x) >> 8) & 255) ) -# define htons(x) ntohs(x) -# else -# define ntohl(x) (x) -# define htonl(x) (x) -# define ntohs(x) (x) -# define htons(x) (x) -# endif -#else -# include <netinet/in.h> -#endif - -extern "C" { - -static const char32_t kByteMask = 0x000000BF; -static const char32_t kByteMark = 0x00000080; - -// Surrogates aren't valid for UTF-32 characters, so define some -// constants that will let us screen them out. -static const char32_t kUnicodeSurrogateHighStart = 0x0000D800; -static const char32_t kUnicodeSurrogateHighEnd = 0x0000DBFF; -static const char32_t kUnicodeSurrogateLowStart = 0x0000DC00; -static const char32_t kUnicodeSurrogateLowEnd = 0x0000DFFF; -static const char32_t kUnicodeSurrogateStart = kUnicodeSurrogateHighStart; -static const char32_t kUnicodeSurrogateEnd = kUnicodeSurrogateLowEnd; -static const char32_t kUnicodeMaxCodepoint = 0x0010FFFF; - -// Mask used to set appropriate bits in first byte of UTF-8 sequence, -// indexed by number of bytes in the sequence. -// 0xxxxxxx -// -> (00-7f) 7bit. Bit mask for the first byte is 0x00000000 -// 110yyyyx 10xxxxxx -// -> (c0-df)(80-bf) 11bit. Bit mask is 0x000000C0 -// 1110yyyy 10yxxxxx 10xxxxxx -// -> (e0-ef)(80-bf)(80-bf) 16bit. Bit mask is 0x000000E0 -// 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx -// -> (f0-f7)(80-bf)(80-bf)(80-bf) 21bit. Bit mask is 0x000000F0 -static const char32_t kFirstByteMark[] = { - 0x00000000, 0x00000000, 0x000000C0, 0x000000E0, 0x000000F0 -}; - -// -------------------------------------------------------------------------- -// UTF-32 -// -------------------------------------------------------------------------- - -/** - * Return number of UTF-8 bytes required for the character. If the character - * is invalid, return size of 0. - */ -static inline size_t utf32_codepoint_utf8_length(char32_t srcChar) -{ - // Figure out how many bytes the result will require. - if (srcChar < 0x00000080) { - return 1; - } else if (srcChar < 0x00000800) { - return 2; - } else if (srcChar < 0x00010000) { - if ((srcChar < kUnicodeSurrogateStart) || (srcChar > kUnicodeSurrogateEnd)) { - return 3; - } else { - // Surrogates are invalid UTF-32 characters. - return 0; - } - } - // Max code point for Unicode is 0x0010FFFF. - else if (srcChar <= kUnicodeMaxCodepoint) { - return 4; - } else { - // Invalid UTF-32 character. - return 0; - } -} - -// Write out the source character to <dstP>. - -static inline void utf32_codepoint_to_utf8(uint8_t* dstP, char32_t srcChar, size_t bytes) -{ - dstP += bytes; - switch (bytes) - { /* note: everything falls through. */ - case 4: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6; - case 3: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6; - case 2: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6; - case 1: *--dstP = (uint8_t)(srcChar | kFirstByteMark[bytes]); - } -} - -size_t strlen32(const char32_t *s) -{ - const char32_t *ss = s; - while ( *ss ) - ss++; - return ss-s; -} - -size_t strnlen32(const char32_t *s, size_t maxlen) -{ - const char32_t *ss = s; - while ((maxlen > 0) && *ss) { - ss++; - maxlen--; - } - return ss-s; -} - -static inline int32_t utf32_at_internal(const char* cur, size_t *num_read) -{ - const char first_char = *cur; - if ((first_char & 0x80) == 0) { // ASCII - *num_read = 1; - return *cur; - } - cur++; - char32_t mask, to_ignore_mask; - size_t num_to_read = 0; - char32_t utf32 = first_char; - for (num_to_read = 1, mask = 0x40, to_ignore_mask = 0xFFFFFF80; - (first_char & mask); - num_to_read++, to_ignore_mask |= mask, mask >>= 1) { - // 0x3F == 00111111 - utf32 = (utf32 << 6) + (*cur++ & 0x3F); - } - to_ignore_mask |= mask; - utf32 &= ~(to_ignore_mask << (6 * (num_to_read - 1))); - - *num_read = num_to_read; - return static_cast<int32_t>(utf32); -} - -int32_t utf32_from_utf8_at(const char *src, size_t src_len, size_t index, size_t *next_index) -{ - if (index >= src_len) { - return -1; - } - size_t dummy_index; - if (next_index == NULL) { - next_index = &dummy_index; - } - size_t num_read; - int32_t ret = utf32_at_internal(src + index, &num_read); - if (ret >= 0) { - *next_index = index + num_read; - } - - return ret; -} - -ssize_t utf32_to_utf8_length(const char32_t *src, size_t src_len) -{ - if (src == NULL || src_len == 0) { - return -1; - } - - size_t ret = 0; - const char32_t *end = src + src_len; - while (src < end) { - ret += utf32_codepoint_utf8_length(*src++); - } - return ret; -} - -void utf32_to_utf8(const char32_t* src, size_t src_len, char* dst) -{ - if (src == NULL || src_len == 0 || dst == NULL) { - return; - } - - const char32_t *cur_utf32 = src; - const char32_t *end_utf32 = src + src_len; - char *cur = dst; - while (cur_utf32 < end_utf32) { - size_t len = utf32_codepoint_utf8_length(*cur_utf32); - utf32_codepoint_to_utf8((uint8_t *)cur, *cur_utf32++, len); - cur += len; - } - *cur = '\0'; -} - -// -------------------------------------------------------------------------- -// UTF-16 -// -------------------------------------------------------------------------- - -int strcmp16(const char16_t *s1, const char16_t *s2) -{ - char16_t ch; - int d = 0; - - while ( 1 ) { - d = (int)(ch = *s1++) - (int)*s2++; - if ( d || !ch ) - break; - } - - return d; -} - -int strncmp16(const char16_t *s1, const char16_t *s2, size_t n) -{ - char16_t ch; - int d = 0; - - while ( n-- ) { - d = (int)(ch = *s1++) - (int)*s2++; - if ( d || !ch ) - break; - } - - return d; -} - -char16_t *strcpy16(char16_t *dst, const char16_t *src) -{ - char16_t *q = dst; - const char16_t *p = src; - char16_t ch; - - do { - *q++ = ch = *p++; - } while ( ch ); - - return dst; -} - -size_t strlen16(const char16_t *s) -{ - const char16_t *ss = s; - while ( *ss ) - ss++; - return ss-s; -} - - -char16_t *strncpy16(char16_t *dst, const char16_t *src, size_t n) -{ - char16_t *q = dst; - const char16_t *p = src; - char ch; - - while (n) { - n--; - *q++ = ch = *p++; - if ( !ch ) - break; - } - - *q = 0; - - return dst; -} - -size_t strnlen16(const char16_t *s, size_t maxlen) -{ - const char16_t *ss = s; - - /* Important: the maxlen test must precede the reference through ss; - since the byte beyond the maximum may segfault */ - while ((maxlen > 0) && *ss) { - ss++; - maxlen--; - } - return ss-s; -} - -int strzcmp16(const char16_t *s1, size_t n1, const char16_t *s2, size_t n2) -{ - const char16_t* e1 = s1+n1; - const char16_t* e2 = s2+n2; - - while (s1 < e1 && s2 < e2) { - const int d = (int)*s1++ - (int)*s2++; - if (d) { - return d; - } - } - - return n1 < n2 - ? (0 - (int)*s2) - : (n1 > n2 - ? ((int)*s1 - 0) - : 0); -} - -int strzcmp16_h_n(const char16_t *s1H, size_t n1, const char16_t *s2N, size_t n2) -{ - const char16_t* e1 = s1H+n1; - const char16_t* e2 = s2N+n2; - - while (s1H < e1 && s2N < e2) { - const char16_t c2 = ntohs(*s2N); - const int d = (int)*s1H++ - (int)c2; - s2N++; - if (d) { - return d; - } - } - - return n1 < n2 - ? (0 - (int)ntohs(*s2N)) - : (n1 > n2 - ? ((int)*s1H - 0) - : 0); -} - -void utf16_to_utf8(const char16_t* src, size_t src_len, char* dst) -{ - if (src == NULL || src_len == 0 || dst == NULL) { - return; - } - - const char16_t* cur_utf16 = src; - const char16_t* const end_utf16 = src + src_len; - char *cur = dst; - while (cur_utf16 < end_utf16) { - char32_t utf32; - // surrogate pairs - if ((*cur_utf16 & 0xFC00) == 0xD800) { - utf32 = (*cur_utf16++ - 0xD800) << 10; - utf32 |= *cur_utf16++ - 0xDC00; - utf32 += 0x10000; - } else { - utf32 = (char32_t) *cur_utf16++; - } - const size_t len = utf32_codepoint_utf8_length(utf32); - utf32_codepoint_to_utf8((uint8_t*)cur, utf32, len); - cur += len; - } - *cur = '\0'; -} - -// -------------------------------------------------------------------------- -// UTF-8 -// -------------------------------------------------------------------------- - -ssize_t utf8_length(const char *src) -{ - const char *cur = src; - size_t ret = 0; - while (*cur != '\0') { - const char first_char = *cur++; - if ((first_char & 0x80) == 0) { // ASCII - ret += 1; - continue; - } - // (UTF-8's character must not be like 10xxxxxx, - // but 110xxxxx, 1110xxxx, ... or 1111110x) - if ((first_char & 0x40) == 0) { - return -1; - } - - int32_t mask, to_ignore_mask; - size_t num_to_read = 0; - char32_t utf32 = 0; - for (num_to_read = 1, mask = 0x40, to_ignore_mask = 0x80; - num_to_read < 5 && (first_char & mask); - num_to_read++, to_ignore_mask |= mask, mask >>= 1) { - if ((*cur & 0xC0) != 0x80) { // must be 10xxxxxx - return -1; - } - // 0x3F == 00111111 - utf32 = (utf32 << 6) + (*cur++ & 0x3F); - } - // "first_char" must be (110xxxxx - 11110xxx) - if (num_to_read == 5) { - return -1; - } - to_ignore_mask |= mask; - utf32 |= ((~to_ignore_mask) & first_char) << (6 * (num_to_read - 1)); - if (utf32 > kUnicodeMaxCodepoint) { - return -1; - } - - ret += num_to_read; - } - return ret; -} - -ssize_t utf16_to_utf8_length(const char16_t *src, size_t src_len) -{ - if (src == NULL || src_len == 0) { - return -1; - } - - size_t ret = 0; - const char16_t* const end = src + src_len; - while (src < end) { - if ((*src & 0xFC00) == 0xD800 && (src + 1) < end - && (*++src & 0xFC00) == 0xDC00) { - // surrogate pairs are always 4 bytes. - ret += 4; - src++; - } else { - ret += utf32_codepoint_utf8_length((char32_t) *src++); - } - } - return ret; -} - -/** - * Returns 1-4 based on the number of leading bits. - * - * 1111 -> 4 - * 1110 -> 3 - * 110x -> 2 - * 10xx -> 1 - * 0xxx -> 1 - */ -static inline size_t utf8_codepoint_len(uint8_t ch) -{ - return ((0xe5000000 >> ((ch >> 3) & 0x1e)) & 3) + 1; -} - -static inline void utf8_shift_and_mask(uint32_t* codePoint, const uint8_t byte) -{ - *codePoint <<= 6; - *codePoint |= 0x3F & byte; -} - -size_t utf8_to_utf32_length(const char *src, size_t src_len) -{ - if (src == NULL || src_len == 0) { - return 0; - } - size_t ret = 0; - const char* cur; - const char* end; - size_t num_to_skip; - for (cur = src, end = src + src_len, num_to_skip = 1; - cur < end; - cur += num_to_skip, ret++) { - const char first_char = *cur; - num_to_skip = 1; - if ((first_char & 0x80) == 0) { // ASCII - continue; - } - int32_t mask; - - for (mask = 0x40; (first_char & mask); num_to_skip++, mask >>= 1) { - } - } - return ret; -} - -void utf8_to_utf32(const char* src, size_t src_len, char32_t* dst) -{ - if (src == NULL || src_len == 0 || dst == NULL) { - return; - } - - const char* cur = src; - const char* const end = src + src_len; - char32_t* cur_utf32 = dst; - while (cur < end) { - size_t num_read; - *cur_utf32++ = static_cast<char32_t>(utf32_at_internal(cur, &num_read)); - cur += num_read; - } - *cur_utf32 = 0; -} - -static inline uint32_t utf8_to_utf32_codepoint(const uint8_t *src, size_t length) -{ - uint32_t unicode; - - switch (length) - { - case 1: - return src[0]; - case 2: - unicode = src[0] & 0x1f; - utf8_shift_and_mask(&unicode, src[1]); - return unicode; - case 3: - unicode = src[0] & 0x0f; - utf8_shift_and_mask(&unicode, src[1]); - utf8_shift_and_mask(&unicode, src[2]); - return unicode; - case 4: - unicode = src[0] & 0x07; - utf8_shift_and_mask(&unicode, src[1]); - utf8_shift_and_mask(&unicode, src[2]); - utf8_shift_and_mask(&unicode, src[3]); - return unicode; - default: - return 0xffff; - } - - //printf("Char at %p: len=%d, utf-16=%p\n", src, length, (void*)result); -} - -ssize_t utf8_to_utf16_length(const uint8_t* u8str, size_t u8len) -{ - const uint8_t* const u8end = u8str + u8len; - const uint8_t* u8cur = u8str; - - /* Validate that the UTF-8 is the correct len */ - size_t u16measuredLen = 0; - while (u8cur < u8end) { - u16measuredLen++; - int u8charLen = utf8_codepoint_len(*u8cur); - uint32_t codepoint = utf8_to_utf32_codepoint(u8cur, u8charLen); - if (codepoint > 0xFFFF) u16measuredLen++; // this will be a surrogate pair in utf16 - u8cur += u8charLen; - } - - /** - * Make sure that we ended where we thought we would and the output UTF-16 - * will be exactly how long we were told it would be. - */ - if (u8cur != u8end) { - return -1; - } - - return u16measuredLen; -} - -char16_t* utf8_to_utf16_no_null_terminator(const uint8_t* u8str, size_t u8len, char16_t* u16str) -{ - const uint8_t* const u8end = u8str + u8len; - const uint8_t* u8cur = u8str; - char16_t* u16cur = u16str; - - while (u8cur < u8end) { - size_t u8len = utf8_codepoint_len(*u8cur); - uint32_t codepoint = utf8_to_utf32_codepoint(u8cur, u8len); - - // Convert the UTF32 codepoint to one or more UTF16 codepoints - if (codepoint <= 0xFFFF) { - // Single UTF16 character - *u16cur++ = (char16_t) codepoint; - } else { - // Multiple UTF16 characters with surrogates - codepoint = codepoint - 0x10000; - *u16cur++ = (char16_t) ((codepoint >> 10) + 0xD800); - *u16cur++ = (char16_t) ((codepoint & 0x3FF) + 0xDC00); - } - - u8cur += u8len; - } - return u16cur; -} - -void utf8_to_utf16(const uint8_t* u8str, size_t u8len, char16_t* u16str) { - char16_t* end = utf8_to_utf16_no_null_terminator(u8str, u8len, u16str); - *end = 0; -} - -} diff --git a/libs/utils/VectorImpl.cpp b/libs/utils/VectorImpl.cpp deleted file mode 100644 index 70f49de..0000000 --- a/libs/utils/VectorImpl.cpp +++ /dev/null @@ -1,644 +0,0 @@ -/* - * Copyright (C) 2005 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "Vector" - -#include <string.h> -#include <stdlib.h> -#include <stdio.h> - -#include <cutils/log.h> - -#include <utils/Errors.h> -#include <utils/SharedBuffer.h> -#include <utils/VectorImpl.h> - -/*****************************************************************************/ - - -namespace android { - -// ---------------------------------------------------------------------------- - -const size_t kMinVectorCapacity = 4; - -static inline size_t max(size_t a, size_t b) { - return a>b ? a : b; -} - -// ---------------------------------------------------------------------------- - -VectorImpl::VectorImpl(size_t itemSize, uint32_t flags) - : mStorage(0), mCount(0), mFlags(flags), mItemSize(itemSize) -{ -} - -VectorImpl::VectorImpl(const VectorImpl& rhs) - : mStorage(rhs.mStorage), mCount(rhs.mCount), - mFlags(rhs.mFlags), mItemSize(rhs.mItemSize) -{ - if (mStorage) { - SharedBuffer::bufferFromData(mStorage)->acquire(); - } -} - -VectorImpl::~VectorImpl() -{ - ALOGW_IF(mCount, - "[%p] subclasses of VectorImpl must call finish_vector()" - " in their destructor. Leaking %d bytes.", - this, (int)(mCount*mItemSize)); - // We can't call _do_destroy() here because the vtable is already gone. -} - -VectorImpl& VectorImpl::operator = (const VectorImpl& rhs) -{ - LOG_ALWAYS_FATAL_IF(mItemSize != rhs.mItemSize, - "Vector<> have different types (this=%p, rhs=%p)", this, &rhs); - if (this != &rhs) { - release_storage(); - if (rhs.mCount) { - mStorage = rhs.mStorage; - mCount = rhs.mCount; - SharedBuffer::bufferFromData(mStorage)->acquire(); - } else { - mStorage = 0; - mCount = 0; - } - } - return *this; -} - -void* VectorImpl::editArrayImpl() -{ - if (mStorage) { - SharedBuffer* sb = SharedBuffer::bufferFromData(mStorage)->attemptEdit(); - if (sb == 0) { - sb = SharedBuffer::alloc(capacity() * mItemSize); - if (sb) { - _do_copy(sb->data(), mStorage, mCount); - release_storage(); - mStorage = sb->data(); - } - } - } - return mStorage; -} - -size_t VectorImpl::capacity() const -{ - if (mStorage) { - return SharedBuffer::bufferFromData(mStorage)->size() / mItemSize; - } - return 0; -} - -ssize_t VectorImpl::insertVectorAt(const VectorImpl& vector, size_t index) -{ - return insertArrayAt(vector.arrayImpl(), index, vector.size()); -} - -ssize_t VectorImpl::appendVector(const VectorImpl& vector) -{ - return insertVectorAt(vector, size()); -} - -ssize_t VectorImpl::insertArrayAt(const void* array, size_t index, size_t length) -{ - if (index > size()) - return BAD_INDEX; - void* where = _grow(index, length); - if (where) { - _do_copy(where, array, length); - } - return where ? index : (ssize_t)NO_MEMORY; -} - -ssize_t VectorImpl::appendArray(const void* array, size_t length) -{ - return insertArrayAt(array, size(), length); -} - -ssize_t VectorImpl::insertAt(size_t index, size_t numItems) -{ - return insertAt(0, index, numItems); -} - -ssize_t VectorImpl::insertAt(const void* item, size_t index, size_t numItems) -{ - if (index > size()) - return BAD_INDEX; - void* where = _grow(index, numItems); - if (where) { - if (item) { - _do_splat(where, item, numItems); - } else { - _do_construct(where, numItems); - } - } - return where ? index : (ssize_t)NO_MEMORY; -} - -static int sortProxy(const void* lhs, const void* rhs, void* func) -{ - return (*(VectorImpl::compar_t)func)(lhs, rhs); -} - -status_t VectorImpl::sort(VectorImpl::compar_t cmp) -{ - return sort(sortProxy, (void*)cmp); -} - -status_t VectorImpl::sort(VectorImpl::compar_r_t cmp, void* state) -{ - // the sort must be stable. we're using insertion sort which - // is well suited for small and already sorted arrays - // for big arrays, it could be better to use mergesort - const ssize_t count = size(); - if (count > 1) { - void* array = const_cast<void*>(arrayImpl()); - void* temp = 0; - ssize_t i = 1; - while (i < count) { - void* item = reinterpret_cast<char*>(array) + mItemSize*(i); - void* curr = reinterpret_cast<char*>(array) + mItemSize*(i-1); - if (cmp(curr, item, state) > 0) { - - if (!temp) { - // we're going to have to modify the array... - array = editArrayImpl(); - if (!array) return NO_MEMORY; - temp = malloc(mItemSize); - if (!temp) return NO_MEMORY; - item = reinterpret_cast<char*>(array) + mItemSize*(i); - curr = reinterpret_cast<char*>(array) + mItemSize*(i-1); - } else { - _do_destroy(temp, 1); - } - - _do_copy(temp, item, 1); - - ssize_t j = i-1; - void* next = reinterpret_cast<char*>(array) + mItemSize*(i); - do { - _do_destroy(next, 1); - _do_copy(next, curr, 1); - next = curr; - --j; - curr = reinterpret_cast<char*>(array) + mItemSize*(j); - } while (j>=0 && (cmp(curr, temp, state) > 0)); - - _do_destroy(next, 1); - _do_copy(next, temp, 1); - } - i++; - } - - if (temp) { - _do_destroy(temp, 1); - free(temp); - } - } - return NO_ERROR; -} - -void VectorImpl::pop() -{ - if (size()) - removeItemsAt(size()-1, 1); -} - -void VectorImpl::push() -{ - push(0); -} - -void VectorImpl::push(const void* item) -{ - insertAt(item, size()); -} - -ssize_t VectorImpl::add() -{ - return add(0); -} - -ssize_t VectorImpl::add(const void* item) -{ - return insertAt(item, size()); -} - -ssize_t VectorImpl::replaceAt(size_t index) -{ - return replaceAt(0, index); -} - -ssize_t VectorImpl::replaceAt(const void* prototype, size_t index) -{ - ALOG_ASSERT(index<size(), - "[%p] replace: index=%d, size=%d", this, (int)index, (int)size()); - - if (index >= size()) { - return BAD_INDEX; - } - - void* item = editItemLocation(index); - if (item != prototype) { - if (item == 0) - return NO_MEMORY; - _do_destroy(item, 1); - if (prototype == 0) { - _do_construct(item, 1); - } else { - _do_copy(item, prototype, 1); - } - } - return ssize_t(index); -} - -ssize_t VectorImpl::removeItemsAt(size_t index, size_t count) -{ - ALOG_ASSERT((index+count)<=size(), - "[%p] remove: index=%d, count=%d, size=%d", - this, (int)index, (int)count, (int)size()); - - if ((index+count) > size()) - return BAD_VALUE; - _shrink(index, count); - return index; -} - -void VectorImpl::finish_vector() -{ - release_storage(); - mStorage = 0; - mCount = 0; -} - -void VectorImpl::clear() -{ - _shrink(0, mCount); -} - -void* VectorImpl::editItemLocation(size_t index) -{ - ALOG_ASSERT(index<capacity(), - "[%p] editItemLocation: index=%d, capacity=%d, count=%d", - this, (int)index, (int)capacity(), (int)mCount); - - if (index < capacity()) { - void* buffer = editArrayImpl(); - if (buffer) { - return reinterpret_cast<char*>(buffer) + index*mItemSize; - } - } - return 0; -} - -const void* VectorImpl::itemLocation(size_t index) const -{ - ALOG_ASSERT(index<capacity(), - "[%p] itemLocation: index=%d, capacity=%d, count=%d", - this, (int)index, (int)capacity(), (int)mCount); - - if (index < capacity()) { - const void* buffer = arrayImpl(); - if (buffer) { - return reinterpret_cast<const char*>(buffer) + index*mItemSize; - } - } - return 0; -} - -ssize_t VectorImpl::setCapacity(size_t new_capacity) -{ - size_t current_capacity = capacity(); - ssize_t amount = new_capacity - size(); - if (amount <= 0) { - // we can't reduce the capacity - return current_capacity; - } - SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize); - if (sb) { - void* array = sb->data(); - _do_copy(array, mStorage, size()); - release_storage(); - mStorage = const_cast<void*>(array); - } else { - return NO_MEMORY; - } - return new_capacity; -} - -ssize_t VectorImpl::resize(size_t size) { - ssize_t result = NO_ERROR; - if (size > mCount) { - result = insertAt(mCount, size - mCount); - } else if (size < mCount) { - result = removeItemsAt(size, mCount - size); - } - return result < 0 ? result : size; -} - -void VectorImpl::release_storage() -{ - if (mStorage) { - const SharedBuffer* sb = SharedBuffer::bufferFromData(mStorage); - if (sb->release(SharedBuffer::eKeepStorage) == 1) { - _do_destroy(mStorage, mCount); - SharedBuffer::dealloc(sb); - } - } -} - -void* VectorImpl::_grow(size_t where, size_t amount) -{ -// ALOGV("_grow(this=%p, where=%d, amount=%d) count=%d, capacity=%d", -// this, (int)where, (int)amount, (int)mCount, (int)capacity()); - - ALOG_ASSERT(where <= mCount, - "[%p] _grow: where=%d, amount=%d, count=%d", - this, (int)where, (int)amount, (int)mCount); // caller already checked - - const size_t new_size = mCount + amount; - if (capacity() < new_size) { - const size_t new_capacity = max(kMinVectorCapacity, ((new_size*3)+1)/2); -// ALOGV("grow vector %p, new_capacity=%d", this, (int)new_capacity); - if ((mStorage) && - (mCount==where) && - (mFlags & HAS_TRIVIAL_COPY) && - (mFlags & HAS_TRIVIAL_DTOR)) - { - const SharedBuffer* cur_sb = SharedBuffer::bufferFromData(mStorage); - SharedBuffer* sb = cur_sb->editResize(new_capacity * mItemSize); - mStorage = sb->data(); - } else { - SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize); - if (sb) { - void* array = sb->data(); - if (where != 0) { - _do_copy(array, mStorage, where); - } - if (where != mCount) { - const void* from = reinterpret_cast<const uint8_t *>(mStorage) + where*mItemSize; - void* dest = reinterpret_cast<uint8_t *>(array) + (where+amount)*mItemSize; - _do_copy(dest, from, mCount-where); - } - release_storage(); - mStorage = const_cast<void*>(array); - } - } - } else { - void* array = editArrayImpl(); - if (where != mCount) { - const void* from = reinterpret_cast<const uint8_t *>(array) + where*mItemSize; - void* to = reinterpret_cast<uint8_t *>(array) + (where+amount)*mItemSize; - _do_move_forward(to, from, mCount - where); - } - } - mCount = new_size; - void* free_space = const_cast<void*>(itemLocation(where)); - return free_space; -} - -void VectorImpl::_shrink(size_t where, size_t amount) -{ - if (!mStorage) - return; - -// ALOGV("_shrink(this=%p, where=%d, amount=%d) count=%d, capacity=%d", -// this, (int)where, (int)amount, (int)mCount, (int)capacity()); - - ALOG_ASSERT(where + amount <= mCount, - "[%p] _shrink: where=%d, amount=%d, count=%d", - this, (int)where, (int)amount, (int)mCount); // caller already checked - - const size_t new_size = mCount - amount; - if (new_size*3 < capacity()) { - const size_t new_capacity = max(kMinVectorCapacity, new_size*2); -// ALOGV("shrink vector %p, new_capacity=%d", this, (int)new_capacity); - if ((where == new_size) && - (mFlags & HAS_TRIVIAL_COPY) && - (mFlags & HAS_TRIVIAL_DTOR)) - { - const SharedBuffer* cur_sb = SharedBuffer::bufferFromData(mStorage); - SharedBuffer* sb = cur_sb->editResize(new_capacity * mItemSize); - mStorage = sb->data(); - } else { - SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize); - if (sb) { - void* array = sb->data(); - if (where != 0) { - _do_copy(array, mStorage, where); - } - if (where != new_size) { - const void* from = reinterpret_cast<const uint8_t *>(mStorage) + (where+amount)*mItemSize; - void* dest = reinterpret_cast<uint8_t *>(array) + where*mItemSize; - _do_copy(dest, from, new_size - where); - } - release_storage(); - mStorage = const_cast<void*>(array); - } - } - } else { - void* array = editArrayImpl(); - void* to = reinterpret_cast<uint8_t *>(array) + where*mItemSize; - _do_destroy(to, amount); - if (where != new_size) { - const void* from = reinterpret_cast<uint8_t *>(array) + (where+amount)*mItemSize; - _do_move_backward(to, from, new_size - where); - } - } - mCount = new_size; -} - -size_t VectorImpl::itemSize() const { - return mItemSize; -} - -void VectorImpl::_do_construct(void* storage, size_t num) const -{ - if (!(mFlags & HAS_TRIVIAL_CTOR)) { - do_construct(storage, num); - } -} - -void VectorImpl::_do_destroy(void* storage, size_t num) const -{ - if (!(mFlags & HAS_TRIVIAL_DTOR)) { - do_destroy(storage, num); - } -} - -void VectorImpl::_do_copy(void* dest, const void* from, size_t num) const -{ - if (!(mFlags & HAS_TRIVIAL_COPY)) { - do_copy(dest, from, num); - } else { - memcpy(dest, from, num*itemSize()); - } -} - -void VectorImpl::_do_splat(void* dest, const void* item, size_t num) const { - do_splat(dest, item, num); -} - -void VectorImpl::_do_move_forward(void* dest, const void* from, size_t num) const { - do_move_forward(dest, from, num); -} - -void VectorImpl::_do_move_backward(void* dest, const void* from, size_t num) const { - do_move_backward(dest, from, num); -} - -void VectorImpl::reservedVectorImpl1() { } -void VectorImpl::reservedVectorImpl2() { } -void VectorImpl::reservedVectorImpl3() { } -void VectorImpl::reservedVectorImpl4() { } -void VectorImpl::reservedVectorImpl5() { } -void VectorImpl::reservedVectorImpl6() { } -void VectorImpl::reservedVectorImpl7() { } -void VectorImpl::reservedVectorImpl8() { } - -/*****************************************************************************/ - -SortedVectorImpl::SortedVectorImpl(size_t itemSize, uint32_t flags) - : VectorImpl(itemSize, flags) -{ -} - -SortedVectorImpl::SortedVectorImpl(const VectorImpl& rhs) -: VectorImpl(rhs) -{ -} - -SortedVectorImpl::~SortedVectorImpl() -{ -} - -SortedVectorImpl& SortedVectorImpl::operator = (const SortedVectorImpl& rhs) -{ - return static_cast<SortedVectorImpl&>( VectorImpl::operator = (static_cast<const VectorImpl&>(rhs)) ); -} - -ssize_t SortedVectorImpl::indexOf(const void* item) const -{ - return _indexOrderOf(item); -} - -size_t SortedVectorImpl::orderOf(const void* item) const -{ - size_t o; - _indexOrderOf(item, &o); - return o; -} - -ssize_t SortedVectorImpl::_indexOrderOf(const void* item, size_t* order) const -{ - // binary search - ssize_t err = NAME_NOT_FOUND; - ssize_t l = 0; - ssize_t h = size()-1; - ssize_t mid; - const void* a = arrayImpl(); - const size_t s = itemSize(); - while (l <= h) { - mid = l + (h - l)/2; - const void* const curr = reinterpret_cast<const char *>(a) + (mid*s); - const int c = do_compare(curr, item); - if (c == 0) { - err = l = mid; - break; - } else if (c < 0) { - l = mid + 1; - } else { - h = mid - 1; - } - } - if (order) *order = l; - return err; -} - -ssize_t SortedVectorImpl::add(const void* item) -{ - size_t order; - ssize_t index = _indexOrderOf(item, &order); - if (index < 0) { - index = VectorImpl::insertAt(item, order, 1); - } else { - index = VectorImpl::replaceAt(item, index); - } - return index; -} - -ssize_t SortedVectorImpl::merge(const VectorImpl& vector) -{ - // naive merge... - if (!vector.isEmpty()) { - const void* buffer = vector.arrayImpl(); - const size_t is = itemSize(); - size_t s = vector.size(); - for (size_t i=0 ; i<s ; i++) { - ssize_t err = add( reinterpret_cast<const char*>(buffer) + i*is ); - if (err<0) { - return err; - } - } - } - return NO_ERROR; -} - -ssize_t SortedVectorImpl::merge(const SortedVectorImpl& vector) -{ - // we've merging a sorted vector... nice! - ssize_t err = NO_ERROR; - if (!vector.isEmpty()) { - // first take care of the case where the vectors are sorted together - if (do_compare(vector.itemLocation(vector.size()-1), arrayImpl()) <= 0) { - err = VectorImpl::insertVectorAt(static_cast<const VectorImpl&>(vector), 0); - } else if (do_compare(vector.arrayImpl(), itemLocation(size()-1)) >= 0) { - err = VectorImpl::appendVector(static_cast<const VectorImpl&>(vector)); - } else { - // this could be made a little better - err = merge(static_cast<const VectorImpl&>(vector)); - } - } - return err; -} - -ssize_t SortedVectorImpl::remove(const void* item) -{ - ssize_t i = indexOf(item); - if (i>=0) { - VectorImpl::removeItemsAt(i, 1); - } - return i; -} - -void SortedVectorImpl::reservedSortedVectorImpl1() { }; -void SortedVectorImpl::reservedSortedVectorImpl2() { }; -void SortedVectorImpl::reservedSortedVectorImpl3() { }; -void SortedVectorImpl::reservedSortedVectorImpl4() { }; -void SortedVectorImpl::reservedSortedVectorImpl5() { }; -void SortedVectorImpl::reservedSortedVectorImpl6() { }; -void SortedVectorImpl::reservedSortedVectorImpl7() { }; -void SortedVectorImpl::reservedSortedVectorImpl8() { }; - - -/*****************************************************************************/ - -}; // namespace android - diff --git a/libs/utils/WorkQueue.cpp b/libs/utils/WorkQueue.cpp deleted file mode 100644 index 3bb99a1..0000000 --- a/libs/utils/WorkQueue.cpp +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// #define LOG_NDEBUG 0 -#define LOG_TAG "WorkQueue" - -#include <utils/Log.h> -#include <utils/WorkQueue.h> - -namespace android { - -// --- WorkQueue --- - -WorkQueue::WorkQueue(size_t maxThreads, bool canCallJava) : - mMaxThreads(maxThreads), mCanCallJava(canCallJava), - mCanceled(false), mFinished(false), mIdleThreads(0) { -} - -WorkQueue::~WorkQueue() { - if (!cancel()) { - finish(); - } -} - -status_t WorkQueue::schedule(WorkUnit* workUnit, size_t backlog) { - AutoMutex _l(mLock); - - if (mFinished || mCanceled) { - return INVALID_OPERATION; - } - - if (mWorkThreads.size() < mMaxThreads - && mIdleThreads < mWorkUnits.size() + 1) { - sp<WorkThread> workThread = new WorkThread(this, mCanCallJava); - status_t status = workThread->run("WorkQueue::WorkThread"); - if (status) { - return status; - } - mWorkThreads.add(workThread); - mIdleThreads += 1; - } else if (backlog) { - while (mWorkUnits.size() >= mMaxThreads * backlog) { - mWorkDequeuedCondition.wait(mLock); - if (mFinished || mCanceled) { - return INVALID_OPERATION; - } - } - } - - mWorkUnits.add(workUnit); - mWorkChangedCondition.broadcast(); - return OK; -} - -status_t WorkQueue::cancel() { - AutoMutex _l(mLock); - - return cancelLocked(); -} - -status_t WorkQueue::cancelLocked() { - if (mFinished) { - return INVALID_OPERATION; - } - - if (!mCanceled) { - mCanceled = true; - - size_t count = mWorkUnits.size(); - for (size_t i = 0; i < count; i++) { - delete mWorkUnits.itemAt(i); - } - mWorkUnits.clear(); - mWorkChangedCondition.broadcast(); - mWorkDequeuedCondition.broadcast(); - } - return OK; -} - -status_t WorkQueue::finish() { - { // acquire lock - AutoMutex _l(mLock); - - if (mFinished) { - return INVALID_OPERATION; - } - - mFinished = true; - mWorkChangedCondition.broadcast(); - } // release lock - - // It is not possible for the list of work threads to change once the mFinished - // flag has been set, so we can access mWorkThreads outside of the lock here. - size_t count = mWorkThreads.size(); - for (size_t i = 0; i < count; i++) { - mWorkThreads.itemAt(i)->join(); - } - mWorkThreads.clear(); - return OK; -} - -bool WorkQueue::threadLoop() { - WorkUnit* workUnit; - { // acquire lock - AutoMutex _l(mLock); - - for (;;) { - if (mCanceled) { - return false; - } - - if (!mWorkUnits.isEmpty()) { - workUnit = mWorkUnits.itemAt(0); - mWorkUnits.removeAt(0); - mIdleThreads -= 1; - mWorkDequeuedCondition.broadcast(); - break; - } - - if (mFinished) { - return false; - } - - mWorkChangedCondition.wait(mLock); - } - } // release lock - - bool shouldContinue = workUnit->run(); - delete workUnit; - - { // acquire lock - AutoMutex _l(mLock); - - mIdleThreads += 1; - - if (!shouldContinue) { - cancelLocked(); - return false; - } - } // release lock - - return true; -} - -// --- WorkQueue::WorkThread --- - -WorkQueue::WorkThread::WorkThread(WorkQueue* workQueue, bool canCallJava) : - Thread(canCallJava), mWorkQueue(workQueue) { -} - -WorkQueue::WorkThread::~WorkThread() { -} - -bool WorkQueue::WorkThread::threadLoop() { - return mWorkQueue->threadLoop(); -} - -}; // namespace android diff --git a/libs/utils/ZipFileCRO.cpp b/libs/utils/ZipFileCRO.cpp deleted file mode 100644 index 55dfd9f..0000000 --- a/libs/utils/ZipFileCRO.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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 <utils/ZipFileCRO.h> -#include <utils/ZipFileRO.h> - -using namespace android; - -ZipFileCRO ZipFileXRO_open(const char* path) { - ZipFileRO* zip = new ZipFileRO(); - if (zip->open(path) == NO_ERROR) { - return (ZipFileCRO)zip; - } - return NULL; -} - -void ZipFileCRO_destroy(ZipFileCRO zipToken) { - ZipFileRO* zip = (ZipFileRO*)zipToken; - delete zip; -} - -ZipEntryCRO ZipFileCRO_findEntryByName(ZipFileCRO zipToken, - const char* fileName) { - ZipFileRO* zip = (ZipFileRO*)zipToken; - return (ZipEntryCRO)zip->findEntryByName(fileName); -} - -bool ZipFileCRO_getEntryInfo(ZipFileCRO zipToken, ZipEntryRO entryToken, - int* pMethod, size_t* pUncompLen, - size_t* pCompLen, off64_t* pOffset, long* pModWhen, long* pCrc32) { - ZipFileRO* zip = (ZipFileRO*)zipToken; - ZipEntryRO entry = (ZipEntryRO)entryToken; - return zip->getEntryInfo(entry, pMethod, pUncompLen, pCompLen, pOffset, - pModWhen, pCrc32); -} - -bool ZipFileCRO_uncompressEntry(ZipFileCRO zipToken, ZipEntryRO entryToken, int fd) { - ZipFileRO* zip = (ZipFileRO*)zipToken; - ZipEntryRO entry = (ZipEntryRO)entryToken; - return zip->uncompressEntry(entry, fd); -} diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp deleted file mode 100644 index a1bfedb..0000000 --- a/libs/utils/ZipFileRO.cpp +++ /dev/null @@ -1,931 +0,0 @@ -/* - * Copyright (C) 2007 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. - */ - -// -// Read-only access to Zip archives, with minimal heap allocation. -// -#define LOG_TAG "zipro" -//#define LOG_NDEBUG 0 -#include <utils/Log.h> -#include <utils/Compat.h> -#include <utils/ZipFileRO.h> -#include <utils/misc.h> -#include <utils/threads.h> - -#include <zlib.h> - -#include <string.h> -#include <fcntl.h> -#include <errno.h> -#include <assert.h> -#include <unistd.h> - -/* - * We must open binary files using open(path, ... | O_BINARY) under Windows. - * Otherwise strange read errors will happen. - */ -#ifndef O_BINARY -# define O_BINARY 0 -#endif - -using namespace android; - -/* - * Zip file constants. - */ -#define kEOCDSignature 0x06054b50 -#define kEOCDLen 22 -#define kEOCDNumEntries 8 // offset to #of entries in file -#define kEOCDSize 12 // size of the central directory -#define kEOCDFileOffset 16 // offset to central directory - -#define kMaxCommentLen 65535 // longest possible in ushort -#define kMaxEOCDSearch (kMaxCommentLen + kEOCDLen) - -#define kLFHSignature 0x04034b50 -#define kLFHLen 30 // excluding variable-len fields -#define kLFHNameLen 26 // offset to filename length -#define kLFHExtraLen 28 // offset to extra length - -#define kCDESignature 0x02014b50 -#define kCDELen 46 // excluding variable-len fields -#define kCDEMethod 10 // offset to compression method -#define kCDEModWhen 12 // offset to modification timestamp -#define kCDECRC 16 // offset to entry CRC -#define kCDECompLen 20 // offset to compressed length -#define kCDEUncompLen 24 // offset to uncompressed length -#define kCDENameLen 28 // offset to filename length -#define kCDEExtraLen 30 // offset to extra length -#define kCDECommentLen 32 // offset to comment length -#define kCDELocalOffset 42 // offset to local hdr - -/* - * The values we return for ZipEntryRO use 0 as an invalid value, so we - * want to adjust the hash table index by a fixed amount. Using a large - * value helps insure that people don't mix & match arguments, e.g. to - * findEntryByIndex(). - */ -#define kZipEntryAdj 10000 - -ZipFileRO::~ZipFileRO() { - free(mHashTable); - if (mDirectoryMap) - mDirectoryMap->release(); - if (mFd >= 0) - TEMP_FAILURE_RETRY(close(mFd)); - if (mFileName) - free(mFileName); -} - -/* - * Convert a ZipEntryRO to a hash table index, verifying that it's in a - * valid range. - */ -int ZipFileRO::entryToIndex(const ZipEntryRO entry) const -{ - long ent = ((intptr_t) entry) - kZipEntryAdj; - if (ent < 0 || ent >= mHashTableSize || mHashTable[ent].name == NULL) { - ALOGW("Invalid ZipEntryRO %p (%ld)\n", entry, ent); - return -1; - } - return ent; -} - - -/* - * Open the specified file read-only. We memory-map the entire thing and - * close the file before returning. - */ -status_t ZipFileRO::open(const char* zipFileName) -{ - int fd = -1; - - assert(mDirectoryMap == NULL); - - /* - * Open and map the specified file. - */ - fd = TEMP_FAILURE_RETRY(::open(zipFileName, O_RDONLY | O_BINARY)); - if (fd < 0) { - ALOGW("Unable to open zip '%s': %s\n", zipFileName, strerror(errno)); - return NAME_NOT_FOUND; - } - - mFileLength = lseek64(fd, 0, SEEK_END); - if (mFileLength < kEOCDLen) { - TEMP_FAILURE_RETRY(close(fd)); - return UNKNOWN_ERROR; - } - - if (mFileName != NULL) { - free(mFileName); - } - mFileName = strdup(zipFileName); - - mFd = fd; - - /* - * Find the Central Directory and store its size and number of entries. - */ - if (!mapCentralDirectory()) { - goto bail; - } - - /* - * Verify Central Directory and create data structures for fast access. - */ - if (!parseZipArchive()) { - goto bail; - } - - return OK; - -bail: - free(mFileName); - mFileName = NULL; - TEMP_FAILURE_RETRY(close(fd)); - return UNKNOWN_ERROR; -} - -/* - * Parse the Zip archive, verifying its contents and initializing internal - * data structures. - */ -bool ZipFileRO::mapCentralDirectory(void) -{ - ssize_t readAmount = kMaxEOCDSearch; - if (readAmount > (ssize_t) mFileLength) - readAmount = mFileLength; - - unsigned char* scanBuf = (unsigned char*) malloc(readAmount); - if (scanBuf == NULL) { - ALOGW("couldn't allocate scanBuf: %s", strerror(errno)); - free(scanBuf); - return false; - } - - /* - * Make sure this is a Zip archive. - */ - if (lseek64(mFd, 0, SEEK_SET) != 0) { - ALOGW("seek to start failed: %s", strerror(errno)); - free(scanBuf); - return false; - } - - ssize_t actual = TEMP_FAILURE_RETRY(read(mFd, scanBuf, sizeof(int32_t))); - if (actual != (ssize_t) sizeof(int32_t)) { - ALOGI("couldn't read first signature from zip archive: %s", strerror(errno)); - free(scanBuf); - return false; - } - - { - unsigned int header = get4LE(scanBuf); - if (header == kEOCDSignature) { - ALOGI("Found Zip archive, but it looks empty\n"); - free(scanBuf); - return false; - } else if (header != kLFHSignature) { - ALOGV("Not a Zip archive (found 0x%08x)\n", header); - free(scanBuf); - return false; - } - } - - /* - * Perform the traditional EOCD snipe hunt. - * - * We're searching for the End of Central Directory magic number, - * which appears at the start of the EOCD block. It's followed by - * 18 bytes of EOCD stuff and up to 64KB of archive comment. We - * need to read the last part of the file into a buffer, dig through - * it to find the magic number, parse some values out, and use those - * to determine the extent of the CD. - * - * We start by pulling in the last part of the file. - */ - off64_t searchStart = mFileLength - readAmount; - - if (lseek64(mFd, searchStart, SEEK_SET) != searchStart) { - ALOGW("seek %ld failed: %s\n", (long) searchStart, strerror(errno)); - free(scanBuf); - return false; - } - actual = TEMP_FAILURE_RETRY(read(mFd, scanBuf, readAmount)); - if (actual != (ssize_t) readAmount) { - ALOGW("Zip: read " ZD ", expected " ZD ". Failed: %s\n", - (ZD_TYPE) actual, (ZD_TYPE) readAmount, strerror(errno)); - free(scanBuf); - return false; - } - - /* - * Scan backward for the EOCD magic. In an archive without a trailing - * comment, we'll find it on the first try. (We may want to consider - * doing an initial minimal read; if we don't find it, retry with a - * second read as above.) - */ - int i; - for (i = readAmount - kEOCDLen; i >= 0; i--) { - if (scanBuf[i] == 0x50 && get4LE(&scanBuf[i]) == kEOCDSignature) { - ALOGV("+++ Found EOCD at buf+%d\n", i); - break; - } - } - if (i < 0) { - ALOGD("Zip: EOCD not found, %s is not zip\n", mFileName); - free(scanBuf); - return false; - } - - off64_t eocdOffset = searchStart + i; - const unsigned char* eocdPtr = scanBuf + i; - - assert(eocdOffset < mFileLength); - - /* - * Grab the CD offset and size, and the number of entries in the - * archive. After that, we can release our EOCD hunt buffer. - */ - unsigned int numEntries = get2LE(eocdPtr + kEOCDNumEntries); - unsigned int dirSize = get4LE(eocdPtr + kEOCDSize); - unsigned int dirOffset = get4LE(eocdPtr + kEOCDFileOffset); - free(scanBuf); - - // Verify that they look reasonable. - if ((long long) dirOffset + (long long) dirSize > (long long) eocdOffset) { - ALOGW("bad offsets (dir %ld, size %u, eocd %ld)\n", - (long) dirOffset, dirSize, (long) eocdOffset); - return false; - } - if (numEntries == 0) { - ALOGW("empty archive?\n"); - return false; - } - - ALOGV("+++ numEntries=%d dirSize=%d dirOffset=%d\n", - numEntries, dirSize, dirOffset); - - mDirectoryMap = new FileMap(); - if (mDirectoryMap == NULL) { - ALOGW("Unable to create directory map: %s", strerror(errno)); - return false; - } - - if (!mDirectoryMap->create(mFileName, mFd, dirOffset, dirSize, true)) { - ALOGW("Unable to map '%s' (" ZD " to " ZD "): %s\n", mFileName, - (ZD_TYPE) dirOffset, (ZD_TYPE) (dirOffset + dirSize), strerror(errno)); - return false; - } - - mNumEntries = numEntries; - mDirectoryOffset = dirOffset; - - return true; -} - - -/* - * Round up to the next highest power of 2. - * - * Found on http://graphics.stanford.edu/~seander/bithacks.html. - */ -static unsigned int roundUpPower2(unsigned int val) -{ - val--; - val |= val >> 1; - val |= val >> 2; - val |= val >> 4; - val |= val >> 8; - val |= val >> 16; - val++; - - return val; -} - -bool ZipFileRO::parseZipArchive(void) -{ - bool result = false; - const unsigned char* cdPtr = (const unsigned char*) mDirectoryMap->getDataPtr(); - size_t cdLength = mDirectoryMap->getDataLength(); - int numEntries = mNumEntries; - - /* - * Create hash table. We have a minimum 75% load factor, possibly as - * low as 50% after we round off to a power of 2. - */ - mHashTableSize = roundUpPower2(1 + (numEntries * 4) / 3); - mHashTable = (HashEntry*) calloc(mHashTableSize, sizeof(HashEntry)); - - /* - * Walk through the central directory, adding entries to the hash - * table. - */ - const unsigned char* ptr = cdPtr; - for (int i = 0; i < numEntries; i++) { - if (get4LE(ptr) != kCDESignature) { - ALOGW("Missed a central dir sig (at %d)\n", i); - goto bail; - } - if (ptr + kCDELen > cdPtr + cdLength) { - ALOGW("Ran off the end (at %d)\n", i); - goto bail; - } - - long localHdrOffset = (long) get4LE(ptr + kCDELocalOffset); - if (localHdrOffset >= mDirectoryOffset) { - ALOGW("bad LFH offset %ld at entry %d\n", localHdrOffset, i); - goto bail; - } - - unsigned int fileNameLen, extraLen, commentLen, hash; - - fileNameLen = get2LE(ptr + kCDENameLen); - extraLen = get2LE(ptr + kCDEExtraLen); - commentLen = get2LE(ptr + kCDECommentLen); - - /* add the CDE filename to the hash table */ - hash = computeHash((const char*)ptr + kCDELen, fileNameLen); - addToHash((const char*)ptr + kCDELen, fileNameLen, hash); - - ptr += kCDELen + fileNameLen + extraLen + commentLen; - if ((size_t)(ptr - cdPtr) > cdLength) { - ALOGW("bad CD advance (%d vs " ZD ") at entry %d\n", - (int) (ptr - cdPtr), (ZD_TYPE) cdLength, i); - goto bail; - } - } - ALOGV("+++ zip good scan %d entries\n", numEntries); - result = true; - -bail: - return result; -} - -/* - * Simple string hash function for non-null-terminated strings. - */ -/*static*/ unsigned int ZipFileRO::computeHash(const char* str, int len) -{ - unsigned int hash = 0; - - while (len--) - hash = hash * 31 + *str++; - - return hash; -} - -/* - * Add a new entry to the hash table. - */ -void ZipFileRO::addToHash(const char* str, int strLen, unsigned int hash) -{ - int ent = hash & (mHashTableSize-1); - - /* - * We over-allocate the table, so we're guaranteed to find an empty slot. - */ - while (mHashTable[ent].name != NULL) - ent = (ent + 1) & (mHashTableSize-1); - - mHashTable[ent].name = str; - mHashTable[ent].nameLen = strLen; -} - -/* - * Find a matching entry. - * - * Returns NULL if not found. - */ -ZipEntryRO ZipFileRO::findEntryByName(const char* fileName) const -{ - /* - * If the ZipFileRO instance is not initialized, the entry number will - * end up being garbage since mHashTableSize is -1. - */ - if (mHashTableSize <= 0) { - return NULL; - } - - int nameLen = strlen(fileName); - unsigned int hash = computeHash(fileName, nameLen); - int ent = hash & (mHashTableSize-1); - - while (mHashTable[ent].name != NULL) { - if (mHashTable[ent].nameLen == nameLen && - memcmp(mHashTable[ent].name, fileName, nameLen) == 0) - { - /* match */ - return (ZipEntryRO)(long)(ent + kZipEntryAdj); - } - - ent = (ent + 1) & (mHashTableSize-1); - } - - return NULL; -} - -/* - * Find the Nth entry. - * - * This currently involves walking through the sparse hash table, counting - * non-empty entries. If we need to speed this up we can either allocate - * a parallel lookup table or (perhaps better) provide an iterator interface. - */ -ZipEntryRO ZipFileRO::findEntryByIndex(int idx) const -{ - if (idx < 0 || idx >= mNumEntries) { - ALOGW("Invalid index %d\n", idx); - return NULL; - } - - for (int ent = 0; ent < mHashTableSize; ent++) { - if (mHashTable[ent].name != NULL) { - if (idx-- == 0) - return (ZipEntryRO) (intptr_t)(ent + kZipEntryAdj); - } - } - - return NULL; -} - -/* - * Get the useful fields from the zip entry. - * - * Returns "false" if the offsets to the fields or the contents of the fields - * appear to be bogus. - */ -bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, - size_t* pCompLen, off64_t* pOffset, long* pModWhen, long* pCrc32) const -{ - bool ret = false; - - const int ent = entryToIndex(entry); - if (ent < 0) - return false; - - HashEntry hashEntry = mHashTable[ent]; - - /* - * Recover the start of the central directory entry from the filename - * pointer. The filename is the first entry past the fixed-size data, - * so we can just subtract back from that. - */ - const unsigned char* ptr = (const unsigned char*) hashEntry.name; - off64_t cdOffset = mDirectoryOffset; - - ptr -= kCDELen; - - int method = get2LE(ptr + kCDEMethod); - if (pMethod != NULL) - *pMethod = method; - - if (pModWhen != NULL) - *pModWhen = get4LE(ptr + kCDEModWhen); - if (pCrc32 != NULL) - *pCrc32 = get4LE(ptr + kCDECRC); - - size_t compLen = get4LE(ptr + kCDECompLen); - if (pCompLen != NULL) - *pCompLen = compLen; - size_t uncompLen = get4LE(ptr + kCDEUncompLen); - if (pUncompLen != NULL) - *pUncompLen = uncompLen; - - /* - * If requested, determine the offset of the start of the data. All we - * have is the offset to the Local File Header, which is variable size, - * so we have to read the contents of the struct to figure out where - * the actual data starts. - * - * We also need to make sure that the lengths are not so large that - * somebody trying to map the compressed or uncompressed data runs - * off the end of the mapped region. - * - * Note we don't verify compLen/uncompLen if they don't request the - * dataOffset, because dataOffset is expensive to determine. However, - * if they don't have the file offset, they're not likely to be doing - * anything with the contents. - */ - if (pOffset != NULL) { - long localHdrOffset = get4LE(ptr + kCDELocalOffset); - if (localHdrOffset + kLFHLen >= cdOffset) { - ALOGE("ERROR: bad local hdr offset in zip\n"); - return false; - } - - unsigned char lfhBuf[kLFHLen]; - -#ifdef HAVE_PREAD - /* - * This file descriptor might be from zygote's preloaded assets, - * so we need to do an pread64() instead of a lseek64() + read() to - * guarantee atomicity across the processes with the shared file - * descriptors. - */ - ssize_t actual = - TEMP_FAILURE_RETRY(pread64(mFd, lfhBuf, sizeof(lfhBuf), localHdrOffset)); - - if (actual != sizeof(lfhBuf)) { - ALOGW("failed reading lfh from offset %ld\n", localHdrOffset); - return false; - } - - if (get4LE(lfhBuf) != kLFHSignature) { - ALOGW("didn't find signature at start of lfh; wanted: offset=%ld data=0x%08x; " - "got: data=0x%08lx\n", - localHdrOffset, kLFHSignature, get4LE(lfhBuf)); - return false; - } -#else /* HAVE_PREAD */ - /* - * For hosts don't have pread64() we cannot guarantee atomic reads from - * an offset in a file. Android should never run on those platforms. - * File descriptors inherited from a fork() share file offsets and - * there would be nothing to protect from two different processes - * calling lseek64() concurrently. - */ - - { - AutoMutex _l(mFdLock); - - if (lseek64(mFd, localHdrOffset, SEEK_SET) != localHdrOffset) { - ALOGW("failed seeking to lfh at offset %ld\n", localHdrOffset); - return false; - } - - ssize_t actual = - TEMP_FAILURE_RETRY(read(mFd, lfhBuf, sizeof(lfhBuf))); - if (actual != sizeof(lfhBuf)) { - ALOGW("failed reading lfh from offset %ld\n", localHdrOffset); - return false; - } - - if (get4LE(lfhBuf) != kLFHSignature) { - off64_t actualOffset = lseek64(mFd, 0, SEEK_CUR); - ALOGW("didn't find signature at start of lfh; wanted: offset=%ld data=0x%08x; " - "got: offset=" ZD " data=0x%08lx\n", - localHdrOffset, kLFHSignature, (ZD_TYPE) actualOffset, get4LE(lfhBuf)); - return false; - } - } -#endif /* HAVE_PREAD */ - - off64_t dataOffset = localHdrOffset + kLFHLen - + get2LE(lfhBuf + kLFHNameLen) + get2LE(lfhBuf + kLFHExtraLen); - if (dataOffset >= cdOffset) { - ALOGW("bad data offset %ld in zip\n", (long) dataOffset); - return false; - } - - /* check lengths */ - if ((off64_t)(dataOffset + compLen) > cdOffset) { - ALOGW("bad compressed length in zip (%ld + " ZD " > %ld)\n", - (long) dataOffset, (ZD_TYPE) compLen, (long) cdOffset); - return false; - } - - if (method == kCompressStored && - (off64_t)(dataOffset + uncompLen) > cdOffset) - { - ALOGE("ERROR: bad uncompressed length in zip (%ld + " ZD " > %ld)\n", - (long) dataOffset, (ZD_TYPE) uncompLen, (long) cdOffset); - return false; - } - - *pOffset = dataOffset; - } - - return true; -} - -/* - * Copy the entry's filename to the buffer. - */ -int ZipFileRO::getEntryFileName(ZipEntryRO entry, char* buffer, int bufLen) - const -{ - int ent = entryToIndex(entry); - if (ent < 0) - return -1; - - int nameLen = mHashTable[ent].nameLen; - if (bufLen < nameLen+1) - return nameLen+1; - - memcpy(buffer, mHashTable[ent].name, nameLen); - buffer[nameLen] = '\0'; - return 0; -} - -/* - * Create a new FileMap object that spans the data in "entry". - */ -FileMap* ZipFileRO::createEntryFileMap(ZipEntryRO entry) const -{ - /* - * TODO: the efficient way to do this is to modify FileMap to allow - * sub-regions of a file to be mapped. A reference-counting scheme - * can manage the base memory mapping. For now, we just create a brand - * new mapping off of the Zip archive file descriptor. - */ - - FileMap* newMap; - size_t compLen; - off64_t offset; - - if (!getEntryInfo(entry, NULL, NULL, &compLen, &offset, NULL, NULL)) - return NULL; - - newMap = new FileMap(); - if (!newMap->create(mFileName, mFd, offset, compLen, true)) { - newMap->release(); - return NULL; - } - - return newMap; -} - -/* - * Uncompress an entry, in its entirety, into the provided output buffer. - * - * This doesn't verify the data's CRC, which might be useful for - * uncompressed data. The caller should be able to manage it. - */ -bool ZipFileRO::uncompressEntry(ZipEntryRO entry, void* buffer) const -{ - const size_t kSequentialMin = 32768; - bool result = false; - int ent = entryToIndex(entry); - if (ent < 0) - return -1; - - int method; - size_t uncompLen, compLen; - off64_t offset; - const unsigned char* ptr; - - getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL); - - FileMap* file = createEntryFileMap(entry); - if (file == NULL) { - goto bail; - } - - ptr = (const unsigned char*) file->getDataPtr(); - - /* - * Experiment with madvise hint. When we want to uncompress a file, - * we pull some stuff out of the central dir entry and then hit a - * bunch of compressed or uncompressed data sequentially. The CDE - * visit will cause a limited amount of read-ahead because it's at - * the end of the file. We could end up doing lots of extra disk - * access if the file we're prying open is small. Bottom line is we - * probably don't want to turn MADV_SEQUENTIAL on and leave it on. - * - * So, if the compressed size of the file is above a certain minimum - * size, temporarily boost the read-ahead in the hope that the extra - * pair of system calls are negated by a reduction in page faults. - */ - if (compLen > kSequentialMin) - file->advise(FileMap::SEQUENTIAL); - - if (method == kCompressStored) { - memcpy(buffer, ptr, uncompLen); - } else { - if (!inflateBuffer(buffer, ptr, uncompLen, compLen)) - goto unmap; - } - - if (compLen > kSequentialMin) - file->advise(FileMap::NORMAL); - - result = true; - -unmap: - file->release(); -bail: - return result; -} - -/* - * Uncompress an entry, in its entirety, to an open file descriptor. - * - * This doesn't verify the data's CRC, but probably should. - */ -bool ZipFileRO::uncompressEntry(ZipEntryRO entry, int fd) const -{ - bool result = false; - int ent = entryToIndex(entry); - if (ent < 0) - return -1; - - int method; - size_t uncompLen, compLen; - off64_t offset; - const unsigned char* ptr; - - getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL); - - FileMap* file = createEntryFileMap(entry); - if (file == NULL) { - goto bail; - } - - ptr = (const unsigned char*) file->getDataPtr(); - - if (method == kCompressStored) { - ssize_t actual = TEMP_FAILURE_RETRY(write(fd, ptr, uncompLen)); - if (actual < 0) { - ALOGE("Write failed: %s\n", strerror(errno)); - goto unmap; - } else if ((size_t) actual != uncompLen) { - ALOGE("Partial write during uncompress (" ZD " of " ZD ")\n", - (ZD_TYPE) actual, (ZD_TYPE) uncompLen); - goto unmap; - } else { - ALOGI("+++ successful write\n"); - } - } else { - if (!inflateBuffer(fd, ptr, uncompLen, compLen)) - goto unmap; - } - - result = true; - -unmap: - file->release(); -bail: - return result; -} - -/* - * Uncompress "deflate" data from one buffer to another. - */ -/*static*/ bool ZipFileRO::inflateBuffer(void* outBuf, const void* inBuf, - size_t uncompLen, size_t compLen) -{ - bool result = false; - z_stream zstream; - int zerr; - - /* - * Initialize the zlib stream struct. - */ - memset(&zstream, 0, sizeof(zstream)); - zstream.zalloc = Z_NULL; - zstream.zfree = Z_NULL; - zstream.opaque = Z_NULL; - zstream.next_in = (Bytef*)inBuf; - zstream.avail_in = compLen; - zstream.next_out = (Bytef*) outBuf; - zstream.avail_out = uncompLen; - zstream.data_type = Z_UNKNOWN; - - /* - * Use the undocumented "negative window bits" feature to tell zlib - * that there's no zlib header waiting for it. - */ - zerr = inflateInit2(&zstream, -MAX_WBITS); - if (zerr != Z_OK) { - if (zerr == Z_VERSION_ERROR) { - ALOGE("Installed zlib is not compatible with linked version (%s)\n", - ZLIB_VERSION); - } else { - ALOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr); - } - goto bail; - } - - /* - * Expand data. - */ - zerr = inflate(&zstream, Z_FINISH); - if (zerr != Z_STREAM_END) { - ALOGW("Zip inflate failed, zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)\n", - zerr, zstream.next_in, zstream.avail_in, - zstream.next_out, zstream.avail_out); - goto z_bail; - } - - /* paranoia */ - if (zstream.total_out != uncompLen) { - ALOGW("Size mismatch on inflated file (%ld vs " ZD ")\n", - zstream.total_out, (ZD_TYPE) uncompLen); - goto z_bail; - } - - result = true; - -z_bail: - inflateEnd(&zstream); /* free up any allocated structures */ - -bail: - return result; -} - -/* - * Uncompress "deflate" data from one buffer to an open file descriptor. - */ -/*static*/ bool ZipFileRO::inflateBuffer(int fd, const void* inBuf, - size_t uncompLen, size_t compLen) -{ - bool result = false; - const size_t kWriteBufSize = 32768; - unsigned char writeBuf[kWriteBufSize]; - z_stream zstream; - int zerr; - - /* - * Initialize the zlib stream struct. - */ - memset(&zstream, 0, sizeof(zstream)); - zstream.zalloc = Z_NULL; - zstream.zfree = Z_NULL; - zstream.opaque = Z_NULL; - zstream.next_in = (Bytef*)inBuf; - zstream.avail_in = compLen; - zstream.next_out = (Bytef*) writeBuf; - zstream.avail_out = sizeof(writeBuf); - zstream.data_type = Z_UNKNOWN; - - /* - * Use the undocumented "negative window bits" feature to tell zlib - * that there's no zlib header waiting for it. - */ - zerr = inflateInit2(&zstream, -MAX_WBITS); - if (zerr != Z_OK) { - if (zerr == Z_VERSION_ERROR) { - ALOGE("Installed zlib is not compatible with linked version (%s)\n", - ZLIB_VERSION); - } else { - ALOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr); - } - goto bail; - } - - /* - * Loop while we have more to do. - */ - do { - /* - * Expand data. - */ - zerr = inflate(&zstream, Z_NO_FLUSH); - if (zerr != Z_OK && zerr != Z_STREAM_END) { - ALOGW("zlib inflate: zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)\n", - zerr, zstream.next_in, zstream.avail_in, - zstream.next_out, zstream.avail_out); - goto z_bail; - } - - /* write when we're full or when we're done */ - if (zstream.avail_out == 0 || - (zerr == Z_STREAM_END && zstream.avail_out != sizeof(writeBuf))) - { - long writeSize = zstream.next_out - writeBuf; - int cc = TEMP_FAILURE_RETRY(write(fd, writeBuf, writeSize)); - if (cc < 0) { - ALOGW("write failed in inflate: %s", strerror(errno)); - goto z_bail; - } else if (cc != (int) writeSize) { - ALOGW("write failed in inflate (%d vs %ld)", cc, writeSize); - goto z_bail; - } - - zstream.next_out = writeBuf; - zstream.avail_out = sizeof(writeBuf); - } - } while (zerr == Z_OK); - - assert(zerr == Z_STREAM_END); /* other errors should've been caught */ - - /* paranoia */ - if (zstream.total_out != uncompLen) { - ALOGW("Size mismatch on inflated file (%ld vs " ZD ")\n", - zstream.total_out, (ZD_TYPE) uncompLen); - goto z_bail; - } - - result = true; - -z_bail: - inflateEnd(&zstream); /* free up any allocated structures */ - -bail: - return result; -} diff --git a/libs/utils/ZipUtils.cpp b/libs/utils/ZipUtils.cpp deleted file mode 100644 index a43bbb0..0000000 --- a/libs/utils/ZipUtils.cpp +++ /dev/null @@ -1,345 +0,0 @@ -/* - * Copyright (C) 2007 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. - */ - -// -// Misc zip/gzip utility functions. -// - -#define LOG_TAG "ziputil" - -#include <utils/Log.h> -#include <utils/Compat.h> -#include <utils/ZipUtils.h> -#include <utils/ZipFileRO.h> - -#include <stdlib.h> -#include <string.h> -#include <assert.h> - -#include <zlib.h> - -using namespace android; - -/* - * Utility function that expands zip/gzip "deflate" compressed data - * into a buffer. - * - * "fd" is an open file positioned at the start of the "deflate" data - * "buf" must hold at least "uncompressedLen" bytes. - */ -/*static*/ bool ZipUtils::inflateToBuffer(int fd, void* buf, - long uncompressedLen, long compressedLen) -{ - bool result = false; - const unsigned long kReadBufSize = 32768; - unsigned char* readBuf = NULL; - z_stream zstream; - int zerr; - unsigned long compRemaining; - - assert(uncompressedLen >= 0); - assert(compressedLen >= 0); - - readBuf = new unsigned char[kReadBufSize]; - if (readBuf == NULL) - goto bail; - compRemaining = compressedLen; - - /* - * Initialize the zlib stream. - */ - memset(&zstream, 0, sizeof(zstream)); - zstream.zalloc = Z_NULL; - zstream.zfree = Z_NULL; - zstream.opaque = Z_NULL; - zstream.next_in = NULL; - zstream.avail_in = 0; - zstream.next_out = (Bytef*) buf; - zstream.avail_out = uncompressedLen; - zstream.data_type = Z_UNKNOWN; - - /* - * Use the undocumented "negative window bits" feature to tell zlib - * that there's no zlib header waiting for it. - */ - zerr = inflateInit2(&zstream, -MAX_WBITS); - if (zerr != Z_OK) { - if (zerr == Z_VERSION_ERROR) { - ALOGE("Installed zlib is not compatible with linked version (%s)\n", - ZLIB_VERSION); - } else { - ALOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr); - } - goto bail; - } - - /* - * Loop while we have data. - */ - do { - unsigned long getSize; - - /* read as much as we can */ - if (zstream.avail_in == 0) { - getSize = (compRemaining > kReadBufSize) ? - kReadBufSize : compRemaining; - ALOGV("+++ reading %ld bytes (%ld left)\n", - getSize, compRemaining); - - int cc = TEMP_FAILURE_RETRY(read(fd, readBuf, getSize)); - if (cc < 0) { - ALOGW("inflate read failed: %s", strerror(errno)); - } else if (cc != (int) getSize) { - ALOGW("inflate read failed (%d vs %ld)", cc, getSize); - goto z_bail; - } - - compRemaining -= getSize; - - zstream.next_in = readBuf; - zstream.avail_in = getSize; - } - - /* uncompress the data */ - zerr = inflate(&zstream, Z_NO_FLUSH); - if (zerr != Z_OK && zerr != Z_STREAM_END) { - ALOGD("zlib inflate call failed (zerr=%d)\n", zerr); - goto z_bail; - } - - /* output buffer holds all, so no need to write the output */ - } while (zerr == Z_OK); - - assert(zerr == Z_STREAM_END); /* other errors should've been caught */ - - if ((long) zstream.total_out != uncompressedLen) { - ALOGW("Size mismatch on inflated file (%ld vs %ld)\n", - zstream.total_out, uncompressedLen); - goto z_bail; - } - - // success! - result = true; - -z_bail: - inflateEnd(&zstream); /* free up any allocated structures */ - -bail: - delete[] readBuf; - return result; -} - -/* - * Utility function that expands zip/gzip "deflate" compressed data - * into a buffer. - * - * (This is a clone of the previous function, but it takes a FILE* instead - * of an fd. We could pass fileno(fd) to the above, but we can run into - * trouble when "fp" has a different notion of what fd's file position is.) - * - * "fp" is an open file positioned at the start of the "deflate" data - * "buf" must hold at least "uncompressedLen" bytes. - */ -/*static*/ bool ZipUtils::inflateToBuffer(FILE* fp, void* buf, - long uncompressedLen, long compressedLen) -{ - bool result = false; - const unsigned long kReadBufSize = 32768; - unsigned char* readBuf = NULL; - z_stream zstream; - int zerr; - unsigned long compRemaining; - - assert(uncompressedLen >= 0); - assert(compressedLen >= 0); - - readBuf = new unsigned char[kReadBufSize]; - if (readBuf == NULL) - goto bail; - compRemaining = compressedLen; - - /* - * Initialize the zlib stream. - */ - memset(&zstream, 0, sizeof(zstream)); - zstream.zalloc = Z_NULL; - zstream.zfree = Z_NULL; - zstream.opaque = Z_NULL; - zstream.next_in = NULL; - zstream.avail_in = 0; - zstream.next_out = (Bytef*) buf; - zstream.avail_out = uncompressedLen; - zstream.data_type = Z_UNKNOWN; - - /* - * Use the undocumented "negative window bits" feature to tell zlib - * that there's no zlib header waiting for it. - */ - zerr = inflateInit2(&zstream, -MAX_WBITS); - if (zerr != Z_OK) { - if (zerr == Z_VERSION_ERROR) { - ALOGE("Installed zlib is not compatible with linked version (%s)\n", - ZLIB_VERSION); - } else { - ALOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr); - } - goto bail; - } - - /* - * Loop while we have data. - */ - do { - unsigned long getSize; - - /* read as much as we can */ - if (zstream.avail_in == 0) { - getSize = (compRemaining > kReadBufSize) ? - kReadBufSize : compRemaining; - ALOGV("+++ reading %ld bytes (%ld left)\n", - getSize, compRemaining); - - int cc = fread(readBuf, 1, getSize, fp); - if (cc != (int) getSize) { - ALOGD("inflate read failed (%d vs %ld)\n", - cc, getSize); - goto z_bail; - } - - compRemaining -= getSize; - - zstream.next_in = readBuf; - zstream.avail_in = getSize; - } - - /* uncompress the data */ - zerr = inflate(&zstream, Z_NO_FLUSH); - if (zerr != Z_OK && zerr != Z_STREAM_END) { - ALOGD("zlib inflate call failed (zerr=%d)\n", zerr); - goto z_bail; - } - - /* output buffer holds all, so no need to write the output */ - } while (zerr == Z_OK); - - assert(zerr == Z_STREAM_END); /* other errors should've been caught */ - - if ((long) zstream.total_out != uncompressedLen) { - ALOGW("Size mismatch on inflated file (%ld vs %ld)\n", - zstream.total_out, uncompressedLen); - goto z_bail; - } - - // success! - result = true; - -z_bail: - inflateEnd(&zstream); /* free up any allocated structures */ - -bail: - delete[] readBuf; - return result; -} - -/* - * Look at the contents of a gzip archive. We want to know where the - * data starts, and how long it will be after it is uncompressed. - * - * We expect to find the CRC and length as the last 8 bytes on the file. - * This is a pretty reasonable thing to expect for locally-compressed - * files, but there's a small chance that some extra padding got thrown - * on (the man page talks about compressed data written to tape). We - * don't currently deal with that here. If "gzip -l" whines, we're going - * to fail too. - * - * On exit, "fp" is pointing at the start of the compressed data. - */ -/*static*/ bool ZipUtils::examineGzip(FILE* fp, int* pCompressionMethod, - long* pUncompressedLen, long* pCompressedLen, unsigned long* pCRC32) -{ - enum { // flags - FTEXT = 0x01, - FHCRC = 0x02, - FEXTRA = 0x04, - FNAME = 0x08, - FCOMMENT = 0x10, - }; - int ic; - int method, flags; - int i; - - ic = getc(fp); - if (ic != 0x1f || getc(fp) != 0x8b) - return false; // not gzip - method = getc(fp); - flags = getc(fp); - - /* quick sanity checks */ - if (method == EOF || flags == EOF) - return false; - if (method != ZipFileRO::kCompressDeflated) - return false; - - /* skip over 4 bytes of mod time, 1 byte XFL, 1 byte OS */ - for (i = 0; i < 6; i++) - (void) getc(fp); - /* consume "extra" field, if present */ - if ((flags & FEXTRA) != 0) { - int len; - - len = getc(fp); - len |= getc(fp) << 8; - while (len-- && getc(fp) != EOF) - ; - } - /* consume filename, if present */ - if ((flags & FNAME) != 0) { - do { - ic = getc(fp); - } while (ic != 0 && ic != EOF); - } - /* consume comment, if present */ - if ((flags & FCOMMENT) != 0) { - do { - ic = getc(fp); - } while (ic != 0 && ic != EOF); - } - /* consume 16-bit header CRC, if present */ - if ((flags & FHCRC) != 0) { - (void) getc(fp); - (void) getc(fp); - } - - if (feof(fp) || ferror(fp)) - return false; - - /* seek to the end; CRC and length are in the last 8 bytes */ - long curPosn = ftell(fp); - unsigned char buf[8]; - fseek(fp, -8, SEEK_END); - *pCompressedLen = ftell(fp) - curPosn; - - if (fread(buf, 1, 8, fp) != 8) - return false; - /* seek back to start of compressed data */ - fseek(fp, curPosn, SEEK_SET); - - *pCompressionMethod = method; - *pCRC32 = ZipFileRO::get4LE(&buf[0]); - *pUncompressedLen = ZipFileRO::get4LE(&buf[4]); - - return true; -} diff --git a/libs/utils/misc.cpp b/libs/utils/misc.cpp deleted file mode 100644 index 445a23a..0000000 --- a/libs/utils/misc.cpp +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (C) 2005 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "misc" - -// -// Miscellaneous utility functions. -// -#include <utils/misc.h> -#include <utils/Log.h> - -#include <sys/stat.h> -#include <string.h> -#include <errno.h> -#include <assert.h> -#include <stdio.h> - -#if defined(HAVE_PTHREADS) -# include <pthread.h> -#endif - -#include <utils/Vector.h> - -using namespace android; - -namespace android { - -/* - * Get a file's type. - */ -FileType getFileType(const char* fileName) -{ - struct stat sb; - - if (stat(fileName, &sb) < 0) { - if (errno == ENOENT || errno == ENOTDIR) - return kFileTypeNonexistent; - else { - fprintf(stderr, "getFileType got errno=%d on '%s'\n", - errno, fileName); - return kFileTypeUnknown; - } - } else { - if (S_ISREG(sb.st_mode)) - return kFileTypeRegular; - else if (S_ISDIR(sb.st_mode)) - return kFileTypeDirectory; - else if (S_ISCHR(sb.st_mode)) - return kFileTypeCharDev; - else if (S_ISBLK(sb.st_mode)) - return kFileTypeBlockDev; - else if (S_ISFIFO(sb.st_mode)) - return kFileTypeFifo; -#ifdef HAVE_SYMLINKS - else if (S_ISLNK(sb.st_mode)) - return kFileTypeSymlink; - else if (S_ISSOCK(sb.st_mode)) - return kFileTypeSocket; -#endif - else - return kFileTypeUnknown; - } -} - -/* - * Get a file's modification date. - */ -time_t getFileModDate(const char* fileName) -{ - struct stat sb; - - if (stat(fileName, &sb) < 0) - return (time_t) -1; - - return sb.st_mtime; -} - -struct sysprop_change_callback_info { - sysprop_change_callback callback; - int priority; -}; - -#if defined(HAVE_PTHREADS) -static pthread_mutex_t gSyspropMutex = PTHREAD_MUTEX_INITIALIZER; -static Vector<sysprop_change_callback_info>* gSyspropList = NULL; -#endif - -void add_sysprop_change_callback(sysprop_change_callback cb, int priority) { -#if defined(HAVE_PTHREADS) - pthread_mutex_lock(&gSyspropMutex); - if (gSyspropList == NULL) { - gSyspropList = new Vector<sysprop_change_callback_info>(); - } - sysprop_change_callback_info info; - info.callback = cb; - info.priority = priority; - bool added = false; - for (size_t i=0; i<gSyspropList->size(); i++) { - if (priority >= gSyspropList->itemAt(i).priority) { - gSyspropList->insertAt(info, i); - added = true; - break; - } - } - if (!added) { - gSyspropList->add(info); - } - pthread_mutex_unlock(&gSyspropMutex); -#endif -} - -void report_sysprop_change() { -#if defined(HAVE_PTHREADS) - pthread_mutex_lock(&gSyspropMutex); - Vector<sysprop_change_callback_info> listeners; - if (gSyspropList != NULL) { - listeners = *gSyspropList; - } - pthread_mutex_unlock(&gSyspropMutex); - - //ALOGI("Reporting sysprop change to %d listeners", listeners.size()); - for (size_t i=0; i<listeners.size(); i++) { - listeners[i].callback(); - } -#endif -} - -}; // namespace android diff --git a/libs/utils/primes.py b/libs/utils/primes.py deleted file mode 100755 index e161dd8..0000000 --- a/libs/utils/primes.py +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env python2.6 -# -# 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. -# - -# -# Generates a table of prime numbers for use in BasicHashtable.cpp. -# -# Each prime is chosen such that it is a little more than twice as large as -# the previous prime in the table. This makes it easier to choose a new -# hashtable size when the underlying array is grown by as nominal factor -# of two each time. -# - -def is_odd_prime(n): - limit = (n - 1) / 2 - d = 3 - while d <= limit: - if n % d == 0: - return False - d += 2 - return True - -print "static size_t PRIMES[] = {" - -n = 5 -max = 2**31 - 1 -while n < max: - print " %d," % (n) - n = n * 2 + 1 - while not is_odd_prime(n): - n += 2 - -print " 0," -print "};" diff --git a/libs/utils/tests/BasicHashtable_test.cpp b/libs/utils/tests/BasicHashtable_test.cpp deleted file mode 100644 index 7dcf750..0000000 --- a/libs/utils/tests/BasicHashtable_test.cpp +++ /dev/null @@ -1,577 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "BasicHashtable_test" - -#include <utils/BasicHashtable.h> -#include <cutils/log.h> -#include <gtest/gtest.h> -#include <unistd.h> - -namespace android { - -typedef int SimpleKey; -typedef int SimpleValue; -typedef key_value_pair_t<SimpleKey, SimpleValue> SimpleEntry; -typedef BasicHashtable<SimpleKey, SimpleEntry> SimpleHashtable; - -struct ComplexKey { - int k; - - explicit ComplexKey(int k) : k(k) { - instanceCount += 1; - } - - ComplexKey(const ComplexKey& other) : k(other.k) { - instanceCount += 1; - } - - ~ComplexKey() { - instanceCount -= 1; - } - - bool operator ==(const ComplexKey& other) const { - return k == other.k; - } - - bool operator !=(const ComplexKey& other) const { - return k != other.k; - } - - static ssize_t instanceCount; -}; - -ssize_t ComplexKey::instanceCount = 0; - -template<> inline hash_t hash_type(const ComplexKey& value) { - return hash_type(value.k); -} - -struct ComplexValue { - int v; - - explicit ComplexValue(int v) : v(v) { - instanceCount += 1; - } - - ComplexValue(const ComplexValue& other) : v(other.v) { - instanceCount += 1; - } - - ~ComplexValue() { - instanceCount -= 1; - } - - static ssize_t instanceCount; -}; - -ssize_t ComplexValue::instanceCount = 0; - -typedef key_value_pair_t<ComplexKey, ComplexValue> ComplexEntry; -typedef BasicHashtable<ComplexKey, ComplexEntry> ComplexHashtable; - -class BasicHashtableTest : public testing::Test { -protected: - virtual void SetUp() { - ComplexKey::instanceCount = 0; - ComplexValue::instanceCount = 0; - } - - virtual void TearDown() { - ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0)); - } - - void assertInstanceCount(ssize_t keys, ssize_t values) { - if (keys != ComplexKey::instanceCount || values != ComplexValue::instanceCount) { - FAIL() << "Expected " << keys << " keys and " << values << " values " - "but there were actually " << ComplexKey::instanceCount << " keys and " - << ComplexValue::instanceCount << " values"; - } - } - -public: - template <typename TKey, typename TEntry> - static void cookieAt(const BasicHashtable<TKey, TEntry>& h, size_t index, - bool* collision, bool* present, hash_t* hash) { - uint32_t cookie = h.cookieAt(index); - *collision = cookie & BasicHashtable<TKey, TEntry>::Bucket::COLLISION; - *present = cookie & BasicHashtable<TKey, TEntry>::Bucket::PRESENT; - *hash = cookie & BasicHashtable<TKey, TEntry>::Bucket::HASH_MASK; - } - - template <typename TKey, typename TEntry> - static const void* getBuckets(const BasicHashtable<TKey, TEntry>& h) { - return h.mBuckets; - } -}; - -template <typename TKey, typename TValue> -static size_t add(BasicHashtable<TKey, key_value_pair_t<TKey, TValue> >& h, - const TKey& key, const TValue& value) { - return h.add(hash_type(key), key_value_pair_t<TKey, TValue>(key, value)); -} - -template <typename TKey, typename TValue> -static ssize_t find(BasicHashtable<TKey, key_value_pair_t<TKey, TValue> >& h, - ssize_t index, const TKey& key) { - return h.find(index, hash_type(key), key); -} - -template <typename TKey, typename TValue> -static bool remove(BasicHashtable<TKey, key_value_pair_t<TKey, TValue> >& h, - const TKey& key) { - ssize_t index = find(h, -1, key); - if (index >= 0) { - h.removeAt(index); - return true; - } - return false; -} - -template <typename TEntry> -static void getKeyValue(const TEntry& entry, int* key, int* value); - -template <> void getKeyValue(const SimpleEntry& entry, int* key, int* value) { - *key = entry.key; - *value = entry.value; -} - -template <> void getKeyValue(const ComplexEntry& entry, int* key, int* value) { - *key = entry.key.k; - *value = entry.value.v; -} - -template <typename TKey, typename TValue> -static void dump(BasicHashtable<TKey, key_value_pair_t<TKey, TValue> >& h) { - ALOGD("hashtable %p, size=%u, capacity=%u, bucketCount=%u", - &h, h.size(), h.capacity(), h.bucketCount()); - for (size_t i = 0; i < h.bucketCount(); i++) { - bool collision, present; - hash_t hash; - BasicHashtableTest::cookieAt(h, i, &collision, &present, &hash); - if (present) { - int key, value; - getKeyValue(h.entryAt(i), &key, &value); - ALOGD(" [%3u] = collision=%d, present=%d, hash=0x%08x, key=%3d, value=%3d, " - "hash_type(key)=0x%08x", - i, collision, present, hash, key, value, hash_type(key)); - } else { - ALOGD(" [%3u] = collision=%d, present=%d", - i, collision, present); - } - } -} - -TEST_F(BasicHashtableTest, DefaultConstructor_WithDefaultProperties) { - SimpleHashtable h; - - EXPECT_EQ(0U, h.size()); - EXPECT_EQ(3U, h.capacity()); - EXPECT_EQ(5U, h.bucketCount()); - EXPECT_EQ(0.75f, h.loadFactor()); -} - -TEST_F(BasicHashtableTest, Constructor_WithNonUnityLoadFactor) { - SimpleHashtable h(52, 0.8f); - - EXPECT_EQ(0U, h.size()); - EXPECT_EQ(77U, h.capacity()); - EXPECT_EQ(97U, h.bucketCount()); - EXPECT_EQ(0.8f, h.loadFactor()); -} - -TEST_F(BasicHashtableTest, Constructor_WithUnityLoadFactorAndExactCapacity) { - SimpleHashtable h(46, 1.0f); - - EXPECT_EQ(0U, h.size()); - EXPECT_EQ(46U, h.capacity()); // must be one less than bucketCount because loadFactor == 1.0f - EXPECT_EQ(47U, h.bucketCount()); - EXPECT_EQ(1.0f, h.loadFactor()); -} - -TEST_F(BasicHashtableTest, Constructor_WithUnityLoadFactorAndInexactCapacity) { - SimpleHashtable h(42, 1.0f); - - EXPECT_EQ(0U, h.size()); - EXPECT_EQ(46U, h.capacity()); // must be one less than bucketCount because loadFactor == 1.0f - EXPECT_EQ(47U, h.bucketCount()); - EXPECT_EQ(1.0f, h.loadFactor()); -} - -TEST_F(BasicHashtableTest, FindAddFindRemoveFind_OneEntry) { - SimpleHashtable h; - ssize_t index = find(h, -1, 8); - ASSERT_EQ(-1, index); - - index = add(h, 8, 1); - ASSERT_EQ(1U, h.size()); - - ASSERT_EQ(index, find(h, -1, 8)); - ASSERT_EQ(8, h.entryAt(index).key); - ASSERT_EQ(1, h.entryAt(index).value); - - index = find(h, index, 8); - ASSERT_EQ(-1, index); - - ASSERT_TRUE(remove(h, 8)); - ASSERT_EQ(0U, h.size()); - - index = find(h, -1, 8); - ASSERT_EQ(-1, index); -} - -TEST_F(BasicHashtableTest, FindAddFindRemoveFind_MultipleEntryWithUniqueKey) { - const size_t N = 11; - - SimpleHashtable h; - for (size_t i = 0; i < N; i++) { - ssize_t index = find(h, -1, int(i)); - ASSERT_EQ(-1, index); - - index = add(h, int(i), int(i * 10)); - ASSERT_EQ(i + 1, h.size()); - - ASSERT_EQ(index, find(h, -1, int(i))); - ASSERT_EQ(int(i), h.entryAt(index).key); - ASSERT_EQ(int(i * 10), h.entryAt(index).value); - - index = find(h, index, int(i)); - ASSERT_EQ(-1, index); - } - - for (size_t i = N; --i > 0; ) { - ASSERT_TRUE(remove(h, int(i))) << "i = " << i; - ASSERT_EQ(i, h.size()); - - ssize_t index = find(h, -1, int(i)); - ASSERT_EQ(-1, index); - } -} - -TEST_F(BasicHashtableTest, FindAddFindRemoveFind_MultipleEntryWithDuplicateKey) { - const size_t N = 11; - const int K = 1; - - SimpleHashtable h; - for (size_t i = 0; i < N; i++) { - ssize_t index = find(h, -1, K); - if (i == 0) { - ASSERT_EQ(-1, index); - } else { - ASSERT_NE(-1, index); - } - - add(h, K, int(i)); - ASSERT_EQ(i + 1, h.size()); - - index = -1; - int values = 0; - for (size_t j = 0; j <= i; j++) { - index = find(h, index, K); - ASSERT_GE(index, 0); - ASSERT_EQ(K, h.entryAt(index).key); - values |= 1 << h.entryAt(index).value; - } - ASSERT_EQ(values, (1 << (i + 1)) - 1); - - index = find(h, index, K); - ASSERT_EQ(-1, index); - } - - for (size_t i = N; --i > 0; ) { - ASSERT_TRUE(remove(h, K)) << "i = " << i; - ASSERT_EQ(i, h.size()); - - ssize_t index = -1; - for (size_t j = 0; j < i; j++) { - index = find(h, index, K); - ASSERT_GE(index, 0); - ASSERT_EQ(K, h.entryAt(index).key); - } - - index = find(h, index, K); - ASSERT_EQ(-1, index); - } -} - -TEST_F(BasicHashtableTest, Clear_WhenAlreadyEmpty_DoesNothing) { - SimpleHashtable h; - h.clear(); - - EXPECT_EQ(0U, h.size()); - EXPECT_EQ(3U, h.capacity()); - EXPECT_EQ(5U, h.bucketCount()); - EXPECT_EQ(0.75f, h.loadFactor()); -} - -TEST_F(BasicHashtableTest, Clear_AfterElementsAdded_RemovesThem) { - SimpleHashtable h; - add(h, 0, 0); - add(h, 1, 0); - h.clear(); - - EXPECT_EQ(0U, h.size()); - EXPECT_EQ(3U, h.capacity()); - EXPECT_EQ(5U, h.bucketCount()); - EXPECT_EQ(0.75f, h.loadFactor()); -} - -TEST_F(BasicHashtableTest, Clear_AfterElementsAdded_DestroysThem) { - ComplexHashtable h; - add(h, ComplexKey(0), ComplexValue(0)); - add(h, ComplexKey(1), ComplexValue(0)); - ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2)); - - h.clear(); - ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0)); - - EXPECT_EQ(0U, h.size()); - EXPECT_EQ(3U, h.capacity()); - EXPECT_EQ(5U, h.bucketCount()); - EXPECT_EQ(0.75f, h.loadFactor()); -} - -TEST_F(BasicHashtableTest, Remove_AfterElementsAdded_DestroysThem) { - ComplexHashtable h; - add(h, ComplexKey(0), ComplexValue(0)); - add(h, ComplexKey(1), ComplexValue(0)); - ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2)); - - ASSERT_TRUE(remove(h, ComplexKey(0))); - ASSERT_NO_FATAL_FAILURE(assertInstanceCount(1, 1)); - - ASSERT_TRUE(remove(h, ComplexKey(1))); - ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0)); - - EXPECT_EQ(0U, h.size()); - EXPECT_EQ(3U, h.capacity()); - EXPECT_EQ(5U, h.bucketCount()); - EXPECT_EQ(0.75f, h.loadFactor()); -} - -TEST_F(BasicHashtableTest, Destructor_AfterElementsAdded_DestroysThem) { - { - ComplexHashtable h; - add(h, ComplexKey(0), ComplexValue(0)); - add(h, ComplexKey(1), ComplexValue(0)); - ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2)); - } // h is destroyed here - - ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0)); -} - -TEST_F(BasicHashtableTest, Next_WhenEmpty_ReturnsMinusOne) { - SimpleHashtable h; - - ASSERT_EQ(-1, h.next(-1)); -} - -TEST_F(BasicHashtableTest, Next_WhenNonEmpty_IteratesOverAllEntries) { - const int N = 88; - - SimpleHashtable h; - for (int i = 0; i < N; i++) { - add(h, i, i * 10); - } - - bool set[N]; - memset(set, 0, sizeof(bool) * N); - int count = 0; - for (ssize_t index = -1; (index = h.next(index)) != -1; ) { - ASSERT_GE(index, 0); - ASSERT_LT(size_t(index), h.bucketCount()); - - const SimpleEntry& entry = h.entryAt(index); - ASSERT_GE(entry.key, 0); - ASSERT_LT(entry.key, N); - ASSERT_EQ(false, set[entry.key]); - ASSERT_EQ(entry.key * 10, entry.value); - - set[entry.key] = true; - count += 1; - } - ASSERT_EQ(N, count); -} - -TEST_F(BasicHashtableTest, Add_RehashesOnDemand) { - SimpleHashtable h; - size_t initialCapacity = h.capacity(); - size_t initialBucketCount = h.bucketCount(); - - for (size_t i = 0; i < initialCapacity; i++) { - add(h, int(i), 0); - } - - EXPECT_EQ(initialCapacity, h.size()); - EXPECT_EQ(initialCapacity, h.capacity()); - EXPECT_EQ(initialBucketCount, h.bucketCount()); - - add(h, -1, -1); - - EXPECT_EQ(initialCapacity + 1, h.size()); - EXPECT_GT(h.capacity(), initialCapacity); - EXPECT_GT(h.bucketCount(), initialBucketCount); - EXPECT_GT(h.bucketCount(), h.capacity()); -} - -TEST_F(BasicHashtableTest, Rehash_WhenCapacityAndBucketCountUnchanged_DoesNothing) { - ComplexHashtable h; - add(h, ComplexKey(0), ComplexValue(0)); - const void* oldBuckets = getBuckets(h); - ASSERT_NE((void*)NULL, oldBuckets); - ASSERT_NO_FATAL_FAILURE(assertInstanceCount(1, 1)); - - h.rehash(h.capacity(), h.loadFactor()); - - ASSERT_EQ(oldBuckets, getBuckets(h)); - ASSERT_NO_FATAL_FAILURE(assertInstanceCount(1, 1)); -} - -TEST_F(BasicHashtableTest, Rehash_WhenEmptyAndHasNoBuckets_ButDoesNotAllocateBuckets) { - ComplexHashtable h; - ASSERT_EQ((void*)NULL, getBuckets(h)); - ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0)); - - h.rehash(9, 1.0f); - - EXPECT_EQ(0U, h.size()); - EXPECT_EQ(10U, h.capacity()); - EXPECT_EQ(11U, h.bucketCount()); - EXPECT_EQ(1.0f, h.loadFactor()); - EXPECT_EQ((void*)NULL, getBuckets(h)); - ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0)); -} - -TEST_F(BasicHashtableTest, Rehash_WhenEmptyAndHasBuckets_ReleasesBucketsAndSetsCapacity) { - ComplexHashtable h(10); - add(h, ComplexKey(0), ComplexValue(0)); - ASSERT_TRUE(remove(h, ComplexKey(0))); - ASSERT_NE((void*)NULL, getBuckets(h)); - ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0)); - - h.rehash(0, 0.75f); - - EXPECT_EQ(0U, h.size()); - EXPECT_EQ(3U, h.capacity()); - EXPECT_EQ(5U, h.bucketCount()); - EXPECT_EQ(0.75f, h.loadFactor()); - EXPECT_EQ((void*)NULL, getBuckets(h)); - ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0)); -} - -TEST_F(BasicHashtableTest, Rehash_WhenLessThanCurrentCapacity_ShrinksBuckets) { - ComplexHashtable h(10); - add(h, ComplexKey(0), ComplexValue(0)); - add(h, ComplexKey(1), ComplexValue(1)); - const void* oldBuckets = getBuckets(h); - ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2)); - - h.rehash(0, 0.75f); - - EXPECT_EQ(2U, h.size()); - EXPECT_EQ(3U, h.capacity()); - EXPECT_EQ(5U, h.bucketCount()); - EXPECT_EQ(0.75f, h.loadFactor()); - EXPECT_NE(oldBuckets, getBuckets(h)); - ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2)); -} - -TEST_F(BasicHashtableTest, CopyOnWrite) { - ComplexHashtable h1; - add(h1, ComplexKey(0), ComplexValue(0)); - add(h1, ComplexKey(1), ComplexValue(1)); - const void* originalBuckets = getBuckets(h1); - ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2)); - ssize_t index0 = find(h1, -1, ComplexKey(0)); - EXPECT_GE(index0, 0); - - // copy constructor acquires shared reference - ComplexHashtable h2(h1); - ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2)); - ASSERT_EQ(originalBuckets, getBuckets(h2)); - EXPECT_EQ(h1.size(), h2.size()); - EXPECT_EQ(h1.capacity(), h2.capacity()); - EXPECT_EQ(h1.bucketCount(), h2.bucketCount()); - EXPECT_EQ(h1.loadFactor(), h2.loadFactor()); - EXPECT_EQ(index0, find(h2, -1, ComplexKey(0))); - - // operator= acquires shared reference - ComplexHashtable h3; - h3 = h2; - ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2)); - ASSERT_EQ(originalBuckets, getBuckets(h3)); - EXPECT_EQ(h1.size(), h3.size()); - EXPECT_EQ(h1.capacity(), h3.capacity()); - EXPECT_EQ(h1.bucketCount(), h3.bucketCount()); - EXPECT_EQ(h1.loadFactor(), h3.loadFactor()); - EXPECT_EQ(index0, find(h3, -1, ComplexKey(0))); - - // editEntryAt copies shared contents - h1.editEntryAt(index0).value.v = 42; - ASSERT_NO_FATAL_FAILURE(assertInstanceCount(4, 4)); - ASSERT_NE(originalBuckets, getBuckets(h1)); - EXPECT_EQ(42, h1.entryAt(index0).value.v); - EXPECT_EQ(0, h2.entryAt(index0).value.v); - EXPECT_EQ(0, h3.entryAt(index0).value.v); - - // clear releases reference to shared contents - h2.clear(); - ASSERT_NO_FATAL_FAILURE(assertInstanceCount(4, 4)); - EXPECT_EQ(0U, h2.size()); - ASSERT_NE(originalBuckets, getBuckets(h2)); - - // operator= acquires shared reference, destroys unshared contents - h1 = h3; - ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2)); - ASSERT_EQ(originalBuckets, getBuckets(h1)); - EXPECT_EQ(h3.size(), h1.size()); - EXPECT_EQ(h3.capacity(), h1.capacity()); - EXPECT_EQ(h3.bucketCount(), h1.bucketCount()); - EXPECT_EQ(h3.loadFactor(), h1.loadFactor()); - EXPECT_EQ(index0, find(h1, -1, ComplexKey(0))); - - // add copies shared contents - add(h1, ComplexKey(2), ComplexValue(2)); - ASSERT_NO_FATAL_FAILURE(assertInstanceCount(5, 5)); - ASSERT_NE(originalBuckets, getBuckets(h1)); - EXPECT_EQ(3U, h1.size()); - EXPECT_EQ(0U, h2.size()); - EXPECT_EQ(2U, h3.size()); - - // remove copies shared contents - h1 = h3; - ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2)); - ASSERT_EQ(originalBuckets, getBuckets(h1)); - h1.removeAt(index0); - ASSERT_NO_FATAL_FAILURE(assertInstanceCount(3, 3)); - ASSERT_NE(originalBuckets, getBuckets(h1)); - EXPECT_EQ(1U, h1.size()); - EXPECT_EQ(0U, h2.size()); - EXPECT_EQ(2U, h3.size()); - - // rehash copies shared contents - h1 = h3; - ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2)); - ASSERT_EQ(originalBuckets, getBuckets(h1)); - h1.rehash(10, 1.0f); - ASSERT_NO_FATAL_FAILURE(assertInstanceCount(4, 4)); - ASSERT_NE(originalBuckets, getBuckets(h1)); - EXPECT_EQ(2U, h1.size()); - EXPECT_EQ(0U, h2.size()); - EXPECT_EQ(2U, h3.size()); -} - -} // namespace android diff --git a/libs/utils/tests/BlobCache_test.cpp b/libs/utils/tests/BlobCache_test.cpp deleted file mode 100644 index b64cc39..0000000 --- a/libs/utils/tests/BlobCache_test.cpp +++ /dev/null @@ -1,421 +0,0 @@ -/* - ** 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 <fcntl.h> -#include <stdio.h> - -#include <gtest/gtest.h> - -#include <utils/BlobCache.h> -#include <utils/Errors.h> - -namespace android { - -class BlobCacheTest : public ::testing::Test { -protected: - enum { - MAX_KEY_SIZE = 6, - MAX_VALUE_SIZE = 8, - MAX_TOTAL_SIZE = 13, - }; - - virtual void SetUp() { - mBC = new BlobCache(MAX_KEY_SIZE, MAX_VALUE_SIZE, MAX_TOTAL_SIZE); - } - - virtual void TearDown() { - mBC.clear(); - } - - sp<BlobCache> mBC; -}; - -TEST_F(BlobCacheTest, CacheSingleValueSucceeds) { - char buf[4] = { 0xee, 0xee, 0xee, 0xee }; - mBC->set("abcd", 4, "efgh", 4); - ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4)); - ASSERT_EQ('e', buf[0]); - ASSERT_EQ('f', buf[1]); - ASSERT_EQ('g', buf[2]); - ASSERT_EQ('h', buf[3]); -} - -TEST_F(BlobCacheTest, CacheTwoValuesSucceeds) { - char buf[2] = { 0xee, 0xee }; - mBC->set("ab", 2, "cd", 2); - mBC->set("ef", 2, "gh", 2); - ASSERT_EQ(size_t(2), mBC->get("ab", 2, buf, 2)); - ASSERT_EQ('c', buf[0]); - ASSERT_EQ('d', buf[1]); - ASSERT_EQ(size_t(2), mBC->get("ef", 2, buf, 2)); - ASSERT_EQ('g', buf[0]); - ASSERT_EQ('h', buf[1]); -} - -TEST_F(BlobCacheTest, GetOnlyWritesInsideBounds) { - char buf[6] = { 0xee, 0xee, 0xee, 0xee, 0xee, 0xee }; - mBC->set("abcd", 4, "efgh", 4); - ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf+1, 4)); - ASSERT_EQ(0xee, buf[0]); - ASSERT_EQ('e', buf[1]); - ASSERT_EQ('f', buf[2]); - ASSERT_EQ('g', buf[3]); - ASSERT_EQ('h', buf[4]); - ASSERT_EQ(0xee, buf[5]); -} - -TEST_F(BlobCacheTest, GetOnlyWritesIfBufferIsLargeEnough) { - char buf[3] = { 0xee, 0xee, 0xee }; - mBC->set("abcd", 4, "efgh", 4); - ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 3)); - ASSERT_EQ(0xee, buf[0]); - ASSERT_EQ(0xee, buf[1]); - ASSERT_EQ(0xee, buf[2]); -} - -TEST_F(BlobCacheTest, GetDoesntAccessNullBuffer) { - mBC->set("abcd", 4, "efgh", 4); - ASSERT_EQ(size_t(4), mBC->get("abcd", 4, NULL, 0)); -} - -TEST_F(BlobCacheTest, MultipleSetsCacheLatestValue) { - char buf[4] = { 0xee, 0xee, 0xee, 0xee }; - mBC->set("abcd", 4, "efgh", 4); - mBC->set("abcd", 4, "ijkl", 4); - ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4)); - ASSERT_EQ('i', buf[0]); - ASSERT_EQ('j', buf[1]); - ASSERT_EQ('k', buf[2]); - ASSERT_EQ('l', buf[3]); -} - -TEST_F(BlobCacheTest, SecondSetKeepsFirstValueIfTooLarge) { - char buf[MAX_VALUE_SIZE+1] = { 0xee, 0xee, 0xee, 0xee }; - mBC->set("abcd", 4, "efgh", 4); - mBC->set("abcd", 4, buf, MAX_VALUE_SIZE+1); - ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4)); - ASSERT_EQ('e', buf[0]); - ASSERT_EQ('f', buf[1]); - ASSERT_EQ('g', buf[2]); - ASSERT_EQ('h', buf[3]); -} - -TEST_F(BlobCacheTest, DoesntCacheIfKeyIsTooBig) { - char key[MAX_KEY_SIZE+1]; - char buf[4] = { 0xee, 0xee, 0xee, 0xee }; - for (int i = 0; i < MAX_KEY_SIZE+1; i++) { - key[i] = 'a'; - } - mBC->set(key, MAX_KEY_SIZE+1, "bbbb", 4); - ASSERT_EQ(size_t(0), mBC->get(key, MAX_KEY_SIZE+1, buf, 4)); - ASSERT_EQ(0xee, buf[0]); - ASSERT_EQ(0xee, buf[1]); - ASSERT_EQ(0xee, buf[2]); - ASSERT_EQ(0xee, buf[3]); -} - -TEST_F(BlobCacheTest, DoesntCacheIfValueIsTooBig) { - char buf[MAX_VALUE_SIZE+1]; - for (int i = 0; i < MAX_VALUE_SIZE+1; i++) { - buf[i] = 'b'; - } - mBC->set("abcd", 4, buf, MAX_VALUE_SIZE+1); - for (int i = 0; i < MAX_VALUE_SIZE+1; i++) { - buf[i] = 0xee; - } - ASSERT_EQ(size_t(0), mBC->get("abcd", 4, buf, MAX_VALUE_SIZE+1)); - for (int i = 0; i < MAX_VALUE_SIZE+1; i++) { - SCOPED_TRACE(i); - ASSERT_EQ(0xee, buf[i]); - } -} - -TEST_F(BlobCacheTest, DoesntCacheIfKeyValuePairIsTooBig) { - // Check a testing assumptions - ASSERT_TRUE(MAX_TOTAL_SIZE < MAX_KEY_SIZE + MAX_VALUE_SIZE); - ASSERT_TRUE(MAX_KEY_SIZE < MAX_TOTAL_SIZE); - - enum { bufSize = MAX_TOTAL_SIZE - MAX_KEY_SIZE + 1 }; - - char key[MAX_KEY_SIZE]; - char buf[bufSize]; - for (int i = 0; i < MAX_KEY_SIZE; i++) { - key[i] = 'a'; - } - for (int i = 0; i < bufSize; i++) { - buf[i] = 'b'; - } - - mBC->set(key, MAX_KEY_SIZE, buf, MAX_VALUE_SIZE); - ASSERT_EQ(size_t(0), mBC->get(key, MAX_KEY_SIZE, NULL, 0)); -} - -TEST_F(BlobCacheTest, CacheMaxKeySizeSucceeds) { - char key[MAX_KEY_SIZE]; - char buf[4] = { 0xee, 0xee, 0xee, 0xee }; - for (int i = 0; i < MAX_KEY_SIZE; i++) { - key[i] = 'a'; - } - mBC->set(key, MAX_KEY_SIZE, "wxyz", 4); - ASSERT_EQ(size_t(4), mBC->get(key, MAX_KEY_SIZE, buf, 4)); - ASSERT_EQ('w', buf[0]); - ASSERT_EQ('x', buf[1]); - ASSERT_EQ('y', buf[2]); - ASSERT_EQ('z', buf[3]); -} - -TEST_F(BlobCacheTest, CacheMaxValueSizeSucceeds) { - char buf[MAX_VALUE_SIZE]; - for (int i = 0; i < MAX_VALUE_SIZE; i++) { - buf[i] = 'b'; - } - mBC->set("abcd", 4, buf, MAX_VALUE_SIZE); - for (int i = 0; i < MAX_VALUE_SIZE; i++) { - buf[i] = 0xee; - } - ASSERT_EQ(size_t(MAX_VALUE_SIZE), mBC->get("abcd", 4, buf, - MAX_VALUE_SIZE)); - for (int i = 0; i < MAX_VALUE_SIZE; i++) { - SCOPED_TRACE(i); - ASSERT_EQ('b', buf[i]); - } -} - -TEST_F(BlobCacheTest, CacheMaxKeyValuePairSizeSucceeds) { - // Check a testing assumption - ASSERT_TRUE(MAX_KEY_SIZE < MAX_TOTAL_SIZE); - - enum { bufSize = MAX_TOTAL_SIZE - MAX_KEY_SIZE }; - - char key[MAX_KEY_SIZE]; - char buf[bufSize]; - for (int i = 0; i < MAX_KEY_SIZE; i++) { - key[i] = 'a'; - } - for (int i = 0; i < bufSize; i++) { - buf[i] = 'b'; - } - - mBC->set(key, MAX_KEY_SIZE, buf, bufSize); - ASSERT_EQ(size_t(bufSize), mBC->get(key, MAX_KEY_SIZE, NULL, 0)); -} - -TEST_F(BlobCacheTest, CacheMinKeyAndValueSizeSucceeds) { - char buf[1] = { 0xee }; - mBC->set("x", 1, "y", 1); - ASSERT_EQ(size_t(1), mBC->get("x", 1, buf, 1)); - ASSERT_EQ('y', buf[0]); -} - -TEST_F(BlobCacheTest, CacheSizeDoesntExceedTotalLimit) { - for (int i = 0; i < 256; i++) { - uint8_t k = i; - mBC->set(&k, 1, "x", 1); - } - int numCached = 0; - for (int i = 0; i < 256; i++) { - uint8_t k = i; - if (mBC->get(&k, 1, NULL, 0) == 1) { - numCached++; - } - } - ASSERT_GE(MAX_TOTAL_SIZE / 2, numCached); -} - -TEST_F(BlobCacheTest, ExceedingTotalLimitHalvesCacheSize) { - // Fill up the entire cache with 1 char key/value pairs. - const int maxEntries = MAX_TOTAL_SIZE / 2; - for (int i = 0; i < maxEntries; i++) { - uint8_t k = i; - mBC->set(&k, 1, "x", 1); - } - // Insert one more entry, causing a cache overflow. - { - uint8_t k = maxEntries; - mBC->set(&k, 1, "x", 1); - } - // Count the number of entries in the cache. - int numCached = 0; - for (int i = 0; i < maxEntries+1; i++) { - uint8_t k = i; - if (mBC->get(&k, 1, NULL, 0) == 1) { - numCached++; - } - } - ASSERT_EQ(maxEntries/2 + 1, numCached); -} - -class BlobCacheFlattenTest : public BlobCacheTest { -protected: - virtual void SetUp() { - BlobCacheTest::SetUp(); - mBC2 = new BlobCache(MAX_KEY_SIZE, MAX_VALUE_SIZE, MAX_TOTAL_SIZE); - } - - virtual void TearDown() { - mBC2.clear(); - BlobCacheTest::TearDown(); - } - - void roundTrip() { - size_t size = mBC->getFlattenedSize(); - uint8_t* flat = new uint8_t[size]; - ASSERT_EQ(OK, mBC->flatten(flat, size, NULL, 0)); - ASSERT_EQ(OK, mBC2->unflatten(flat, size, NULL, 0)); - delete[] flat; - } - - sp<BlobCache> mBC2; -}; - -TEST_F(BlobCacheFlattenTest, FlattenOneValue) { - char buf[4] = { 0xee, 0xee, 0xee, 0xee }; - mBC->set("abcd", 4, "efgh", 4); - roundTrip(); - ASSERT_EQ(size_t(4), mBC2->get("abcd", 4, buf, 4)); - ASSERT_EQ('e', buf[0]); - ASSERT_EQ('f', buf[1]); - ASSERT_EQ('g', buf[2]); - ASSERT_EQ('h', buf[3]); -} - -TEST_F(BlobCacheFlattenTest, FlattenFullCache) { - // Fill up the entire cache with 1 char key/value pairs. - const int maxEntries = MAX_TOTAL_SIZE / 2; - for (int i = 0; i < maxEntries; i++) { - uint8_t k = i; - mBC->set(&k, 1, &k, 1); - } - - roundTrip(); - - // Verify the deserialized cache - for (int i = 0; i < maxEntries; i++) { - uint8_t k = i; - uint8_t v = 0xee; - ASSERT_EQ(size_t(1), mBC2->get(&k, 1, &v, 1)); - ASSERT_EQ(k, v); - } -} - -TEST_F(BlobCacheFlattenTest, FlattenDoesntChangeCache) { - // Fill up the entire cache with 1 char key/value pairs. - const int maxEntries = MAX_TOTAL_SIZE / 2; - for (int i = 0; i < maxEntries; i++) { - uint8_t k = i; - mBC->set(&k, 1, &k, 1); - } - - size_t size = mBC->getFlattenedSize(); - uint8_t* flat = new uint8_t[size]; - ASSERT_EQ(OK, mBC->flatten(flat, size, NULL, 0)); - delete[] flat; - - // Verify the cache that we just serialized - for (int i = 0; i < maxEntries; i++) { - uint8_t k = i; - uint8_t v = 0xee; - ASSERT_EQ(size_t(1), mBC->get(&k, 1, &v, 1)); - ASSERT_EQ(k, v); - } -} - -TEST_F(BlobCacheFlattenTest, FlattenCatchesBufferTooSmall) { - // Fill up the entire cache with 1 char key/value pairs. - const int maxEntries = MAX_TOTAL_SIZE / 2; - for (int i = 0; i < maxEntries; i++) { - uint8_t k = i; - mBC->set(&k, 1, &k, 1); - } - - size_t size = mBC->getFlattenedSize() - 1; - uint8_t* flat = new uint8_t[size]; - ASSERT_EQ(BAD_VALUE, mBC->flatten(flat, size, NULL, 0)); - delete[] flat; -} - -TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadMagic) { - char buf[4] = { 0xee, 0xee, 0xee, 0xee }; - mBC->set("abcd", 4, "efgh", 4); - - size_t size = mBC->getFlattenedSize(); - uint8_t* flat = new uint8_t[size]; - ASSERT_EQ(OK, mBC->flatten(flat, size, NULL, 0)); - flat[1] = ~flat[1]; - - // Bad magic should cause an error. - ASSERT_EQ(BAD_VALUE, mBC2->unflatten(flat, size, NULL, 0)); - delete[] flat; - - // The error should cause the unflatten to result in an empty cache - ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4)); -} - -TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheVersion) { - char buf[4] = { 0xee, 0xee, 0xee, 0xee }; - mBC->set("abcd", 4, "efgh", 4); - - size_t size = mBC->getFlattenedSize(); - uint8_t* flat = new uint8_t[size]; - ASSERT_EQ(OK, mBC->flatten(flat, size, NULL, 0)); - flat[5] = ~flat[5]; - - // Version mismatches shouldn't cause errors, but should not use the - // serialized entries - ASSERT_EQ(OK, mBC2->unflatten(flat, size, NULL, 0)); - delete[] flat; - - // The version mismatch should cause the unflatten to result in an empty - // cache - ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4)); -} - -TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheDeviceVersion) { - char buf[4] = { 0xee, 0xee, 0xee, 0xee }; - mBC->set("abcd", 4, "efgh", 4); - - size_t size = mBC->getFlattenedSize(); - uint8_t* flat = new uint8_t[size]; - ASSERT_EQ(OK, mBC->flatten(flat, size, NULL, 0)); - flat[10] = ~flat[10]; - - // Version mismatches shouldn't cause errors, but should not use the - // serialized entries - ASSERT_EQ(OK, mBC2->unflatten(flat, size, NULL, 0)); - delete[] flat; - - // The version mismatch should cause the unflatten to result in an empty - // cache - ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4)); -} - -TEST_F(BlobCacheFlattenTest, UnflattenCatchesBufferTooSmall) { - char buf[4] = { 0xee, 0xee, 0xee, 0xee }; - mBC->set("abcd", 4, "efgh", 4); - - size_t size = mBC->getFlattenedSize(); - uint8_t* flat = new uint8_t[size]; - ASSERT_EQ(OK, mBC->flatten(flat, size, NULL, 0)); - - // A buffer truncation shouldt cause an error - ASSERT_EQ(BAD_VALUE, mBC2->unflatten(flat, size-1, NULL, 0)); - delete[] flat; - - // The error should cause the unflatten to result in an empty cache - ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4)); -} - -} // namespace android diff --git a/libs/utils/tests/Looper_test.cpp b/libs/utils/tests/Looper_test.cpp deleted file mode 100644 index 8bf2ba2..0000000 --- a/libs/utils/tests/Looper_test.cpp +++ /dev/null @@ -1,693 +0,0 @@ -// -// Copyright 2010 The Android Open Source Project -// - -#include <utils/Looper.h> -#include <utils/Timers.h> -#include <utils/StopWatch.h> -#include <gtest/gtest.h> -#include <unistd.h> -#include <time.h> - -#include "TestHelpers.h" - -// # of milliseconds to fudge stopwatch measurements -#define TIMING_TOLERANCE_MS 25 - -namespace android { - -enum { - MSG_TEST1 = 1, - MSG_TEST2 = 2, - MSG_TEST3 = 3, - MSG_TEST4 = 4, -}; - -class DelayedWake : public DelayedTask { - sp<Looper> mLooper; - -public: - DelayedWake(int delayMillis, const sp<Looper> looper) : - DelayedTask(delayMillis), mLooper(looper) { - } - -protected: - virtual void doTask() { - mLooper->wake(); - } -}; - -class DelayedWriteSignal : public DelayedTask { - Pipe* mPipe; - -public: - DelayedWriteSignal(int delayMillis, Pipe* pipe) : - DelayedTask(delayMillis), mPipe(pipe) { - } - -protected: - virtual void doTask() { - mPipe->writeSignal(); - } -}; - -class CallbackHandler { -public: - void setCallback(const sp<Looper>& looper, int fd, int events) { - looper->addFd(fd, 0, events, staticHandler, this); - } - -protected: - virtual ~CallbackHandler() { } - - virtual int handler(int fd, int events) = 0; - -private: - static int staticHandler(int fd, int events, void* data) { - return static_cast<CallbackHandler*>(data)->handler(fd, events); - } -}; - -class StubCallbackHandler : public CallbackHandler { -public: - int nextResult; - int callbackCount; - - int fd; - int events; - - StubCallbackHandler(int nextResult) : nextResult(nextResult), - callbackCount(0), fd(-1), events(-1) { - } - -protected: - virtual int handler(int fd, int events) { - callbackCount += 1; - this->fd = fd; - this->events = events; - return nextResult; - } -}; - -class StubMessageHandler : public MessageHandler { -public: - Vector<Message> messages; - - virtual void handleMessage(const Message& message) { - messages.push(message); - } -}; - -class LooperTest : public testing::Test { -protected: - sp<Looper> mLooper; - - virtual void SetUp() { - mLooper = new Looper(true); - } - - virtual void TearDown() { - mLooper.clear(); - } -}; - - -TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndNotAwoken_WaitsForTimeout) { - StopWatch stopWatch("pollOnce"); - int result = mLooper->pollOnce(100); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should approx. equal timeout"; - EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result) - << "pollOnce result should be ALOOPER_POLL_TIMEOUT"; -} - -TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndAwokenBeforeWaiting_ImmediatelyReturns) { - mLooper->wake(); - - StopWatch stopWatch("pollOnce"); - int result = mLooper->pollOnce(1000); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should approx. zero because wake() was called before waiting"; - EXPECT_EQ(ALOOPER_POLL_WAKE, result) - << "pollOnce result should be ALOOPER_POLL_CALLBACK because loop was awoken"; -} - -TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndAwokenWhileWaiting_PromptlyReturns) { - sp<DelayedWake> delayedWake = new DelayedWake(100, mLooper); - delayedWake->run(); - - StopWatch stopWatch("pollOnce"); - int result = mLooper->pollOnce(1000); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should approx. equal wake delay"; - EXPECT_EQ(ALOOPER_POLL_WAKE, result) - << "pollOnce result should be ALOOPER_POLL_CALLBACK because loop was awoken"; -} - -TEST_F(LooperTest, PollOnce_WhenZeroTimeoutAndNoRegisteredFDs_ImmediatelyReturns) { - StopWatch stopWatch("pollOnce"); - int result = mLooper->pollOnce(0); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should be approx. zero"; - EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result) - << "pollOnce result should be ALOOPER_POLL_TIMEOUT"; -} - -TEST_F(LooperTest, PollOnce_WhenZeroTimeoutAndNoSignalledFDs_ImmediatelyReturns) { - Pipe pipe; - StubCallbackHandler handler(true); - - handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT); - - StopWatch stopWatch("pollOnce"); - int result = mLooper->pollOnce(0); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should be approx. zero"; - EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result) - << "pollOnce result should be ALOOPER_POLL_TIMEOUT"; - EXPECT_EQ(0, handler.callbackCount) - << "callback should not have been invoked because FD was not signalled"; -} - -TEST_F(LooperTest, PollOnce_WhenZeroTimeoutAndSignalledFD_ImmediatelyInvokesCallbackAndReturns) { - Pipe pipe; - StubCallbackHandler handler(true); - - ASSERT_EQ(OK, pipe.writeSignal()); - handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT); - - StopWatch stopWatch("pollOnce"); - int result = mLooper->pollOnce(0); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should be approx. zero"; - EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) - << "pollOnce result should be ALOOPER_POLL_CALLBACK because FD was signalled"; - EXPECT_EQ(1, handler.callbackCount) - << "callback should be invoked exactly once"; - EXPECT_EQ(pipe.receiveFd, handler.fd) - << "callback should have received pipe fd as parameter"; - EXPECT_EQ(ALOOPER_EVENT_INPUT, handler.events) - << "callback should have received ALOOPER_EVENT_INPUT as events"; -} - -TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndNoSignalledFDs_WaitsForTimeoutAndReturns) { - Pipe pipe; - StubCallbackHandler handler(true); - - handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT); - - StopWatch stopWatch("pollOnce"); - int result = mLooper->pollOnce(100); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should approx. equal timeout"; - EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result) - << "pollOnce result should be ALOOPER_POLL_TIMEOUT"; - EXPECT_EQ(0, handler.callbackCount) - << "callback should not have been invoked because FD was not signalled"; -} - -TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndSignalledFDBeforeWaiting_ImmediatelyInvokesCallbackAndReturns) { - Pipe pipe; - StubCallbackHandler handler(true); - - pipe.writeSignal(); - handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT); - - StopWatch stopWatch("pollOnce"); - int result = mLooper->pollOnce(100); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - ASSERT_EQ(OK, pipe.readSignal()) - << "signal should actually have been written"; - EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should be approx. zero"; - EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) - << "pollOnce result should be ALOOPER_POLL_CALLBACK because FD was signalled"; - EXPECT_EQ(1, handler.callbackCount) - << "callback should be invoked exactly once"; - EXPECT_EQ(pipe.receiveFd, handler.fd) - << "callback should have received pipe fd as parameter"; - EXPECT_EQ(ALOOPER_EVENT_INPUT, handler.events) - << "callback should have received ALOOPER_EVENT_INPUT as events"; -} - -TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndSignalledFDWhileWaiting_PromptlyInvokesCallbackAndReturns) { - Pipe pipe; - StubCallbackHandler handler(true); - sp<DelayedWriteSignal> delayedWriteSignal = new DelayedWriteSignal(100, & pipe); - - handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT); - delayedWriteSignal->run(); - - StopWatch stopWatch("pollOnce"); - int result = mLooper->pollOnce(1000); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - ASSERT_EQ(OK, pipe.readSignal()) - << "signal should actually have been written"; - EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should approx. equal signal delay"; - EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) - << "pollOnce result should be ALOOPER_POLL_CALLBACK because FD was signalled"; - EXPECT_EQ(1, handler.callbackCount) - << "callback should be invoked exactly once"; - EXPECT_EQ(pipe.receiveFd, handler.fd) - << "callback should have received pipe fd as parameter"; - EXPECT_EQ(ALOOPER_EVENT_INPUT, handler.events) - << "callback should have received ALOOPER_EVENT_INPUT as events"; -} - -TEST_F(LooperTest, PollOnce_WhenCallbackAddedThenRemoved_CallbackShouldNotBeInvoked) { - Pipe pipe; - StubCallbackHandler handler(true); - - handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT); - pipe.writeSignal(); // would cause FD to be considered signalled - mLooper->removeFd(pipe.receiveFd); - - StopWatch stopWatch("pollOnce"); - int result = mLooper->pollOnce(100); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - ASSERT_EQ(OK, pipe.readSignal()) - << "signal should actually have been written"; - EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should approx. equal timeout because FD was no longer registered"; - EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result) - << "pollOnce result should be ALOOPER_POLL_TIMEOUT"; - EXPECT_EQ(0, handler.callbackCount) - << "callback should not be invoked"; -} - -TEST_F(LooperTest, PollOnce_WhenCallbackReturnsFalse_CallbackShouldNotBeInvokedAgainLater) { - Pipe pipe; - StubCallbackHandler handler(false); - - handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT); - - // First loop: Callback is registered and FD is signalled. - pipe.writeSignal(); - - StopWatch stopWatch("pollOnce"); - int result = mLooper->pollOnce(0); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - ASSERT_EQ(OK, pipe.readSignal()) - << "signal should actually have been written"; - EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should approx. equal zero because FD was already signalled"; - EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) - << "pollOnce result should be ALOOPER_POLL_CALLBACK because FD was signalled"; - EXPECT_EQ(1, handler.callbackCount) - << "callback should be invoked"; - - // Second loop: Callback is no longer registered and FD is signalled. - pipe.writeSignal(); - - stopWatch.reset(); - result = mLooper->pollOnce(0); - elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - ASSERT_EQ(OK, pipe.readSignal()) - << "signal should actually have been written"; - EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should approx. equal zero because timeout was zero"; - EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result) - << "pollOnce result should be ALOOPER_POLL_TIMEOUT"; - EXPECT_EQ(1, handler.callbackCount) - << "callback should not be invoked this time"; -} - -TEST_F(LooperTest, PollOnce_WhenNonCallbackFdIsSignalled_ReturnsIdent) { - const int expectedIdent = 5; - void* expectedData = this; - - Pipe pipe; - - pipe.writeSignal(); - mLooper->addFd(pipe.receiveFd, expectedIdent, ALOOPER_EVENT_INPUT, NULL, expectedData); - - StopWatch stopWatch("pollOnce"); - int fd; - int events; - void* data; - int result = mLooper->pollOnce(100, &fd, &events, &data); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - ASSERT_EQ(OK, pipe.readSignal()) - << "signal should actually have been written"; - EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should be approx. zero"; - EXPECT_EQ(expectedIdent, result) - << "pollOnce result should be the ident of the FD that was signalled"; - EXPECT_EQ(pipe.receiveFd, fd) - << "pollOnce should have returned the received pipe fd"; - EXPECT_EQ(ALOOPER_EVENT_INPUT, events) - << "pollOnce should have returned ALOOPER_EVENT_INPUT as events"; - EXPECT_EQ(expectedData, data) - << "pollOnce should have returned the data"; -} - -TEST_F(LooperTest, AddFd_WhenCallbackAdded_ReturnsOne) { - Pipe pipe; - int result = mLooper->addFd(pipe.receiveFd, 0, ALOOPER_EVENT_INPUT, NULL, NULL); - - EXPECT_EQ(1, result) - << "addFd should return 1 because FD was added"; -} - -TEST_F(LooperTest, AddFd_WhenIdentIsNegativeAndCallbackIsNull_ReturnsError) { - Pipe pipe; - int result = mLooper->addFd(pipe.receiveFd, -1, ALOOPER_EVENT_INPUT, NULL, NULL); - - EXPECT_EQ(-1, result) - << "addFd should return -1 because arguments were invalid"; -} - -TEST_F(LooperTest, AddFd_WhenNoCallbackAndAllowNonCallbacksIsFalse_ReturnsError) { - Pipe pipe; - sp<Looper> looper = new Looper(false /*allowNonCallbacks*/); - int result = looper->addFd(pipe.receiveFd, 0, 0, NULL, NULL); - - EXPECT_EQ(-1, result) - << "addFd should return -1 because arguments were invalid"; -} - -TEST_F(LooperTest, RemoveFd_WhenCallbackNotAdded_ReturnsZero) { - int result = mLooper->removeFd(1); - - EXPECT_EQ(0, result) - << "removeFd should return 0 because FD not registered"; -} - -TEST_F(LooperTest, RemoveFd_WhenCallbackAddedThenRemovedTwice_ReturnsOnceFirstTimeAndReturnsZeroSecondTime) { - Pipe pipe; - StubCallbackHandler handler(false); - handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT); - - // First time. - int result = mLooper->removeFd(pipe.receiveFd); - - EXPECT_EQ(1, result) - << "removeFd should return 1 first time because FD was registered"; - - // Second time. - result = mLooper->removeFd(pipe.receiveFd); - - EXPECT_EQ(0, result) - << "removeFd should return 0 second time because FD was no longer registered"; -} - -TEST_F(LooperTest, PollOnce_WhenCallbackAddedTwice_OnlySecondCallbackShouldBeInvoked) { - Pipe pipe; - StubCallbackHandler handler1(true); - StubCallbackHandler handler2(true); - - handler1.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT); - handler2.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT); // replace it - pipe.writeSignal(); // would cause FD to be considered signalled - - StopWatch stopWatch("pollOnce"); - int result = mLooper->pollOnce(100); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - ASSERT_EQ(OK, pipe.readSignal()) - << "signal should actually have been written"; - EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should approx. zero because FD was already signalled"; - EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) - << "pollOnce result should be ALOOPER_POLL_CALLBACK because FD was signalled"; - EXPECT_EQ(0, handler1.callbackCount) - << "original handler callback should not be invoked because it was replaced"; - EXPECT_EQ(1, handler2.callbackCount) - << "replacement handler callback should be invoked"; -} - -TEST_F(LooperTest, SendMessage_WhenOneMessageIsEnqueue_ShouldInvokeHandlerDuringNextPoll) { - sp<StubMessageHandler> handler = new StubMessageHandler(); - mLooper->sendMessage(handler, Message(MSG_TEST1)); - - StopWatch stopWatch("pollOnce"); - int result = mLooper->pollOnce(100); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should approx. zero because message was already sent"; - EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) - << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent"; - EXPECT_EQ(size_t(1), handler->messages.size()) - << "handled message"; - EXPECT_EQ(MSG_TEST1, handler->messages[0].what) - << "handled message"; -} - -TEST_F(LooperTest, SendMessage_WhenMultipleMessagesAreEnqueued_ShouldInvokeHandlersInOrderDuringNextPoll) { - sp<StubMessageHandler> handler1 = new StubMessageHandler(); - sp<StubMessageHandler> handler2 = new StubMessageHandler(); - mLooper->sendMessage(handler1, Message(MSG_TEST1)); - mLooper->sendMessage(handler2, Message(MSG_TEST2)); - mLooper->sendMessage(handler1, Message(MSG_TEST3)); - mLooper->sendMessage(handler1, Message(MSG_TEST4)); - - StopWatch stopWatch("pollOnce"); - int result = mLooper->pollOnce(1000); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should approx. zero because message was already sent"; - EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) - << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent"; - EXPECT_EQ(size_t(3), handler1->messages.size()) - << "handled message"; - EXPECT_EQ(MSG_TEST1, handler1->messages[0].what) - << "handled message"; - EXPECT_EQ(MSG_TEST3, handler1->messages[1].what) - << "handled message"; - EXPECT_EQ(MSG_TEST4, handler1->messages[2].what) - << "handled message"; - EXPECT_EQ(size_t(1), handler2->messages.size()) - << "handled message"; - EXPECT_EQ(MSG_TEST2, handler2->messages[0].what) - << "handled message"; -} - -TEST_F(LooperTest, SendMessageDelayed_WhenSentToTheFuture_ShouldInvokeHandlerAfterDelayTime) { - sp<StubMessageHandler> handler = new StubMessageHandler(); - mLooper->sendMessageDelayed(ms2ns(100), handler, Message(MSG_TEST1)); - - StopWatch stopWatch("pollOnce"); - int result = mLooper->pollOnce(1000); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) - << "first poll should end quickly because next message timeout was computed"; - EXPECT_EQ(ALOOPER_POLL_WAKE, result) - << "pollOnce result should be ALOOPER_POLL_WAKE due to wakeup"; - EXPECT_EQ(size_t(0), handler->messages.size()) - << "no message handled yet"; - - result = mLooper->pollOnce(1000); - elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - EXPECT_EQ(size_t(1), handler->messages.size()) - << "handled message"; - EXPECT_EQ(MSG_TEST1, handler->messages[0].what) - << "handled message"; - EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) - << "second poll should end around the time of the delayed message dispatch"; - EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) - << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent"; - - result = mLooper->pollOnce(100); - elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - EXPECT_NEAR(100 + 100, elapsedMillis, TIMING_TOLERANCE_MS) - << "third poll should timeout"; - EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result) - << "pollOnce result should be ALOOPER_POLL_TIMEOUT because there were no messages left"; -} - -TEST_F(LooperTest, SendMessageDelayed_WhenSentToThePast_ShouldInvokeHandlerDuringNextPoll) { - sp<StubMessageHandler> handler = new StubMessageHandler(); - mLooper->sendMessageDelayed(ms2ns(-1000), handler, Message(MSG_TEST1)); - - StopWatch stopWatch("pollOnce"); - int result = mLooper->pollOnce(100); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should approx. zero because message was already sent"; - EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) - << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent"; - EXPECT_EQ(size_t(1), handler->messages.size()) - << "handled message"; - EXPECT_EQ(MSG_TEST1, handler->messages[0].what) - << "handled message"; -} - -TEST_F(LooperTest, SendMessageDelayed_WhenSentToThePresent_ShouldInvokeHandlerDuringNextPoll) { - sp<StubMessageHandler> handler = new StubMessageHandler(); - mLooper->sendMessageDelayed(0, handler, Message(MSG_TEST1)); - - StopWatch stopWatch("pollOnce"); - int result = mLooper->pollOnce(100); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should approx. zero because message was already sent"; - EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) - << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent"; - EXPECT_EQ(size_t(1), handler->messages.size()) - << "handled message"; - EXPECT_EQ(MSG_TEST1, handler->messages[0].what) - << "handled message"; -} - -TEST_F(LooperTest, SendMessageAtTime_WhenSentToTheFuture_ShouldInvokeHandlerAfterDelayTime) { - nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); - sp<StubMessageHandler> handler = new StubMessageHandler(); - mLooper->sendMessageAtTime(now + ms2ns(100), handler, Message(MSG_TEST1)); - - StopWatch stopWatch("pollOnce"); - int result = mLooper->pollOnce(1000); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) - << "first poll should end quickly because next message timeout was computed"; - EXPECT_EQ(ALOOPER_POLL_WAKE, result) - << "pollOnce result should be ALOOPER_POLL_WAKE due to wakeup"; - EXPECT_EQ(size_t(0), handler->messages.size()) - << "no message handled yet"; - - result = mLooper->pollOnce(1000); - elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - EXPECT_EQ(size_t(1), handler->messages.size()) - << "handled message"; - EXPECT_EQ(MSG_TEST1, handler->messages[0].what) - << "handled message"; - EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) - << "second poll should end around the time of the delayed message dispatch"; - EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) - << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent"; - - result = mLooper->pollOnce(100); - elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - EXPECT_NEAR(100 + 100, elapsedMillis, TIMING_TOLERANCE_MS) - << "third poll should timeout"; - EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result) - << "pollOnce result should be ALOOPER_POLL_TIMEOUT because there were no messages left"; -} - -TEST_F(LooperTest, SendMessageAtTime_WhenSentToThePast_ShouldInvokeHandlerDuringNextPoll) { - nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); - sp<StubMessageHandler> handler = new StubMessageHandler(); - mLooper->sendMessageAtTime(now - ms2ns(1000), handler, Message(MSG_TEST1)); - - StopWatch stopWatch("pollOnce"); - int result = mLooper->pollOnce(100); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should approx. zero because message was already sent"; - EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) - << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent"; - EXPECT_EQ(size_t(1), handler->messages.size()) - << "handled message"; - EXPECT_EQ(MSG_TEST1, handler->messages[0].what) - << "handled message"; -} - -TEST_F(LooperTest, SendMessageAtTime_WhenSentToThePresent_ShouldInvokeHandlerDuringNextPoll) { - nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); - sp<StubMessageHandler> handler = new StubMessageHandler(); - mLooper->sendMessageAtTime(now, handler, Message(MSG_TEST1)); - - StopWatch stopWatch("pollOnce"); - int result = mLooper->pollOnce(100); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should approx. zero because message was already sent"; - EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) - << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent"; - EXPECT_EQ(size_t(1), handler->messages.size()) - << "handled message"; - EXPECT_EQ(MSG_TEST1, handler->messages[0].what) - << "handled message"; -} - -TEST_F(LooperTest, RemoveMessage_WhenRemovingAllMessagesForHandler_ShouldRemoveThoseMessage) { - sp<StubMessageHandler> handler = new StubMessageHandler(); - mLooper->sendMessage(handler, Message(MSG_TEST1)); - mLooper->sendMessage(handler, Message(MSG_TEST2)); - mLooper->sendMessage(handler, Message(MSG_TEST3)); - mLooper->removeMessages(handler); - - StopWatch stopWatch("pollOnce"); - int result = mLooper->pollOnce(0); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should approx. zero because message was sent so looper was awoken"; - EXPECT_EQ(ALOOPER_POLL_WAKE, result) - << "pollOnce result should be ALOOPER_POLL_WAKE because looper was awoken"; - EXPECT_EQ(size_t(0), handler->messages.size()) - << "no messages to handle"; - - result = mLooper->pollOnce(0); - - EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result) - << "pollOnce result should be ALOOPER_POLL_TIMEOUT because there was nothing to do"; - EXPECT_EQ(size_t(0), handler->messages.size()) - << "no messages to handle"; -} - -TEST_F(LooperTest, RemoveMessage_WhenRemovingSomeMessagesForHandler_ShouldRemoveThoseMessage) { - sp<StubMessageHandler> handler = new StubMessageHandler(); - mLooper->sendMessage(handler, Message(MSG_TEST1)); - mLooper->sendMessage(handler, Message(MSG_TEST2)); - mLooper->sendMessage(handler, Message(MSG_TEST3)); - mLooper->sendMessage(handler, Message(MSG_TEST4)); - mLooper->removeMessages(handler, MSG_TEST3); - mLooper->removeMessages(handler, MSG_TEST1); - - StopWatch stopWatch("pollOnce"); - int result = mLooper->pollOnce(0); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should approx. zero because message was sent so looper was awoken"; - EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) - << "pollOnce result should be ALOOPER_POLL_CALLBACK because two messages were sent"; - EXPECT_EQ(size_t(2), handler->messages.size()) - << "no messages to handle"; - EXPECT_EQ(MSG_TEST2, handler->messages[0].what) - << "handled message"; - EXPECT_EQ(MSG_TEST4, handler->messages[1].what) - << "handled message"; - - result = mLooper->pollOnce(0); - - EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result) - << "pollOnce result should be ALOOPER_POLL_TIMEOUT because there was nothing to do"; - EXPECT_EQ(size_t(2), handler->messages.size()) - << "no more messages to handle"; -} - -} // namespace android diff --git a/libs/utils/tests/LruCache_test.cpp b/libs/utils/tests/LruCache_test.cpp deleted file mode 100644 index e573952..0000000 --- a/libs/utils/tests/LruCache_test.cpp +++ /dev/null @@ -1,291 +0,0 @@ -/* - * Copyright (C) 2012 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 <stdlib.h> -#include <utils/JenkinsHash.h> -#include <utils/LruCache.h> -#include <cutils/log.h> -#include <gtest/gtest.h> - -namespace android { - -typedef int SimpleKey; -typedef const char* StringValue; - -struct ComplexKey { - int k; - - explicit ComplexKey(int k) : k(k) { - instanceCount += 1; - } - - ComplexKey(const ComplexKey& other) : k(other.k) { - instanceCount += 1; - } - - ~ComplexKey() { - instanceCount -= 1; - } - - bool operator ==(const ComplexKey& other) const { - return k == other.k; - } - - bool operator !=(const ComplexKey& other) const { - return k != other.k; - } - - static ssize_t instanceCount; -}; - -ssize_t ComplexKey::instanceCount = 0; - -template<> inline hash_t hash_type(const ComplexKey& value) { - return hash_type(value.k); -} - -struct ComplexValue { - int v; - - explicit ComplexValue(int v) : v(v) { - instanceCount += 1; - } - - ComplexValue(const ComplexValue& other) : v(other.v) { - instanceCount += 1; - } - - ~ComplexValue() { - instanceCount -= 1; - } - - static ssize_t instanceCount; -}; - -ssize_t ComplexValue::instanceCount = 0; - -typedef LruCache<ComplexKey, ComplexValue> ComplexCache; - -class EntryRemovedCallback : public OnEntryRemoved<SimpleKey, StringValue> { -public: - EntryRemovedCallback() : callbackCount(0), lastKey(-1), lastValue(NULL) { } - ~EntryRemovedCallback() {} - void operator()(SimpleKey& k, StringValue& v) { - callbackCount += 1; - lastKey = k; - lastValue = v; - } - ssize_t callbackCount; - SimpleKey lastKey; - StringValue lastValue; -}; - -class LruCacheTest : public testing::Test { -protected: - virtual void SetUp() { - ComplexKey::instanceCount = 0; - ComplexValue::instanceCount = 0; - } - - virtual void TearDown() { - ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0)); - } - - void assertInstanceCount(ssize_t keys, ssize_t values) { - if (keys != ComplexKey::instanceCount || values != ComplexValue::instanceCount) { - FAIL() << "Expected " << keys << " keys and " << values << " values " - "but there were actually " << ComplexKey::instanceCount << " keys and " - << ComplexValue::instanceCount << " values"; - } - } -}; - -TEST_F(LruCacheTest, Empty) { - LruCache<SimpleKey, StringValue> cache(100); - - EXPECT_EQ(NULL, cache.get(0)); - EXPECT_EQ(0u, cache.size()); -} - -TEST_F(LruCacheTest, Simple) { - LruCache<SimpleKey, StringValue> cache(100); - - cache.put(1, "one"); - cache.put(2, "two"); - cache.put(3, "three"); - EXPECT_STREQ("one", cache.get(1)); - EXPECT_STREQ("two", cache.get(2)); - EXPECT_STREQ("three", cache.get(3)); - EXPECT_EQ(3u, cache.size()); -} - -TEST_F(LruCacheTest, MaxCapacity) { - LruCache<SimpleKey, StringValue> cache(2); - - cache.put(1, "one"); - cache.put(2, "two"); - cache.put(3, "three"); - EXPECT_EQ(NULL, cache.get(1)); - EXPECT_STREQ("two", cache.get(2)); - EXPECT_STREQ("three", cache.get(3)); - EXPECT_EQ(2u, cache.size()); -} - -TEST_F(LruCacheTest, RemoveLru) { - LruCache<SimpleKey, StringValue> cache(100); - - cache.put(1, "one"); - cache.put(2, "two"); - cache.put(3, "three"); - cache.removeOldest(); - EXPECT_EQ(NULL, cache.get(1)); - EXPECT_STREQ("two", cache.get(2)); - EXPECT_STREQ("three", cache.get(3)); - EXPECT_EQ(2u, cache.size()); -} - -TEST_F(LruCacheTest, GetUpdatesLru) { - LruCache<SimpleKey, StringValue> cache(100); - - cache.put(1, "one"); - cache.put(2, "two"); - cache.put(3, "three"); - EXPECT_STREQ("one", cache.get(1)); - cache.removeOldest(); - EXPECT_STREQ("one", cache.get(1)); - EXPECT_EQ(NULL, cache.get(2)); - EXPECT_STREQ("three", cache.get(3)); - EXPECT_EQ(2u, cache.size()); -} - -uint32_t hash_int(int x) { - return JenkinsHashWhiten(JenkinsHashMix(0, x)); -} - -TEST_F(LruCacheTest, StressTest) { - const size_t kCacheSize = 512; - LruCache<SimpleKey, StringValue> cache(512); - const size_t kNumKeys = 16 * 1024; - const size_t kNumIters = 100000; - char* strings[kNumKeys]; - - for (size_t i = 0; i < kNumKeys; i++) { - strings[i] = (char *)malloc(16); - sprintf(strings[i], "%d", i); - } - - srandom(12345); - int hitCount = 0; - for (size_t i = 0; i < kNumIters; i++) { - int index = random() % kNumKeys; - uint32_t key = hash_int(index); - const char *val = cache.get(key); - if (val != NULL) { - EXPECT_EQ(strings[index], val); - hitCount++; - } else { - cache.put(key, strings[index]); - } - } - size_t expectedHitCount = kNumIters * kCacheSize / kNumKeys; - EXPECT_LT(int(expectedHitCount * 0.9), hitCount); - EXPECT_GT(int(expectedHitCount * 1.1), hitCount); - EXPECT_EQ(kCacheSize, cache.size()); - - for (size_t i = 0; i < kNumKeys; i++) { - free((void *)strings[i]); - } -} - -TEST_F(LruCacheTest, NoLeak) { - ComplexCache cache(100); - - cache.put(ComplexKey(0), ComplexValue(0)); - cache.put(ComplexKey(1), ComplexValue(1)); - EXPECT_EQ(2, cache.size()); - assertInstanceCount(2, 3); // the null value counts as an instance -} - -TEST_F(LruCacheTest, Clear) { - ComplexCache cache(100); - - cache.put(ComplexKey(0), ComplexValue(0)); - cache.put(ComplexKey(1), ComplexValue(1)); - EXPECT_EQ(2, cache.size()); - assertInstanceCount(2, 3); - cache.clear(); - assertInstanceCount(0, 1); -} - -TEST_F(LruCacheTest, ClearNoDoubleFree) { - { - ComplexCache cache(100); - - cache.put(ComplexKey(0), ComplexValue(0)); - cache.put(ComplexKey(1), ComplexValue(1)); - EXPECT_EQ(2, cache.size()); - assertInstanceCount(2, 3); - cache.removeOldest(); - cache.clear(); - assertInstanceCount(0, 1); - } - assertInstanceCount(0, 0); -} - -TEST_F(LruCacheTest, ClearReuseOk) { - ComplexCache cache(100); - - cache.put(ComplexKey(0), ComplexValue(0)); - cache.put(ComplexKey(1), ComplexValue(1)); - EXPECT_EQ(2, cache.size()); - assertInstanceCount(2, 3); - cache.clear(); - assertInstanceCount(0, 1); - cache.put(ComplexKey(0), ComplexValue(0)); - cache.put(ComplexKey(1), ComplexValue(1)); - EXPECT_EQ(2, cache.size()); - assertInstanceCount(2, 3); -} - -TEST_F(LruCacheTest, Callback) { - LruCache<SimpleKey, StringValue> cache(100); - EntryRemovedCallback callback; - cache.setOnEntryRemovedListener(&callback); - - cache.put(1, "one"); - cache.put(2, "two"); - cache.put(3, "three"); - EXPECT_EQ(3, cache.size()); - cache.removeOldest(); - EXPECT_EQ(1, callback.callbackCount); - EXPECT_EQ(1, callback.lastKey); - EXPECT_STREQ("one", callback.lastValue); -} - -TEST_F(LruCacheTest, CallbackOnClear) { - LruCache<SimpleKey, StringValue> cache(100); - EntryRemovedCallback callback; - cache.setOnEntryRemovedListener(&callback); - - cache.put(1, "one"); - cache.put(2, "two"); - cache.put(3, "three"); - EXPECT_EQ(3, cache.size()); - cache.clear(); - EXPECT_EQ(3, callback.callbackCount); -} - -} diff --git a/libs/utils/tests/String8_test.cpp b/libs/utils/tests/String8_test.cpp deleted file mode 100644 index c42c68d..0000000 --- a/libs/utils/tests/String8_test.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "String8_test" -#include <utils/Log.h> -#include <utils/String8.h> - -#include <gtest/gtest.h> - -namespace android { - -class String8Test : public testing::Test { -protected: - virtual void SetUp() { - } - - virtual void TearDown() { - } -}; - -TEST_F(String8Test, Cstr) { - String8 tmp("Hello, world!"); - - EXPECT_STREQ(tmp.string(), "Hello, world!"); -} - -TEST_F(String8Test, OperatorPlus) { - String8 src1("Hello, "); - - // Test adding String8 + const char* - const char* ccsrc2 = "world!"; - String8 dst1 = src1 + ccsrc2; - EXPECT_STREQ(dst1.string(), "Hello, world!"); - EXPECT_STREQ(src1.string(), "Hello, "); - EXPECT_STREQ(ccsrc2, "world!"); - - // Test adding String8 + String8 - String8 ssrc2("world!"); - String8 dst2 = src1 + ssrc2; - EXPECT_STREQ(dst2.string(), "Hello, world!"); - EXPECT_STREQ(src1.string(), "Hello, "); - EXPECT_STREQ(ssrc2.string(), "world!"); -} - -TEST_F(String8Test, OperatorPlusEquals) { - String8 src1("My voice"); - - // Testing String8 += String8 - String8 src2(" is my passport."); - src1 += src2; - EXPECT_STREQ(src1.string(), "My voice is my passport."); - EXPECT_STREQ(src2.string(), " is my passport."); - - // Adding const char* to the previous string. - const char* src3 = " Verify me."; - src1 += src3; - EXPECT_STREQ(src1.string(), "My voice is my passport. Verify me."); - EXPECT_STREQ(src2.string(), " is my passport."); - EXPECT_STREQ(src3, " Verify me."); -} - -} diff --git a/libs/utils/tests/Unicode_test.cpp b/libs/utils/tests/Unicode_test.cpp deleted file mode 100644 index 18c130c..0000000 --- a/libs/utils/tests/Unicode_test.cpp +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "Unicode_test" -#include <utils/Log.h> -#include <utils/Unicode.h> - -#include <gtest/gtest.h> - -namespace android { - -class UnicodeTest : public testing::Test { -protected: - virtual void SetUp() { - } - - virtual void TearDown() { - } -}; - -TEST_F(UnicodeTest, UTF8toUTF16ZeroLength) { - ssize_t measured; - - const uint8_t str[] = { }; - - measured = utf8_to_utf16_length(str, 0); - EXPECT_EQ(0, measured) - << "Zero length input should return zero length output."; -} - -TEST_F(UnicodeTest, UTF8toUTF16ASCIILength) { - ssize_t measured; - - // U+0030 or ASCII '0' - const uint8_t str[] = { 0x30 }; - - measured = utf8_to_utf16_length(str, sizeof(str)); - EXPECT_EQ(1, measured) - << "ASCII glyphs should have a length of 1 char16_t"; -} - -TEST_F(UnicodeTest, UTF8toUTF16Plane1Length) { - ssize_t measured; - - // U+2323 SMILE - const uint8_t str[] = { 0xE2, 0x8C, 0xA3 }; - - measured = utf8_to_utf16_length(str, sizeof(str)); - EXPECT_EQ(1, measured) - << "Plane 1 glyphs should have a length of 1 char16_t"; -} - -TEST_F(UnicodeTest, UTF8toUTF16SurrogateLength) { - ssize_t measured; - - // U+10000 - const uint8_t str[] = { 0xF0, 0x90, 0x80, 0x80 }; - - measured = utf8_to_utf16_length(str, sizeof(str)); - EXPECT_EQ(2, measured) - << "Surrogate pairs should have a length of 2 char16_t"; -} - -TEST_F(UnicodeTest, UTF8toUTF16TruncatedUTF8) { - ssize_t measured; - - // Truncated U+2323 SMILE - // U+2323 SMILE - const uint8_t str[] = { 0xE2, 0x8C }; - - measured = utf8_to_utf16_length(str, sizeof(str)); - EXPECT_EQ(-1, measured) - << "Truncated UTF-8 should return -1 to indicate invalid"; -} - -TEST_F(UnicodeTest, UTF8toUTF16Normal) { - const uint8_t str[] = { - 0x30, // U+0030, 1 UTF-16 character - 0xC4, 0x80, // U+0100, 1 UTF-16 character - 0xE2, 0x8C, 0xA3, // U+2323, 1 UTF-16 character - 0xF0, 0x90, 0x80, 0x80, // U+10000, 2 UTF-16 character - }; - - char16_t output[1 + 1 + 1 + 2 + 1]; // Room for NULL - - utf8_to_utf16(str, sizeof(str), output); - - EXPECT_EQ(0x0030, output[0]) - << "should be U+0030"; - EXPECT_EQ(0x0100, output[1]) - << "should be U+0100"; - EXPECT_EQ(0x2323, output[2]) - << "should be U+2323"; - EXPECT_EQ(0xD800, output[3]) - << "should be first half of surrogate U+10000"; - EXPECT_EQ(0xDC00, output[4]) - << "should be second half of surrogate U+10000"; - EXPECT_EQ(NULL, output[5]) - << "should be NULL terminated"; -} - -} diff --git a/libs/utils/tests/Vector_test.cpp b/libs/utils/tests/Vector_test.cpp deleted file mode 100644 index d29c054..0000000 --- a/libs/utils/tests/Vector_test.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "Vector_test" - -#include <utils/Vector.h> -#include <cutils/log.h> -#include <gtest/gtest.h> -#include <unistd.h> - -namespace android { - -class VectorTest : public testing::Test { -protected: - virtual void SetUp() { - } - - virtual void TearDown() { - } - -public: -}; - - -TEST_F(VectorTest, CopyOnWrite_CopyAndAddElements) { - - Vector<int> vector; - Vector<int> other; - vector.setCapacity(8); - - vector.add(1); - vector.add(2); - vector.add(3); - - EXPECT_EQ(vector.size(), 3); - - // copy the vector - other = vector; - - EXPECT_EQ(other.size(), 3); - - // add an element to the first vector - vector.add(4); - - // make sure the sizes are correct - EXPECT_EQ(vector.size(), 4); - EXPECT_EQ(other.size(), 3); - - // add an element to the copy - other.add(5); - - // make sure the sizes are correct - EXPECT_EQ(vector.size(), 4); - EXPECT_EQ(other.size(), 4); - - // make sure the content of both vectors are correct - EXPECT_EQ(vector[3], 4); - EXPECT_EQ(other[3], 5); -} - - -} // namespace android diff --git a/libs/utils/tests/ZipFileRO_test.cpp b/libs/utils/tests/ZipFileRO_test.cpp deleted file mode 100644 index 7a1d0bd..0000000 --- a/libs/utils/tests/ZipFileRO_test.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "ZipFileRO_test" -#include <utils/Log.h> -#include <utils/ZipFileRO.h> - -#include <gtest/gtest.h> - -#include <fcntl.h> -#include <string.h> - -namespace android { - -class ZipFileROTest : public testing::Test { -protected: - virtual void SetUp() { - } - - virtual void TearDown() { - } -}; - -TEST_F(ZipFileROTest, ZipTimeConvertSuccess) { - struct tm t; - - // 2011-06-29 14:40:40 - long when = 0x3EDD7514; - - ZipFileRO::zipTimeToTimespec(when, &t); - - EXPECT_EQ(2011, t.tm_year + 1900) - << "Year was improperly converted."; - - EXPECT_EQ(6, t.tm_mon) - << "Month was improperly converted."; - - EXPECT_EQ(29, t.tm_mday) - << "Day was improperly converted."; - - EXPECT_EQ(14, t.tm_hour) - << "Hour was improperly converted."; - - EXPECT_EQ(40, t.tm_min) - << "Minute was improperly converted."; - - EXPECT_EQ(40, t.tm_sec) - << "Second was improperly converted."; -} - -} diff --git a/opengl/include/EGL/eglext.h b/opengl/include/EGL/eglext.h index 6c505ed..3b2984a 100644 --- a/opengl/include/EGL/eglext.h +++ b/opengl/include/EGL/eglext.h @@ -494,6 +494,14 @@ typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYNATIVEPIXMAPNVPROC)(EGLDisplay dpy, #define EGL_FRAMEBUFFER_TARGET_ANDROID 0x3147 #endif +#ifndef EGL_ANDROID_image_crop +#define EGL_ANDROID_image_crop 1 +#define EGL_IMAGE_CROP_LEFT_ANDROID 0x3148 +#define EGL_IMAGE_CROP_TOP_ANDROID 0x3149 +#define EGL_IMAGE_CROP_RIGHT_ANDROID 0x314A +#define EGL_IMAGE_CROP_BOTTOM_ANDROID 0x314B +#endif + #ifndef EGL_ANDROID_blob_cache #define EGL_ANDROID_blob_cache 1 typedef khronos_ssize_t EGLsizeiANDROID; diff --git a/opengl/libagl/egl.cpp b/opengl/libagl/egl.cpp index 0ed5727..bbbda76 100644 --- a/opengl/libagl/egl.cpp +++ b/opengl/libagl/egl.cpp @@ -2051,8 +2051,6 @@ EGLImageKHR eglCreateImageKHR(EGLDisplay dpy, EGLContext ctx, EGLenum target, case HAL_PIXEL_FORMAT_RGB_888: case HAL_PIXEL_FORMAT_RGB_565: case HAL_PIXEL_FORMAT_BGRA_8888: - case HAL_PIXEL_FORMAT_RGBA_5551: - case HAL_PIXEL_FORMAT_RGBA_4444: break; default: return setError(EGL_BAD_PARAMETER, EGL_NO_IMAGE_KHR); diff --git a/opengl/libs/Android.mk b/opengl/libs/Android.mk index b4756dd..528b983 100644 --- a/opengl/libs/Android.mk +++ b/opengl/libs/Android.mk @@ -48,16 +48,9 @@ LOCAL_CFLAGS += -DEGL_TRACE=1 ifeq ($(BOARD_ALLOW_EGL_HIBERNATION),true) LOCAL_CFLAGS += -DBOARD_ALLOW_EGL_HIBERNATION endif - -ifeq ($(TARGET_BOARD_PLATFORM),msm7k) - LOCAL_CFLAGS += -DADRENO130=1 -endif - -ifeq ($(TARGET_BOARD_PLATFORM), s5pc110) - # see Loader.cpp for details - LOCAL_CFLAGS += -DSYSTEMUI_PBSIZE_HACK=1 +ifeq ($(TARGET_BOARD_PLATFORM), omap4) + LOCAL_CFLAGS += -DWORKAROUND_BUG_10194508=1 endif - ifneq ($(MAX_EGL_CACHE_ENTRY_SIZE),) LOCAL_CFLAGS += -DMAX_EGL_CACHE_ENTRY_SIZE=$(MAX_EGL_CACHE_ENTRY_SIZE) endif diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp index 56550b3..02914a0 100644 --- a/opengl/libs/EGL/Loader.cpp +++ b/opengl/libs/EGL/Loader.cpp @@ -1,16 +1,16 @@ -/* +/* ** Copyright 2007, 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 + ** 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 + ** 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 + ** 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. */ @@ -21,6 +21,7 @@ #include <errno.h> #include <dlfcn.h> #include <limits.h> +#include <dirent.h> #include <cutils/log.h> #include <cutils/properties.h> @@ -38,10 +39,26 @@ namespace android { /* - * EGL drivers are called - * - * /system/lib/egl/lib{[EGL|GLESv1_CM|GLESv2] | GLES}_$TAG.so - * + * EGL userspace drivers must be provided either: + * - as a single library: + * /vendor/lib/egl/libGLES.so + * + * - as separate libraries: + * /vendor/lib/egl/libEGL.so + * /vendor/lib/egl/libGLESv1_CM.so + * /vendor/lib/egl/libGLESv2.so + * + * The software renderer for the emulator must be provided as a single + * library at: + * + * /system/lib/egl/libGLES_android.so + * + * + * For backward compatibility and to facilitate the transition to + * this new naming scheme, the loader will additionally look for: + * + * /{vendor|system}/lib/egl/lib{GLES | [EGL|GLESv1_CM|GLESv2]}_*.so + * */ ANDROID_SINGLETON_STATIC_INSTANCE( Loader ) @@ -99,14 +116,14 @@ static char const * getProcessCmdline() { // ---------------------------------------------------------------------------- -Loader::driver_t::driver_t(void* gles) +Loader::driver_t::driver_t(void* gles) { dso[0] = gles; for (size_t i=1 ; i<NELEM(dso) ; i++) dso[i] = 0; } -Loader::driver_t::~driver_t() +Loader::driver_t::~driver_t() { for (size_t i=0 ; i<NELEM(dso) ; i++) { if (dso[i]) { @@ -137,41 +154,10 @@ status_t Loader::driver_t::set(void* hnd, int32_t api) // ---------------------------------------------------------------------------- Loader::Loader() -{ - char line[256]; - char tag[256]; - - /* Special case for GLES emulation */ - if (checkGlesEmulationStatus() == 0) { - ALOGD("Emulator without GPU support detected. " - "Fallback to software renderer."); - mDriverTag.setTo("android"); - return; - } - - /* Otherwise, use egl.cfg */ - FILE* cfg = fopen("/system/lib/egl/egl.cfg", "r"); - if (cfg == NULL) { - // default config - ALOGD("egl.cfg not found, using default config"); - mDriverTag.setTo("android"); - } else { - while (fgets(line, 256, cfg)) { - int dpy, impl; - if (sscanf(line, "%u %u %s", &dpy, &impl, tag) == 3) { - //ALOGD(">>> %u %u %s", dpy, impl, tag); - // We only load the h/w accelerated implementation - if (tag != String8("android")) { - mDriverTag = tag; - } - } - } - fclose(cfg); - } + : getProcAddress(NULL) { } -Loader::~Loader() -{ +Loader::~Loader() { GLTrace_stop(); } @@ -185,30 +171,24 @@ void* Loader::open(egl_connection_t* cnx) { void* dso; driver_t* hnd = 0; - - char const* tag = mDriverTag.string(); - if (tag) { - dso = load_driver("GLES", tag, cnx, EGL | GLESv1_CM | GLESv2); + + dso = load_driver("GLES", cnx, EGL | GLESv1_CM | GLESv2); + if (dso) { + hnd = new driver_t(dso); + } else { + // Always load EGL first + dso = load_driver("EGL", cnx, EGL); if (dso) { hnd = new driver_t(dso); - } else { - // Always load EGL first - dso = load_driver("EGL", tag, cnx, EGL); - if (dso) { - hnd = new driver_t(dso); - // TODO: make this more automated - hnd->set( load_driver("GLESv1_CM", tag, cnx, GLESv1_CM), GLESv1_CM ); - hnd->set( load_driver("GLESv2", tag, cnx, GLESv2), GLESv2 ); - } + hnd->set( load_driver("GLESv1_CM", cnx, GLESv1_CM), GLESv1_CM ); + hnd->set( load_driver("GLESv2", cnx, GLESv2), GLESv2 ); } } - LOG_FATAL_IF(!index && !hnd, - "couldn't find the default OpenGL ES implementation " - "for default display"); + LOG_ALWAYS_FATAL_IF(!hnd, "couldn't find an OpenGL ES implementation"); - cnx->libGles2 = load_wrapper("system/lib/libGLESv2.so"); - cnx->libGles1 = load_wrapper("system/lib/libGLESv1_CM.so"); + cnx->libGles2 = load_wrapper("/system/lib/libGLESv2.so"); + cnx->libGles1 = load_wrapper("/system/lib/libGLESv1_CM.so"); LOG_ALWAYS_FATAL_IF(!cnx->libGles2 || !cnx->libGles1, "couldn't load system OpenGL ES wrapper libraries"); @@ -222,16 +202,16 @@ status_t Loader::close(void* driver) return NO_ERROR; } -void Loader::init_api(void* dso, - char const * const * api, - __eglMustCastToProperFunctionPointerType* curr, - getProcAddressType getProcAddress) +void Loader::init_api(void* dso, + char const * const * api, + __eglMustCastToProperFunctionPointerType* curr, + getProcAddressType getProcAddress) { const ssize_t SIZE = 256; char scrap[SIZE]; while (*api) { char const * name = *api; - __eglMustCastToProperFunctionPointerType f = + __eglMustCastToProperFunctionPointerType f = (__eglMustCastToProperFunctionPointerType)dlsym(dso, name); if (f == NULL) { // couldn't find the entry-point, use eglGetProcAddress() @@ -278,21 +258,106 @@ void Loader::init_api(void* dso, } } -void *Loader::load_driver(const char* kind, const char *tag, +void *Loader::load_driver(const char* kind, egl_connection_t* cnx, uint32_t mask) { - char driver_absolute_path[PATH_MAX]; - const char* const search1 = "/vendor/lib/egl/lib%s_%s.so"; - const char* const search2 = "/system/lib/egl/lib%s_%s.so"; - - snprintf(driver_absolute_path, PATH_MAX, search1, kind, tag); - if (access(driver_absolute_path, R_OK)) { - snprintf(driver_absolute_path, PATH_MAX, search2, kind, tag); - if (access(driver_absolute_path, R_OK)) { - // this happens often, we don't want to log an error - return 0; + class MatchFile { + public: + static String8 find(const char* kind) { + String8 result; + String8 pattern; + pattern.appendFormat("lib%s", kind); + const char* const searchPaths[] = { + "/vendor/lib/egl", + "/system/lib/egl" + }; + + // first, we search for the exact name of the GLES userspace + // driver in both locations. + // i.e.: + // libGLES.so, or: + // libEGL.so, libGLESv1_CM.so, libGLESv2.so + + for (size_t i=0 ; i<NELEM(searchPaths) ; i++) { + if (find(result, pattern, searchPaths[i], true)) { + return result; + } + } + + // for compatibility with the old "egl.cfg" naming convention + // we look for files that match: + // libGLES_*.so, or: + // libEGL_*.so, libGLESv1_CM_*.so, libGLESv2_*.so + + pattern.append("_"); + for (size_t i=0 ; i<NELEM(searchPaths) ; i++) { + if (find(result, pattern, searchPaths[i], false)) { + return result; + } + } + + // we didn't find the driver. gah. + result.clear(); + return result; + } + + private: + static bool find(String8& result, + const String8& pattern, const char* const search, bool exact) { + + // in the emulator case, we just return the hardcoded name + // of the software renderer. + if (checkGlesEmulationStatus() == 0) { + ALOGD("Emulator without GPU support detected. " + "Fallback to software renderer."); + result.setTo("/system/lib/egl/libGLES_android.so"); + return true; + } + + if (exact) { + String8 absolutePath; + absolutePath.appendFormat("%s/%s.so", search, pattern.string()); + if (!access(absolutePath.string(), R_OK)) { + result = absolutePath; + return true; + } + return false; + } + + DIR* d = opendir(search); + if (d != NULL) { + struct dirent cur; + struct dirent* e; + while (readdir_r(d, &cur, &e) == 0 && e) { + if (e->d_type == DT_DIR) { + continue; + } + if (!strcmp(e->d_name, "libGLES_android.so")) { + // always skip the software renderer + continue; + } + if (strstr(e->d_name, pattern.string()) == e->d_name) { + if (!strcmp(e->d_name + strlen(e->d_name) - 3, ".so")) { + result.clear(); + result.appendFormat("%s/%s", search, e->d_name); + closedir(d); + return true; + } + } + } + closedir(d); + } + return false; } + }; + + + String8 absolutePath = MatchFile::find(kind); + if (absolutePath.isEmpty()) { + // this happens often, we don't want to log an error + return 0; } + const char* const driver_absolute_path = absolutePath.string(); void* dso = dlopen(driver_absolute_path, RTLD_NOW | RTLD_LOCAL); if (dso == 0) { @@ -306,45 +371,16 @@ void *Loader::load_driver(const char* kind, const char *tag, if (mask & EGL) { getProcAddress = (getProcAddressType)dlsym(dso, "eglGetProcAddress"); - ALOGE_IF(!getProcAddress, + ALOGE_IF(!getProcAddress, "can't find eglGetProcAddress() in %s", driver_absolute_path); -#ifdef SYSTEMUI_PBSIZE_HACK -#warning "SYSTEMUI_PBSIZE_HACK enabled" - /* - * TODO: replace SYSTEMUI_PBSIZE_HACK by something less hackish - * - * Here we adjust the PB size from its default value to 512KB which - * is the minimum acceptable for the systemui process. - * We do this on low-end devices only because it allows us to enable - * h/w acceleration in the systemui process while keeping the - * memory usage down. - * - * Obviously, this is the wrong place and wrong way to make this - * adjustment, but at the time of this writing this was the safest - * solution. - */ - const char *cmdline = getProcessCmdline(); - if (strstr(cmdline, "systemui")) { - void *imgegl = dlopen("/vendor/lib/libIMGegl.so", RTLD_LAZY); - if (imgegl) { - unsigned int *PVRDefaultPBS = - (unsigned int *)dlsym(imgegl, "PVRDefaultPBS"); - if (PVRDefaultPBS) { - ALOGD("setting default PBS to 512KB, was %d KB", *PVRDefaultPBS / 1024); - *PVRDefaultPBS = 512*1024; - } - } - } -#endif - egl_t* egl = &cnx->egl; __eglMustCastToProperFunctionPointerType* curr = (__eglMustCastToProperFunctionPointerType*)egl; char const * const * api = egl_names; while (*api) { char const * name = *api; - __eglMustCastToProperFunctionPointerType f = + __eglMustCastToProperFunctionPointerType f = (__eglMustCastToProperFunctionPointerType)dlsym(dso, name); if (f == NULL) { // couldn't find the entry-point, use eglGetProcAddress() @@ -357,7 +393,7 @@ void *Loader::load_driver(const char* kind, const char *tag, api++; } } - + if (mask & GLESv1_CM) { init_api(dso, gl_names, (__eglMustCastToProperFunctionPointerType*) @@ -371,7 +407,7 @@ void *Loader::load_driver(const char* kind, const char *tag, &cnx->hooks[egl_connection_t::GLESv2_INDEX]->gl, getProcAddress); } - + return dso; } diff --git a/opengl/libs/EGL/Loader.h b/opengl/libs/EGL/Loader.h index 30773cb..8cefe32 100644 --- a/opengl/libs/EGL/Loader.h +++ b/opengl/libs/EGL/Loader.h @@ -52,7 +52,6 @@ class Loader : public Singleton<Loader> void* dso[3]; }; - String8 mDriverTag; getProcAddressType getProcAddress; public: @@ -63,7 +62,7 @@ public: private: Loader(); - void *load_driver(const char* kind, const char *tag, egl_connection_t* cnx, uint32_t mask); + void *load_driver(const char* kind, egl_connection_t* cnx, uint32_t mask); static __attribute__((noinline)) void init_api(void* dso, diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp index 6ac8724..f759e6b 100644 --- a/opengl/libs/EGL/egl.cpp +++ b/opengl/libs/EGL/egl.cpp @@ -230,9 +230,6 @@ static int gl_no_context() { static void early_egl_init(void) { -#if !USE_FAST_TLS_KEY - pthread_key_create(&gGLWrapperKey, NULL); -#endif #if EGL_TRACE pthread_key_create(&gGLTraceKey, NULL); initEglTraceLevel(); @@ -334,6 +331,11 @@ EGLBoolean egl_init_drivers() { void gl_unimplemented() { ALOGE("called unimplemented OpenGL ES API"); + char value[PROPERTY_VALUE_MAX]; + property_get("debug.egl.callstack", value, "0"); + if (atoi(value)) { + CallStack stack(LOG_TAG); + } } void gl_noop() { @@ -341,42 +343,11 @@ void gl_noop() { // ---------------------------------------------------------------------------- -#if USE_FAST_TLS_KEY - -// We have a dedicated TLS slot in bionic -static inline gl_hooks_t const * volatile * get_tls_hooks() { - volatile void *tls_base = __get_tls(); - gl_hooks_t const * volatile * tls_hooks = - reinterpret_cast<gl_hooks_t const * volatile *>(tls_base); - return tls_hooks; -} - void setGlThreadSpecific(gl_hooks_t const *value) { gl_hooks_t const * volatile * tls_hooks = get_tls_hooks(); tls_hooks[TLS_SLOT_OPENGL_API] = value; } -gl_hooks_t const* getGlThreadSpecific() { - gl_hooks_t const * volatile * tls_hooks = get_tls_hooks(); - gl_hooks_t const* hooks = tls_hooks[TLS_SLOT_OPENGL_API]; - if (hooks) return hooks; - return &gHooksNoContext; -} - -#else - -void setGlThreadSpecific(gl_hooks_t const *value) { - pthread_setspecific(gGLWrapperKey, value); -} - -gl_hooks_t const* getGlThreadSpecific() { - gl_hooks_t const* hooks = static_cast<gl_hooks_t*>(pthread_getspecific(gGLWrapperKey)); - if (hooks) return hooks; - return &gHooksNoContext; -} - -#endif - // ---------------------------------------------------------------------------- // GL / EGL hooks // ---------------------------------------------------------------------------- diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp index f39c386..0cc5265 100644 --- a/opengl/libs/EGL/eglApi.cpp +++ b/opengl/libs/EGL/eglApi.cpp @@ -49,6 +49,10 @@ using namespace android; +// This extension has not been ratified yet, so can't be shipped. +// Implementation is incomplete and untested. +#define ENABLE_EGL_KHR_GL_COLORSPACE 0 + // ---------------------------------------------------------------------------- namespace android { @@ -59,21 +63,32 @@ struct extention_map_t { }; /* - * This is the list of EGL extensions exposed to applications, - * some of them are mandatory because used by the ANDROID system. + * This is the list of EGL extensions exposed to applications. + * + * Some of them (gBuiltinExtensionString) are implemented entirely in this EGL + * wrapper and are always available. * - * Mandatory extensions are required per the CDD and not explicitly - * checked during EGL initialization. the system *assumes* these extensions - * are present. the system may not function properly if some mandatory - * extensions are missing. + * The rest (gExtensionString) depend on support in the EGL driver, and are + * only available if the driver supports them. However, some of these must be + * supported because they are used by the Android system itself; these are + * listd as mandatory below and are required by the CDD. The system *assumes* + * the mandatory extensions are present and may not function properly if some + * are missing. * - * NOTE: gExtensionString MUST have a single space as the last character. + * NOTE: Both strings MUST have a single space as the last character. */ +extern char const * const gBuiltinExtensionString = + "EGL_KHR_get_all_proc_addresses " + "EGL_ANDROID_presentation_time " + ; extern char const * const gExtensionString = "EGL_KHR_image " // mandatory "EGL_KHR_image_base " // mandatory "EGL_KHR_image_pixmap " "EGL_KHR_lock_surface " +#if (ENABLE_EGL_KHR_GL_COLORSPACE != 0) + "EGL_KHR_gl_colorspace " +#endif "EGL_KHR_gl_texture_2D_image " "EGL_KHR_gl_texture_cubemap_image " "EGL_KHR_gl_renderbuffer_image " @@ -84,7 +99,7 @@ extern char const * const gExtensionString = "EGL_NV_system_time " "EGL_ANDROID_image_native_buffer " // mandatory "EGL_KHR_wait_sync " // strongly recommended - "EGL_ANDROID_presentation_time " + "EGL_ANDROID_recordable " // mandatory ; // extensions not exposed to applications but used by the ANDROID system @@ -92,8 +107,7 @@ extern char const * const gExtensionString = // "EGL_IMG_hibernate_process " // optional // "EGL_ANDROID_native_fence_sync " // strongly recommended // "EGL_ANDROID_framebuffer_target " // mandatory for HWC 1.1 -// "EGL_ANDROID_recordable " // mandatory - +// "EGL_ANDROID_image_crop " // optional /* * EGL Extensions entry-points exposed to 3rd party applications @@ -358,6 +372,31 @@ EGLBoolean eglGetConfigAttrib(EGLDisplay dpy, EGLConfig config, // surfaces // ---------------------------------------------------------------------------- +// The EGL_KHR_gl_colorspace spec hasn't been ratified yet, so these haven't +// been added to the Khronos egl.h. +#define EGL_GL_COLORSPACE_KHR EGL_VG_COLORSPACE +#define EGL_GL_COLORSPACE_SRGB_KHR EGL_VG_COLORSPACE_sRGB +#define EGL_GL_COLORSPACE_LINEAR_KHR EGL_VG_COLORSPACE_LINEAR + +// Turn linear formats into corresponding sRGB formats when colorspace is +// EGL_GL_COLORSPACE_SRGB_KHR, or turn sRGB formats into corresponding linear +// formats when colorspace is EGL_GL_COLORSPACE_LINEAR_KHR. In any cases where +// the modification isn't possible, the original format is returned. +static int modifyFormatColorspace(int fmt, EGLint colorspace) { + if (colorspace == EGL_GL_COLORSPACE_LINEAR_KHR) { + switch (fmt) { + case HAL_PIXEL_FORMAT_sRGB_A_8888: return HAL_PIXEL_FORMAT_RGBA_8888; + case HAL_PIXEL_FORMAT_sRGB_X_8888: return HAL_PIXEL_FORMAT_RGBX_8888; + } + } else if (colorspace == EGL_GL_COLORSPACE_SRGB_KHR) { + switch (fmt) { + case HAL_PIXEL_FORMAT_RGBA_8888: return HAL_PIXEL_FORMAT_sRGB_A_8888; + case HAL_PIXEL_FORMAT_RGBX_8888: return HAL_PIXEL_FORMAT_sRGB_X_8888; + } + } + return fmt; +} + EGLSurface eglCreateWindowSurface( EGLDisplay dpy, EGLConfig config, NativeWindowType window, const EGLint *attrib_list) @@ -368,7 +407,6 @@ EGLSurface eglCreateWindowSurface( EGLDisplay dpy, EGLConfig config, egl_display_ptr dp = validate_display_connection(dpy, cnx); if (dp) { EGLDisplay iDpy = dp->disp.dpy; - EGLint format; if (native_window_api_connect(window, NATIVE_WINDOW_API_EGL) != OK) { ALOGE("EGLNativeWindowType %p already connected to another API", @@ -376,19 +414,90 @@ EGLSurface eglCreateWindowSurface( EGLDisplay dpy, EGLConfig config, return setError(EGL_BAD_ALLOC, EGL_NO_SURFACE); } - // set the native window's buffers format to match this config - if (cnx->egl.eglGetConfigAttrib(iDpy, - config, EGL_NATIVE_VISUAL_ID, &format)) { - if (format != 0) { - int err = native_window_set_buffers_format(window, format); - if (err != 0) { - ALOGE("error setting native window pixel format: %s (%d)", - strerror(-err), err); - native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL); - return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE); + // Set the native window's buffers format to match what this config requests. + // Whether to use sRGB gamma is not part of the EGLconfig, but is part + // of our native format. So if sRGB gamma is requested, we have to + // modify the EGLconfig's format before setting the native window's + // format. +#if WORKAROUND_BUG_10194508 +#warning "WORKAROUND_10194508 enabled" + EGLint format; + if (!cnx->egl.eglGetConfigAttrib(iDpy, config, EGL_NATIVE_VISUAL_ID, + &format)) { + ALOGE("eglGetConfigAttrib(EGL_NATIVE_VISUAL_ID) failed: %#x", + eglGetError()); + format = 0; + } + if (attrib_list) { + for (const EGLint* attr = attrib_list; *attr != EGL_NONE; + attr += 2) { + if (*attr == EGL_GL_COLORSPACE_KHR && + dp->haveExtension("EGL_KHR_gl_colorspace")) { + if (ENABLE_EGL_KHR_GL_COLORSPACE) { + format = modifyFormatColorspace(format, *(attr+1)); + } else { + // Normally we'd pass through unhandled attributes to + // the driver. But in case the driver implements this + // extension but we're disabling it, we want to prevent + // it getting through -- support will be broken without + // our help. + ALOGE("sRGB window surfaces not supported"); + return setError(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE); + } + } + } + } +#else + // by default, just pick RGBA_8888 + EGLint format = HAL_PIXEL_FORMAT_RGBA_8888; + + EGLint a = 0; + cnx->egl.eglGetConfigAttrib(iDpy, config, EGL_ALPHA_SIZE, &a); + if (a > 0) { + // alpha-channel requested, there's really only one suitable format + format = HAL_PIXEL_FORMAT_RGBA_8888; + } else { + EGLint r, g, b; + r = g = b = 0; + cnx->egl.eglGetConfigAttrib(iDpy, config, EGL_RED_SIZE, &r); + cnx->egl.eglGetConfigAttrib(iDpy, config, EGL_GREEN_SIZE, &g); + cnx->egl.eglGetConfigAttrib(iDpy, config, EGL_BLUE_SIZE, &b); + EGLint colorDepth = r + g + b; + if (colorDepth <= 16) { + format = HAL_PIXEL_FORMAT_RGB_565; + } else { + format = HAL_PIXEL_FORMAT_RGBX_8888; + } + } + + // now select a corresponding sRGB format if needed + if (attrib_list && dp->haveExtension("EGL_KHR_gl_colorspace")) { + for (const EGLint* attr = attrib_list; *attr != EGL_NONE; attr += 2) { + if (*attr == EGL_GL_COLORSPACE_KHR) { + if (ENABLE_EGL_KHR_GL_COLORSPACE) { + format = modifyFormatColorspace(format, *(attr+1)); + } else { + // Normally we'd pass through unhandled attributes to + // the driver. But in case the driver implements this + // extension but we're disabling it, we want to prevent + // it getting through -- support will be broken without + // our help. + ALOGE("sRGB window surfaces not supported"); + return setError(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE); + } } } } +#endif + if (format != 0) { + int err = native_window_set_buffers_format(window, format); + if (err != 0) { + ALOGE("error setting native window pixel format: %s (%d)", + strerror(-err), err); + native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL); + return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE); + } + } // the EGL spec requires that a new EGLSurface default to swap interval // 1, so explicitly set that on the window here. @@ -499,11 +608,6 @@ void EGLAPI eglBeginFrame(EGLDisplay dpy, EGLSurface surface) { setError(EGL_BAD_SURFACE, EGL_FALSE); return; } - - int64_t timestamp = systemTime(SYSTEM_TIME_MONOTONIC); - - egl_surface_t const * const s = get_surface(surface); - native_window_set_buffers_timestamp(s->win.get(), timestamp); } // ---------------------------------------------------------------------------- @@ -550,12 +654,6 @@ EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config, GLTrace_eglCreateContext(version, c); #endif return c; - } else { - EGLint error = eglGetError(); - ALOGE_IF(error == EGL_SUCCESS, - "eglCreateContext(%p, %p, %p, %p) returned EGL_NO_CONTEXT " - "but no EGL error!", - dpy, config, share_list, attrib_list); } } return EGL_NO_CONTEXT; @@ -673,7 +771,8 @@ EGLBoolean eglMakeCurrent( EGLDisplay dpy, EGLSurface draw, } } else { // this will ALOGE the error - result = setError(c->cnx->egl.eglGetError(), EGL_FALSE); + egl_connection_t* const cnx = &gEGLImpl; + result = setError(cnx->egl.eglGetError(), EGL_FALSE); } return result; } @@ -866,9 +965,7 @@ __eglMustCastToProperFunctionPointerType eglGetProcAddress(const char *procname) } if (found) { -#if USE_FAST_TLS_KEY addr = gExtensionForwarders[slot]; -#endif sGLExtentionMap.add(name, addr); sGLExtentionSlot++; } @@ -1199,6 +1296,11 @@ EGLBoolean eglReleaseThread(void) { clearError(); +#if EGL_TRACE + if (getEGLDebugLevel() > 0) + GLTrace_eglReleaseThread(); +#endif + // If there is context bound to the thread, release it egl_display_t::loseCurrent(get_context(getContext())); @@ -1206,12 +1308,7 @@ EGLBoolean eglReleaseThread(void) if (cnx->dso && cnx->egl.eglReleaseThread) { cnx->egl.eglReleaseThread(); } - egl_tls_t::clearTLS(); -#if EGL_TRACE - if (getEGLDebugLevel() > 0) - GLTrace_eglReleaseThread(); -#endif return EGL_TRUE; } diff --git a/opengl/libs/EGL/egl_cache.cpp b/opengl/libs/EGL/egl_cache.cpp index 03397a9..b0798a1 100644 --- a/opengl/libs/EGL/egl_cache.cpp +++ b/opengl/libs/EGL/egl_cache.cpp @@ -126,11 +126,8 @@ void egl_cache_t::initialize(egl_display_t *display) { void egl_cache_t::terminate() { Mutex::Autolock lock(mMutex); - if (mBlobCache != NULL) { - saveBlobCacheLocked(); - mBlobCache = NULL; - } - mInitialized = false; + saveBlobCacheLocked(); + mBlobCache = NULL; } void egl_cache_t::setBlob(const void* key, EGLsizeiANDROID keySize, @@ -218,7 +215,7 @@ static uint32_t crc32c(const uint8_t* buf, size_t len) { } void egl_cache_t::saveBlobCacheLocked() { - if (mFilename.length() > 0) { + if (mFilename.length() > 0 && mBlobCache != NULL) { size_t cacheSize = mBlobCache->getFlattenedSize(); size_t headerSize = cacheFileHeaderSize; const char* fname = mFilename.string(); @@ -256,8 +253,7 @@ void egl_cache_t::saveBlobCacheLocked() { return; } - status_t err = mBlobCache->flatten(buf + headerSize, cacheSize, NULL, - 0); + status_t err = mBlobCache->flatten(buf + headerSize, cacheSize); if (err != OK) { ALOGE("error writing cache contents: %s (%d)", strerror(-err), -err); @@ -338,8 +334,7 @@ void egl_cache_t::loadBlobCacheLocked() { return; } - status_t err = mBlobCache->unflatten(buf + headerSize, cacheSize, NULL, - 0); + status_t err = mBlobCache->unflatten(buf + headerSize, cacheSize); if (err != OK) { ALOGE("error reading cache contents: %s (%d)", strerror(-err), -err); diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp index 1955904..26240f1 100644 --- a/opengl/libs/EGL/egl_display.cpp +++ b/opengl/libs/EGL/egl_display.cpp @@ -1,16 +1,16 @@ -/* +/* ** Copyright 2007, 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 + ** 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 + ** 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 + ** 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. */ @@ -35,6 +35,7 @@ static char const * const sVendorString = "Android"; static char const * const sVersionString = "1.4 Android META-EGL"; static char const * const sClientApiString = "OpenGL_ES"; +extern char const * const gBuiltinExtensionString; extern char const * const gExtensionString; extern void initEglTraceLevel(); @@ -43,6 +44,16 @@ extern void setGLHooksThreadSpecific(gl_hooks_t const *value); // ---------------------------------------------------------------------------- +static bool findExtension(const char* exts, const char* name, size_t nameLen) { + if (exts) { + const char* match = strstr(exts, name); + if (match && (match[nameLen] == '\0' || match[nameLen] == ' ')) { + return true; + } + } + return false; +} + egl_display_t egl_display_t::sDisplay[NUM_DISPLAYS]; egl_display_t::egl_display_t() : @@ -139,21 +150,6 @@ EGLBoolean egl_display_t::initialize(EGLint *major, EGLint *minor) { cnx->major = -1; cnx->minor = -1; if (cnx->dso) { - -#if defined(ADRENO130) -#warning "Adreno-130 eglInitialize() workaround" - /* - * The ADRENO 130 driver returns a different EGLDisplay each time - * eglGetDisplay() is called, but also makes the EGLDisplay invalid - * after eglTerminate() has been called, so that eglInitialize() - * cannot be called again. Therefore, we need to make sure to call - * eglGetDisplay() before calling eglInitialize(); - */ - if (i == IMPL_HARDWARE) { - disp[i].dpy = cnx->egl.eglGetDisplay(EGL_DEFAULT_DISPLAY); - } -#endif - EGLDisplay idpy = disp.dpy; if (cnx->egl.eglInitialize(idpy, &cnx->major, &cnx->minor)) { //ALOGD("initialized dpy=%p, ver=%d.%d, cnx=%p", @@ -183,7 +179,7 @@ EGLBoolean egl_display_t::initialize(EGLint *major, EGLint *minor) { mVersionString.setTo(sVersionString); mClientApiString.setTo(sClientApiString); - // we only add extensions that exist in the implementation + mExtensionString.setTo(gBuiltinExtensionString); char const* start = gExtensionString; char const* end; do { @@ -195,14 +191,9 @@ EGLBoolean egl_display_t::initialize(EGLint *major, EGLint *minor) { if (len) { // NOTE: we could avoid the copy if we had strnstr. const String8 ext(start, len); - // now look for this extension - if (disp.queryString.extensions) { - // if we find it, add this extension string to our list - // (and don't forget the space) - const char* match = strstr(disp.queryString.extensions, ext.string()); - if (match && (match[len] == ' ' || match[len] == 0)) { - mExtensionString.append(start, len+1); - } + if (findExtension(disp.queryString.extensions, ext.string(), + len)) { + mExtensionString.append(start, len+1); } } // process the next extension string, and skip the space. @@ -366,6 +357,13 @@ EGLBoolean egl_display_t::makeCurrent(egl_context_t* c, egl_context_t* cur_c, return result; } +bool egl_display_t::haveExtension(const char* name, size_t nameLen) const { + if (!nameLen) { + nameLen = strlen(name); + } + return findExtension(mExtensionString.string(), name, nameLen); +} + // ---------------------------------------------------------------------------- bool egl_display_t::HibernationMachine::incWakeCount(WakeRefStrength strength) { diff --git a/opengl/libs/EGL/egl_display.h b/opengl/libs/EGL/egl_display.h index 754085c..87f27f8 100644 --- a/opengl/libs/EGL/egl_display.h +++ b/opengl/libs/EGL/egl_display.h @@ -99,6 +99,8 @@ public: char const * getClientApiString() const { return mClientApiString.string(); } char const * getExtensionString() const { return mExtensionString.string(); } + bool haveExtension(const char* name, size_t nameLen = 0) const; + inline uint32_t getRefsCount() const { return refs; } struct strings_t { diff --git a/opengl/libs/EGL/egl_tls.cpp b/opengl/libs/EGL/egl_tls.cpp index 52312a2..f3739aa 100644 --- a/opengl/libs/EGL/egl_tls.cpp +++ b/opengl/libs/EGL/egl_tls.cpp @@ -29,8 +29,8 @@ namespace android { -pthread_key_t egl_tls_t::sKey = -1; -pthread_mutex_t egl_tls_t::sLockKey = PTHREAD_MUTEX_INITIALIZER; +pthread_key_t egl_tls_t::sKey = TLS_KEY_NOT_INITIALIZED; +pthread_once_t egl_tls_t::sOnceKey = PTHREAD_ONCE_INIT; egl_tls_t::egl_tls_t() : error(EGL_SUCCESS), ctx(0), logCallWithNoContext(EGL_TRUE) { @@ -59,12 +59,12 @@ const char *egl_tls_t::egl_strerror(EGLint err) { void egl_tls_t::validateTLSKey() { - if (sKey == -1) { - pthread_mutex_lock(&sLockKey); - if (sKey == -1) - pthread_key_create(&sKey, NULL); - pthread_mutex_unlock(&sLockKey); - } + struct TlsKeyInitializer { + static void create() { + pthread_key_create(&sKey, (void (*)(void*))&eglReleaseThread); + } + }; + pthread_once(&sOnceKey, TlsKeyInitializer::create); } void egl_tls_t::setErrorEtcImpl( @@ -104,11 +104,11 @@ egl_tls_t* egl_tls_t::getTLS() { } void egl_tls_t::clearTLS() { - if (sKey != -1) { + if (sKey != TLS_KEY_NOT_INITIALIZED) { egl_tls_t* tls = (egl_tls_t*)pthread_getspecific(sKey); if (tls) { - delete tls; pthread_setspecific(sKey, 0); + delete tls; } } } @@ -120,10 +120,13 @@ void egl_tls_t::clearError() { } EGLint egl_tls_t::getError() { - if (sKey == -1) + if (sKey == TLS_KEY_NOT_INITIALIZED) { return EGL_SUCCESS; + } egl_tls_t* tls = (egl_tls_t*)pthread_getspecific(sKey); - if (!tls) return EGL_SUCCESS; + if (!tls) { + return EGL_SUCCESS; + } EGLint error = tls->error; tls->error = EGL_SUCCESS; return error; @@ -135,8 +138,9 @@ void egl_tls_t::setContext(EGLContext ctx) { } EGLContext egl_tls_t::getContext() { - if (sKey == -1) + if (sKey == TLS_KEY_NOT_INITIALIZED) { return EGL_NO_CONTEXT; + } egl_tls_t* tls = (egl_tls_t *)pthread_getspecific(sKey); if (!tls) return EGL_NO_CONTEXT; return tls->ctx; diff --git a/opengl/libs/EGL/egl_tls.h b/opengl/libs/EGL/egl_tls.h index 56c5dba..5af4f5b 100644 --- a/opengl/libs/EGL/egl_tls.h +++ b/opengl/libs/EGL/egl_tls.h @@ -30,8 +30,9 @@ namespace android { class DbgContext; class egl_tls_t { + enum { TLS_KEY_NOT_INITIALIZED = -1 }; static pthread_key_t sKey; - static pthread_mutex_t sLockKey; + static pthread_once_t sOnceKey; EGLint error; EGLContext ctx; diff --git a/opengl/libs/EGL/getProcAddress.cpp b/opengl/libs/EGL/getProcAddress.cpp index c160aa0..add2a79 100644 --- a/opengl/libs/EGL/getProcAddress.cpp +++ b/opengl/libs/EGL/getProcAddress.cpp @@ -34,9 +34,7 @@ namespace android { #undef GL_EXTENSION_LIST #undef GET_TLS -#if USE_FAST_TLS_KEY - - #if defined(__arm__) +#if defined(__arm__) #define GET_TLS(reg) "mrc p15, 0, " #reg ", c13, c0, 3 \n" @@ -58,7 +56,7 @@ namespace android { : \ ); - #elif defined(__mips__) +#elif defined(__mips__) #define API_ENTRY(_api) __attribute__((noinline)) _api @@ -88,27 +86,21 @@ namespace android { ext.extensions[_api])) \ : \ ); +#endif - #else - #error Unsupported architecture - #endif - +#if defined(CALL_GL_EXTENSION_API) #define GL_EXTENSION_NAME(_n) __glExtFwd##_n #define GL_EXTENSION(_n) \ void API_ENTRY(GL_EXTENSION_NAME(_n))() { \ CALL_GL_EXTENSION_API(_n); \ } - - #else + #define GL_EXTENSION_NAME(_n) NULL - #define GL_EXTENSION_NAME(_n) NULL - - #define GL_EXTENSION(_n) - - #warning "eglGetProcAddress() partially supported" + #define GL_EXTENSION(_n) + #warning "eglGetProcAddress() partially supported" #endif diff --git a/opengl/libs/GLES2/gl2.cpp b/opengl/libs/GLES2/gl2.cpp index fad2176..3134e56 100644 --- a/opengl/libs/GLES2/gl2.cpp +++ b/opengl/libs/GLES2/gl2.cpp @@ -40,13 +40,11 @@ using namespace android; #undef CALL_GL_API #undef CALL_GL_API_RETURN -#if USE_FAST_TLS_KEY - - #if defined(__arm__) +#if defined(__arm__) && !USE_SLOW_BINDING #define GET_TLS(reg) "mrc p15, 0, " #reg ", c13, c0, 3 \n" - #define API_ENTRY(_api) __attribute__((naked)) _api + #define API_ENTRY(_api) __attribute__((noinline)) _api #define CALL_GL_API(_api, ...) \ asm volatile( \ @@ -54,15 +52,13 @@ using namespace android; "ldr r12, [r12, %[tls]] \n" \ "cmp r12, #0 \n" \ "ldrne pc, [r12, %[api]] \n" \ - "mov r0, #0 \n" \ - "bx lr \n" \ : \ : [tls] "J"(TLS_SLOT_OPENGL_API*4), \ [api] "J"(__builtin_offsetof(gl_hooks_t, gl._api)) \ : \ ); - #elif defined(__mips__) +#elif defined(__mips__) && !USE_SLOW_BINDING #define API_ENTRY(_api) __attribute__((noinline)) _api @@ -94,30 +90,21 @@ using namespace android; : \ ); - #else - - #error Unsupported architecture - - #endif - - #define CALL_GL_API_RETURN(_api, ...) \ - CALL_GL_API(_api, __VA_ARGS__) \ - return 0; // placate gcc's warnings. never reached. - #else #define API_ENTRY(_api) _api #define CALL_GL_API(_api, ...) \ gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; \ - _c->_api(__VA_ARGS__); - - #define CALL_GL_API_RETURN(_api, ...) \ - gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; \ - return _c->_api(__VA_ARGS__) + if (_c) return _c->_api(__VA_ARGS__); #endif +#define CALL_GL_API_RETURN(_api, ...) \ + CALL_GL_API(_api, __VA_ARGS__) \ + return 0; + + extern "C" { #include "gl3_api.in" @@ -139,7 +126,8 @@ const GLubyte * glGetString(GLenum name) { const GLubyte * ret = egl_get_string_for_current_context(name); if (ret == NULL) { - ret = __glGetString(name); + gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; + ret = _c->glGetString(name); } return ret; } diff --git a/opengl/libs/GLES_CM/gl.cpp b/opengl/libs/GLES_CM/gl.cpp index a5bbdc6..18ef6f9 100644 --- a/opengl/libs/GLES_CM/gl.cpp +++ b/opengl/libs/GLES_CM/gl.cpp @@ -31,9 +31,6 @@ using namespace android; -// set this to 1 for crude GL debugging -#define CHECK_FOR_GL_ERRORS 0 - // ---------------------------------------------------------------------------- // extensions for the framework // ---------------------------------------------------------------------------- @@ -95,13 +92,11 @@ GL_API void GL_APIENTRY glWeightPointerOESBounds(GLint size, GLenum type, #undef CALL_GL_API #undef CALL_GL_API_RETURN -#if USE_FAST_TLS_KEY && !CHECK_FOR_GL_ERRORS - - #if defined(__arm__) +#if defined(__arm__) && !USE_SLOW_BINDING #define GET_TLS(reg) "mrc p15, 0, " #reg ", c13, c0, 3 \n" - #define API_ENTRY(_api) __attribute__((naked)) _api + #define API_ENTRY(_api) __attribute__((noinline)) _api #define CALL_GL_API(_api, ...) \ asm volatile( \ @@ -109,15 +104,13 @@ GL_API void GL_APIENTRY glWeightPointerOESBounds(GLint size, GLenum type, "ldr r12, [r12, %[tls]] \n" \ "cmp r12, #0 \n" \ "ldrne pc, [r12, %[api]] \n" \ - "mov r0, #0 \n" \ - "bx lr \n" \ : \ : [tls] "J"(TLS_SLOT_OPENGL_API*4), \ [api] "J"(__builtin_offsetof(gl_hooks_t, gl._api)) \ : \ ); - #elif defined(__mips__) +#elif defined(__mips__) && !USE_SLOW_BINDING #define API_ENTRY(_api) __attribute__((noinline)) _api @@ -149,43 +142,20 @@ GL_API void GL_APIENTRY glWeightPointerOESBounds(GLint size, GLenum type, : \ ); - #else - #error Unsupported architecture - #endif - - #define CALL_GL_API_RETURN(_api, ...) \ - CALL_GL_API(_api, __VA_ARGS__) \ - return 0; // placate gcc's warnings. never reached. - #else - #if CHECK_FOR_GL_ERRORS - - #define CHECK_GL_ERRORS(_api) \ - do { GLint err = glGetError(); \ - ALOGE_IF(err != GL_NO_ERROR, "%s failed (0x%04X)", #_api, err); \ - } while(false); - - #else - - #define CHECK_GL_ERRORS(_api) do { } while(false); - - #endif - - #define API_ENTRY(_api) _api - #define CALL_GL_API(_api, ...) \ - gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; \ - _c->_api(__VA_ARGS__); \ - CHECK_GL_ERRORS(_api) - - #define CALL_GL_API_RETURN(_api, ...) \ - gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; \ - return _c->_api(__VA_ARGS__) + #define CALL_GL_API(_api, ...) \ + gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; \ + if (_c) return _c->_api(__VA_ARGS__); #endif +#define CALL_GL_API_RETURN(_api, ...) \ + CALL_GL_API(_api, __VA_ARGS__) \ + return 0; + extern "C" { #include "gl_api.in" @@ -202,11 +172,11 @@ extern "C" { extern "C" const GLubyte * __glGetString(GLenum name); -const GLubyte * glGetString(GLenum name) -{ +const GLubyte * glGetString(GLenum name) { const GLubyte * ret = egl_get_string_for_current_context(name); if (ret == NULL) { - ret = __glGetString(name); + gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; + ret = _c->glGetString(name); } return ret; } diff --git a/opengl/libs/GLES_trace/src/gltrace_context.cpp b/opengl/libs/GLES_trace/src/gltrace_context.cpp index 3a8decc..0323e8f 100644 --- a/opengl/libs/GLES_trace/src/gltrace_context.cpp +++ b/opengl/libs/GLES_trace/src/gltrace_context.cpp @@ -32,7 +32,7 @@ static pthread_key_t sTLSKey = -1; static pthread_once_t sPthreadOnceKey = PTHREAD_ONCE_INIT; void createTLSKey() { - pthread_key_create(&sTLSKey, NULL); + pthread_key_create(&sTLSKey, (void (*)(void*))&releaseContext); } GLTraceContext *getGLTraceContext() { diff --git a/opengl/libs/hooks.h b/opengl/libs/hooks.h index b2a684c..4b43198 100644 --- a/opengl/libs/hooks.h +++ b/opengl/libs/hooks.h @@ -32,13 +32,11 @@ #include <GLES3/gl3.h> #include <GLES3/gl3ext.h> -#if !defined(__arm__) && !defined(__mips__) -#define USE_SLOW_BINDING 1 -#else -#define USE_SLOW_BINDING 0 -#endif +// set to 1 for debugging +#define USE_SLOW_BINDING 0 + #undef NELEM -#define NELEM(x) (sizeof(x)/sizeof(*(x))) +#define NELEM(x) (sizeof(x)/sizeof(*(x))) // maximum number of GL extensions that can be used simultaneously in // a given process. this limitation exists because we need to have @@ -47,15 +45,7 @@ #define MAX_NUMBER_OF_GL_EXTENSIONS 256 -#if defined(HAVE_ANDROID_OS) && !USE_SLOW_BINDING && __OPTIMIZE__ -#define USE_FAST_TLS_KEY 1 -#else -#define USE_FAST_TLS_KEY 0 -#endif - -#if USE_FAST_TLS_KEY -# include <bionic_tls.h> /* special private C library header */ -#endif +#include <bionic_tls.h> /* special private C library header */ // ---------------------------------------------------------------------------- namespace android { @@ -84,7 +74,20 @@ struct gl_hooks_t { #undef EGL_ENTRY EGLAPI void setGlThreadSpecific(gl_hooks_t const *value); -EGLAPI gl_hooks_t const* getGlThreadSpecific(); + +// We have a dedicated TLS slot in bionic +inline gl_hooks_t const * volatile * get_tls_hooks() { + volatile void *tls_base = __get_tls(); + gl_hooks_t const * volatile * tls_hooks = + reinterpret_cast<gl_hooks_t const * volatile *>(tls_base); + return tls_hooks; +} + +inline EGLAPI gl_hooks_t const* getGlThreadSpecific() { + gl_hooks_t const * volatile * tls_hooks = get_tls_hooks(); + gl_hooks_t const* hooks = tls_hooks[TLS_SLOT_OPENGL_API]; + return hooks; +} // ---------------------------------------------------------------------------- }; // namespace android diff --git a/opengl/specs/EGL_ANDROID_presentation_time.txt b/opengl/specs/EGL_ANDROID_presentation_time.txt index 09b3938..e1dab34 100644 --- a/opengl/specs/EGL_ANDROID_presentation_time.txt +++ b/opengl/specs/EGL_ANDROID_presentation_time.txt @@ -10,6 +10,7 @@ Contributors Jamie Gennis Andy McFadden + Jesse Hall Contact @@ -21,7 +22,7 @@ Status Version - Version 2, April 1, 2013 + Version 3, June 26, 2013 Number @@ -92,7 +93,9 @@ Changes to Chapter 3 of the EGL 1.2 Specification (EGL Functions and Errors) If the surface presentation time is successfully set, EGL_TRUE is returned. Otherwise EGL_FALSE is returned and an appropriate error is - set. + set. If <dpy> is not the name of a valid, initialized EGLDisplay, an + EGL_BAD_DISPLAY error is generated. If <surface> is not a valid EGLSurface + then an EGL_BAD_SURFACE error is generated. Issues @@ -110,9 +113,21 @@ Issues System.nanoTime() method, or from the native clock_gettime function by passing CLOCK_MONOTONIC as the clock identifier. + 3. Should the presentation time be state which is used by eglSwapBuffers, + or should it be a new parameter to an extended variant of eglSwapBuffers? + + RESOLVED: The presentation time should be new state which is used by + the existing eglSwapBuffers call. Adding new state composes better with + other (hypothetical) extensions that also modify the behavior of + eglSwapBuffers. + Revision History -#1 (Jamie Gennis, April 1, 2013) +#3 (Jesse Hall, June 26, 2013) + - Enumerated errors generated by eglPresentationTimeANDROID. + - Added Issue #3 with resolution. + +#2 (Jamie Gennis, April 1, 2013) - Clarified how uses that either do or do not need an absolute time should be handled. - Specified the eglPresentationTimeANDROID return value. diff --git a/opengl/specs/README b/opengl/specs/README index eb86869..f4de1b3 100644 --- a/opengl/specs/README +++ b/opengl/specs/README @@ -14,4 +14,8 @@ for use by Android extensions. 0x3145 EGL_SYNC_NATIVE_FENCE_FD_ANDROID (EGL_ANDROID_native_fence_sync) 0x3146 EGL_SYNC_NATIVE_FENCE_SIGNALED_ANDROID (EGL_ANDROID_native_fence_sync) 0x3147 EGL_FRAMEBUFFER_TARGET_ANDROID (EGL_ANDROID_framebuffer_target) -0x3148 - 0x314F (unused) +0x3148 EGL_IMAGE_CROP_LEFT_ANDROID (EGL_ANDROID_image_crop) +0x3149 EGL_IMAGE_CROP_TOP_ANDROID (EGL_ANDROID_image_crop) +0x314A EGL_IMAGE_CROP_RIGHT_ANDROID (EGL_ANDROID_image_crop) +0x314B EGL_IMAGE_CROP_BOTTOM_ANDROID (EGL_ANDROID_image_crop) +0x314C - 0x314F (unused) diff --git a/opengl/tests/EGLTest/Android.mk b/opengl/tests/EGLTest/Android.mk index 1a9ee5c..f37efec 100644 --- a/opengl/tests/EGLTest/Android.mk +++ b/opengl/tests/EGLTest/Android.mk @@ -13,6 +13,7 @@ LOCAL_SRC_FILES := \ LOCAL_SHARED_LIBRARIES := \ libEGL \ libcutils \ + libbinder \ libstlport \ libutils \ libgui \ diff --git a/opengl/tests/EGLTest/EGL_test.cpp b/opengl/tests/EGLTest/EGL_test.cpp index c0daba2..f6644fb 100644 --- a/opengl/tests/EGLTest/EGL_test.cpp +++ b/opengl/tests/EGLTest/EGL_test.cpp @@ -20,7 +20,6 @@ #include <EGL/egl.h> #include <gui/Surface.h> -#include <gui/DummyConsumer.h> namespace android { @@ -101,9 +100,14 @@ TEST_F(EGLTest, EGLTerminateSucceedsWithRemainingObjects) { }; EXPECT_TRUE(eglChooseConfig(mEglDisplay, attrs, &config, 1, &numConfigs)); + struct DummyConsumer : public BnConsumerListener { + virtual void onFrameAvailable() {} + virtual void onBuffersReleased() {} + }; + // Create a EGLSurface sp<BufferQueue> bq = new BufferQueue(); - bq->consumerConnect(new DummyConsumer()); + bq->consumerConnect(new DummyConsumer, false); sp<Surface> mSTC = new Surface(static_cast<sp<IGraphicBufferProducer> >( bq)); sp<ANativeWindow> mANW = mSTC; diff --git a/opengl/tests/hwc/hwcTestLib.cpp b/opengl/tests/hwc/hwcTestLib.cpp index d567e6e..9b224e2 100644 --- a/opengl/tests/hwc/hwcTestLib.cpp +++ b/opengl/tests/hwc/hwcTestLib.cpp @@ -560,8 +560,6 @@ uint32_t hwcTestColor2Pixel(uint32_t format, ColorFract color, float alpha) {HAL_PIXEL_FORMAT_RGB_888, false, 3, 0, 8, 8, 8, 16, 8, 0, 0}, {HAL_PIXEL_FORMAT_RGB_565, true, 2, 0, 5, 5, 6, 11, 5, 0, 0}, {HAL_PIXEL_FORMAT_BGRA_8888, false, 4, 16, 8, 8, 8, 0, 8, 24, 8}, - {HAL_PIXEL_FORMAT_RGBA_5551, true , 2, 0, 5, 5, 5, 10, 5, 15, 1}, - {HAL_PIXEL_FORMAT_RGBA_4444, false, 2, 12, 4, 0, 4, 4, 4, 8, 4}, {HAL_PIXEL_FORMAT_YV12, true, 3, 16, 8, 8, 8, 0, 8, 0, 0}, }; @@ -614,8 +612,6 @@ void hwcTestSetPixel(GraphicBuffer *gBuf, unsigned char *buf, {HAL_PIXEL_FORMAT_RGB_888, 3}, {HAL_PIXEL_FORMAT_RGB_565, 2}, {HAL_PIXEL_FORMAT_BGRA_8888, 4}, - {HAL_PIXEL_FORMAT_RGBA_5551, 2}, - {HAL_PIXEL_FORMAT_RGBA_4444, 2}, }; if (gBuf->getPixelFormat() == HAL_PIXEL_FORMAT_YV12) { @@ -813,10 +809,6 @@ void hwcTestColorConvert(uint32_t fromFormat, uint32_t toFormat, 0, 0, 31, 31, 0, 0, 63, 63, 0, 0, 31, 31}, {HAL_PIXEL_FORMAT_BGRA_8888, true, false, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255}, - {HAL_PIXEL_FORMAT_RGBA_5551, true, false, - 0, 0, 31, 31, 0, 0, 31, 31, 0, 0, 31, 31}, - {HAL_PIXEL_FORMAT_RGBA_4444, true, false, - 0, 0, 15, 15, 0, 0, 15, 15, 0, 0, 15, 15}, {HAL_PIXEL_FORMAT_YV12, false, true, 0, 16, 235, 255, 0, 16, 240, 255, 0, 16, 240, 255}, }; diff --git a/opengl/tests/hwc/hwcTestLib.h b/opengl/tests/hwc/hwcTestLib.h index d7d5837..d403308 100644 --- a/opengl/tests/hwc/hwcTestLib.h +++ b/opengl/tests/hwc/hwcTestLib.h @@ -46,8 +46,6 @@ const struct hwcTestGraphicFormat { {HAL_PIXEL_FORMAT_RGB_888, "RGB888", 1, 1}, {HAL_PIXEL_FORMAT_RGB_565, "RGB565", 1, 1}, {HAL_PIXEL_FORMAT_BGRA_8888, "BGRA8888", 1, 1}, - {HAL_PIXEL_FORMAT_RGBA_5551, "RGBA5551", 1, 1}, - {HAL_PIXEL_FORMAT_RGBA_4444, "RGBA4444", 1, 1}, {HAL_PIXEL_FORMAT_YV12, "YV12", 2, 2}, }; diff --git a/opengl/tools/glgen/gen b/opengl/tools/glgen/gen index d236c1e..7146a29 100755 --- a/opengl/tools/glgen/gen +++ b/opengl/tools/glgen/gen @@ -32,10 +32,6 @@ echo "package android.os; public class RemoteException extends Exception {}" > o echo "package android.util; public class Log {public static void w(String a, String b) {} public static void e(String a, String b) {}}" > out/android/util/Log.java echo "package android.opengl; public abstract class EGLObjectHandle { public int getHandle() { return 0; } }" > out/android/opengl/EGLObjectHandle.java -echo "package android.opengl; public class EGLSurface extends EGLObjectHandle { }" > out/android/opengl/EGLSurface.java -echo "package android.opengl; public class EGLContext extends EGLObjectHandle { }" > out/android/opengl/EGLContext.java -echo "package android.opengl; public class EGLDisplay extends EGLObjectHandle { }" > out/android/opengl/EGLDisplay.java -echo "package android.opengl; public class EGLConfig extends EGLObjectHandle { }" > out/android/opengl/EGLConfig.java echo "package android.graphics;" > out/android/graphics/SurfaceTexture.java @@ -47,6 +43,7 @@ echo "public interface Surface {}" >> out/android/view/Surface.java echo "package android.view;" > out/android/view/SurfaceHolder.java echo "public interface SurfaceHolder { Surface getSurface(); }" >> out/android/view/SurfaceHolder.java +cp static/egl/*.java out/android/opengl/ GLFILE=out/javax/microedition/khronos/opengles/GL.java cp stubs/jsr239/GLHeader.java-if $GLFILE @@ -141,8 +138,8 @@ compareGenerated() { echo SAID_PLEASE=1 fi - echo " " cp $2/$3 $1 - echo " " git add $1/$3 + echo " cp $2/$3 $1" + echo " (cd $1; git add $3)" KEEP_GENERATED=1 fi } @@ -161,6 +158,11 @@ do compareGenerated ../../../../base/core/jni generated/C android_opengl_${x}.cpp done +for x in EGLConfig EGLContext EGLDisplay EGLObjectHandle EGLSurface +do + compareGenerated ../../../../base/opengl/java/android/opengl generated/android/opengl ${x}.java +done + if [ $KEEP_GENERATED == "0" ] ; then rm -rf generated fi diff --git a/opengl/tools/glgen/src/JniCodeEmitter.java b/opengl/tools/glgen/src/JniCodeEmitter.java index d5e2d34..b1bd1fd 100644 --- a/opengl/tools/glgen/src/JniCodeEmitter.java +++ b/opengl/tools/glgen/src/JniCodeEmitter.java @@ -1073,6 +1073,7 @@ public class JniCodeEmitter { String decl = type.getDeclaration(); needsExit = true; out.println(indent + "if (!" + cname + ") {"); + out.println(indent + indent + "_exception = 1;"); out.println(indent + indent + "_exceptionType = \"java/lang/IllegalArgumentException\";"); out.println(indent + indent + diff --git a/opengl/tools/glgen/static/egl/EGLConfig.java b/opengl/tools/glgen/static/egl/EGLConfig.java index d457c9f..a7a6bbb 100644 --- a/opengl/tools/glgen/static/egl/EGLConfig.java +++ b/opengl/tools/glgen/static/egl/EGLConfig.java @@ -29,7 +29,7 @@ public class EGLConfig extends EGLObjectHandle { @Override public boolean equals(Object o) { if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (!(o instanceof EGLConfig)) return false; EGLConfig that = (EGLConfig) o; return getHandle() == that.getHandle(); diff --git a/opengl/tools/glgen/static/egl/EGLContext.java b/opengl/tools/glgen/static/egl/EGLContext.java index 41b8ef1..c93bd6e 100644 --- a/opengl/tools/glgen/static/egl/EGLContext.java +++ b/opengl/tools/glgen/static/egl/EGLContext.java @@ -29,7 +29,7 @@ public class EGLContext extends EGLObjectHandle { @Override public boolean equals(Object o) { if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (!(o instanceof EGLContext)) return false; EGLContext that = (EGLContext) o; return getHandle() == that.getHandle(); diff --git a/opengl/tools/glgen/static/egl/EGLDisplay.java b/opengl/tools/glgen/static/egl/EGLDisplay.java index 17d1a64..5b8043a 100644 --- a/opengl/tools/glgen/static/egl/EGLDisplay.java +++ b/opengl/tools/glgen/static/egl/EGLDisplay.java @@ -29,7 +29,7 @@ public class EGLDisplay extends EGLObjectHandle { @Override public boolean equals(Object o) { if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (!(o instanceof EGLDisplay)) return false; EGLDisplay that = (EGLDisplay) o; return getHandle() == that.getHandle(); diff --git a/opengl/tools/glgen/static/egl/EGLSurface.java b/opengl/tools/glgen/static/egl/EGLSurface.java index 65bec4f..c379dc9 100644 --- a/opengl/tools/glgen/static/egl/EGLSurface.java +++ b/opengl/tools/glgen/static/egl/EGLSurface.java @@ -29,7 +29,7 @@ public class EGLSurface extends EGLObjectHandle { @Override public boolean equals(Object o) { if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (!(o instanceof EGLSurface)) return false; EGLSurface that = (EGLSurface) o; return getHandle() == that.getHandle(); diff --git a/opengl/tools/glgen/stubs/egl/eglCreateWindowSurface.cpp b/opengl/tools/glgen/stubs/egl/eglCreateWindowSurface.cpp index 906cd80..0cfd886 100644 --- a/opengl/tools/glgen/stubs/egl/eglCreateWindowSurface.cpp +++ b/opengl/tools/glgen/stubs/egl/eglCreateWindowSurface.cpp @@ -90,7 +90,7 @@ android_eglCreateWindowSurfaceTexture jint _remaining; EGLint *attrib_list = (EGLint *) 0; android::sp<ANativeWindow> window; - android::sp<android::GLConsumer> glConsumer; + android::sp<android::IGraphicBufferProducer> producer; if (!attrib_list_ref) { _exception = 1; @@ -111,12 +111,12 @@ not_valid_surface: _exceptionMessage = "Make sure the SurfaceView or associated SurfaceHolder has a valid Surface"; goto exit; } - glConsumer = android::SurfaceTexture_getSurfaceTexture(_env, win); + producer = android::SurfaceTexture_getProducer(_env, win); - if (glConsumer == NULL) + if (producer == NULL) goto not_valid_surface; - window = new android::Surface(glConsumer->getBufferQueue()); + window = new android::Surface(producer); if (window == NULL) goto not_valid_surface; diff --git a/opengl/tools/glgen/stubs/gles11/common.cpp b/opengl/tools/glgen/stubs/gles11/common.cpp index 579d573..75b75cb 100644 --- a/opengl/tools/glgen/stubs/gles11/common.cpp +++ b/opengl/tools/glgen/stubs/gles11/common.cpp @@ -272,6 +272,7 @@ getarray int _needed = 0; params = (CTYPE *)getPointer(_env, params_buf, &_array, &_remaining, &_bufferOffset); + _remaining /= sizeof(CTYPE); // convert from bytes to item count _needed = getNeededCount(pname); // if we didn't find this pname, we just assume the user passed // an array of the right size -- this might happen with extensions diff --git a/services/batteryservice/Android.mk b/services/batteryservice/Android.mk new file mode 100644 index 0000000..0a29c36 --- /dev/null +++ b/services/batteryservice/Android.mk @@ -0,0 +1,17 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + BatteryProperties.cpp \ + IBatteryPropertiesListener.cpp \ + IBatteryPropertiesRegistrar.cpp + +LOCAL_STATIC_LIBRARIES := \ + libutils \ + libbinder + +LOCAL_MODULE:= libbatteryservice + +LOCAL_MODULE_TAGS := optional + +include $(BUILD_STATIC_LIBRARY) diff --git a/services/batteryservice/BatteryProperties.cpp b/services/batteryservice/BatteryProperties.cpp new file mode 100644 index 0000000..e4a42ed --- /dev/null +++ b/services/batteryservice/BatteryProperties.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdint.h> +#include <sys/types.h> +#include <batteryservice/BatteryService.h> +#include <binder/Parcel.h> +#include <utils/Errors.h> +#include <utils/String8.h> +#include <utils/String16.h> + +namespace android { + +/* + * Parcel read/write code must be kept in sync with + * frameworks/base/core/java/android/os/BatteryProperties.java + */ + +status_t BatteryProperties::readFromParcel(Parcel* p) { + chargerAcOnline = p->readInt32() == 1 ? true : false; + chargerUsbOnline = p->readInt32() == 1 ? true : false; + chargerWirelessOnline = p->readInt32() == 1 ? true : false; + batteryStatus = p->readInt32(); + batteryHealth = p->readInt32(); + batteryPresent = p->readInt32() == 1 ? true : false; + batteryLevel = p->readInt32(); + batteryVoltage = p->readInt32(); + batteryCurrentNow = p->readInt32(); + batteryChargeCounter = p->readInt32(); + batteryTemperature = p->readInt32(); + batteryTechnology = String8((p->readString16()).string()); + return OK; +} + +status_t BatteryProperties::writeToParcel(Parcel* p) const { + p->writeInt32(chargerAcOnline ? 1 : 0); + p->writeInt32(chargerUsbOnline ? 1 : 0); + p->writeInt32(chargerWirelessOnline ? 1 : 0); + p->writeInt32(batteryStatus); + p->writeInt32(batteryHealth); + p->writeInt32(batteryPresent ? 1 : 0); + p->writeInt32(batteryLevel); + p->writeInt32(batteryVoltage); + p->writeInt32(batteryCurrentNow); + p->writeInt32(batteryChargeCounter); + p->writeInt32(batteryTemperature); + p->writeString16(String16(batteryTechnology)); + return OK; +} + +}; // namespace android diff --git a/services/batteryservice/IBatteryPropertiesListener.cpp b/services/batteryservice/IBatteryPropertiesListener.cpp new file mode 100644 index 0000000..19ac7f0 --- /dev/null +++ b/services/batteryservice/IBatteryPropertiesListener.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdint.h> +#include <sys/types.h> +#include <batteryservice/IBatteryPropertiesListener.h> +#include <binder/Parcel.h> + +namespace android { + +class BpBatteryPropertiesListener : public BpInterface<IBatteryPropertiesListener> +{ +public: + BpBatteryPropertiesListener(const sp<IBinder>& impl) + : BpInterface<IBatteryPropertiesListener>(impl) + { + } + + void batteryPropertiesChanged(struct BatteryProperties props) + { + Parcel data, reply; + data.writeInterfaceToken(IBatteryPropertiesListener::getInterfaceDescriptor()); + data.writeInt32(1); + props.writeToParcel(&data); + status_t err = remote()->transact(TRANSACT_BATTERYPROPERTIESCHANGED, data, &reply, IBinder::FLAG_ONEWAY); + } +}; + +IMPLEMENT_META_INTERFACE(BatteryPropertiesListener, "android.os.IBatteryPropertiesListener"); + +// ---------------------------------------------------------------------------- + +}; // namespace android diff --git a/services/batteryservice/IBatteryPropertiesRegistrar.cpp b/services/batteryservice/IBatteryPropertiesRegistrar.cpp new file mode 100644 index 0000000..6c2d2a5 --- /dev/null +++ b/services/batteryservice/IBatteryPropertiesRegistrar.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "IBatteryPropertiesRegistrar" +//#define LOG_NDEBUG 0 +#include <utils/Log.h> + +#include <batteryservice/IBatteryPropertiesListener.h> +#include <batteryservice/IBatteryPropertiesRegistrar.h> +#include <stdint.h> +#include <sys/types.h> +#include <binder/Parcel.h> + +namespace android { + +class BpBatteryPropertiesRegistrar : public BpInterface<IBatteryPropertiesRegistrar> { +public: + BpBatteryPropertiesRegistrar(const sp<IBinder>& impl) + : BpInterface<IBatteryPropertiesRegistrar>(impl) {} + + void registerListener(const sp<IBatteryPropertiesListener>& listener) { + Parcel data; + data.writeInterfaceToken(IBatteryPropertiesRegistrar::getInterfaceDescriptor()); + data.writeStrongBinder(listener->asBinder()); + remote()->transact(REGISTER_LISTENER, data, NULL); + } + + void unregisterListener(const sp<IBatteryPropertiesListener>& listener) { + Parcel data; + data.writeInterfaceToken(IBatteryPropertiesRegistrar::getInterfaceDescriptor()); + data.writeStrongBinder(listener->asBinder()); + remote()->transact(UNREGISTER_LISTENER, data, NULL); + } +}; + +IMPLEMENT_META_INTERFACE(BatteryPropertiesRegistrar, "android.os.IBatteryPropertiesRegistrar"); + +status_t BnBatteryPropertiesRegistrar::onTransact(uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags) +{ + switch(code) { + case REGISTER_LISTENER: { + CHECK_INTERFACE(IBatteryPropertiesRegistrar, data, reply); + sp<IBatteryPropertiesListener> listener = + interface_cast<IBatteryPropertiesListener>(data.readStrongBinder()); + registerListener(listener); + return OK; + } + + case UNREGISTER_LISTENER: { + CHECK_INTERFACE(IBatteryPropertiesRegistrar, data, reply); + sp<IBatteryPropertiesListener> listener = + interface_cast<IBatteryPropertiesListener>(data.readStrongBinder()); + unregisterListener(listener); + return OK; + } + } + return BBinder::onTransact(code, data, reply, flags); +}; + +// ---------------------------------------------------------------------------- + +}; // namespace android diff --git a/services/connectivitymanager/Android.mk b/services/connectivitymanager/Android.mk new file mode 100644 index 0000000..e986abc --- /dev/null +++ b/services/connectivitymanager/Android.mk @@ -0,0 +1,13 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= ConnectivityManager.cpp + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libutils \ + libbinder + +LOCAL_MODULE:= libconnectivitymanager + +include $(BUILD_SHARED_LIBRARY) diff --git a/services/connectivitymanager/ConnectivityManager.cpp b/services/connectivitymanager/ConnectivityManager.cpp new file mode 100644 index 0000000..949c2ac --- /dev/null +++ b/services/connectivitymanager/ConnectivityManager.cpp @@ -0,0 +1,51 @@ +/** + * Copyright (c) 2013, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <sys/types.h> + +#include <utils/Singleton.h> + +#include <binder/BinderService.h> +#include <binder/Parcel.h> + +#include "ConnectivityManager.h" + +namespace android { + +ConnectivityManager::ConnectivityManager() { + const sp<IServiceManager> sm(defaultServiceManager()); + if (sm != NULL) { + const String16 name("connectivity"); + mConnectivityService = sm->getService(name); + } +} + +void ConnectivityManager::markSocketAsUserImpl(int fd, uid_t uid) { + Parcel data, reply; + data.writeInterfaceToken(DESCRIPTOR); + // parcelable objects are preceded by a 1 if not null in aidl generated code. + // Play nice with the generated Java + data.writeInt32(1); + data.writeFileDescriptor(fd); + data.writeInt32(uid); + mConnectivityService->transact(TRANSACTION_markSocketAsUser, data, &reply, 0); +} + +const String16 ConnectivityManager::DESCRIPTOR("android.net.IConnectivityManager"); + +ANDROID_SINGLETON_STATIC_INSTANCE(ConnectivityManager) + +}; diff --git a/services/connectivitymanager/ConnectivityManager.h b/services/connectivitymanager/ConnectivityManager.h new file mode 100644 index 0000000..37f5d98 --- /dev/null +++ b/services/connectivitymanager/ConnectivityManager.h @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2013, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdint.h> +#include <sys/types.h> + +#include <utils/Singleton.h> + +namespace android { + +class ConnectivityManager : public Singleton<ConnectivityManager> { + // Keep this in sync with IConnectivityManager.aidl + static const int TRANSACTION_markSocketAsUser = IBinder::FIRST_CALL_TRANSACTION; + static const String16 DESCRIPTOR; + + friend class Singleton<ConnectivityManager>; + sp<IBinder> mConnectivityService; + + ConnectivityManager(); + + void markSocketAsUserImpl(int fd, uid_t uid); + +public: + static void markSocketAsUser(int fd, uid_t uid) { + ConnectivityManager::getInstance().markSocketAsUserImpl(fd, uid); + } +}; + +}; diff --git a/services/powermanager/IPowerManager.cpp b/services/powermanager/IPowerManager.cpp index 0265df3..9f60e75 100644 --- a/services/powermanager/IPowerManager.cpp +++ b/services/powermanager/IPowerManager.cpp @@ -30,7 +30,8 @@ namespace android { // must be kept in sync with IPowerManager.aidl enum { ACQUIRE_WAKE_LOCK = IBinder::FIRST_CALL_TRANSACTION, - RELEASE_WAKE_LOCK = IBinder::FIRST_CALL_TRANSACTION + 1, + ACQUIRE_WAKE_LOCK_UID = IBinder::FIRST_CALL_TRANSACTION + 1, + RELEASE_WAKE_LOCK = IBinder::FIRST_CALL_TRANSACTION + 2, }; class BpPowerManager : public BpInterface<IPowerManager> @@ -41,7 +42,8 @@ public: { } - virtual status_t acquireWakeLock(int flags, const sp<IBinder>& lock, const String16& tag) + virtual status_t acquireWakeLock(int flags, const sp<IBinder>& lock, const String16& tag, + const String16& packageName) { Parcel data, reply; data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor()); @@ -49,10 +51,25 @@ public: data.writeStrongBinder(lock); data.writeInt32(flags); data.writeString16(tag); + data.writeString16(packageName); data.writeInt32(0); // no WorkSource return remote()->transact(ACQUIRE_WAKE_LOCK, data, &reply); } + virtual status_t acquireWakeLockWithUid(int flags, const sp<IBinder>& lock, const String16& tag, + const String16& packageName, int uid) + { + Parcel data, reply; + data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor()); + + data.writeStrongBinder(lock); + data.writeInt32(flags); + data.writeString16(tag); + data.writeString16(packageName); + data.writeInt32(uid); // uid to blame for the work + return remote()->transact(ACQUIRE_WAKE_LOCK_UID, data, &reply); + } + virtual status_t releaseWakeLock(const sp<IBinder>& lock, int flags) { Parcel data, reply; diff --git a/services/sensorservice/Android.mk b/services/sensorservice/Android.mk index dd698c5..4f24ddc 100644 --- a/services/sensorservice/Android.mk +++ b/services/sensorservice/Android.mk @@ -12,11 +12,12 @@ LOCAL_SRC_FILES:= \ SensorDevice.cpp \ SensorFusion.cpp \ SensorInterface.cpp \ - SensorService.cpp \ - + SensorService.cpp LOCAL_CFLAGS:= -DLOG_TAG=\"SensorService\" +LOCAL_CFLAGS += -fvisibility=hidden + LOCAL_SHARED_LIBRARIES := \ libcutils \ libhardware \ @@ -27,8 +28,24 @@ LOCAL_SHARED_LIBRARIES := \ libui \ libgui - - LOCAL_MODULE:= libsensorservice include $(BUILD_SHARED_LIBRARY) + +##################################################################### +# build executable +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + main_sensorservice.cpp + +LOCAL_SHARED_LIBRARIES := \ + libsensorservice \ + libbinder \ + libutils + +LOCAL_MODULE_TAGS := optional + +LOCAL_MODULE:= sensorservice + +include $(BUILD_EXECUTABLE) diff --git a/services/sensorservice/BatteryService.cpp b/services/sensorservice/BatteryService.cpp index 70b65ab..38dc749 100644 --- a/services/sensorservice/BatteryService.cpp +++ b/services/sensorservice/BatteryService.cpp @@ -33,7 +33,7 @@ namespace android { BatteryService::BatteryService() { const sp<IServiceManager> sm(defaultServiceManager()); if (sm != NULL) { - const String16 name("batteryinfo"); + const String16 name("batterystats"); mBatteryStatService = sm->getService(name); } } diff --git a/services/sensorservice/CorrectedGyroSensor.cpp b/services/sensorservice/CorrectedGyroSensor.cpp index 1857443..31487a7 100644 --- a/services/sensorservice/CorrectedGyroSensor.cpp +++ b/services/sensorservice/CorrectedGyroSensor.cpp @@ -57,19 +57,19 @@ bool CorrectedGyroSensor::process(sensors_event_t* outEvent, } status_t CorrectedGyroSensor::activate(void* ident, bool enabled) { - mSensorDevice.activate(this, mGyro.getHandle(), enabled); - return mSensorFusion.activate(this, enabled); + mSensorDevice.activate(ident, mGyro.getHandle(), enabled); + return mSensorFusion.activate(ident, enabled); } status_t CorrectedGyroSensor::setDelay(void* ident, int handle, int64_t ns) { - mSensorDevice.setDelay(this, mGyro.getHandle(), ns); - return mSensorFusion.setDelay(this, ns); + mSensorDevice.setDelay(ident, mGyro.getHandle(), ns); + return mSensorFusion.setDelay(ident, ns); } Sensor CorrectedGyroSensor::getSensor() const { sensor_t hwSensor; hwSensor.name = "Corrected Gyroscope Sensor"; - hwSensor.vendor = "Google Inc."; + hwSensor.vendor = "AOSP"; hwSensor.version = 1; hwSensor.handle = '_cgy'; hwSensor.type = SENSOR_TYPE_GYROSCOPE; diff --git a/services/sensorservice/Fusion.cpp b/services/sensorservice/Fusion.cpp index 93d6127..4f63c31 100644 --- a/services/sensorservice/Fusion.cpp +++ b/services/sensorservice/Fusion.cpp @@ -220,22 +220,6 @@ void Fusion::initFusion(const vec4_t& q, float dT) // initial covariance: Var{ x(t0) } // TODO: initialize P correctly P = 0; - - // it is unclear how to set the initial covariance. It does affect - // how quickly the fusion converges. Experimentally it would take - // about 10 seconds at 200 Hz to estimate the gyro-drift with an - // initial covariance of 0, and about a second with an initial covariance - // of about 1 deg/s. - const float covv = 0; - const float covu = 0.5f * (float(M_PI) / 180); - mat33_t& Pv = P[0][0]; - Pv[0][0] = covv; - Pv[1][1] = covv; - Pv[2][2] = covv; - mat33_t& Pu = P[1][1]; - Pu[0][0] = covu; - Pu[1][1] = covu; - Pu[2][2] = covu; } bool Fusion::hasEstimate() const { diff --git a/services/sensorservice/GravitySensor.cpp b/services/sensorservice/GravitySensor.cpp index c57715f..dd1f650 100644 --- a/services/sensorservice/GravitySensor.cpp +++ b/services/sensorservice/GravitySensor.cpp @@ -67,17 +67,17 @@ bool GravitySensor::process(sensors_event_t* outEvent, } status_t GravitySensor::activate(void* ident, bool enabled) { - return mSensorFusion.activate(this, enabled); + return mSensorFusion.activate(ident, enabled); } status_t GravitySensor::setDelay(void* ident, int handle, int64_t ns) { - return mSensorFusion.setDelay(this, ns); + return mSensorFusion.setDelay(ident, ns); } Sensor GravitySensor::getSensor() const { sensor_t hwSensor; hwSensor.name = "Gravity Sensor"; - hwSensor.vendor = "Google Inc."; + hwSensor.vendor = "AOSP"; hwSensor.version = 3; hwSensor.handle = '_grv'; hwSensor.type = SENSOR_TYPE_GRAVITY; diff --git a/services/sensorservice/LinearAccelerationSensor.cpp b/services/sensorservice/LinearAccelerationSensor.cpp index f0054f2..d5f20d2 100644 --- a/services/sensorservice/LinearAccelerationSensor.cpp +++ b/services/sensorservice/LinearAccelerationSensor.cpp @@ -51,18 +51,18 @@ bool LinearAccelerationSensor::process(sensors_event_t* outEvent, } status_t LinearAccelerationSensor::activate(void* ident, bool enabled) { - return mGravitySensor.activate(this, enabled); + return mGravitySensor.activate(ident, enabled); } status_t LinearAccelerationSensor::setDelay(void* ident, int handle, int64_t ns) { - return mGravitySensor.setDelay(this, handle, ns); + return mGravitySensor.setDelay(ident, handle, ns); } Sensor LinearAccelerationSensor::getSensor() const { Sensor gsensor(mGravitySensor.getSensor()); sensor_t hwSensor; hwSensor.name = "Linear Acceleration Sensor"; - hwSensor.vendor = "Google Inc."; + hwSensor.vendor = "AOSP"; hwSensor.version = gsensor.getVersion(); hwSensor.handle = '_lin'; hwSensor.type = SENSOR_TYPE_LINEAR_ACCELERATION; diff --git a/services/sensorservice/OrientationSensor.cpp b/services/sensorservice/OrientationSensor.cpp index 037adaa..10b391c 100644 --- a/services/sensorservice/OrientationSensor.cpp +++ b/services/sensorservice/OrientationSensor.cpp @@ -33,6 +33,9 @@ OrientationSensor::OrientationSensor() : mSensorDevice(SensorDevice::getInstance()), mSensorFusion(SensorFusion::getInstance()) { + // FIXME: instead of using the SensorFusion code, we should use + // the SENSOR_TYPE_ROTATION_VECTOR instead. This way we could use the + // HAL's implementation. } bool OrientationSensor::process(sensors_event_t* outEvent, @@ -63,17 +66,17 @@ bool OrientationSensor::process(sensors_event_t* outEvent, } status_t OrientationSensor::activate(void* ident, bool enabled) { - return mSensorFusion.activate(this, enabled); + return mSensorFusion.activate(ident, enabled); } status_t OrientationSensor::setDelay(void* ident, int handle, int64_t ns) { - return mSensorFusion.setDelay(this, ns); + return mSensorFusion.setDelay(ident, ns); } Sensor OrientationSensor::getSensor() const { sensor_t hwSensor; hwSensor.name = "Orientation Sensor"; - hwSensor.vendor = "Google Inc."; + hwSensor.vendor = "AOSP"; hwSensor.version = 1; hwSensor.handle = '_ypr'; hwSensor.type = SENSOR_TYPE_ORIENTATION; diff --git a/services/sensorservice/RotationVectorSensor.cpp b/services/sensorservice/RotationVectorSensor.cpp index 5ea9568..a2157b4 100644 --- a/services/sensorservice/RotationVectorSensor.cpp +++ b/services/sensorservice/RotationVectorSensor.cpp @@ -53,17 +53,17 @@ bool RotationVectorSensor::process(sensors_event_t* outEvent, } status_t RotationVectorSensor::activate(void* ident, bool enabled) { - return mSensorFusion.activate(this, enabled); + return mSensorFusion.activate(ident, enabled); } status_t RotationVectorSensor::setDelay(void* ident, int handle, int64_t ns) { - return mSensorFusion.setDelay(this, ns); + return mSensorFusion.setDelay(ident, ns); } Sensor RotationVectorSensor::getSensor() const { sensor_t hwSensor; hwSensor.name = "Rotation Vector Sensor"; - hwSensor.vendor = "Google Inc."; + hwSensor.vendor = "AOSP"; hwSensor.version = 3; hwSensor.handle = '_rov'; hwSensor.type = SENSOR_TYPE_ROTATION_VECTOR; @@ -102,17 +102,17 @@ bool GyroDriftSensor::process(sensors_event_t* outEvent, } status_t GyroDriftSensor::activate(void* ident, bool enabled) { - return mSensorFusion.activate(this, enabled); + return mSensorFusion.activate(ident, enabled); } status_t GyroDriftSensor::setDelay(void* ident, int handle, int64_t ns) { - return mSensorFusion.setDelay(this, ns); + return mSensorFusion.setDelay(ident, ns); } Sensor GyroDriftSensor::getSensor() const { sensor_t hwSensor; hwSensor.name = "Gyroscope Bias (debug)"; - hwSensor.vendor = "Google Inc."; + hwSensor.vendor = "AOSP"; hwSensor.version = 1; hwSensor.handle = '_gbs'; hwSensor.type = SENSOR_TYPE_ACCELEROMETER; diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp index a12529e..19caa5c 100644 --- a/services/sensorservice/SensorDevice.cpp +++ b/services/sensorservice/SensorDevice.cpp @@ -47,7 +47,7 @@ SensorDevice::SensorDevice() SENSORS_HARDWARE_MODULE_ID, strerror(-err)); if (mSensorModule) { - err = sensors_open(&mSensorModule->common, &mSensorDevice); + err = sensors_open_1(&mSensorModule->common, &mSensorDevice); ALOGE_IF(err, "couldn't open device for module %s (%s)", SENSORS_HARDWARE_MODULE_ID, strerror(-err)); @@ -59,36 +59,42 @@ SensorDevice::SensorDevice() Info model; for (size_t i=0 ; i<size_t(count) ; i++) { mActivationCount.add(list[i].handle, model); - mSensorDevice->activate(mSensorDevice, list[i].handle, 0); + mSensorDevice->activate( + reinterpret_cast<struct sensors_poll_device_t *>(mSensorDevice), + list[i].handle, 0); } } } } -void SensorDevice::dump(String8& result, char* buffer, size_t SIZE) +void SensorDevice::dump(String8& result) { if (!mSensorModule) return; sensor_t const* list; ssize_t count = mSensorModule->get_sensors_list(mSensorModule, &list); - snprintf(buffer, SIZE, "%d h/w sensors:\n", int(count)); - result.append(buffer); + result.appendFormat("%d h/w sensors:\n", int(count)); Mutex::Autolock _l(mLock); for (size_t i=0 ; i<size_t(count) ; i++) { const Info& info = mActivationCount.valueFor(list[i].handle); - snprintf(buffer, SIZE, "handle=0x%08x, active-count=%d, rates(ms)={ ", - list[i].handle, - info.rates.size()); - result.append(buffer); - for (size_t j=0 ; j<info.rates.size() ; j++) { - snprintf(buffer, SIZE, "%4.1f%s", - info.rates.valueAt(j) / 1e6f, - j<info.rates.size()-1 ? ", " : ""); - result.append(buffer); + result.appendFormat("handle=0x%08x, active-count=%d, batch_period(ms)={ ", list[i].handle, + info.batchParams.size()); + for (size_t j = 0; j < info.batchParams.size(); j++) { + BatchParams params = info.batchParams.valueAt(j); + result.appendFormat("%4.1f%s", params.batchDelay / 1e6f, + j < info.batchParams.size() - 1 ? ", " : ""); } - snprintf(buffer, SIZE, " }, selected=%4.1f ms\n", info.delay / 1e6f); - result.append(buffer); + result.appendFormat(" }, selected=%4.1f ms\n", info.bestBatchParams.batchDelay / 1e6f); + + result.appendFormat("handle=0x%08x, active-count=%d, batch_timeout(ms)={ ", list[i].handle, + info.batchParams.size()); + for (size_t j = 0; j < info.batchParams.size(); j++) { + BatchParams params = info.batchParams.valueAt(j); + result.appendFormat("%4.1f%s", params.batchTimeout / 1e6f, + j < info.batchParams.size() - 1 ? ", " : ""); + } + result.appendFormat(" }, selected=%4.1f ms\n", info.bestBatchParams.batchTimeout / 1e6f); } } @@ -106,7 +112,8 @@ ssize_t SensorDevice::poll(sensors_event_t* buffer, size_t count) { if (!mSensorDevice) return NO_INIT; ssize_t c; do { - c = mSensorDevice->poll(mSensorDevice, buffer, count); + c = mSensorDevice->poll(reinterpret_cast<struct sensors_poll_device_t *> (mSensorDevice), + buffer, count); } while (c == -EINTR); return c; } @@ -114,7 +121,7 @@ ssize_t SensorDevice::poll(sensors_event_t* buffer, size_t count) { void SensorDevice::autoDisable(void *ident, int handle) { Info& info( mActivationCount.editValueFor(handle) ); Mutex::Autolock _l(mLock); - info.rates.removeItem(ident); + info.removeBatchParamsForIdent(ident); } status_t SensorDevice::activate(void* ident, int handle, int enabled) @@ -123,35 +130,46 @@ status_t SensorDevice::activate(void* ident, int handle, int enabled) status_t err(NO_ERROR); bool actuateHardware = false; + Mutex::Autolock _l(mLock); Info& info( mActivationCount.editValueFor(handle) ); - ALOGD_IF(DEBUG_CONNECTIONS, - "SensorDevice::activate: ident=%p, handle=0x%08x, enabled=%d, count=%d", - ident, handle, enabled, info.rates.size()); + "SensorDevice::activate: ident=%p, handle=0x%08x, enabled=%d, count=%d", + ident, handle, enabled, info.batchParams.size()); if (enabled) { - Mutex::Autolock _l(mLock); - ALOGD_IF(DEBUG_CONNECTIONS, "... index=%ld", - info.rates.indexOfKey(ident)); + ALOGD_IF(DEBUG_CONNECTIONS, "enable index=%d", info.batchParams.indexOfKey(ident)); - if (info.rates.indexOfKey(ident) < 0) { - info.rates.add(ident, DEFAULT_EVENTS_PERIOD); - if (info.rates.size() == 1) { - actuateHardware = true; - } + if (info.batchParams.indexOfKey(ident) >= 0) { + if (info.batchParams.size() == 1) { + // This is the first connection, we need to activate the underlying h/w sensor. + actuateHardware = true; + } } else { - // sensor was already activated for this ident + // Log error. Every activate call should be preceded by a batch() call. + ALOGE("\t >>>ERROR: activate called without batch"); } } else { - Mutex::Autolock _l(mLock); - ALOGD_IF(DEBUG_CONNECTIONS, "... index=%ld", - info.rates.indexOfKey(ident)); + ALOGD_IF(DEBUG_CONNECTIONS, "disable index=%d", info.batchParams.indexOfKey(ident)); - ssize_t idx = info.rates.removeItem(ident); - if (idx >= 0) { - if (info.rates.size() == 0) { + if (info.removeBatchParamsForIdent(ident) >= 0) { + if (info.batchParams.size() == 0) { + // This is the last connection, we need to de-activate the underlying h/w sensor. actuateHardware = true; + } else { + const int halVersion = getHalDeviceVersion(); + if (halVersion >= SENSORS_DEVICE_API_VERSION_1_1) { + // Call batch for this sensor with the previously calculated best effort + // batch_rate and timeout. One of the apps has unregistered for sensor + // events, and the best effort batch parameters might have changed. + ALOGD_IF(DEBUG_CONNECTIONS, + "\t>>> actuating h/w batch %d %d %lld %lld ", handle, + info.bestBatchParams.flags, info.bestBatchParams.batchDelay, + info.bestBatchParams.batchTimeout); + mSensorDevice->batch(mSensorDevice, handle,info.bestBatchParams.flags, + info.bestBatchParams.batchDelay, + info.bestBatchParams.batchTimeout); + } } } else { // sensor wasn't enabled for this ident @@ -159,41 +177,130 @@ status_t SensorDevice::activate(void* ident, int handle, int enabled) } if (actuateHardware) { - ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w"); + ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w activate handle=%d enabled=%d", handle, enabled); + err = mSensorDevice->activate( + reinterpret_cast<struct sensors_poll_device_t *> (mSensorDevice), handle, enabled); + ALOGE_IF(err, "Error %s sensor %d (%s)", enabled ? "activating" : "disabling", handle, + strerror(-err)); + + if (err != NO_ERROR && enabled) { + // Failure when enabling the sensor. Clean up on failure. + info.removeBatchParamsForIdent(ident); + } + } - err = mSensorDevice->activate(mSensorDevice, handle, enabled); - ALOGE_IF(err, "Error %s sensor %d (%s)", - enabled ? "activating" : "disabling", - handle, strerror(-err)); + // On older devices which do not support batch, call setDelay(). + if (getHalDeviceVersion() < SENSORS_DEVICE_API_VERSION_1_1 && info.batchParams.size() > 0) { + ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w setDelay %d %lld ", handle, + info.bestBatchParams.batchDelay); + mSensorDevice->setDelay( + reinterpret_cast<struct sensors_poll_device_t *>(mSensorDevice), + handle, info.bestBatchParams.batchDelay); + } + return err; +} - if (err != NO_ERROR) { - // clean-up on failure - if (enabled) { - // failure when enabling the sensor - Mutex::Autolock _l(mLock); - info.rates.removeItem(ident); +status_t SensorDevice::batch(void* ident, int handle, int flags, int64_t samplingPeriodNs, + int64_t maxBatchReportLatencyNs) { + if (!mSensorDevice) return NO_INIT; + + if (samplingPeriodNs < MINIMUM_EVENTS_PERIOD) { + samplingPeriodNs = MINIMUM_EVENTS_PERIOD; + } + + const int halVersion = getHalDeviceVersion(); + if (halVersion >= SENSORS_DEVICE_API_VERSION_1_1) { + if (flags & SENSORS_BATCH_DRY_RUN) { + return mSensorDevice->batch(mSensorDevice, handle, flags, samplingPeriodNs, + maxBatchReportLatencyNs); + } else { + // Call h/w with dry run to see if the given parameters are feasible or not. Return if + // there is an error. + status_t errDryRun(NO_ERROR); + errDryRun = mSensorDevice->batch(mSensorDevice, handle, flags | SENSORS_BATCH_DRY_RUN, + samplingPeriodNs, maxBatchReportLatencyNs); + if (errDryRun != NO_ERROR) { + ALOGD_IF(DEBUG_CONNECTIONS, "SensorDevice::batch dry run error %s", + strerror(-errDryRun)); + return errDryRun; } } + } else if (maxBatchReportLatencyNs != 0) { + // Batch is not supported on older devices. + return INVALID_OPERATION; } - { // scope for the lock - Mutex::Autolock _l(mLock); - nsecs_t ns = info.selectDelay(); - mSensorDevice->setDelay(mSensorDevice, handle, ns); + ALOGD_IF(DEBUG_CONNECTIONS, + "SensorDevice::batch: ident=%p, handle=0x%08x, flags=%d, period_ns=%lld timeout=%lld", + ident, handle, flags, samplingPeriodNs, maxBatchReportLatencyNs); + + Mutex::Autolock _l(mLock); + Info& info(mActivationCount.editValueFor(handle)); + + if (info.batchParams.indexOfKey(ident) < 0) { + BatchParams params(flags, samplingPeriodNs, maxBatchReportLatencyNs); + info.batchParams.add(ident, params); + } else { + // A batch has already been called with this ident. Update the batch parameters. + info.setBatchParamsForIdent(ident, flags, samplingPeriodNs, maxBatchReportLatencyNs); } + BatchParams prevBestBatchParams = info.bestBatchParams; + // Find the minimum of all timeouts and batch_rates for this sensor. + info.selectBatchParams(); + + ALOGD_IF(DEBUG_CONNECTIONS, + "\t>>> curr_period=%lld min_period=%lld curr_timeout=%lld min_timeout=%lld", + prevBestBatchParams.batchDelay, info.bestBatchParams.batchDelay, + prevBestBatchParams.batchTimeout, info.bestBatchParams.batchTimeout); + + status_t err(NO_ERROR); + // If the min period or min timeout has changed since the last batch call, call batch. + if (prevBestBatchParams != info.bestBatchParams) { + if (halVersion >= SENSORS_DEVICE_API_VERSION_1_1) { + ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w BATCH %d %d %lld %lld ", handle, + info.bestBatchParams.flags, info.bestBatchParams.batchDelay, + info.bestBatchParams.batchTimeout); + err = mSensorDevice->batch(mSensorDevice, handle, info.bestBatchParams.flags, + info.bestBatchParams.batchDelay, + info.bestBatchParams.batchTimeout); + } else { + // For older devices which do not support batch, call setDelay() after activate() is + // called. Some older devices may not support calling setDelay before activate(), so + // call setDelay in SensorDevice::activate() method. + } + if (err != NO_ERROR) { + ALOGE("sensor batch failed %p %d %d %lld %lld err=%s", mSensorDevice, handle, + info.bestBatchParams.flags, info.bestBatchParams.batchDelay, + info.bestBatchParams.batchTimeout, strerror(-err)); + info.removeBatchParamsForIdent(ident); + } + } return err; } -status_t SensorDevice::setDelay(void* ident, int handle, int64_t ns) +status_t SensorDevice::setDelay(void* ident, int handle, int64_t samplingPeriodNs) { if (!mSensorDevice) return NO_INIT; + if (samplingPeriodNs < MINIMUM_EVENTS_PERIOD) { + samplingPeriodNs = MINIMUM_EVENTS_PERIOD; + } Mutex::Autolock _l(mLock); Info& info( mActivationCount.editValueFor(handle) ); - status_t err = info.setDelayForIdent(ident, ns); - if (err < 0) return err; - ns = info.selectDelay(); - return mSensorDevice->setDelay(mSensorDevice, handle, ns); + // If the underlying sensor is NOT in continuous mode, setDelay() should return an error. + // Calling setDelay() in batch mode is an invalid operation. + if (info.bestBatchParams.batchTimeout != 0) { + return INVALID_OPERATION; + } + ssize_t index = info.batchParams.indexOfKey(ident); + if (index < 0) { + return BAD_INDEX; + } + BatchParams& params = info.batchParams.editValueAt(index); + params.batchDelay = samplingPeriodNs; + info.selectBatchParams(); + return mSensorDevice->setDelay(reinterpret_cast<struct sensors_poll_device_t *>(mSensorDevice), + handle, info.bestBatchParams.batchDelay); } int SensorDevice::getHalDeviceVersion() const { @@ -202,31 +309,58 @@ int SensorDevice::getHalDeviceVersion() const { return mSensorDevice->common.version; } +status_t SensorDevice::flush(void* ident, int handle) { + if (getHalDeviceVersion() < SENSORS_DEVICE_API_VERSION_1_1) { + return INVALID_OPERATION; + } + ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w flush %d", handle); + return mSensorDevice->flush(mSensorDevice, handle); +} + // --------------------------------------------------------------------------- -status_t SensorDevice::Info::setDelayForIdent(void* ident, int64_t ns) -{ - ssize_t index = rates.indexOfKey(ident); +status_t SensorDevice::Info::setBatchParamsForIdent(void* ident, int flags, + int64_t samplingPeriodNs, + int64_t maxBatchReportLatencyNs) { + ssize_t index = batchParams.indexOfKey(ident); if (index < 0) { - ALOGE("Info::setDelayForIdent(ident=%p, ns=%lld) failed (%s)", - ident, ns, strerror(-index)); + ALOGE("Info::setBatchParamsForIdent(ident=%p, period_ns=%lld timeout=%lld) failed (%s)", + ident, samplingPeriodNs, maxBatchReportLatencyNs, strerror(-index)); return BAD_INDEX; } - rates.editValueAt(index) = ns; + BatchParams& params = batchParams.editValueAt(index); + params.flags = flags; + params.batchDelay = samplingPeriodNs; + params.batchTimeout = maxBatchReportLatencyNs; return NO_ERROR; } -nsecs_t SensorDevice::Info::selectDelay() -{ - nsecs_t ns = rates.valueAt(0); - for (size_t i=1 ; i<rates.size() ; i++) { - nsecs_t cur = rates.valueAt(i); - if (cur < ns) { - ns = cur; +void SensorDevice::Info::selectBatchParams() { + BatchParams bestParams(-1, -1, -1); + + if (batchParams.size() > 0) { + BatchParams params = batchParams.valueAt(0); + bestParams = params; + } + + for (size_t i = 1; i < batchParams.size(); ++i) { + BatchParams params = batchParams.valueAt(i); + if (params.batchDelay < bestParams.batchDelay) { + bestParams.batchDelay = params.batchDelay; + } + if (params.batchTimeout < bestParams.batchTimeout) { + bestParams.batchTimeout = params.batchTimeout; } } - delay = ns; - return ns; + bestBatchParams = bestParams; +} + +ssize_t SensorDevice::Info::removeBatchParamsForIdent(void* ident) { + ssize_t idx = batchParams.removeItem(ident); + if (idx >= 0) { + selectBatchParams(); + } + return idx; } // --------------------------------------------------------------------------- diff --git a/services/sensorservice/SensorDevice.h b/services/sensorservice/SensorDevice.h index 227dab6..761b48c 100644 --- a/services/sensorservice/SensorDevice.h +++ b/services/sensorservice/SensorDevice.h @@ -31,20 +31,50 @@ namespace android { // --------------------------------------------------------------------------- -static const nsecs_t DEFAULT_EVENTS_PERIOD = 200000000; // 5 Hz - class SensorDevice : public Singleton<SensorDevice> { friend class Singleton<SensorDevice>; - struct sensors_poll_device_t* mSensorDevice; + sensors_poll_device_1_t* mSensorDevice; struct sensors_module_t* mSensorModule; - mutable Mutex mLock; // protect mActivationCount[].rates + static const nsecs_t MINIMUM_EVENTS_PERIOD = 1000000; // 1000 Hz + mutable Mutex mLock; // protect mActivationCount[].batchParams // fixed-size array after construction + + // Struct to store all the parameters(samplingPeriod, maxBatchReportLatency and flags) from + // batch call. For continous mode clients, maxBatchReportLatency is set to zero. + struct BatchParams { + int flags; + nsecs_t batchDelay, batchTimeout; + BatchParams() : flags(0), batchDelay(0), batchTimeout(0) {} + BatchParams(int flag, nsecs_t delay, nsecs_t timeout): flags(flag), batchDelay(delay), + batchTimeout(timeout) { } + bool operator != (const BatchParams& other) { + return other.batchDelay != batchDelay || other.batchTimeout != batchTimeout || + other.flags != flags; + } + }; + + // Store batch parameters in the KeyedVector and the optimal batch_rate and timeout in + // bestBatchParams. For every batch() call corresponding params are stored in batchParams + // vector. A continuous mode request is batch(... timeout=0 ..) followed by activate(). A batch + // mode request is batch(... timeout > 0 ...) followed by activate(). + // Info is a per-sensor data structure which contains the batch parameters for each client that + // has registered for this sensor. struct Info { - Info() : delay(0) { } - KeyedVector<void*, nsecs_t> rates; - nsecs_t delay; - status_t setDelayForIdent(void* ident, int64_t ns); - nsecs_t selectDelay(); + BatchParams bestBatchParams; + // Key is the unique identifier(ident) for each client, value is the batch parameters + // requested by the client. + KeyedVector<void*, BatchParams> batchParams; + + Info() : bestBatchParams(-1, -1, -1) {} + // Sets batch parameters for this ident. Returns error if this ident is not already present + // in the KeyedVector above. + status_t setBatchParamsForIdent(void* ident, int flags, int64_t samplingPeriodNs, + int64_t maxBatchReportLatencyNs); + // Finds the optimal parameters for batching and stores them in bestBatchParams variable. + void selectBatchParams(); + // Removes batchParams for an ident and re-computes bestBatchParams. Returns the index of + // the removed ident. If index >=0, ident is present and successfully removed. + ssize_t removeBatchParamsForIdent(void* ident); }; DefaultKeyedVector<int, Info> mActivationCount; @@ -55,9 +85,13 @@ public: int getHalDeviceVersion() const; ssize_t poll(sensors_event_t* buffer, size_t count); status_t activate(void* ident, int handle, int enabled); + status_t batch(void* ident, int handle, int flags, int64_t samplingPeriodNs, + int64_t maxBatchReportLatencyNs); + // Call batch with timeout zero instead of calling setDelay() for newer devices. status_t setDelay(void* ident, int handle, int64_t ns); + status_t flush(void* ident, int handle); void autoDisable(void *ident, int handle); - void dump(String8& result, char* buffer, size_t SIZE); + void dump(String8& result); }; // --------------------------------------------------------------------------- diff --git a/services/sensorservice/SensorFusion.cpp b/services/sensorservice/SensorFusion.cpp index d23906d..03f94be 100644 --- a/services/sensorservice/SensorFusion.cpp +++ b/services/sensorservice/SensorFusion.cpp @@ -28,6 +28,7 @@ SensorFusion::SensorFusion() mEnabled(false), mGyroTime(0) { sensor_t const* list; + Sensor uncalibratedGyro; ssize_t count = mSensorDevice.getSensorList(&list); if (count > 0) { for (size_t i=0 ; i<size_t(count) ; i++) { @@ -39,28 +40,38 @@ SensorFusion::SensorFusion() } if (list[i].type == SENSOR_TYPE_GYROSCOPE) { mGyro = Sensor(list + i); - // 200 Hz for gyro events is a good compromise between precision - // and power/cpu usage. - mGyroRate = 200; - mTargetDelayNs = 1000000000LL/mGyroRate; } + if (list[i].type == SENSOR_TYPE_GYROSCOPE_UNCALIBRATED) { + uncalibratedGyro = Sensor(list + i); + } + } + + // Use the uncalibrated gyroscope for sensor fusion when available + if (uncalibratedGyro.getType() == SENSOR_TYPE_GYROSCOPE_UNCALIBRATED) { + mGyro = uncalibratedGyro; } + + // 200 Hz for gyro events is a good compromise between precision + // and power/cpu usage. + mEstimatedGyroRate = 200; + mTargetDelayNs = 1000000000LL/mEstimatedGyroRate; mFusion.init(); } } void SensorFusion::process(const sensors_event_t& event) { - if (event.type == SENSOR_TYPE_GYROSCOPE) { + if (event.type == mGyro.getType()) { if (mGyroTime != 0) { const float dT = (event.timestamp - mGyroTime) / 1000000000.0f; + mFusion.handleGyro(vec3_t(event.data), dT); + // here we estimate the gyro rate (useful for debugging) const float freq = 1 / dT; if (freq >= 100 && freq<1000) { // filter values obviously wrong const float alpha = 1 / (1 + dT); // 1s time-constant - mGyroRate = freq + (mGyroRate - freq)*alpha; + mEstimatedGyroRate = freq + (mEstimatedGyroRate - freq)*alpha; } } mGyroTime = event.timestamp; - mFusion.handleGyro(vec3_t(event.data), 1.0f/mGyroRate); } else if (event.type == SENSOR_TYPE_MAGNETIC_FIELD) { const vec3_t mag(event.data); mFusion.handleMag(mag); @@ -91,6 +102,15 @@ status_t SensorFusion::activate(void* ident, bool enabled) { } } + if (enabled) { + ALOGD_IF(DEBUG_CONNECTIONS, "SensorFusion calling batch ident=%p ", ident); + // Activating a sensor in continuous mode is equivalent to calling batch with the default + // period and timeout equal to ZERO, followed by a call to activate. + mSensorDevice.batch(ident, mAcc.getHandle(), 0, DEFAULT_EVENTS_PERIOD, 0); + mSensorDevice.batch(ident, mMag.getHandle(), 0, DEFAULT_EVENTS_PERIOD, 0); + mSensorDevice.batch(ident, mGyro.getHandle(), 0, DEFAULT_EVENTS_PERIOD, 0); + } + mSensorDevice.activate(ident, mAcc.getHandle(), enabled); mSensorDevice.activate(ident, mMag.getHandle(), enabled); mSensorDevice.activate(ident, mGyro.getHandle(), enabled); @@ -125,14 +145,14 @@ int32_t SensorFusion::getMinDelay() const { return mAcc.getMinDelay(); } -void SensorFusion::dump(String8& result, char* buffer, size_t SIZE) { +void SensorFusion::dump(String8& result) { const Fusion& fusion(mFusion); - snprintf(buffer, SIZE, "9-axis fusion %s (%d clients), gyro-rate=%7.2fHz, " + result.appendFormat("9-axis fusion %s (%d clients), gyro-rate=%7.2fHz, " "q=< %g, %g, %g, %g > (%g), " "b=< %g, %g, %g >\n", mEnabled ? "enabled" : "disabled", mClients.size(), - mGyroRate, + mEstimatedGyroRate, fusion.getAttitude().x, fusion.getAttitude().y, fusion.getAttitude().z, @@ -141,7 +161,6 @@ void SensorFusion::dump(String8& result, char* buffer, size_t SIZE) { fusion.getBias().x, fusion.getBias().y, fusion.getBias().z); - result.append(buffer); } // --------------------------------------------------------------------------- diff --git a/services/sensorservice/SensorFusion.h b/services/sensorservice/SensorFusion.h index 4c99bcb..b8f360f 100644 --- a/services/sensorservice/SensorFusion.h +++ b/services/sensorservice/SensorFusion.h @@ -37,6 +37,7 @@ class SensorDevice; class SensorFusion : public Singleton<SensorFusion> { friend class Singleton<SensorFusion>; + static const nsecs_t DEFAULT_EVENTS_PERIOD = 200000000; // 5 Hz SensorDevice& mSensorDevice; Sensor mAcc; @@ -44,7 +45,7 @@ class SensorFusion : public Singleton<SensorFusion> { Sensor mGyro; Fusion mFusion; bool mEnabled; - float mGyroRate; + float mEstimatedGyroRate; nsecs_t mTargetDelayNs; nsecs_t mGyroTime; vec4_t mAttitude; @@ -60,7 +61,7 @@ public: mat33_t getRotationMatrix() const { return mFusion.getRotationMatrix(); } vec4_t getAttitude() const { return mAttitude; } vec3_t getGyroBias() const { return mFusion.getBias(); } - float getEstimatedRate() const { return mGyroRate; } + float getEstimatedRate() const { return mEstimatedGyroRate; } status_t activate(void* ident, bool enabled); status_t setDelay(void* ident, int64_t ns); @@ -68,7 +69,7 @@ public: float getPowerUsage() const; int32_t getMinDelay() const; - void dump(String8& result, char* buffer, size_t SIZE); + void dump(String8& result); }; diff --git a/services/sensorservice/SensorInterface.cpp b/services/sensorservice/SensorInterface.cpp index b483b75..f1d1663 100644 --- a/services/sensorservice/SensorInterface.cpp +++ b/services/sensorservice/SensorInterface.cpp @@ -32,7 +32,7 @@ SensorInterface::~SensorInterface() HardwareSensor::HardwareSensor(const sensor_t& sensor) : mSensorDevice(SensorDevice::getInstance()), - mSensor(&sensor) + mSensor(&sensor, mSensorDevice.getHalDeviceVersion()) { ALOGI("%s", sensor.name); } @@ -50,6 +50,16 @@ status_t HardwareSensor::activate(void* ident, bool enabled) { return mSensorDevice.activate(ident, mSensor.getHandle(), enabled); } +status_t HardwareSensor::batch(void* ident, int handle, int flags, + int64_t samplingPeriodNs, int64_t maxBatchReportLatencyNs) { + return mSensorDevice.batch(ident, mSensor.getHandle(), flags, samplingPeriodNs, + maxBatchReportLatencyNs); +} + +status_t HardwareSensor::flush(void* ident, int handle) { + return mSensorDevice.flush(ident, handle); +} + status_t HardwareSensor::setDelay(void* ident, int handle, int64_t ns) { return mSensorDevice.setDelay(ident, handle, ns); } diff --git a/services/sensorservice/SensorInterface.h b/services/sensorservice/SensorInterface.h index 2e14e57..c295e22 100644 --- a/services/sensorservice/SensorInterface.h +++ b/services/sensorservice/SensorInterface.h @@ -38,6 +38,20 @@ public: virtual status_t activate(void* ident, bool enabled) = 0; virtual status_t setDelay(void* ident, int handle, int64_t ns) = 0; + + // Not all sensors need to support batching. + virtual status_t batch(void* ident, int handle, int flags, int64_t samplingPeriodNs, + int64_t maxBatchReportLatencyNs) { + if (maxBatchReportLatencyNs == 0) { + return setDelay(ident, handle, samplingPeriodNs); + } + return -EINVAL; + } + + virtual status_t flush(void* ident, int handle) { + return -EINVAL; + } + virtual Sensor getSensor() const = 0; virtual bool isVirtual() const = 0; virtual void autoDisable(void *ident, int handle) { } @@ -59,7 +73,10 @@ public: const sensors_event_t& event); virtual status_t activate(void* ident, bool enabled); + virtual status_t batch(void* ident, int handle, int flags, int64_t samplingPeriodNs, + int64_t maxBatchReportLatencyNs); virtual status_t setDelay(void* ident, int handle, int64_t ns); + virtual status_t flush(void* ident, int handle); virtual Sensor getSensor() const; virtual bool isVirtual() const { return false; } virtual void autoDisable(void *ident, int handle); diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp index 6481584..555d843 100644 --- a/services/sensorservice/SensorService.cpp +++ b/services/sensorservice/SensorService.cpp @@ -93,6 +93,7 @@ void SensorService::onFirstRef() orientationIndex = i; break; case SENSOR_TYPE_GYROSCOPE: + case SENSOR_TYPE_GYROSCOPE_UNCALIBRATED: hasGyro = true; break; case SENSOR_TYPE_GRAVITY: @@ -108,54 +109,59 @@ void SensorService::onFirstRef() // registered) const SensorFusion& fusion(SensorFusion::getInstance()); + // build the sensor list returned to users + mUserSensorList = mSensorList; + if (hasGyro) { - // Always instantiate Android's virtual sensors. Since they are - // instantiated behind sensors from the HAL, they won't - // interfere with applications, unless they looks specifically - // for them (by name). + Sensor aSensor; - registerVirtualSensor( new RotationVectorSensor() ); - registerVirtualSensor( new GravitySensor(list, count) ); - registerVirtualSensor( new LinearAccelerationSensor(list, count) ); + // Add Android virtual sensors if they're not already + // available in the HAL - // these are optional - registerVirtualSensor( new OrientationSensor() ); - registerVirtualSensor( new CorrectedGyroSensor(list, count) ); - } + aSensor = registerVirtualSensor( new RotationVectorSensor() ); + if (virtualSensorsNeeds & (1<<SENSOR_TYPE_ROTATION_VECTOR)) { + mUserSensorList.add(aSensor); + } - // build the sensor list returned to users - mUserSensorList = mSensorList; + aSensor = registerVirtualSensor( new GravitySensor(list, count) ); + if (virtualSensorsNeeds & (1<<SENSOR_TYPE_GRAVITY)) { + mUserSensorList.add(aSensor); + } - if (hasGyro) { - // virtual debugging sensors are not added to mUserSensorList - registerVirtualSensor( new GyroDriftSensor() ); - } + aSensor = registerVirtualSensor( new LinearAccelerationSensor(list, count) ); + if (virtualSensorsNeeds & (1<<SENSOR_TYPE_LINEAR_ACCELERATION)) { + mUserSensorList.add(aSensor); + } - if (hasGyro && - (virtualSensorsNeeds & (1<<SENSOR_TYPE_ROTATION_VECTOR))) { - // if we have the fancy sensor fusion, and it's not provided by the - // HAL, use our own (fused) orientation sensor by removing the - // HAL supplied one form the user list. - if (orientationIndex >= 0) { - mUserSensorList.removeItemsAt(orientationIndex); + aSensor = registerVirtualSensor( new OrientationSensor() ); + if (virtualSensorsNeeds & (1<<SENSOR_TYPE_ROTATION_VECTOR)) { + // if we are doing our own rotation-vector, also add + // the orientation sensor and remove the HAL provided one. + mUserSensorList.replaceAt(aSensor, orientationIndex); } + + // virtual debugging sensors are not added to mUserSensorList + registerVirtualSensor( new CorrectedGyroSensor(list, count) ); + registerVirtualSensor( new GyroDriftSensor() ); } // debugging sensor list - for (size_t i=0 ; i<mSensorList.size() ; i++) { - switch (mSensorList[i].getType()) { - case SENSOR_TYPE_GRAVITY: - case SENSOR_TYPE_LINEAR_ACCELERATION: - case SENSOR_TYPE_ROTATION_VECTOR: - if (strstr(mSensorList[i].getVendor().string(), "Google")) { - mUserSensorListDebug.add(mSensorList[i]); - } - break; - default: - mUserSensorListDebug.add(mSensorList[i]); - break; + mUserSensorListDebug = mSensorList; + + mSocketBufferSize = SOCKET_BUFFER_SIZE_NON_BATCHED; + FILE *fp = fopen("/proc/sys/net/core/wmem_max", "r"); + char line[128]; + if (fp != NULL && fgets(line, sizeof(line), fp) != NULL) { + line[sizeof(line) - 1] = '\0'; + sscanf(line, "%u", &mSocketBufferSize); + if (mSocketBufferSize > MAX_SOCKET_BUFFER_SIZE_BATCHED) { + mSocketBufferSize = MAX_SOCKET_BUFFER_SIZE_BATCHED; } } + ALOGD("Max socket buffer size %u", mSocketBufferSize); + if (fp) { + fclose(fp); + } run("SensorService", PRIORITY_URGENT_DISPLAY); mInitCheck = NO_ERROR; @@ -163,7 +169,7 @@ void SensorService::onFirstRef() } } -void SensorService::registerSensor(SensorInterface* s) +Sensor SensorService::registerSensor(SensorInterface* s) { sensors_event_t event; memset(&event, 0, sizeof(event)); @@ -175,12 +181,15 @@ void SensorService::registerSensor(SensorInterface* s) mSensorMap.add(sensor.getHandle(), s); // create an entry in the mLastEventSeen array mLastEventSeen.add(sensor.getHandle(), event); + + return sensor; } -void SensorService::registerVirtualSensor(SensorInterface* s) +Sensor SensorService::registerVirtualSensor(SensorInterface* s) { - registerSensor(s); + Sensor sensor = registerSensor(s); mVirtualSensorList.add( s ); + return sensor; } SensorService::~SensorService() @@ -193,47 +202,92 @@ static const String16 sDump("android.permission.DUMP"); status_t SensorService::dump(int fd, const Vector<String16>& args) { - const size_t SIZE = 1024; - char buffer[SIZE]; String8 result; if (!PermissionCache::checkCallingPermission(sDump)) { - snprintf(buffer, SIZE, "Permission Denial: " + result.appendFormat("Permission Denial: " "can't dump SurfaceFlinger from pid=%d, uid=%d\n", IPCThreadState::self()->getCallingPid(), IPCThreadState::self()->getCallingUid()); - result.append(buffer); } else { Mutex::Autolock _l(mLock); - snprintf(buffer, SIZE, "Sensor List:\n"); - result.append(buffer); + result.append("Sensor List:\n"); for (size_t i=0 ; i<mSensorList.size() ; i++) { const Sensor& s(mSensorList[i]); const sensors_event_t& e(mLastEventSeen.valueFor(s.getHandle())); - snprintf(buffer, SIZE, - "%-48s| %-32s | 0x%08x | maxRate=%7.2fHz | " - "last=<%5.1f,%5.1f,%5.1f>\n", + result.appendFormat( + "%-48s| %-32s | 0x%08x | ", s.getName().string(), s.getVendor().string(), - s.getHandle(), - s.getMinDelay() ? (1000000.0f / s.getMinDelay()) : 0.0f, - e.data[0], e.data[1], e.data[2]); - result.append(buffer); + s.getHandle()); + + if (s.getMinDelay() > 0) { + result.appendFormat( + "maxRate=%7.2fHz | ", 1e6f / s.getMinDelay()); + } else { + result.append(s.getMinDelay() == 0 + ? "on-demand | " + : "one-shot | "); + } + if (s.getFifoMaxEventCount() > 0) { + result.appendFormat("getFifoMaxEventCount=%d events | ", s.getFifoMaxEventCount()); + } else { + result.append("no batching support | "); + } + + switch (s.getType()) { + case SENSOR_TYPE_ROTATION_VECTOR: + case SENSOR_TYPE_GEOMAGNETIC_ROTATION_VECTOR: + result.appendFormat( + "last=<%5.1f,%5.1f,%5.1f,%5.1f,%5.1f>\n", + e.data[0], e.data[1], e.data[2], e.data[3], e.data[4]); + break; + case SENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED: + case SENSOR_TYPE_GYROSCOPE_UNCALIBRATED: + result.appendFormat( + "last=<%5.1f,%5.1f,%5.1f,%5.1f,%5.1f,%5.1f>\n", + e.data[0], e.data[1], e.data[2], e.data[3], e.data[4], e.data[5]); + break; + case SENSOR_TYPE_GAME_ROTATION_VECTOR: + result.appendFormat( + "last=<%5.1f,%5.1f,%5.1f,%5.1f>\n", + e.data[0], e.data[1], e.data[2], e.data[3]); + break; + case SENSOR_TYPE_SIGNIFICANT_MOTION: + case SENSOR_TYPE_STEP_DETECTOR: + result.appendFormat( "last=<%f>\n", e.data[0]); + break; + case SENSOR_TYPE_STEP_COUNTER: + result.appendFormat( "last=<%llu>\n", e.u64.step_counter); + break; + default: + // default to 3 values + result.appendFormat( + "last=<%5.1f,%5.1f,%5.1f>\n", + e.data[0], e.data[1], e.data[2]); + break; + } } - SensorFusion::getInstance().dump(result, buffer, SIZE); - SensorDevice::getInstance().dump(result, buffer, SIZE); - - snprintf(buffer, SIZE, "%d active connections\n", - mActiveConnections.size()); - result.append(buffer); - snprintf(buffer, SIZE, "Active sensors:\n"); - result.append(buffer); + SensorFusion::getInstance().dump(result); + SensorDevice::getInstance().dump(result); + + result.append("Active sensors:\n"); for (size_t i=0 ; i<mActiveSensors.size() ; i++) { int handle = mActiveSensors.keyAt(i); - snprintf(buffer, SIZE, "%s (handle=0x%08x, connections=%d)\n", + result.appendFormat("%s (handle=0x%08x, connections=%d)\n", getSensorName(handle).string(), handle, mActiveSensors.valueAt(i)->getNumConnections()); - result.append(buffer); + } + + result.appendFormat("%u Max Socket Buffer size\n", mSocketBufferSize); + result.appendFormat("%d active connections\n", mActiveConnections.size()); + + for (size_t i=0 ; i < mActiveConnections.size() ; i++) { + sp<SensorEventConnection> connection(mActiveConnections[i].promote()); + if (connection != 0) { + result.appendFormat("Connection Number: %d \n", i); + connection->dump(result); + } } } write(fd, result.string(), result.size()); @@ -246,7 +300,8 @@ void SensorService::cleanupAutoDisabledSensor(const sp<SensorEventConnection>& c status_t err = NO_ERROR; for (int i=0 ; i<count ; i++) { int handle = buffer[i].sensor; - if (getSensorType(handle) == SENSOR_TYPE_SIGNIFICANT_MOTION) { + int type = buffer[i].type; + if (type == SENSOR_TYPE_SIGNIFICANT_MOTION) { if (connection->hasSensor(handle)) { sensor = mSensorMap.valueFor(handle); if (sensor != NULL) { @@ -262,8 +317,12 @@ bool SensorService::threadLoop() { ALOGD("nuSensorService thread starting..."); - const size_t numEventMax = 16; - const size_t minBufferSize = numEventMax + numEventMax * mVirtualSensorList.size(); + // each virtual sensor could generate an event per "real" event, that's why we need + // to size numEventMax much smaller than MAX_RECEIVE_BUFFER_EVENT_COUNT. + // in practice, this is too aggressive, but guaranteed to be enough. + const size_t minBufferSize = SensorEventQueue::MAX_RECEIVE_BUFFER_EVENT_COUNT; + const size_t numEventMax = minBufferSize / (1 + mVirtualSensorList.size()); + sensors_event_t buffer[minBufferSize]; sensors_event_t scratch[minBufferSize]; SensorDevice& device(SensorDevice::getInstance()); @@ -283,7 +342,7 @@ bool SensorService::threadLoop() // Todo(): add a flag to the sensors definitions to indicate // the sensors which can wake up the AP for (int i = 0; i < count; i++) { - if (getSensorType(buffer[i].sensor) == SENSOR_TYPE_SIGNIFICANT_MOTION) { + if (buffer[i].type == SENSOR_TYPE_SIGNIFICANT_MOTION) { acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME); wakeLockAcquired = true; break; @@ -335,7 +394,7 @@ bool SensorService::threadLoop() // handle backward compatibility for RotationVector sensor if (halVersion < SENSORS_DEVICE_API_VERSION_1_0) { for (int i = 0; i < count; i++) { - if (getSensorType(buffer[i].sensor) == SENSOR_TYPE_ROTATION_VECTOR) { + if (buffer[i].type == SENSOR_TYPE_ROTATION_VECTOR) { // All the 4 components of the quaternion should be available // No heading accuracy. Set it to -1 buffer[i].data[4] = -1; @@ -359,7 +418,6 @@ bool SensorService::threadLoop() // We have read the data, upper layers should hold the wakelock. if (wakeLockAcquired) release_wake_lock(WAKE_LOCK_NAME); - } while (count >= 0 || Thread::exitPending()); ALOGW("Exiting SensorService::threadLoop => aborting..."); @@ -371,7 +429,6 @@ void SensorService::recordLastValue( sensors_event_t const * buffer, size_t count) { Mutex::Autolock _l(mLock); - // record the last event for each sensor int32_t prev = buffer[0].sensor; for (size_t i=1 ; i<count ; i++) { @@ -423,18 +480,6 @@ String8 SensorService::getSensorName(int handle) const { return result; } -int SensorService::getSensorType(int handle) const { - size_t count = mUserSensorList.size(); - for (size_t i=0 ; i<count ; i++) { - const Sensor& sensor(mUserSensorList[i]); - if (sensor.getHandle() == handle) { - return sensor.getType(); - } - } - return -1; -} - - Vector<Sensor> SensorService::getSensorList() { char value[PROPERTY_VALUE_MAX]; @@ -489,7 +534,7 @@ void SensorService::cleanupConnection(SensorEventConnection* c) } status_t SensorService::enable(const sp<SensorEventConnection>& connection, - int handle) + int handle, nsecs_t samplingPeriodNs, nsecs_t maxBatchReportLatencyNs, int reservedFlags) { if (mInitCheck != NO_ERROR) return mInitCheck; @@ -498,7 +543,6 @@ status_t SensorService::enable(const sp<SensorEventConnection>& connection, if (sensor == NULL) { return BAD_VALUE; } - Mutex::Autolock _l(mLock); SensorRecord* rec = mActiveSensors.valueFor(handle); if (rec == 0) { @@ -535,10 +579,33 @@ status_t SensorService::enable(const sp<SensorEventConnection>& connection, handle, connection.get()); } - // we are setup, now enable the sensor. - status_t err = sensor->activate(connection.get(), true); + nsecs_t minDelayNs = sensor->getSensor().getMinDelayNs(); + if (samplingPeriodNs < minDelayNs) { + samplingPeriodNs = minDelayNs; + } + + ALOGD_IF(DEBUG_CONNECTIONS, "Calling batch handle==%d flags=%d rate=%lld timeout== %lld", + handle, reservedFlags, samplingPeriodNs, maxBatchReportLatencyNs); + + status_t err = sensor->batch(connection.get(), handle, reservedFlags, samplingPeriodNs, + maxBatchReportLatencyNs); + if (err == NO_ERROR) { + connection->setFirstFlushPending(handle, true); + status_t err_flush = sensor->flush(connection.get(), handle); + // Flush may return error if the sensor is not activated or the underlying h/w sensor does + // not support flush. + if (err_flush != NO_ERROR) { + connection->setFirstFlushPending(handle, false); + } + } + + if (err == NO_ERROR) { + ALOGD_IF(DEBUG_CONNECTIONS, "Calling activate on %d", handle); + err = sensor->activate(connection.get(), true); + } + if (err != NO_ERROR) { - // enable has failed, reset our state. + // batch/activate has failed, reset our state. cleanupWithoutDisableLocked(connection, handle); } return err; @@ -605,12 +672,22 @@ status_t SensorService::setEventRate(const sp<SensorEventConnection>& connection ns = minDelayNs; } - if (ns < MINIMUM_EVENTS_PERIOD) - ns = MINIMUM_EVENTS_PERIOD; - return sensor->setDelay(connection.get(), handle, ns); } +status_t SensorService::flushSensor(const sp<SensorEventConnection>& connection, + int handle) { + if (mInitCheck != NO_ERROR) return mInitCheck; + SensorInterface* sensor = mSensorMap.valueFor(handle); + if (sensor == NULL) { + return BAD_VALUE; + } + if (sensor->getSensor().getType() == SENSOR_TYPE_SIGNIFICANT_MOTION) { + ALOGE("flush called on Significant Motion sensor"); + return INVALID_OPERATION; + } + return sensor->flush(connection.get(), handle); +} // --------------------------------------------------------------------------- SensorService::SensorRecord::SensorRecord( @@ -643,8 +720,15 @@ bool SensorService::SensorRecord::removeConnection( SensorService::SensorEventConnection::SensorEventConnection( const sp<SensorService>& service, uid_t uid) - : mService(service), mChannel(new BitTube()), mUid(uid) + : mService(service), mUid(uid) { + const SensorDevice& device(SensorDevice::getInstance()); + if (device.getHalDeviceVersion() >= SENSORS_DEVICE_API_VERSION_1_1) { + // Increase socket buffer size to 1MB for batching capabilities. + mChannel = new BitTube(service->mSocketBufferSize); + } else { + mChannel = new BitTube(SOCKET_BUFFER_SIZE_NON_BATCHED); + } } SensorService::SensorEventConnection::~SensorEventConnection() @@ -657,10 +741,22 @@ void SensorService::SensorEventConnection::onFirstRef() { } +void SensorService::SensorEventConnection::dump(String8& result) { + Mutex::Autolock _l(mConnectionLock); + for (size_t i = 0; i < mSensorInfo.size(); ++i) { + const FlushInfo& flushInfo = mSensorInfo.valueAt(i); + result.appendFormat("\t %s | status: %s | pending flush events %d\n", + mService->getSensorName(mSensorInfo.keyAt(i)).string(), + flushInfo.mFirstFlushPending ? "First flush pending" : + "active", + flushInfo.mPendingFlushEventsToSend); + } +} + bool SensorService::SensorEventConnection::addSensor(int32_t handle) { Mutex::Autolock _l(mConnectionLock); - if (mSensorInfo.indexOf(handle) < 0) { - mSensorInfo.add(handle); + if (mSensorInfo.indexOfKey(handle) < 0) { + mSensorInfo.add(handle, FlushInfo()); return true; } return false; @@ -668,7 +764,7 @@ bool SensorService::SensorEventConnection::addSensor(int32_t handle) { bool SensorService::SensorEventConnection::removeSensor(int32_t handle) { Mutex::Autolock _l(mConnectionLock); - if (mSensorInfo.remove(handle) >= 0) { + if (mSensorInfo.removeItem(handle) >= 0) { return true; } return false; @@ -676,7 +772,7 @@ bool SensorService::SensorEventConnection::removeSensor(int32_t handle) { bool SensorService::SensorEventConnection::hasSensor(int32_t handle) const { Mutex::Autolock _l(mConnectionLock); - return mSensorInfo.indexOf(handle) >= 0; + return mSensorInfo.indexOfKey(handle) >= 0; } bool SensorService::SensorEventConnection::hasAnySensor() const { @@ -684,21 +780,50 @@ bool SensorService::SensorEventConnection::hasAnySensor() const { return mSensorInfo.size() ? true : false; } +void SensorService::SensorEventConnection::setFirstFlushPending(int32_t handle, + bool value) { + Mutex::Autolock _l(mConnectionLock); + ssize_t index = mSensorInfo.indexOfKey(handle); + if (index >= 0) { + FlushInfo& flushInfo = mSensorInfo.editValueAt(index); + flushInfo.mFirstFlushPending = value; + } +} + status_t SensorService::SensorEventConnection::sendEvents( sensors_event_t const* buffer, size_t numEvents, sensors_event_t* scratch) { // filter out events not for this connection size_t count = 0; + if (scratch) { Mutex::Autolock _l(mConnectionLock); size_t i=0; while (i<numEvents) { - const int32_t curr = buffer[i].sensor; - if (mSensorInfo.indexOf(curr) >= 0) { + int32_t curr = buffer[i].sensor; + if (buffer[i].type == SENSOR_TYPE_META_DATA) { + ALOGD_IF(DEBUG_CONNECTIONS, "flush complete event sensor==%d ", + buffer[i].meta_data.sensor); + // Setting curr to the correct sensor to ensure the sensor events per connection are + // filtered correctly. buffer[i].sensor is zero for meta_data events. + curr = buffer[i].meta_data.sensor; + } + ssize_t index = mSensorInfo.indexOfKey(curr); + if (index >= 0 && mSensorInfo[index].mFirstFlushPending == true && + buffer[i].type == SENSOR_TYPE_META_DATA) { + // This is the first flush before activate is called. Events can now be sent for + // this sensor on this connection. + ALOGD_IF(DEBUG_CONNECTIONS, "First flush event for sensor==%d ", + buffer[i].meta_data.sensor); + mSensorInfo.editValueAt(index).mFirstFlushPending = false; + } + if (index >= 0 && mSensorInfo[index].mFirstFlushPending == false) { do { scratch[count++] = buffer[i++]; - } while ((i<numEvents) && (buffer[i].sensor == curr)); + } while ((i<numEvents) && ((buffer[i].sensor == curr) || + (buffer[i].type == SENSOR_TYPE_META_DATA && + buffer[i].meta_data.sensor == curr))); } else { i++; } @@ -708,30 +833,75 @@ status_t SensorService::SensorEventConnection::sendEvents( count = numEvents; } + // Send pending flush events (if any) before sending events from the cache. + { + ASensorEvent flushCompleteEvent; + flushCompleteEvent.type = SENSOR_TYPE_META_DATA; + flushCompleteEvent.sensor = 0; + Mutex::Autolock _l(mConnectionLock); + // Loop through all the sensors for this connection and check if there are any pending + // flush complete events to be sent. + for (size_t i = 0; i < mSensorInfo.size(); ++i) { + FlushInfo& flushInfo = mSensorInfo.editValueAt(i); + while (flushInfo.mPendingFlushEventsToSend > 0) { + flushCompleteEvent.meta_data.sensor = mSensorInfo.keyAt(i); + ssize_t size = SensorEventQueue::write(mChannel, &flushCompleteEvent, 1); + if (size < 0) { + // ALOGW("dropping %d events on the floor", count); + countFlushCompleteEventsLocked(scratch, count); + return size; + } + ALOGD_IF(DEBUG_CONNECTIONS, "sent dropped flush complete event==%d ", + flushCompleteEvent.meta_data.sensor); + flushInfo.mPendingFlushEventsToSend--; + } + } + } + // NOTE: ASensorEvent and sensors_event_t are the same type ssize_t size = SensorEventQueue::write(mChannel, reinterpret_cast<ASensorEvent const*>(scratch), count); if (size == -EAGAIN) { // the destination doesn't accept events anymore, it's probably // full. For now, we just drop the events on the floor. - //ALOGW("dropping %d events on the floor", count); + // ALOGW("dropping %d events on the floor", count); + Mutex::Autolock _l(mConnectionLock); + countFlushCompleteEventsLocked(scratch, count); return size; } return size < 0 ? status_t(size) : status_t(NO_ERROR); } +void SensorService::SensorEventConnection::countFlushCompleteEventsLocked( + sensors_event_t* scratch, const int numEventsDropped) { + ALOGD_IF(DEBUG_CONNECTIONS, "dropping %d events ", numEventsDropped); + // Count flushComplete events in the events that are about to the dropped. These will be sent + // separately before the next batch of events. + for (int j = 0; j < numEventsDropped; ++j) { + if (scratch[j].type == SENSOR_TYPE_META_DATA) { + FlushInfo& flushInfo = mSensorInfo.editValueFor(scratch[j].meta_data.sensor); + flushInfo.mPendingFlushEventsToSend++; + ALOGD_IF(DEBUG_CONNECTIONS, "increment pendingFlushCount %d", + flushInfo.mPendingFlushEventsToSend); + } + } + return; +} + sp<BitTube> SensorService::SensorEventConnection::getSensorChannel() const { return mChannel; } status_t SensorService::SensorEventConnection::enableDisable( - int handle, bool enabled) + int handle, bool enabled, nsecs_t samplingPeriodNs, nsecs_t maxBatchReportLatencyNs, + int reservedFlags) { status_t err; if (enabled) { - err = mService->enable(this, handle); + err = mService->enable(this, handle, samplingPeriodNs, maxBatchReportLatencyNs, + reservedFlags); } else { err = mService->disable(this, handle); } @@ -739,9 +909,33 @@ status_t SensorService::SensorEventConnection::enableDisable( } status_t SensorService::SensorEventConnection::setEventRate( - int handle, nsecs_t ns) + int handle, nsecs_t samplingPeriodNs) { - return mService->setEventRate(this, handle, ns); + return mService->setEventRate(this, handle, samplingPeriodNs); +} + +status_t SensorService::SensorEventConnection::flush() { + SensorDevice& dev(SensorDevice::getInstance()); + const int halVersion = dev.getHalDeviceVersion(); + Mutex::Autolock _l(mConnectionLock); + status_t err(NO_ERROR); + // Loop through all sensors for this connection and call flush on each of them. + for (size_t i = 0; i < mSensorInfo.size(); ++i) { + const int handle = mSensorInfo.keyAt(i); + if (halVersion < SENSORS_DEVICE_API_VERSION_1_1) { + // For older devices just increment pending flush count which will send a trivial + // flush complete event. + FlushInfo& flushInfo = mSensorInfo.editValueFor(handle); + flushInfo.mPendingFlushEventsToSend++; + } else { + status_t err_flush = mService->flushSensor(this, handle); + if (err_flush != NO_ERROR) { + ALOGE("Flush error handle=%d %s", handle, strerror(-err_flush)); + } + err = (err_flush != NO_ERROR) ? err_flush : err; + } + } + return err; } // --------------------------------------------------------------------------- diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h index fcdbc7d..6c1691a 100644 --- a/services/sensorservice/SensorService.h +++ b/services/sensorservice/SensorService.h @@ -38,6 +38,9 @@ // --------------------------------------------------------------------------- #define DEBUG_CONNECTIONS false +// Max size is 1 MB which is enough to accept a batch of about 10k events. +#define MAX_SOCKET_BUFFER_SIZE_BATCHED 1024 * 1024 +#define SOCKET_BUFFER_SIZE_NON_BATCHED 4 * 1024 struct sensors_poll_device_t; struct sensors_module_t; @@ -50,12 +53,12 @@ class SensorService : public BnSensorServer, protected Thread { - friend class BinderService<SensorService>; + friend class BinderService<SensorService>; - static const nsecs_t MINIMUM_EVENTS_PERIOD = 1000000; // 1000 Hz - static const char* WAKE_LOCK_NAME; + static const char* WAKE_LOCK_NAME; - SensorService(); + static char const* getServiceName() ANDROID_API { return "sensorservice"; } + SensorService() ANDROID_API; virtual ~SensorService(); virtual void onFirstRef(); @@ -73,16 +76,31 @@ class SensorService : virtual ~SensorEventConnection(); virtual void onFirstRef(); virtual sp<BitTube> getSensorChannel() const; - virtual status_t enableDisable(int handle, bool enabled); - virtual status_t setEventRate(int handle, nsecs_t ns); + virtual status_t enableDisable(int handle, bool enabled, nsecs_t samplingPeriodNs, + nsecs_t maxBatchReportLatencyNs, int reservedFlags); + virtual status_t setEventRate(int handle, nsecs_t samplingPeriodNs); + virtual status_t flush(); + // Count the number of flush complete events which are about to be dropped in the buffer. + // Increment mPendingFlushEventsToSend in mSensorInfo. These flush complete events will be + // sent separately before the next batch of events. + void countFlushCompleteEventsLocked(sensors_event_t* scratch, int numEventsDropped); sp<SensorService> const mService; - sp<BitTube> const mChannel; + sp<BitTube> mChannel; uid_t mUid; mutable Mutex mConnectionLock; - // protected by SensorService::mLock - SortedVector<int> mSensorInfo; + struct FlushInfo { + // The number of flush complete events dropped for this sensor is stored here. + // They are sent separately before the next batch of events. + int mPendingFlushEventsToSend; + // Every activate is preceded by a flush. Only after the first flush complete is + // received, the events for the sensor are sent on that *connection*. + bool mFirstFlushPending; + FlushInfo() : mPendingFlushEventsToSend(0), mFirstFlushPending(false) {} + }; + // protected by SensorService::mLock. Key for this vector is the sensor handle. + KeyedVector<int, FlushInfo> mSensorInfo; public: SensorEventConnection(const sp<SensorService>& service, uid_t uid); @@ -93,6 +111,8 @@ class SensorService : bool hasAnySensor() const; bool addSensor(int32_t handle); bool removeSensor(int32_t handle); + void setFirstFlushPending(int32_t handle, bool value); + void dump(String8& result); uid_t getUid() const { return mUid; } }; @@ -110,17 +130,16 @@ class SensorService : DefaultKeyedVector<int, SensorInterface*> getActiveVirtualSensors() const; String8 getSensorName(int handle) const; - int getSensorType(int handle) const; void recordLastValue(sensors_event_t const * buffer, size_t count); static void sortEventBuffer(sensors_event_t* buffer, size_t count); - void registerSensor(SensorInterface* sensor); - void registerVirtualSensor(SensorInterface* sensor); + Sensor registerSensor(SensorInterface* sensor); + Sensor registerVirtualSensor(SensorInterface* sensor); status_t cleanupWithoutDisable( const sp<SensorEventConnection>& connection, int handle); status_t cleanupWithoutDisableLocked( const sp<SensorEventConnection>& connection, int handle); void cleanupAutoDisabledSensor(const sp<SensorEventConnection>& connection, - sensors_event_t const* buffer, const int count); + sensors_event_t const* buffer, const int count); // constants Vector<Sensor> mSensorList; @@ -129,6 +148,7 @@ class SensorService : DefaultKeyedVector<int, SensorInterface*> mSensorMap; Vector<SensorInterface *> mVirtualSensorList; status_t mInitCheck; + size_t mSocketBufferSize; // protected by mLock mutable Mutex mLock; @@ -140,12 +160,12 @@ class SensorService : KeyedVector<int32_t, sensors_event_t> mLastEventSeen; public: - static char const* getServiceName() { return "sensorservice"; } - void cleanupConnection(SensorEventConnection* connection); - status_t enable(const sp<SensorEventConnection>& connection, int handle); + status_t enable(const sp<SensorEventConnection>& connection, int handle, + nsecs_t samplingPeriodNs, nsecs_t maxBatchReportLatencyNs, int reservedFlags); status_t disable(const sp<SensorEventConnection>& connection, int handle); status_t setEventRate(const sp<SensorEventConnection>& connection, int handle, nsecs_t ns); + status_t flushSensor(const sp<SensorEventConnection>& connection, int handle); }; // --------------------------------------------------------------------------- diff --git a/cmds/sensorservice/main_sensorservice.cpp b/services/sensorservice/main_sensorservice.cpp index 8610627..303b65f 100644 --- a/cmds/sensorservice/main_sensorservice.cpp +++ b/services/sensorservice/main_sensorservice.cpp @@ -15,7 +15,7 @@ */ #include <binder/BinderService.h> -#include <SensorService.h> +#include "SensorService.h" using namespace android; diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk index ec296d3..c3daa64 100644 --- a/services/surfaceflinger/Android.mk +++ b/services/surfaceflinger/Android.mk @@ -4,9 +4,10 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ Client.cpp \ DisplayDevice.cpp \ + DispSync.cpp \ + EventControlThread.cpp \ EventThread.cpp \ FrameTracker.cpp \ - GLExtensions.cpp \ Layer.cpp \ LayerDim.cpp \ MessageQueue.cpp \ @@ -18,6 +19,20 @@ LOCAL_SRC_FILES:= \ DisplayHardware/HWComposer.cpp \ DisplayHardware/PowerHAL.cpp \ DisplayHardware/VirtualDisplaySurface.cpp \ + Effects/Daltonizer.cpp \ + EventLog/EventLogTags.logtags \ + EventLog/EventLog.cpp \ + RenderEngine/Description.cpp \ + RenderEngine/Mesh.cpp \ + RenderEngine/Program.cpp \ + RenderEngine/ProgramCache.cpp \ + RenderEngine/GLExtensions.cpp \ + RenderEngine/RenderEngine.cpp \ + RenderEngine/Texture.cpp \ + RenderEngine/GLES10RenderEngine.cpp \ + RenderEngine/GLES11RenderEngine.cpp \ + RenderEngine/GLES20RenderEngine.cpp + LOCAL_CFLAGS:= -DLOG_TAG=\"SurfaceFlinger\" LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES @@ -30,7 +45,6 @@ ifeq ($(TARGET_BOARD_PLATFORM),omap4) endif ifeq ($(TARGET_BOARD_PLATFORM),s5pc110) LOCAL_CFLAGS += -DHAS_CONTEXT_PRIORITY - LOCAL_CFLAGS += -DNEVER_DEFAULT_TO_ASYNC_MODE endif ifeq ($(TARGET_DISABLE_TRIPLE_BUFFERING),true) @@ -41,6 +55,32 @@ ifneq ($(NUM_FRAMEBUFFER_SURFACE_BUFFERS),) LOCAL_CFLAGS += -DNUM_FRAMEBUFFER_SURFACE_BUFFERS=$(NUM_FRAMEBUFFER_SURFACE_BUFFERS) endif +ifeq ($(TARGET_RUNNING_WITHOUT_SYNC_FRAMEWORK),true) + LOCAL_CFLAGS += -DRUNNING_WITHOUT_SYNC_FRAMEWORK +endif + +# See build/target/board/generic/BoardConfig.mk for a description of this setting. +ifneq ($(VSYNC_EVENT_PHASE_OFFSET_NS),) + LOCAL_CFLAGS += -DVSYNC_EVENT_PHASE_OFFSET_NS=$(VSYNC_EVENT_PHASE_OFFSET_NS) +else + LOCAL_CFLAGS += -DVSYNC_EVENT_PHASE_OFFSET_NS=0 +endif + +# See build/target/board/generic/BoardConfig.mk for a description of this setting. +ifneq ($(SF_VSYNC_EVENT_PHASE_OFFSET_NS),) + LOCAL_CFLAGS += -DSF_VSYNC_EVENT_PHASE_OFFSET_NS=$(SF_VSYNC_EVENT_PHASE_OFFSET_NS) +else + LOCAL_CFLAGS += -DSF_VSYNC_EVENT_PHASE_OFFSET_NS=0 +endif + +ifneq ($(PRESENT_TIME_OFFSET_FROM_VSYNC_NS),) + LOCAL_CFLAGS += -DPRESENT_TIME_OFFSET_FROM_VSYNC_NS=$(PRESENT_TIME_OFFSET_FROM_VSYNC_NS) +else + LOCAL_CFLAGS += -DPRESENT_TIME_OFFSET_FROM_VSYNC_NS=0 +endif + +LOCAL_CFLAGS += -fvisibility=hidden + LOCAL_SHARED_LIBRARIES := \ libcutils \ liblog \ @@ -49,6 +89,7 @@ LOCAL_SHARED_LIBRARIES := \ libutils \ libEGL \ libGLESv1_CM \ + libGLESv2 \ libbinder \ libui \ libgui @@ -58,6 +99,26 @@ LOCAL_MODULE:= libsurfaceflinger include $(BUILD_SHARED_LIBRARY) ############################################################### +# build surfaceflinger's executable +include $(CLEAR_VARS) + +LOCAL_CFLAGS:= -DLOG_TAG=\"SurfaceFlinger\" + +LOCAL_SRC_FILES:= \ + main_surfaceflinger.cpp + +LOCAL_SHARED_LIBRARIES := \ + libsurfaceflinger \ + libcutils \ + liblog \ + libbinder \ + libutils + +LOCAL_MODULE:= surfaceflinger + +include $(BUILD_EXECUTABLE) + +############################################################### # uses jni which may not be available in PDK ifneq ($(wildcard libnativehelper/include),) include $(CLEAR_VARS) diff --git a/services/surfaceflinger/Client.cpp b/services/surfaceflinger/Client.cpp index dd65348..975631c 100644 --- a/services/surfaceflinger/Client.cpp +++ b/services/surfaceflinger/Client.cpp @@ -18,6 +18,7 @@ #include <sys/types.h> #include <binder/PermissionCache.h> +#include <binder/IPCThreadState.h> #include <private/android_filesystem_config.h> diff --git a/services/surfaceflinger/Colorizer.h b/services/surfaceflinger/Colorizer.h new file mode 100644 index 0000000..6524481 --- /dev/null +++ b/services/surfaceflinger/Colorizer.h @@ -0,0 +1,65 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_SURFACE_FLINGER_COLORIZER_H +#define ANDROID_SURFACE_FLINGER_COLORIZER_H + +namespace android { + +// --------------------------------------------------------------------------- + +class Colorizer { + bool mEnabled; +public: + enum color { + RED = 31, + GREEN = 32, + YELLOW = 33, + BLUE = 34, + MAGENTA = 35, + CYAN = 36, + WHITE = 37 + }; + + Colorizer(bool enabled) + : mEnabled(enabled) { + } + + void colorize(String8& out, color c) { + if (mEnabled) { + out.appendFormat("\e[%dm", c); + } + } + + void bold(String8& out) { + if (mEnabled) { + out.append("\e[1m"); + } + } + + void reset(String8& out) { + if (mEnabled) { + out.append("\e[0m"); + } + } +}; + +// --------------------------------------------------------------------------- + +}; // namespace android + + +#endif /* ANDROID_SURFACE_FLINGER_COLORIZER_H */ diff --git a/services/surfaceflinger/DispSync.cpp b/services/surfaceflinger/DispSync.cpp new file mode 100644 index 0000000..167c6f0 --- /dev/null +++ b/services/surfaceflinger/DispSync.cpp @@ -0,0 +1,483 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +// This is needed for stdint.h to define INT64_MAX in C++ +#define __STDC_LIMIT_MACROS + +#include <math.h> + +#include <cutils/log.h> + +#include <ui/Fence.h> + +#include <utils/String8.h> +#include <utils/Thread.h> +#include <utils/Trace.h> +#include <utils/Vector.h> + +#include "DispSync.h" +#include "EventLog/EventLog.h" + +namespace android { + +// Setting this to true enables verbose tracing that can be used to debug +// vsync event model or phase issues. +static const bool traceDetailedInfo = false; + +// This is the threshold used to determine when hardware vsync events are +// needed to re-synchronize the software vsync model with the hardware. The +// error metric used is the mean of the squared difference between each +// present time and the nearest software-predicted vsync. +static const nsecs_t errorThreshold = 160000000000; + +// This works around the lack of support for the sync framework on some +// devices. +#ifdef RUNNING_WITHOUT_SYNC_FRAMEWORK +static const bool runningWithoutSyncFramework = true; +#else +static const bool runningWithoutSyncFramework = false; +#endif + +// This is the offset from the present fence timestamps to the corresponding +// vsync event. +static const int64_t presentTimeOffset = PRESENT_TIME_OFFSET_FROM_VSYNC_NS; + +class DispSyncThread: public Thread { +public: + + DispSyncThread(): + mStop(false), + mPeriod(0), + mPhase(0), + mWakeupLatency(0) { + } + + virtual ~DispSyncThread() {} + + void updateModel(nsecs_t period, nsecs_t phase) { + Mutex::Autolock lock(mMutex); + mPeriod = period; + mPhase = phase; + mCond.signal(); + } + + void stop() { + Mutex::Autolock lock(mMutex); + mStop = true; + mCond.signal(); + } + + virtual bool threadLoop() { + status_t err; + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t nextEventTime = 0; + + while (true) { + Vector<CallbackInvocation> callbackInvocations; + + nsecs_t targetTime = 0; + + { // Scope for lock + Mutex::Autolock lock(mMutex); + + if (mStop) { + return false; + } + + if (mPeriod == 0) { + err = mCond.wait(mMutex); + if (err != NO_ERROR) { + ALOGE("error waiting for new events: %s (%d)", + strerror(-err), err); + return false; + } + continue; + } + + nextEventTime = computeNextEventTimeLocked(now); + targetTime = nextEventTime; + + bool isWakeup = false; + + if (now < targetTime) { + err = mCond.waitRelative(mMutex, targetTime - now); + + if (err == TIMED_OUT) { + isWakeup = true; + } else if (err != NO_ERROR) { + ALOGE("error waiting for next event: %s (%d)", + strerror(-err), err); + return false; + } + } + + now = systemTime(SYSTEM_TIME_MONOTONIC); + + if (isWakeup) { + mWakeupLatency = ((mWakeupLatency * 63) + + (now - targetTime)) / 64; + if (mWakeupLatency > 500000) { + // Don't correct by more than 500 us + mWakeupLatency = 500000; + } + if (traceDetailedInfo) { + ATRACE_INT64("DispSync:WakeupLat", now - nextEventTime); + ATRACE_INT64("DispSync:AvgWakeupLat", mWakeupLatency); + } + } + + callbackInvocations = gatherCallbackInvocationsLocked(now); + } + + if (callbackInvocations.size() > 0) { + fireCallbackInvocations(callbackInvocations); + } + } + + return false; + } + + status_t addEventListener(nsecs_t phase, const sp<DispSync::Callback>& callback) { + Mutex::Autolock lock(mMutex); + + for (size_t i = 0; i < mEventListeners.size(); i++) { + if (mEventListeners[i].mCallback == callback) { + return BAD_VALUE; + } + } + + EventListener listener; + listener.mPhase = phase; + listener.mCallback = callback; + listener.mLastEventTime = systemTime(SYSTEM_TIME_MONOTONIC); + mEventListeners.push(listener); + + mCond.signal(); + + return NO_ERROR; + } + + status_t removeEventListener(const sp<DispSync::Callback>& callback) { + Mutex::Autolock lock(mMutex); + + for (size_t i = 0; i < mEventListeners.size(); i++) { + if (mEventListeners[i].mCallback == callback) { + mEventListeners.removeAt(i); + mCond.signal(); + return NO_ERROR; + } + } + + return BAD_VALUE; + } + + // This method is only here to handle the runningWithoutSyncFramework + // case. + bool hasAnyEventListeners() { + Mutex::Autolock lock(mMutex); + return !mEventListeners.empty(); + } + +private: + + struct EventListener { + nsecs_t mPhase; + nsecs_t mLastEventTime; + sp<DispSync::Callback> mCallback; + }; + + struct CallbackInvocation { + sp<DispSync::Callback> mCallback; + nsecs_t mEventTime; + }; + + nsecs_t computeNextEventTimeLocked(nsecs_t now) { + nsecs_t nextEventTime = INT64_MAX; + for (size_t i = 0; i < mEventListeners.size(); i++) { + nsecs_t t = computeListenerNextEventTimeLocked(mEventListeners[i], + now); + + if (t < nextEventTime) { + nextEventTime = t; + } + } + + return nextEventTime; + } + + Vector<CallbackInvocation> gatherCallbackInvocationsLocked(nsecs_t now) { + Vector<CallbackInvocation> callbackInvocations; + nsecs_t ref = now - mPeriod; + + for (size_t i = 0; i < mEventListeners.size(); i++) { + nsecs_t t = computeListenerNextEventTimeLocked(mEventListeners[i], + ref); + + if (t < now) { + CallbackInvocation ci; + ci.mCallback = mEventListeners[i].mCallback; + ci.mEventTime = t; + callbackInvocations.push(ci); + mEventListeners.editItemAt(i).mLastEventTime = t; + } + } + + return callbackInvocations; + } + + nsecs_t computeListenerNextEventTimeLocked(const EventListener& listener, + nsecs_t ref) { + + nsecs_t lastEventTime = listener.mLastEventTime; + if (ref < lastEventTime) { + ref = lastEventTime; + } + + nsecs_t phase = mPhase + listener.mPhase; + nsecs_t t = (((ref - phase) / mPeriod) + 1) * mPeriod + phase; + + if (t - listener.mLastEventTime < mPeriod / 2) { + t += mPeriod; + } + + return t; + } + + void fireCallbackInvocations(const Vector<CallbackInvocation>& callbacks) { + for (size_t i = 0; i < callbacks.size(); i++) { + callbacks[i].mCallback->onDispSyncEvent(callbacks[i].mEventTime); + } + } + + bool mStop; + + nsecs_t mPeriod; + nsecs_t mPhase; + nsecs_t mWakeupLatency; + + Vector<EventListener> mEventListeners; + + Mutex mMutex; + Condition mCond; +}; + +class ZeroPhaseTracer : public DispSync::Callback { +public: + ZeroPhaseTracer() : mParity(false) {} + + virtual void onDispSyncEvent(nsecs_t when) { + mParity = !mParity; + ATRACE_INT("ZERO_PHASE_VSYNC", mParity ? 1 : 0); + } + +private: + bool mParity; +}; + +DispSync::DispSync() { + mThread = new DispSyncThread(); + mThread->run("DispSync", PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE); + + reset(); + beginResync(); + + if (traceDetailedInfo) { + // If runningWithoutSyncFramework is true then the ZeroPhaseTracer + // would prevent HW vsync event from ever being turned off. + // Furthermore the zero-phase tracing is not needed because any time + // there is an event registered we will turn on the HW vsync events. + if (!runningWithoutSyncFramework) { + addEventListener(0, new ZeroPhaseTracer()); + } + } +} + +DispSync::~DispSync() {} + +void DispSync::reset() { + Mutex::Autolock lock(mMutex); + + mNumResyncSamples = 0; + mFirstResyncSample = 0; + mNumResyncSamplesSincePresent = 0; + resetErrorLocked(); +} + +bool DispSync::addPresentFence(const sp<Fence>& fence) { + Mutex::Autolock lock(mMutex); + + mPresentFences[mPresentSampleOffset] = fence; + mPresentTimes[mPresentSampleOffset] = 0; + mPresentSampleOffset = (mPresentSampleOffset + 1) % NUM_PRESENT_SAMPLES; + mNumResyncSamplesSincePresent = 0; + + for (size_t i = 0; i < NUM_PRESENT_SAMPLES; i++) { + const sp<Fence>& f(mPresentFences[i]); + if (f != NULL) { + nsecs_t t = f->getSignalTime(); + if (t < INT64_MAX) { + mPresentFences[i].clear(); + mPresentTimes[i] = t + presentTimeOffset; + } + } + } + + updateErrorLocked(); + + return mPeriod == 0 || mError > errorThreshold; +} + +void DispSync::beginResync() { + Mutex::Autolock lock(mMutex); + + mNumResyncSamples = 0; +} + +bool DispSync::addResyncSample(nsecs_t timestamp) { + Mutex::Autolock lock(mMutex); + + size_t idx = (mFirstResyncSample + mNumResyncSamples) % MAX_RESYNC_SAMPLES; + mResyncSamples[idx] = timestamp; + + if (mNumResyncSamples < MAX_RESYNC_SAMPLES) { + mNumResyncSamples++; + } else { + mFirstResyncSample = (mFirstResyncSample + 1) % MAX_RESYNC_SAMPLES; + } + + updateModelLocked(); + + if (mNumResyncSamplesSincePresent++ > MAX_RESYNC_SAMPLES_WITHOUT_PRESENT) { + resetErrorLocked(); + } + + if (runningWithoutSyncFramework) { + // If we don't have the sync framework we will never have + // addPresentFence called. This means we have no way to know whether + // or not we're synchronized with the HW vsyncs, so we just request + // that the HW vsync events be turned on whenever we need to generate + // SW vsync events. + return mThread->hasAnyEventListeners(); + } + + return mPeriod == 0 || mError > errorThreshold; +} + +void DispSync::endResync() { +} + +status_t DispSync::addEventListener(nsecs_t phase, + const sp<Callback>& callback) { + + Mutex::Autolock lock(mMutex); + return mThread->addEventListener(phase, callback); +} + +status_t DispSync::removeEventListener(const sp<Callback>& callback) { + Mutex::Autolock lock(mMutex); + return mThread->removeEventListener(callback); +} + +void DispSync::setPeriod(nsecs_t period) { + Mutex::Autolock lock(mMutex); + mPeriod = period; + mPhase = 0; + mThread->updateModel(mPeriod, mPhase); +} + +void DispSync::updateModelLocked() { + if (mNumResyncSamples >= MIN_RESYNC_SAMPLES_FOR_UPDATE) { + nsecs_t durationSum = 0; + for (size_t i = 1; i < mNumResyncSamples; i++) { + size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES; + size_t prev = (idx + MAX_RESYNC_SAMPLES - 1) % MAX_RESYNC_SAMPLES; + durationSum += mResyncSamples[idx] - mResyncSamples[prev]; + } + + mPeriod = durationSum / (mNumResyncSamples - 1); + + double sampleAvgX = 0; + double sampleAvgY = 0; + double scale = 2.0 * M_PI / double(mPeriod); + for (size_t i = 0; i < mNumResyncSamples; i++) { + size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES; + nsecs_t sample = mResyncSamples[idx]; + double samplePhase = double(sample % mPeriod) * scale; + sampleAvgX += cos(samplePhase); + sampleAvgY += sin(samplePhase); + } + + sampleAvgX /= double(mNumResyncSamples); + sampleAvgY /= double(mNumResyncSamples); + + mPhase = nsecs_t(atan2(sampleAvgY, sampleAvgX) / scale); + + if (mPhase < 0) { + mPhase += mPeriod; + } + + if (traceDetailedInfo) { + ATRACE_INT64("DispSync:Period", mPeriod); + ATRACE_INT64("DispSync:Phase", mPhase); + } + + mThread->updateModel(mPeriod, mPhase); + } +} + +void DispSync::updateErrorLocked() { + if (mPeriod == 0) { + return; + } + + int numErrSamples = 0; + nsecs_t sqErrSum = 0; + + for (size_t i = 0; i < NUM_PRESENT_SAMPLES; i++) { + nsecs_t sample = mPresentTimes[i]; + if (sample > mPhase) { + nsecs_t sampleErr = (sample - mPhase) % mPeriod; + if (sampleErr > mPeriod / 2) { + sampleErr -= mPeriod; + } + sqErrSum += sampleErr * sampleErr; + numErrSamples++; + } + } + + if (numErrSamples > 0) { + mError = sqErrSum / numErrSamples; + } else { + mError = 0; + } + + if (traceDetailedInfo) { + ATRACE_INT64("DispSync:Error", mError); + } +} + +void DispSync::resetErrorLocked() { + mPresentSampleOffset = 0; + mError = 0; + for (size_t i = 0; i < NUM_PRESENT_SAMPLES; i++) { + mPresentFences[i].clear(); + mPresentTimes[i] = 0; + } +} + +} // namespace android diff --git a/services/surfaceflinger/DispSync.h b/services/surfaceflinger/DispSync.h new file mode 100644 index 0000000..c4280aa --- /dev/null +++ b/services/surfaceflinger/DispSync.h @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2012 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_DISPSYNC_H +#define ANDROID_DISPSYNC_H + +#include <stddef.h> + +#include <utils/Mutex.h> +#include <utils/Timers.h> +#include <utils/RefBase.h> + +namespace android { + +class String8; +class Fence; +class DispSyncThread; + +// DispSync maintains a model of the periodic hardware-based vsync events of a +// display and uses that model to execute period callbacks at specific phase +// offsets from the hardware vsync events. The model is constructed by +// feeding consecutive hardware event timestamps to the DispSync object via +// the addResyncSample method. +// +// The model is validated using timestamps from Fence objects that are passed +// to the DispSync object via the addPresentFence method. These fence +// timestamps should correspond to a hardware vsync event, but they need not +// be consecutive hardware vsync times. If this method determines that the +// current model accurately represents the hardware event times it will return +// false to indicate that a resynchronization (via addResyncSample) is not +// needed. +class DispSync { + +public: + + class Callback: public virtual RefBase { + public: + virtual ~Callback() {}; + virtual void onDispSyncEvent(nsecs_t when) = 0; + }; + + DispSync(); + ~DispSync(); + + void reset(); + + // addPresentFence adds a fence for use in validating the current vsync + // event model. The fence need not be signaled at the time + // addPresentFence is called. When the fence does signal, its timestamp + // should correspond to a hardware vsync event. Unlike the + // addResyncSample method, the timestamps of consecutive fences need not + // correspond to consecutive hardware vsync events. + // + // This method should be called with the retire fence from each HWComposer + // set call that affects the display. + bool addPresentFence(const sp<Fence>& fence); + + // The beginResync, addResyncSample, and endResync methods are used to re- + // synchronize the DispSync's model to the hardware vsync events. The re- + // synchronization process involves first calling beginResync, then + // calling addResyncSample with a sequence of consecutive hardware vsync + // event timestamps, and finally calling endResync when addResyncSample + // indicates that no more samples are needed by returning false. + // + // This resynchronization process should be performed whenever the display + // is turned on (i.e. once immediately after it's turned on) and whenever + // addPresentFence returns true indicating that the model has drifted away + // from the hardware vsync events. + void beginResync(); + bool addResyncSample(nsecs_t timestamp); + void endResync(); + + // The setPreiod method sets the vsync event model's period to a specific + // value. This should be used to prime the model when a display is first + // turned on. It should NOT be used after that. + void setPeriod(nsecs_t period); + + // addEventListener registers a callback to be called repeatedly at the + // given phase offset from the hardware vsync events. The callback is + // called from a separate thread and it should return reasonably quickly + // (i.e. within a few hundred microseconds). + status_t addEventListener(nsecs_t phase, const sp<Callback>& callback); + + // removeEventListener removes an already-registered event callback. Once + // this method returns that callback will no longer be called by the + // DispSync object. + status_t removeEventListener(const sp<Callback>& callback); + +private: + + void updateModelLocked(); + void updateErrorLocked(); + void resetErrorLocked(); + + enum { MAX_RESYNC_SAMPLES = 32 }; + enum { MIN_RESYNC_SAMPLES_FOR_UPDATE = 3 }; + enum { NUM_PRESENT_SAMPLES = 8 }; + enum { MAX_RESYNC_SAMPLES_WITHOUT_PRESENT = 12 }; + + // mPeriod is the computed period of the modeled vsync events in + // nanoseconds. + nsecs_t mPeriod; + + // mPhase is the phase offset of the modeled vsync events. It is the + // number of nanoseconds from time 0 to the first vsync event. + nsecs_t mPhase; + + // mError is the computed model error. It is based on the difference + // between the estimated vsync event times and those observed in the + // mPresentTimes array. + nsecs_t mError; + + // These member variables are the state used during the resynchronization + // process to store information about the hardware vsync event times used + // to compute the model. + nsecs_t mResyncSamples[MAX_RESYNC_SAMPLES]; + size_t mFirstResyncSample; + size_t mNumResyncSamples; + int mNumResyncSamplesSincePresent; + + // These member variables store information about the present fences used + // to validate the currently computed model. + sp<Fence> mPresentFences[NUM_PRESENT_SAMPLES]; + nsecs_t mPresentTimes[NUM_PRESENT_SAMPLES]; + size_t mPresentSampleOffset; + + // mThread is the thread from which all the callbacks are called. + sp<DispSyncThread> mThread; + + // mMutex is used to protect access to all member variables. + mutable Mutex mMutex; +}; + +} + +#endif // ANDROID_DISPSYNC_H diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp index 68b0b7f..800137b 100644 --- a/services/surfaceflinger/DisplayDevice.cpp +++ b/services/surfaceflinger/DisplayDevice.cpp @@ -29,18 +29,14 @@ #include <gui/Surface.h> -#include <GLES/gl.h> -#include <EGL/egl.h> -#include <EGL/eglext.h> - #include <hardware/gralloc.h> #include "DisplayHardware/DisplaySurface.h" #include "DisplayHardware/HWComposer.h" +#include "RenderEngine/RenderEngine.h" #include "clz.h" #include "DisplayDevice.h" -#include "GLExtensions.h" #include "SurfaceFlinger.h" #include "Layer.h" @@ -48,20 +44,6 @@ using namespace android; // ---------------------------------------------------------------------------- -static __attribute__((noinline)) -void checkGLErrors() -{ - do { - // there could be more than one error flag - GLenum error = glGetError(); - if (error == GL_NO_ERROR) - break; - ALOGE("GL error 0x%04x", int(error)); - } while(true); -} - -// ---------------------------------------------------------------------------- - /* * Initialize the display to the specified values. * @@ -74,6 +56,7 @@ DisplayDevice::DisplayDevice( bool isSecure, const wp<IBinder>& displayToken, const sp<DisplaySurface>& displaySurface, + const sp<IGraphicBufferProducer>& producer, EGLConfig config) : mFlinger(flinger), mType(type), mHwcDisplayId(hwcId), @@ -81,7 +64,6 @@ DisplayDevice::DisplayDevice( mDisplaySurface(displaySurface), mDisplay(EGL_NO_DISPLAY), mSurface(EGL_NO_SURFACE), - mContext(EGL_NO_CONTEXT), mDisplayWidth(), mDisplayHeight(), mFormat(), mFlags(), mPageFlipCount(), @@ -91,12 +73,22 @@ DisplayDevice::DisplayDevice( mLayerStack(NO_LAYER_STACK), mOrientation() { - mNativeWindow = new Surface(mDisplaySurface->getIGraphicBufferProducer()); + mNativeWindow = new Surface(producer, false); ANativeWindow* const window = mNativeWindow.get(); int format; window->query(window, NATIVE_WINDOW_FORMAT, &format); + // Make sure that composition can never be stalled by a virtual display + // consumer that isn't processing buffers fast enough. We have to do this + // in two places: + // * Here, in case the display is composed entirely by HWC. + // * In makeCurrent(), using eglSwapInterval. Some EGL drivers set the + // window's swap interval in eglMakeCurrent, so they'll override the + // interval we set here. + if (mType >= DisplayDevice::DISPLAY_VIRTUAL) + window->setSwapInterval(window, 0); + /* * Create our display's surface */ @@ -189,7 +181,7 @@ status_t DisplayDevice::compositionComplete() const { void DisplayDevice::flip(const Region& dirty) const { - checkGLErrors(); + mFlinger->getRenderEngine().checkErrors(); EGLDisplay dpy = mDisplay; EGLSurface surface = mSurface; @@ -206,14 +198,39 @@ void DisplayDevice::flip(const Region& dirty) const mPageFlipCount++; } +status_t DisplayDevice::beginFrame() const { + return mDisplaySurface->beginFrame(); +} + +status_t DisplayDevice::prepareFrame(const HWComposer& hwc) const { + DisplaySurface::CompositionType compositionType; + bool haveGles = hwc.hasGlesComposition(mHwcDisplayId); + bool haveHwc = hwc.hasHwcComposition(mHwcDisplayId); + if (haveGles && haveHwc) { + compositionType = DisplaySurface::COMPOSITION_MIXED; + } else if (haveGles) { + compositionType = DisplaySurface::COMPOSITION_GLES; + } else if (haveHwc) { + compositionType = DisplaySurface::COMPOSITION_HWC; + } else { + // Nothing to do -- when turning the screen off we get a frame like + // this. Call it a HWC frame since we won't be doing any GLES work but + // will do a prepare/set cycle. + compositionType = DisplaySurface::COMPOSITION_HWC; + } + return mDisplaySurface->prepareFrame(compositionType); +} + void DisplayDevice::swapBuffers(HWComposer& hwc) const { - // We need to call eglSwapBuffers() unless: - // (a) there was no GLES composition this frame, or - // (b) we're using a legacy HWC with no framebuffer target support (in - // which case HWComposer::commit() handles things). + // We need to call eglSwapBuffers() if: + // (1) we don't have a hardware composer, or + // (2) we did GLES composition this frame, and either + // (a) we have framebuffer target support (not present on legacy + // devices, where HWComposer::commit() handles things); or + // (b) this is a virtual display if (hwc.initCheck() != NO_ERROR || (hwc.hasGlesComposition(mHwcDisplayId) && - hwc.supportsFramebufferTarget())) { + (hwc.supportsFramebufferTarget() || mType >= DISPLAY_VIRTUAL))) { EGLBoolean success = eglSwapBuffers(mDisplay, mSurface); if (!success) { EGLint error = eglGetError(); @@ -246,28 +263,24 @@ uint32_t DisplayDevice::getFlags() const return mFlags; } -EGLBoolean DisplayDevice::makeCurrent(EGLDisplay dpy, - const sp<const DisplayDevice>& hw, EGLContext ctx) { +EGLBoolean DisplayDevice::makeCurrent(EGLDisplay dpy, EGLContext ctx) const { EGLBoolean result = EGL_TRUE; EGLSurface sur = eglGetCurrentSurface(EGL_DRAW); - if (sur != hw->mSurface) { - result = eglMakeCurrent(dpy, hw->mSurface, hw->mSurface, ctx); + if (sur != mSurface) { + result = eglMakeCurrent(dpy, mSurface, mSurface, ctx); if (result == EGL_TRUE) { - setViewportAndProjection(hw); + if (mType >= DisplayDevice::DISPLAY_VIRTUAL) + eglSwapInterval(dpy, 0); } } + setViewportAndProjection(); return result; } -void DisplayDevice::setViewportAndProjection(const sp<const DisplayDevice>& hw) { - GLsizei w = hw->mDisplayWidth; - GLsizei h = hw->mDisplayHeight; - glViewport(0, 0, w, h); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - // put the origin in the left-bottom corner - glOrthof(0, w, 0, h, 0, 1); // l=0, r=w ; b=0, t=h - glMatrixMode(GL_MODELVIEW); +void DisplayDevice::setViewportAndProjection() const { + size_t w = mDisplayWidth; + size_t h = mDisplayHeight; + mFlinger->getRenderEngine().setViewportAndProjection(w, h, w, h, false); } // ---------------------------------------------------------------------------- @@ -331,6 +344,25 @@ void DisplayDevice::setLayerStack(uint32_t stack) { // ---------------------------------------------------------------------------- +uint32_t DisplayDevice::getOrientationTransform() const { + uint32_t transform = 0; + switch (mOrientation) { + case DisplayState::eOrientationDefault: + transform = Transform::ROT_0; + break; + case DisplayState::eOrientation90: + transform = Transform::ROT_90; + break; + case DisplayState::eOrientation180: + transform = Transform::ROT_180; + break; + case DisplayState::eOrientation270: + transform = Transform::ROT_270; + break; + } + return transform; +} + status_t DisplayDevice::orientationToTransfrom( int orientation, int w, int h, Transform* tr) { @@ -416,7 +448,7 @@ void DisplayDevice::setProjection(int orientation, mScissor = mGlobalTransform.transform(viewport); if (mScissor.isEmpty()) { - mScissor.set(getBounds()); + mScissor = getBounds(); } mOrientation = orientation; @@ -424,9 +456,9 @@ void DisplayDevice::setProjection(int orientation, mFrame = frame; } -void DisplayDevice::dump(String8& result, char* buffer, size_t SIZE) const { +void DisplayDevice::dump(String8& result) const { const Transform& tr(mGlobalTransform); - snprintf(buffer, SIZE, + result.appendFormat( "+ DisplayDevice: %s\n" " type=%x, hwcId=%d, layerStack=%u, (%4dx%4d), ANativeWindow=%p, orient=%2d (type=%08x), " "flips=%u, isSecure=%d, secureVis=%d, acquired=%d, numLayers=%u\n" @@ -443,8 +475,6 @@ void DisplayDevice::dump(String8& result, char* buffer, size_t SIZE) const { tr[0][1], tr[1][1], tr[2][1], tr[0][2], tr[1][2], tr[2][2]); - result.append(buffer); - String8 surfaceDump; mDisplaySurface->dump(surfaceDump); result.append(surfaceDump); diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h index 377d924..c3abe89 100644 --- a/services/surfaceflinger/DisplayDevice.h +++ b/services/surfaceflinger/DisplayDevice.h @@ -26,6 +26,7 @@ #include <EGL/eglext.h> #include <utils/Mutex.h> +#include <utils/String8.h> #include <utils/Timers.h> #include <hardware/hwcomposer_defs.h> @@ -38,6 +39,7 @@ namespace android { class DisplayInfo; class DisplaySurface; +class IGraphicBufferProducer; class Layer; class SurfaceFlinger; class HWComposer; @@ -56,8 +58,8 @@ public: DISPLAY_ID_INVALID = -1, DISPLAY_PRIMARY = HWC_DISPLAY_PRIMARY, DISPLAY_EXTERNAL = HWC_DISPLAY_EXTERNAL, - NUM_DISPLAY_TYPES = HWC_NUM_DISPLAY_TYPES, - DISPLAY_VIRTUAL = HWC_NUM_DISPLAY_TYPES + DISPLAY_VIRTUAL = HWC_DISPLAY_VIRTUAL, + NUM_BUILTIN_DISPLAY_TYPES = HWC_NUM_PHYSICAL_DISPLAY_TYPES, }; enum { @@ -76,6 +78,7 @@ public: bool isSecure, const wp<IBinder>& displayToken, const sp<DisplaySurface>& displaySurface, + const sp<IGraphicBufferProducer>& producer, EGLConfig config); ~DisplayDevice(); @@ -108,6 +111,7 @@ public: void setProjection(int orientation, const Rect& viewport, const Rect& frame); int getOrientation() const { return mOrientation; } + uint32_t getOrientationTransform() const; const Transform& getTransform() const { return mGlobalTransform; } const Rect getViewport() const { return mViewport; } const Rect getFrame() const { return mFrame; } @@ -119,6 +123,9 @@ public: int32_t getHwcDisplayId() const { return mHwcDisplayId; } const wp<IBinder>& getDisplayToken() const { return mDisplayToken; } + status_t beginFrame() const; + status_t prepareFrame(const HWComposer& hwc) const; + void swapBuffers(HWComposer& hwc) const; status_t compositionComplete() const; @@ -133,10 +140,8 @@ public: void setDisplayName(const String8& displayName); const String8& getDisplayName() const { return mDisplayName; } - static EGLBoolean makeCurrent(EGLDisplay dpy, - const sp<const DisplayDevice>& hw, EGLContext ctx); - - static void setViewportAndProjection(const sp<const DisplayDevice>& hw); + EGLBoolean makeCurrent(EGLDisplay dpy, EGLContext ctx) const; + void setViewportAndProjection() const; /* ------------------------------------------------------------------------ * blank / unblank management @@ -153,7 +158,7 @@ public: * Debugging */ uint32_t getPageFlipCount() const; - void dump(String8& result, char* buffer, size_t SIZE) const; + void dump(String8& result) const; private: /* @@ -170,7 +175,6 @@ private: EGLDisplay mDisplay; EGLSurface mSurface; - EGLContext mContext; int mDisplayWidth; int mDisplayHeight; PixelFormat mFormat; diff --git a/services/surfaceflinger/DisplayHardware/DisplaySurface.h b/services/surfaceflinger/DisplayHardware/DisplaySurface.h index 2eca3cb..48bf3f2 100644 --- a/services/surfaceflinger/DisplayHardware/DisplaySurface.h +++ b/services/surfaceflinger/DisplayHardware/DisplaySurface.h @@ -30,7 +30,22 @@ class String8; class DisplaySurface : public virtual RefBase { public: - virtual sp<IGraphicBufferProducer> getIGraphicBufferProducer() const = 0; + // beginFrame is called at the beginning of the composition loop, before + // the configuration is known. The DisplaySurface should do anything it + // needs to do to enable HWComposer to decide how to compose the frame. + virtual status_t beginFrame() = 0; + + // prepareFrame is called after the composition configuration is known but + // before composition takes place. The DisplaySurface can use the + // composition type to decide how to manage the flow of buffers between + // GLES and HWC for this frame. + enum CompositionType { + COMPOSITION_UNKNOWN = 0, + COMPOSITION_GLES = 1, + COMPOSITION_HWC = 2, + COMPOSITION_MIXED = COMPOSITION_GLES | COMPOSITION_HWC + }; + virtual status_t prepareFrame(CompositionType compositionType) = 0; // Should be called when composition rendering is complete for a frame (but // eglSwapBuffers hasn't necessarily been called). Required by certain diff --git a/libs/gui/DummyConsumer.cpp b/services/surfaceflinger/DisplayHardware/FloatRect.h index be47e0e..b08a951 100644 --- a/libs/gui/DummyConsumer.cpp +++ b/services/surfaceflinger/DisplayHardware/FloatRect.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Android Open Source Project + * Copyright 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,30 +14,29 @@ * limitations under the License. */ -#define LOG_TAG "DummyConsumer" -// #define LOG_NDEBUG 0 +#ifndef ANDROID_SF_FLOAT_RECT +#define ANDROID_SF_FLOAT_RECT -#include <gui/DummyConsumer.h> - -#include <utils/Log.h> -#include <utils/String8.h> +#include <utils/TypeHelpers.h> namespace android { -DummyConsumer::DummyConsumer() { - ALOGV("DummyConsumer"); -} - -DummyConsumer::~DummyConsumer() { - ALOGV("~DummyConsumer"); -} +class FloatRect +{ +public: + float left; + float top; + float right; + float bottom; -void DummyConsumer::onFrameAvailable() { - ALOGV("onFrameAvailable"); -} + inline FloatRect() { } + inline FloatRect(const Rect& other) + : left(other.left), top(other.top), right(other.right), bottom(other.bottom) { } -void DummyConsumer::onBuffersReleased() { - ALOGV("onBuffersReleased"); -} + inline float getWidth() const { return right - left; } + inline float getHeight() const { return bottom - top; } +}; }; // namespace android + +#endif // ANDROID_SF_FLOAT_RECT diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp index 54a3ce8..8c634ed 100644 --- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp +++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp @@ -50,26 +50,30 @@ namespace android { * */ -FramebufferSurface::FramebufferSurface(HWComposer& hwc, int disp) : - ConsumerBase(new BufferQueue(true, new GraphicBufferAlloc())), +FramebufferSurface::FramebufferSurface(HWComposer& hwc, int disp, + const sp<IGraphicBufferConsumer>& consumer) : + ConsumerBase(consumer), mDisplayType(disp), mCurrentBufferSlot(-1), mCurrentBuffer(0), mHwc(hwc) { mName = "FramebufferSurface"; - mBufferQueue->setConsumerName(mName); - mBufferQueue->setConsumerUsageBits(GRALLOC_USAGE_HW_FB | + mConsumer->setConsumerName(mName); + mConsumer->setConsumerUsageBits(GRALLOC_USAGE_HW_FB | GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_COMPOSER); - mBufferQueue->setDefaultBufferFormat(mHwc.getFormat(disp)); - mBufferQueue->setDefaultBufferSize(mHwc.getWidth(disp), mHwc.getHeight(disp)); - mBufferQueue->setSynchronousMode(true); - mBufferQueue->setDefaultMaxBufferCount(NUM_FRAMEBUFFER_SURFACE_BUFFERS); + mConsumer->setDefaultBufferFormat(mHwc.getFormat(disp)); + mConsumer->setDefaultBufferSize(mHwc.getWidth(disp), mHwc.getHeight(disp)); + mConsumer->setDefaultMaxBufferCount(NUM_FRAMEBUFFER_SURFACE_BUFFERS); } -sp<IGraphicBufferProducer> FramebufferSurface::getIGraphicBufferProducer() const { - return getBufferQueue(); +status_t FramebufferSurface::beginFrame() { + return NO_ERROR; +} + +status_t FramebufferSurface::prepareFrame(CompositionType compositionType) { + return NO_ERROR; } status_t FramebufferSurface::advanceFrame() { @@ -83,7 +87,7 @@ status_t FramebufferSurface::nextBuffer(sp<GraphicBuffer>& outBuffer, sp<Fence>& Mutex::Autolock lock(mMutex); BufferQueue::BufferItem item; - status_t err = acquireBufferLocked(&item); + status_t err = acquireBufferLocked(&item, 0); if (err == BufferQueue::NO_BUFFER_AVAILABLE) { outBuffer = mCurrentBuffer; return NO_ERROR; @@ -103,9 +107,9 @@ status_t FramebufferSurface::nextBuffer(sp<GraphicBuffer>& outBuffer, sp<Fence>& if (mCurrentBufferSlot != BufferQueue::INVALID_BUFFER_SLOT && item.mBuf != mCurrentBufferSlot) { // Release the previous buffer. - err = releaseBufferLocked(mCurrentBufferSlot, EGL_NO_DISPLAY, - EGL_NO_SYNC_KHR); - if (err != NO_ERROR && err != BufferQueue::STALE_BUFFER_SLOT) { + err = releaseBufferLocked(mCurrentBufferSlot, mCurrentBuffer, + EGL_NO_DISPLAY, EGL_NO_SYNC_KHR); + if (err < NO_ERROR) { ALOGE("error releasing buffer: %s (%d)", strerror(-err), err); return err; } @@ -144,7 +148,8 @@ void FramebufferSurface::onFrameCommitted() { sp<Fence> fence = mHwc.getAndResetReleaseFence(mDisplayType); if (fence->isValid() && mCurrentBufferSlot != BufferQueue::INVALID_BUFFER_SLOT) { - status_t err = addReleaseFence(mCurrentBufferSlot, fence); + status_t err = addReleaseFence(mCurrentBufferSlot, + mCurrentBuffer, fence); ALOGE_IF(err, "setReleaseFenceFd: failed to add the fence: %s (%d)", strerror(-err), err); } @@ -175,11 +180,10 @@ void FramebufferSurface::dump(String8& result) const { ConsumerBase::dump(result); } -void FramebufferSurface::dumpLocked(String8& result, const char* prefix, - char* buffer, size_t SIZE) const +void FramebufferSurface::dumpLocked(String8& result, const char* prefix) const { mHwc.fbDump(result); - ConsumerBase::dumpLocked(result, prefix, buffer, SIZE); + ConsumerBase::dumpLocked(result, prefix); } // ---------------------------------------------------------------------------- diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h index 2fde789..1d67446 100644 --- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h +++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h @@ -37,10 +37,10 @@ class HWComposer; class FramebufferSurface : public ConsumerBase, public DisplaySurface { public: - FramebufferSurface(HWComposer& hwc, int disp); - - virtual sp<IGraphicBufferProducer> getIGraphicBufferProducer() const; + FramebufferSurface(HWComposer& hwc, int disp, const sp<IGraphicBufferConsumer>& consumer); + virtual status_t beginFrame(); + virtual status_t prepareFrame(CompositionType compositionType); virtual status_t compositionComplete(); virtual status_t advanceFrame(); virtual void onFrameCommitted(); @@ -55,8 +55,7 @@ private: virtual void onFrameAvailable(); virtual void freeBufferLocked(int slotIndex); - virtual void dumpLocked(String8& result, const char* prefix, - char* buffer, size_t SIZE) const; + virtual void dumpLocked(String8& result, const char* prefix) const; // nextBuffer waits for and then latches the next buffer from the // BufferQueue and releases the previously latched buffer to the diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index a9afbe5..2469f0c 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -16,14 +16,12 @@ #define ATRACE_TAG ATRACE_TAG_GRAPHICS -// Uncomment this to remove support for HWC_DEVICE_API_VERSION_0_3 and older -#define HWC_REMOVE_DEPRECATED_VERSIONS 1 - #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> +#include <math.h> #include <utils/CallStack.h> #include <utils/Errors.h> @@ -50,15 +48,8 @@ namespace android { -// This is not a real HWC version. It's used for in-development features that -// haven't been committed to a specific real HWC version. -#define HWC_DEVICE_API_VERSION_1_EXP HARDWARE_DEVICE_API_VERSION_2(1, 0xFF, HWC_HEADER_VERSION) - #define MIN_HWC_HEADER_VERSION HWC_HEADER_VERSION -#define NUM_PHYSICAL_DISPLAYS HWC_NUM_DISPLAY_TYPES -#define VIRTUAL_DISPLAY_ID_BASE HWC_NUM_DISPLAY_TYPES - static uint32_t hwcApiVersion(const hwc_composer_device_1_t* hwc) { uint32_t hwcVersion = hwc->common.version; return hwcVersion & HARDWARE_API_VERSION_2_MAJ_MIN_MASK; @@ -96,12 +87,17 @@ HWComposer::HWComposer( mFbDev(0), mHwc(0), mNumDisplays(1), mCBContext(new cb_context), mEventHandler(handler), - mVSyncCount(0), mDebugForceFakeVSync(false) + mDebugForceFakeVSync(false) { - for (size_t i =0 ; i<MAX_DISPLAYS ; i++) { + for (size_t i =0 ; i<MAX_HWC_DISPLAYS ; i++) { mLists[i] = 0; } + for (size_t i=0 ; i<HWC_NUM_PHYSICAL_DISPLAY_TYPES ; i++) { + mLastHwVSync[i] = 0; + mVSyncCounts[i] = 0; + } + char value[PROPERTY_VALUE_MAX]; property_get("debug.sf.no_hw_vsync", value, "0"); mDebugForceFakeVSync = atoi(value); @@ -129,7 +125,7 @@ HWComposer::HWComposer( } // these display IDs are always reserved - for (size_t i=0 ; i<NUM_PHYSICAL_DISPLAYS ; i++) { + for (size_t i=0 ; i<NUM_BUILTIN_DISPLAYS ; i++) { mAllocatedDisplayIDs.markBit(i); } @@ -156,12 +152,12 @@ HWComposer::HWComposer( // the number of displays we actually have depends on the // hw composer version - if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_EXP)) { - // 1.?? adds support for virtual displays - mNumDisplays = MAX_DISPLAYS; + if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_3)) { + // 1.3 adds support for virtual displays + mNumDisplays = MAX_HWC_DISPLAYS; } else if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) { // 1.1 adds support for multiple displays - mNumDisplays = NUM_PHYSICAL_DISPLAYS; + mNumDisplays = NUM_BUILTIN_DISPLAYS; } else { mNumDisplays = 1; } @@ -189,7 +185,7 @@ HWComposer::HWComposer( } } else if (mHwc) { // here we're guaranteed to have at least HWC 1.1 - for (size_t i =0 ; i<NUM_PHYSICAL_DISPLAYS ; i++) { + for (size_t i =0 ; i<NUM_BUILTIN_DISPLAYS ; i++) { queryDisplayProperties(i); } } @@ -287,10 +283,29 @@ void HWComposer::invalidate() { } void HWComposer::vsync(int disp, int64_t timestamp) { - ATRACE_INT("VSYNC", ++mVSyncCount&1); - mEventHandler.onVSyncReceived(disp, timestamp); - Mutex::Autolock _l(mLock); - mLastHwVSync = timestamp; + if (uint32_t(disp) < HWC_NUM_PHYSICAL_DISPLAY_TYPES) { + { + Mutex::Autolock _l(mLock); + + // There have been reports of HWCs that signal several vsync events + // with the same timestamp when turning the display off and on. This + // is a bug in the HWC implementation, but filter the extra events + // out here so they don't cause havoc downstream. + if (timestamp == mLastHwVSync[disp]) { + ALOGW("Ignoring duplicate VSYNC event from HWC (t=%lld)", + timestamp); + return; + } + + mLastHwVSync[disp] = timestamp; + } + + char tag[16]; + snprintf(tag, sizeof(tag), "HW_VSYNC_%1u", disp); + ATRACE_INT(tag, ++mVSyncCounts[disp] & 1); + + mEventHandler.onVSyncReceived(disp, timestamp); + } } void HWComposer::hotplug(int disp, int connected) { @@ -402,7 +417,7 @@ int32_t HWComposer::allocateDisplayId() { } status_t HWComposer::freeDisplayId(int32_t id) { - if (id < NUM_PHYSICAL_DISPLAYS) { + if (id < NUM_BUILTIN_DISPLAYS) { // cannot free the reserved IDs return BAD_VALUE; } @@ -424,7 +439,7 @@ nsecs_t HWComposer::getRefreshTimestamp(int disp) const { // the refresh period and whatever closest timestamp we have. Mutex::Autolock _l(mLock); nsecs_t now = systemTime(CLOCK_MONOTONIC); - return now - ((now - mLastHwVSync) % mDisplayData[disp].refresh); + return now - ((now - mLastHwVSync[disp]) % mDisplayData[disp].refresh); } sp<Fence> HWComposer::getDisplayFence(int disp) const { @@ -523,7 +538,14 @@ status_t HWComposer::createWorkList(int32_t id, size_t numLayers) { disp.framebufferTarget->handle = disp.fbTargetHandle; disp.framebufferTarget->transform = 0; disp.framebufferTarget->blending = HWC_BLENDING_PREMULT; - disp.framebufferTarget->sourceCrop = r; + if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_3)) { + disp.framebufferTarget->sourceCropf.left = 0; + disp.framebufferTarget->sourceCropf.top = 0; + disp.framebufferTarget->sourceCropf.right = disp.width; + disp.framebufferTarget->sourceCropf.bottom = disp.height; + } else { + disp.framebufferTarget->sourceCrop = r; + } disp.framebufferTarget->displayFrame = r; disp.framebufferTarget->visibleRegionScreen.numRects = 1; disp.framebufferTarget->visibleRegionScreen.rects = @@ -550,9 +572,6 @@ status_t HWComposer::setFramebufferTarget(int32_t id, // triggers a Surface::queueBuffer() on some // devices (!?) -- log and ignore. ALOGE("HWComposer: framebufferTarget is null"); -// CallStack stack; -// stack.update(); -// stack.dump(""); return NO_ERROR; } @@ -585,7 +604,7 @@ status_t HWComposer::prepare() { } mLists[i] = disp.list; if (mLists[i]) { - if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_EXP)) { + if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_3)) { mLists[i]->outbuf = NULL; mLists[i]->outbufAcquireFenceFd = -1; } else if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) { @@ -606,6 +625,10 @@ status_t HWComposer::prepare() { // here we're just making sure that "skip" layers are set // to HWC_FRAMEBUFFER and we're also counting how many layers // we have of each type. + // + // If there are no window layers, we treat the display has having FB + // composition, because SurfaceFlinger will use GLES to draw the + // wormhole region. for (size_t i=0 ; i<mNumDisplays ; i++) { DisplayData& disp(mDisplayData[i]); disp.hasFbComp = false; @@ -627,6 +650,11 @@ status_t HWComposer::prepare() { disp.hasOvComp = true; } } + if (disp.list->numHwLayers == (disp.framebufferTarget ? 1 : 0)) { + disp.hasFbComp = true; + } + } else { + disp.hasFbComp = true; } } } @@ -883,10 +911,25 @@ public: getLayer()->transform = transform; } virtual void setFrame(const Rect& frame) { - reinterpret_cast<Rect&>(getLayer()->displayFrame) = frame; + getLayer()->displayFrame = reinterpret_cast<hwc_rect_t const&>(frame); } - virtual void setCrop(const Rect& crop) { - reinterpret_cast<Rect&>(getLayer()->sourceCrop) = crop; + virtual void setCrop(const FloatRect& crop) { + if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_3)) { + getLayer()->sourceCropf = reinterpret_cast<hwc_frect_t const&>(crop); + } else { + /* + * Since h/w composer didn't support a flot crop rect before version 1.3, + * using integer coordinates instead produces a different output from the GL code in + * Layer::drawWithOpenGL(). The difference can be large if the buffer crop to + * window size ratio is large and a window crop is defined + * (i.e.: if we scale the buffer a lot and we also crop it with a window crop). + */ + hwc_rect_t& r = getLayer()->sourceCrop; + r.left = int(ceilf(crop.left)); + r.top = int(ceilf(crop.top)); + r.right = int(floorf(crop.right)); + r.bottom= int(floorf(crop.bottom)); + } } virtual void setVisibleRegionScreen(const Region& reg) { // Region::getSharedBuffer creates a reference to the underlying @@ -962,7 +1005,7 @@ HWComposer::LayerListIterator HWComposer::end(int32_t id) { return getLayerIterator(id, numLayers); } -void HWComposer::dump(String8& result, char* buffer, size_t SIZE) const { +void HWComposer::dump(String8& result) const { if (mHwc) { result.appendFormat("Hardware Composer state (version %8x):\n", hwcApiVersion(mHwc)); result.appendFormat(" mDebugForceFakeVSync=%d\n", mDebugForceFakeVSync); @@ -984,9 +1027,9 @@ void HWComposer::dump(String8& result, char* buffer, size_t SIZE) const { disp.list->numHwLayers, disp.list->flags); result.append( - " type | handle | hints | flags | tr | blend | format | source crop | frame name \n" - "------------+----------+----------+----------+----+-------+----------+---------------------------+--------------------------------\n"); - // " __________ | ________ | ________ | ________ | __ | _____ | ________ | [_____,_____,_____,_____] | [_____,_____,_____,_____] + " type | handle | hints | flags | tr | blend | format | source crop | frame name \n" + "------------+----------+----------+----------+----+-------+----------+---------------------------------+--------------------------------\n"); + // " __________ | ________ | ________ | ________ | __ | _____ | ________ | [_____._,_____._,_____._,_____._] | [_____,_____,_____,_____] for (size_t i=0 ; i<disp.list->numHwLayers ; i++) { const hwc_layer_1_t&l = disp.list->hwLayers[i]; int32_t format = -1; @@ -1017,19 +1060,31 @@ void HWComposer::dump(String8& result, char* buffer, size_t SIZE) const { if (type >= NELEM(compositionTypeName)) type = NELEM(compositionTypeName) - 1; - result.appendFormat( - " %10s | %08x | %08x | %08x | %02x | %05x | %08x | [%5d,%5d,%5d,%5d] | [%5d,%5d,%5d,%5d] %s\n", - compositionTypeName[type], - intptr_t(l.handle), l.hints, l.flags, l.transform, l.blending, format, - l.sourceCrop.left, l.sourceCrop.top, l.sourceCrop.right, l.sourceCrop.bottom, - l.displayFrame.left, l.displayFrame.top, l.displayFrame.right, l.displayFrame.bottom, - name.string()); + if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_3)) { + result.appendFormat( + " %10s | %08x | %08x | %08x | %02x | %05x | %08x | [%7.1f,%7.1f,%7.1f,%7.1f] | [%5d,%5d,%5d,%5d] %s\n", + compositionTypeName[type], + intptr_t(l.handle), l.hints, l.flags, l.transform, l.blending, format, + l.sourceCropf.left, l.sourceCropf.top, l.sourceCropf.right, l.sourceCropf.bottom, + l.displayFrame.left, l.displayFrame.top, l.displayFrame.right, l.displayFrame.bottom, + name.string()); + } else { + result.appendFormat( + " %10s | %08x | %08x | %08x | %02x | %05x | %08x | [%7d,%7d,%7d,%7d] | [%5d,%5d,%5d,%5d] %s\n", + compositionTypeName[type], + intptr_t(l.handle), l.hints, l.flags, l.transform, l.blending, format, + l.sourceCrop.left, l.sourceCrop.top, l.sourceCrop.right, l.sourceCrop.bottom, + l.displayFrame.left, l.displayFrame.top, l.displayFrame.right, l.displayFrame.bottom, + name.string()); + } } } } } if (mHwc && mHwc->dump) { + const size_t SIZE = 4096; + char buffer[SIZE]; mHwc->dump(mHwc, buffer, SIZE); result.append(buffer); } diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h index 604de38..9f96113 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.h +++ b/services/surfaceflinger/DisplayHardware/HWComposer.h @@ -47,6 +47,7 @@ namespace android { class GraphicBuffer; class Fence; +class FloatRect; class Region; class String8; class SurfaceFlinger; @@ -63,7 +64,9 @@ public: }; enum { - MAX_DISPLAYS = HWC_NUM_DISPLAY_TYPES + 1 + NUM_BUILTIN_DISPLAYS = HWC_NUM_PHYSICAL_DISPLAY_TYPES, + MAX_HWC_DISPLAYS = HWC_NUM_DISPLAY_TYPES, + VIRTUAL_DISPLAY_ID_BASE = HWC_DISPLAY_VIRTUAL, }; HWComposer( @@ -74,15 +77,16 @@ public: status_t initCheck() const; - // returns a display ID starting at MAX_DISPLAYS, this ID - // is to be used with createWorkList (and all other - // methods requiring an ID below). - // IDs below MAX_DISPLAY are pre-defined and therefore are always valid. - // returns a negative error code if an ID cannot be allocated + // Returns a display ID starting at VIRTUAL_DISPLAY_ID_BASE, this ID is to + // be used with createWorkList (and all other methods requiring an ID + // below). + // IDs below NUM_BUILTIN_DISPLAYS are pre-defined and therefore are + // always valid. + // Returns -1 if an ID cannot be allocated int32_t allocateDisplayId(); - // recycles the given ID and frees the associated worklist. - // IDs below MAX_DISPLAYS are not recycled + // Recycles the given virtual display ID and frees the associated worklist. + // IDs below NUM_BUILTIN_DISPLAYS are not recycled. status_t freeDisplayId(int32_t id); @@ -158,7 +162,7 @@ public: virtual void setBlending(uint32_t blending) = 0; virtual void setTransform(uint32_t transform) = 0; virtual void setFrame(const Rect& frame) = 0; - virtual void setCrop(const Rect& crop) = 0; + virtual void setCrop(const FloatRect& crop) = 0; virtual void setVisibleRegionScreen(const Region& reg) = 0; virtual void setBuffer(const sp<GraphicBuffer>& buffer) = 0; virtual void setAcquireFenceFd(int fenceFd) = 0; @@ -275,7 +279,7 @@ public: friend class VSyncThread; // for debugging ---------------------------------------------------------- - void dump(String8& out, char* scratch, size_t SIZE) const; + void dump(String8& out) const; private: void loadHwcModule(); @@ -332,20 +336,20 @@ private: struct hwc_composer_device_1* mHwc; // invariant: mLists[0] != NULL iff mHwc != NULL // mLists[i>0] can be NULL. that display is to be ignored - struct hwc_display_contents_1* mLists[MAX_DISPLAYS]; - DisplayData mDisplayData[MAX_DISPLAYS]; + struct hwc_display_contents_1* mLists[MAX_HWC_DISPLAYS]; + DisplayData mDisplayData[MAX_HWC_DISPLAYS]; size_t mNumDisplays; cb_context* mCBContext; EventHandler& mEventHandler; - size_t mVSyncCount; + size_t mVSyncCounts[HWC_NUM_PHYSICAL_DISPLAY_TYPES]; sp<VSyncThread> mVSyncThread; bool mDebugForceFakeVSync; BitSet32 mAllocatedDisplayIDs; // protected by mLock mutable Mutex mLock; - mutable nsecs_t mLastHwVSync; + mutable nsecs_t mLastHwVSync[HWC_NUM_PHYSICAL_DISPLAY_TYPES]; // thread-safe mutable Mutex mEventControlLock; diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp index 2838b23..c5a14b0 100644 --- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp +++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp @@ -14,27 +14,95 @@ * limitations under the License. */ +// #define LOG_NDEBUG 0 #include "VirtualDisplaySurface.h" - -#include <cutils/log.h> -#include <gui/IGraphicBufferProducer.h> +#include "HWComposer.h" // --------------------------------------------------------------------------- namespace android { // --------------------------------------------------------------------------- +#define VDS_LOGE(msg, ...) ALOGE("[%s] "msg, \ + mDisplayName.string(), ##__VA_ARGS__) +#define VDS_LOGW_IF(cond, msg, ...) ALOGW_IF(cond, "[%s] "msg, \ + mDisplayName.string(), ##__VA_ARGS__) +#define VDS_LOGV(msg, ...) ALOGV("[%s] "msg, \ + mDisplayName.string(), ##__VA_ARGS__) + +static const char* dbgCompositionTypeStr(DisplaySurface::CompositionType type) { + switch (type) { + case DisplaySurface::COMPOSITION_UNKNOWN: return "UNKNOWN"; + case DisplaySurface::COMPOSITION_GLES: return "GLES"; + case DisplaySurface::COMPOSITION_HWC: return "HWC"; + case DisplaySurface::COMPOSITION_MIXED: return "MIXED"; + default: return "<INVALID>"; + } +} + VirtualDisplaySurface::VirtualDisplaySurface(HWComposer& hwc, int32_t dispId, - const sp<IGraphicBufferProducer>& sink, const String8& name) -: mSink(sink) + const sp<IGraphicBufferProducer>& sink, + const sp<BufferQueue>& bq, + const String8& name) +: ConsumerBase(bq), + mHwc(hwc), + mDisplayId(dispId), + mDisplayName(name), + mProducerUsage(GRALLOC_USAGE_HW_COMPOSER), + mProducerSlotSource(0), + mDbgState(DBG_STATE_IDLE), + mDbgLastCompositionType(COMPOSITION_UNKNOWN) { - LOG_ALWAYS_FATAL_IF(dispId >= 0); + mSource[SOURCE_SINK] = sink; + mSource[SOURCE_SCRATCH] = bq; + + resetPerFrameState(); + + int sinkWidth, sinkHeight; + mSource[SOURCE_SINK]->query(NATIVE_WINDOW_WIDTH, &sinkWidth); + mSource[SOURCE_SINK]->query(NATIVE_WINDOW_HEIGHT, &sinkHeight); + + ConsumerBase::mName = String8::format("VDS: %s", mDisplayName.string()); + mConsumer->setConsumerName(ConsumerBase::mName); + mConsumer->setConsumerUsageBits(GRALLOC_USAGE_HW_COMPOSER); + mConsumer->setDefaultBufferSize(sinkWidth, sinkHeight); + mConsumer->setDefaultMaxBufferCount(2); } VirtualDisplaySurface::~VirtualDisplaySurface() { } -sp<IGraphicBufferProducer> VirtualDisplaySurface::getIGraphicBufferProducer() const { - return mSink; +status_t VirtualDisplaySurface::beginFrame() { + if (mDisplayId < 0) + return NO_ERROR; + + VDS_LOGW_IF(mDbgState != DBG_STATE_IDLE, + "Unexpected beginFrame() in %s state", dbgStateStr()); + mDbgState = DBG_STATE_BEGUN; + + uint32_t transformHint, numPendingBuffers; + mQueueBufferOutput.deflate(&mSinkBufferWidth, &mSinkBufferHeight, + &transformHint, &numPendingBuffers); + + return refreshOutputBuffer(); +} + +status_t VirtualDisplaySurface::prepareFrame(CompositionType compositionType) { + if (mDisplayId < 0) + return NO_ERROR; + + VDS_LOGW_IF(mDbgState != DBG_STATE_BEGUN, + "Unexpected prepareFrame() in %s state", dbgStateStr()); + mDbgState = DBG_STATE_PREPARED; + + mCompositionType = compositionType; + + if (mCompositionType != mDbgLastCompositionType) { + VDS_LOGV("prepareFrame: composition type changed to %s", + dbgCompositionTypeStr(mCompositionType)); + mDbgLastCompositionType = mCompositionType; + } + + return NO_ERROR; } status_t VirtualDisplaySurface::compositionComplete() { @@ -42,15 +110,363 @@ status_t VirtualDisplaySurface::compositionComplete() { } status_t VirtualDisplaySurface::advanceFrame() { - return NO_ERROR; + if (mDisplayId < 0) + return NO_ERROR; + + if (mCompositionType == COMPOSITION_HWC) { + VDS_LOGW_IF(mDbgState != DBG_STATE_PREPARED, + "Unexpected advanceFrame() in %s state on HWC frame", + dbgStateStr()); + } else { + VDS_LOGW_IF(mDbgState != DBG_STATE_GLES_DONE, + "Unexpected advanceFrame() in %s state on GLES/MIXED frame", + dbgStateStr()); + } + mDbgState = DBG_STATE_HWC; + + if (mCompositionType == COMPOSITION_HWC) { + // Use the output buffer for the FB as well, though conceptually the + // FB is unused on this frame. + mFbProducerSlot = mOutputProducerSlot; + mFbFence = mOutputFence; + } + + if (mFbProducerSlot < 0 || mOutputProducerSlot < 0) { + // Last chance bailout if something bad happened earlier. For example, + // in a GLES configuration, if the sink disappears then dequeueBuffer + // will fail, the GLES driver won't queue a buffer, but SurfaceFlinger + // will soldier on. So we end up here without a buffer. There should + // be lots of scary messages in the log just before this. + VDS_LOGE("advanceFrame: no buffer, bailing out"); + return NO_MEMORY; + } + + sp<GraphicBuffer> fbBuffer = mProducerBuffers[mFbProducerSlot]; + sp<GraphicBuffer> outBuffer = mProducerBuffers[mOutputProducerSlot]; + VDS_LOGV("advanceFrame: fb=%d(%p) out=%d(%p)", + mFbProducerSlot, fbBuffer.get(), + mOutputProducerSlot, outBuffer.get()); + + // At this point we know the output buffer acquire fence, + // so update HWC state with it. + mHwc.setOutputBuffer(mDisplayId, mOutputFence, outBuffer); + + return mHwc.fbPost(mDisplayId, mFbFence, fbBuffer); } void VirtualDisplaySurface::onFrameCommitted() { + if (mDisplayId < 0) + return; + + VDS_LOGW_IF(mDbgState != DBG_STATE_HWC, + "Unexpected onFrameCommitted() in %s state", dbgStateStr()); + mDbgState = DBG_STATE_IDLE; + + sp<Fence> fbFence = mHwc.getAndResetReleaseFence(mDisplayId); + if (mCompositionType == COMPOSITION_MIXED && mFbProducerSlot >= 0) { + // release the scratch buffer back to the pool + Mutex::Autolock lock(mMutex); + int sslot = mapProducer2SourceSlot(SOURCE_SCRATCH, mFbProducerSlot); + VDS_LOGV("onFrameCommitted: release scratch sslot=%d", sslot); + addReleaseFenceLocked(sslot, mProducerBuffers[mFbProducerSlot], fbFence); + releaseBufferLocked(sslot, mProducerBuffers[mFbProducerSlot], + EGL_NO_DISPLAY, EGL_NO_SYNC_KHR); + } + + if (mOutputProducerSlot >= 0) { + int sslot = mapProducer2SourceSlot(SOURCE_SINK, mOutputProducerSlot); + QueueBufferOutput qbo; + sp<Fence> outFence = mHwc.getLastRetireFence(mDisplayId); + VDS_LOGV("onFrameCommitted: queue sink sslot=%d", sslot); + status_t result = mSource[SOURCE_SINK]->queueBuffer(sslot, + QueueBufferInput( + systemTime(), false /* isAutoTimestamp */, + Rect(mSinkBufferWidth, mSinkBufferHeight), + NATIVE_WINDOW_SCALING_MODE_FREEZE, 0 /* transform */, + true /* async*/, + outFence), + &qbo); + if (result == NO_ERROR) { + updateQueueBufferOutput(qbo); + } + } + + resetPerFrameState(); } void VirtualDisplaySurface::dump(String8& result) const { } +status_t VirtualDisplaySurface::requestBuffer(int pslot, + sp<GraphicBuffer>* outBuf) { + VDS_LOGW_IF(mDbgState != DBG_STATE_GLES, + "Unexpected requestBuffer pslot=%d in %s state", + pslot, dbgStateStr()); + + *outBuf = mProducerBuffers[pslot]; + return NO_ERROR; +} + +status_t VirtualDisplaySurface::setBufferCount(int bufferCount) { + return mSource[SOURCE_SINK]->setBufferCount(bufferCount); +} + +status_t VirtualDisplaySurface::dequeueBuffer(Source source, + uint32_t format, int* sslot, sp<Fence>* fence) { + // Don't let a slow consumer block us + bool async = (source == SOURCE_SINK); + + status_t result = mSource[source]->dequeueBuffer(sslot, fence, async, + mSinkBufferWidth, mSinkBufferHeight, format, mProducerUsage); + if (result < 0) + return result; + int pslot = mapSource2ProducerSlot(source, *sslot); + VDS_LOGV("dequeueBuffer(%s): sslot=%d pslot=%d result=%d", + dbgSourceStr(source), *sslot, pslot, result); + uint32_t sourceBit = static_cast<uint32_t>(source) << pslot; + + if ((mProducerSlotSource & (1u << pslot)) != sourceBit) { + // This slot was previously dequeued from the other source; must + // re-request the buffer. + result |= BUFFER_NEEDS_REALLOCATION; + mProducerSlotSource &= ~(1u << pslot); + mProducerSlotSource |= sourceBit; + } + + if (result & RELEASE_ALL_BUFFERS) { + for (uint32_t i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { + if ((mProducerSlotSource & (1u << i)) == sourceBit) + mProducerBuffers[i].clear(); + } + } + if (result & BUFFER_NEEDS_REALLOCATION) { + mSource[source]->requestBuffer(*sslot, &mProducerBuffers[pslot]); + VDS_LOGV("dequeueBuffer(%s): buffers[%d]=%p", + dbgSourceStr(source), pslot, mProducerBuffers[pslot].get()); + } + + return result; +} + +status_t VirtualDisplaySurface::dequeueBuffer(int* pslot, sp<Fence>* fence, bool async, + uint32_t w, uint32_t h, uint32_t format, uint32_t usage) { + VDS_LOGW_IF(mDbgState != DBG_STATE_PREPARED, + "Unexpected dequeueBuffer() in %s state", dbgStateStr()); + mDbgState = DBG_STATE_GLES; + + VDS_LOGW_IF(!async, "EGL called dequeueBuffer with !async despite eglSwapInterval(0)"); + VDS_LOGV("dequeueBuffer %dx%d fmt=%d usage=%#x", w, h, format, usage); + + status_t result = NO_ERROR; + mProducerUsage = usage | GRALLOC_USAGE_HW_COMPOSER; + Source source = fbSourceForCompositionType(mCompositionType); + + if (source == SOURCE_SINK) { + + if (mOutputProducerSlot < 0) { + // Last chance bailout if something bad happened earlier. For example, + // in a GLES configuration, if the sink disappears then dequeueBuffer + // will fail, the GLES driver won't queue a buffer, but SurfaceFlinger + // will soldier on. So we end up here without a buffer. There should + // be lots of scary messages in the log just before this. + VDS_LOGE("dequeueBuffer: no buffer, bailing out"); + return NO_MEMORY; + } + + // We already dequeued the output buffer. If the GLES driver wants + // something incompatible, we have to cancel and get a new one. This + // will mean that HWC will see a different output buffer between + // prepare and set, but since we're in GLES-only mode already it + // shouldn't matter. + + const sp<GraphicBuffer>& buf = mProducerBuffers[mOutputProducerSlot]; + if ((mProducerUsage & ~buf->getUsage()) != 0 || + (format != 0 && format != (uint32_t)buf->getPixelFormat()) || + (w != 0 && w != mSinkBufferWidth) || + (h != 0 && h != mSinkBufferHeight)) { + VDS_LOGV("dequeueBuffer: output buffer doesn't satisfy GLES " + "request, getting a new buffer"); + result = refreshOutputBuffer(); + if (result < 0) + return result; + } + } + + if (source == SOURCE_SINK) { + *pslot = mOutputProducerSlot; + *fence = mOutputFence; + } else { + int sslot; + result = dequeueBuffer(source, format, &sslot, fence); + if (result >= 0) { + *pslot = mapSource2ProducerSlot(source, sslot); + } + } + return result; +} + +status_t VirtualDisplaySurface::queueBuffer(int pslot, + const QueueBufferInput& input, QueueBufferOutput* output) { + VDS_LOGW_IF(mDbgState != DBG_STATE_GLES, + "Unexpected queueBuffer(pslot=%d) in %s state", pslot, + dbgStateStr()); + mDbgState = DBG_STATE_GLES_DONE; + + VDS_LOGV("queueBuffer pslot=%d", pslot); + + status_t result; + if (mCompositionType == COMPOSITION_MIXED) { + // Queue the buffer back into the scratch pool + QueueBufferOutput scratchQBO; + int sslot = mapProducer2SourceSlot(SOURCE_SCRATCH, pslot); + result = mSource[SOURCE_SCRATCH]->queueBuffer(sslot, input, &scratchQBO); + if (result != NO_ERROR) + return result; + + // Now acquire the buffer from the scratch pool -- should be the same + // slot and fence as we just queued. + Mutex::Autolock lock(mMutex); + BufferQueue::BufferItem item; + result = acquireBufferLocked(&item, 0); + if (result != NO_ERROR) + return result; + VDS_LOGW_IF(item.mBuf != sslot, + "queueBuffer: acquired sslot %d from SCRATCH after queueing sslot %d", + item.mBuf, sslot); + mFbProducerSlot = mapSource2ProducerSlot(SOURCE_SCRATCH, item.mBuf); + mFbFence = mSlots[item.mBuf].mFence; + + } else { + LOG_FATAL_IF(mCompositionType != COMPOSITION_GLES, + "Unexpected queueBuffer in state %s for compositionType %s", + dbgStateStr(), dbgCompositionTypeStr(mCompositionType)); + + // Extract the GLES release fence for HWC to acquire + int64_t timestamp; + bool isAutoTimestamp; + Rect crop; + int scalingMode; + uint32_t transform; + bool async; + input.deflate(×tamp, &isAutoTimestamp, &crop, &scalingMode, + &transform, &async, &mFbFence); + + mFbProducerSlot = pslot; + mOutputFence = mFbFence; + } + + *output = mQueueBufferOutput; + return NO_ERROR; +} + +void VirtualDisplaySurface::cancelBuffer(int pslot, const sp<Fence>& fence) { + VDS_LOGW_IF(mDbgState != DBG_STATE_GLES, + "Unexpected cancelBuffer(pslot=%d) in %s state", pslot, + dbgStateStr()); + VDS_LOGV("cancelBuffer pslot=%d", pslot); + Source source = fbSourceForCompositionType(mCompositionType); + return mSource[source]->cancelBuffer( + mapProducer2SourceSlot(source, pslot), fence); +} + +int VirtualDisplaySurface::query(int what, int* value) { + return mSource[SOURCE_SINK]->query(what, value); +} + +status_t VirtualDisplaySurface::connect(const sp<IBinder>& token, + int api, bool producerControlledByApp, + QueueBufferOutput* output) { + QueueBufferOutput qbo; + status_t result = mSource[SOURCE_SINK]->connect(token, api, producerControlledByApp, &qbo); + if (result == NO_ERROR) { + updateQueueBufferOutput(qbo); + *output = mQueueBufferOutput; + } + return result; +} + +status_t VirtualDisplaySurface::disconnect(int api) { + return mSource[SOURCE_SINK]->disconnect(api); +} + +void VirtualDisplaySurface::updateQueueBufferOutput( + const QueueBufferOutput& qbo) { + uint32_t w, h, transformHint, numPendingBuffers; + qbo.deflate(&w, &h, &transformHint, &numPendingBuffers); + mQueueBufferOutput.inflate(w, h, 0, numPendingBuffers); +} + +void VirtualDisplaySurface::resetPerFrameState() { + mCompositionType = COMPOSITION_UNKNOWN; + mSinkBufferWidth = 0; + mSinkBufferHeight = 0; + mFbFence = Fence::NO_FENCE; + mOutputFence = Fence::NO_FENCE; + mFbProducerSlot = -1; + mOutputProducerSlot = -1; +} + +status_t VirtualDisplaySurface::refreshOutputBuffer() { + if (mOutputProducerSlot >= 0) { + mSource[SOURCE_SINK]->cancelBuffer( + mapProducer2SourceSlot(SOURCE_SINK, mOutputProducerSlot), + mOutputFence); + } + + int sslot; + status_t result = dequeueBuffer(SOURCE_SINK, 0, &sslot, &mOutputFence); + if (result < 0) + return result; + mOutputProducerSlot = mapSource2ProducerSlot(SOURCE_SINK, sslot); + + // On GLES-only frames, we don't have the right output buffer acquire fence + // until after GLES calls queueBuffer(). So here we just set the buffer + // (for use in HWC prepare) but not the fence; we'll call this again with + // the proper fence once we have it. + result = mHwc.setOutputBuffer(mDisplayId, Fence::NO_FENCE, + mProducerBuffers[mOutputProducerSlot]); + + return result; +} + +// This slot mapping function is its own inverse, so two copies are unnecessary. +// Both are kept to make the intent clear where the function is called, and for +// the (unlikely) chance that we switch to a different mapping function. +int VirtualDisplaySurface::mapSource2ProducerSlot(Source source, int sslot) { + if (source == SOURCE_SCRATCH) { + return BufferQueue::NUM_BUFFER_SLOTS - sslot - 1; + } else { + return sslot; + } +} +int VirtualDisplaySurface::mapProducer2SourceSlot(Source source, int pslot) { + return mapSource2ProducerSlot(source, pslot); +} + +VirtualDisplaySurface::Source +VirtualDisplaySurface::fbSourceForCompositionType(CompositionType type) { + return type == COMPOSITION_MIXED ? SOURCE_SCRATCH : SOURCE_SINK; +} + +const char* VirtualDisplaySurface::dbgStateStr() const { + switch (mDbgState) { + case DBG_STATE_IDLE: return "IDLE"; + case DBG_STATE_PREPARED: return "PREPARED"; + case DBG_STATE_GLES: return "GLES"; + case DBG_STATE_GLES_DONE: return "GLES_DONE"; + case DBG_STATE_HWC: return "HWC"; + default: return "INVALID"; + } +} + +const char* VirtualDisplaySurface::dbgSourceStr(Source s) { + switch (s) { + case SOURCE_SINK: return "SINK"; + case SOURCE_SCRATCH: return "SCRATCH"; + default: return "INVALID"; + } +} + // --------------------------------------------------------------------------- } // namespace android // --------------------------------------------------------------------------- diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h index f321795..18fb5a7 100644 --- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h +++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h @@ -17,6 +17,9 @@ #ifndef ANDROID_SF_VIRTUAL_DISPLAY_SURFACE_H #define ANDROID_SF_VIRTUAL_DISPLAY_SURFACE_H +#include <gui/ConsumerBase.h> +#include <gui/IGraphicBufferProducer.h> + #include "DisplaySurface.h" // --------------------------------------------------------------------------- @@ -25,26 +28,197 @@ namespace android { class HWComposer; -/* This DisplaySurface implementation is a stub used for developing HWC - * virtual display support. It is currently just a passthrough. +/* This DisplaySurface implementation supports virtual displays, where GLES + * and/or HWC compose into a buffer that is then passed to an arbitrary + * consumer (the sink) running in another process. + * + * The simplest case is when the virtual display will never use the h/w + * composer -- either the h/w composer doesn't support writing to buffers, or + * there are more virtual displays than it supports simultaneously. In this + * case, the GLES driver works directly with the output buffer queue, and + * calls to the VirtualDisplay from SurfaceFlinger and DisplayHardware do + * nothing. + * + * If h/w composer might be used, then each frame will fall into one of three + * configurations: GLES-only, HWC-only, and MIXED composition. In all of these, + * we must provide a FB target buffer and output buffer for the HWC set() call. + * + * In GLES-only composition, the GLES driver is given a buffer from the sink to + * render into. When the GLES driver queues the buffer to the + * VirtualDisplaySurface, the VirtualDisplaySurface holds onto it instead of + * immediately queueing it to the sink. The buffer is used as both the FB + * target and output buffer for HWC, though on these frames the HWC doesn't + * do any work for this display and doesn't write to the output buffer. After + * composition is complete, the buffer is queued to the sink. + * + * In HWC-only composition, the VirtualDisplaySurface dequeues a buffer from + * the sink and passes it to HWC as both the FB target buffer and output + * buffer. The HWC doesn't need to read from the FB target buffer, but does + * write to the output buffer. After composition is complete, the buffer is + * queued to the sink. + * + * On MIXED frames, things become more complicated, since some h/w composer + * implementations can't read from and write to the same buffer. This class has + * an internal BufferQueue that it uses as a scratch buffer pool. The GLES + * driver is given a scratch buffer to render into. When it finishes rendering, + * the buffer is queued and then immediately acquired by the + * VirtualDisplaySurface. The scratch buffer is then used as the FB target + * buffer for HWC, and a separate buffer is dequeued from the sink and used as + * the HWC output buffer. When HWC composition is complete, the scratch buffer + * is released and the output buffer is queued to the sink. */ -class VirtualDisplaySurface : public DisplaySurface { +class VirtualDisplaySurface : public DisplaySurface, + public BnGraphicBufferProducer, + private ConsumerBase { public: VirtualDisplaySurface(HWComposer& hwc, int32_t dispId, const sp<IGraphicBufferProducer>& sink, + const sp<BufferQueue>& bq, const String8& name); - virtual sp<IGraphicBufferProducer> getIGraphicBufferProducer() const; - + // + // DisplaySurface interface + // + virtual status_t beginFrame(); + virtual status_t prepareFrame(CompositionType compositionType); virtual status_t compositionComplete(); virtual status_t advanceFrame(); virtual void onFrameCommitted(); virtual void dump(String8& result) const; private: + enum Source {SOURCE_SINK = 0, SOURCE_SCRATCH = 1}; + virtual ~VirtualDisplaySurface(); - sp<IGraphicBufferProducer> mSink; + // + // IGraphicBufferProducer interface, used by the GLES driver. + // + virtual status_t requestBuffer(int pslot, sp<GraphicBuffer>* outBuf); + virtual status_t setBufferCount(int bufferCount); + virtual status_t dequeueBuffer(int* pslot, sp<Fence>* fence, bool async, + uint32_t w, uint32_t h, uint32_t format, uint32_t usage); + virtual status_t queueBuffer(int pslot, + const QueueBufferInput& input, QueueBufferOutput* output); + virtual void cancelBuffer(int pslot, const sp<Fence>& fence); + virtual int query(int what, int* value); + virtual status_t connect(const sp<IBinder>& token, + int api, bool producerControlledByApp, QueueBufferOutput* output); + virtual status_t disconnect(int api); + + // + // Utility methods + // + static Source fbSourceForCompositionType(CompositionType type); + status_t dequeueBuffer(Source source, uint32_t format, + int* sslot, sp<Fence>* fence); + void updateQueueBufferOutput(const QueueBufferOutput& qbo); + void resetPerFrameState(); + status_t refreshOutputBuffer(); + + // Both the sink and scratch buffer pools have their own set of slots + // ("source slots", or "sslot"). We have to merge these into the single + // set of slots used by the GLES producer ("producer slots" or "pslot") and + // internally in the VirtualDisplaySurface. To minimize the number of times + // a producer slot switches which source it comes from, we map source slot + // numbers to producer slot numbers differently for each source. + static int mapSource2ProducerSlot(Source source, int sslot); + static int mapProducer2SourceSlot(Source source, int pslot); + + // + // Immutable after construction + // + HWComposer& mHwc; + const int32_t mDisplayId; + const String8 mDisplayName; + sp<IGraphicBufferProducer> mSource[2]; // indexed by SOURCE_* + + // + // Inter-frame state + // + + // To avoid buffer reallocations, we track the buffer usage requested by + // the GLES driver in dequeueBuffer so we can use the same flags on + // HWC-only frames. + uint32_t mProducerUsage; + + // Since we present a single producer interface to the GLES driver, but + // are internally muxing between the sink and scratch producers, we have + // to keep track of which source last returned each producer slot from + // dequeueBuffer. Each bit in mLastSlotSource corresponds to a producer + // slot. Both mProducerSlotSource and mProducerBuffers are indexed by a + // "producer slot"; see the mapSlot*() functions. + uint32_t mProducerSlotSource; + sp<GraphicBuffer> mProducerBuffers[BufferQueue::NUM_BUFFER_SLOTS]; + + // The QueueBufferOutput with the latest info from the sink, and with the + // transform hint cleared. Since we defer queueBuffer from the GLES driver + // to the sink, we have to return the previous version. + QueueBufferOutput mQueueBufferOutput; + + // + // Intra-frame state + // + + // Composition type and GLES buffer source for the current frame. + // Valid after prepareFrame(), cleared in onFrameCommitted. + CompositionType mCompositionType; + + // Details of the current sink buffer. These become valid when a buffer is + // dequeued from the sink, and are used when queueing the buffer. + uint32_t mSinkBufferWidth, mSinkBufferHeight; + + // mFbFence is the fence HWC should wait for before reading the framebuffer + // target buffer. + sp<Fence> mFbFence; + + // mOutputFence is the fence HWC should wait for before writing to the + // output buffer. + sp<Fence> mOutputFence; + + // Producer slot numbers for the buffers to use for HWC framebuffer target + // and output. + int mFbProducerSlot; + int mOutputProducerSlot; + + // Debug only -- track the sequence of events in each frame so we can make + // sure they happen in the order we expect. This class implicitly models + // a state machine; this enum/variable makes it explicit. + // + // +-----------+-------------------+-------------+ + // | State | Event || Next State | + // +-----------+-------------------+-------------+ + // | IDLE | beginFrame || BEGUN | + // | BEGUN | prepareFrame || PREPARED | + // | PREPARED | dequeueBuffer [1] || GLES | + // | PREPARED | advanceFrame [2] || HWC | + // | GLES | queueBuffer || GLES_DONE | + // | GLES_DONE | advanceFrame || HWC | + // | HWC | onFrameCommitted || IDLE | + // +-----------+-------------------++------------+ + // [1] COMPOSITION_GLES and COMPOSITION_MIXED frames. + // [2] COMPOSITION_HWC frames. + // + enum DbgState { + // no buffer dequeued, don't know anything about the next frame + DBG_STATE_IDLE, + // output buffer dequeued, framebuffer source not yet known + DBG_STATE_BEGUN, + // output buffer dequeued, framebuffer source known but not provided + // to GLES yet. + DBG_STATE_PREPARED, + // GLES driver has a buffer dequeued + DBG_STATE_GLES, + // GLES driver has queued the buffer, we haven't sent it to HWC yet + DBG_STATE_GLES_DONE, + // HWC has the buffer for this frame + DBG_STATE_HWC, + }; + DbgState mDbgState; + CompositionType mDbgLastCompositionType; + + const char* dbgStateStr() const; + static const char* dbgSourceStr(Source s); }; // --------------------------------------------------------------------------- diff --git a/services/surfaceflinger/Effects/Daltonizer.cpp b/services/surfaceflinger/Effects/Daltonizer.cpp new file mode 100644 index 0000000..f384ba4 --- /dev/null +++ b/services/surfaceflinger/Effects/Daltonizer.cpp @@ -0,0 +1,183 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Daltonizer.h" +#include <ui/mat4.h> + +namespace android { + +Daltonizer::Daltonizer() : + mType(deuteranomaly), mMode(simulation), mDirty(true) { +} + +Daltonizer::~Daltonizer() { +} + +void Daltonizer::setType(Daltonizer::ColorBlindnessTypes type) { + if (type != mType) { + mDirty = true; + mType = type; + } +} + +void Daltonizer::setMode(Daltonizer::Mode mode) { + if (mode != mMode) { + mDirty = true; + mMode = mode; + } +} + +const mat4& Daltonizer::operator()() { + if (mDirty) { + mDirty = false; + update(); + } + return mColorTransform; +} + +void Daltonizer::update() { + // converts a linear RGB color to the XYZ space + const mat4 rgb2xyz( 0.4124, 0.2126, 0.0193, 0, + 0.3576, 0.7152, 0.1192, 0, + 0.1805, 0.0722, 0.9505, 0, + 0 , 0 , 0 , 1); + + // converts a XYZ color to the LMS space. + const mat4 xyz2lms( 0.7328,-0.7036, 0.0030, 0, + 0.4296, 1.6975, 0.0136, 0, + -0.1624, 0.0061, 0.9834, 0, + 0 , 0 , 0 , 1); + + // Direct conversion from linear RGB to LMS + const mat4 rgb2lms(xyz2lms*rgb2xyz); + + // And back from LMS to linear RGB + const mat4 lms2rgb(inverse(rgb2lms)); + + // To simulate color blindness we need to "remove" the data lost by the absence of + // a cone. This cannot be done by just zeroing out the corresponding LMS component + // because it would create a color outside of the RGB gammut. + // Instead we project the color along the axis of the missing component onto a plane + // within the RGB gammut: + // - since the projection happens along the axis of the missing component, a + // color blind viewer perceives the projected color the same. + // - We use the plane defined by 3 points in LMS space: black, white and + // blue and red for protanopia/deuteranopia and tritanopia respectively. + + // LMS space red + const vec3& lms_r(rgb2lms[0].rgb); + // LMS space blue + const vec3& lms_b(rgb2lms[2].rgb); + // LMS space white + const vec3 lms_w((rgb2lms * vec4(1)).rgb); + + // To find the planes we solve the a*L + b*M + c*S = 0 equation for the LMS values + // of the three known points. This equation is trivially solved, and has for + // solution the following cross-products: + const vec3 p0 = cross(lms_w, lms_b); // protanopia/deuteranopia + const vec3 p1 = cross(lms_w, lms_r); // tritanopia + + // The following 3 matrices perform the projection of a LMS color onto the given plane + // along the selected axis + + // projection for protanopia (L = 0) + const mat4 lms2lmsp( 0.0000, 0.0000, 0.0000, 0, + -p0.y / p0.x, 1.0000, 0.0000, 0, + -p0.z / p0.x, 0.0000, 1.0000, 0, + 0 , 0 , 0 , 1); + + // projection for deuteranopia (M = 0) + const mat4 lms2lmsd( 1.0000, -p0.x / p0.y, 0.0000, 0, + 0.0000, 0.0000, 0.0000, 0, + 0.0000, -p0.z / p0.y, 1.0000, 0, + 0 , 0 , 0 , 1); + + // projection for tritanopia (S = 0) + const mat4 lms2lmst( 1.0000, 0.0000, -p1.x / p1.z, 0, + 0.0000, 1.0000, -p1.y / p1.z, 0, + 0.0000, 0.0000, 0.0000, 0, + 0 , 0 , 0 , 1); + + // We will calculate the error between the color and the color viewed by + // a color blind user and "spread" this error onto the healthy cones. + // The matrices below perform this last step and have been chosen arbitrarily. + + // The amount of correction can be adjusted here. + + // error spread for protanopia + const mat4 errp( 1.0, 0.7, 0.7, 0, + 0.0, 1.0, 0.0, 0, + 0.0, 0.0, 1.0, 0, + 0, 0, 0, 1); + + // error spread for deuteranopia + const mat4 errd( 1.0, 0.0, 0.0, 0, + 0.7, 1.0, 0.7, 0, + 0.0, 0.0, 1.0, 0, + 0, 0, 0, 1); + + // error spread for tritanopia + const mat4 errt( 1.0, 0.0, 0.0, 0, + 0.0, 1.0, 0.0, 0, + 0.7, 0.7, 1.0, 0, + 0, 0, 0, 1); + + const mat4 identity; + + // And the magic happens here... + // We construct the matrix that will perform the whole correction. + + // simulation: type of color blindness to simulate: + // set to either lms2lmsp, lms2lmsd, lms2lmst + mat4 simulation; + + // correction: type of color blindness correction (should match the simulation above): + // set to identity, errp, errd, errt ([0] for simulation only) + mat4 correction(0); + + // control: simulation post-correction (used for debugging): + // set to identity or lms2lmsp, lms2lmsd, lms2lmst + mat4 control; + switch (mType) { + case protanopia: + case protanomaly: + simulation = lms2lmsp; + if (mMode == Daltonizer::correction) + correction = errp; + break; + case deuteranopia: + case deuteranomaly: + simulation = lms2lmsd; + if (mMode == Daltonizer::correction) + correction = errd; + break; + case tritanopia: + case tritanomaly: + simulation = lms2lmst; + if (mMode == Daltonizer::correction) + correction = errt; + break; + } + + if (true) { + control = simulation; + } + + mColorTransform = lms2rgb * control * + (simulation * rgb2lms + correction * (rgb2lms - simulation * rgb2lms)); +} + +} /* namespace android */ diff --git a/services/surfaceflinger/Effects/Daltonizer.h b/services/surfaceflinger/Effects/Daltonizer.h new file mode 100644 index 0000000..e816437 --- /dev/null +++ b/services/surfaceflinger/Effects/Daltonizer.h @@ -0,0 +1,59 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SF_EFFECTS_DALTONIZER_H_ +#define SF_EFFECTS_DALTONIZER_H_ + +#include <ui/mat4.h> + +namespace android { + +class Daltonizer { +public: + enum ColorBlindnessTypes { + protanopia, // L (red) cone missing + deuteranopia, // M (green) cone missing + tritanopia, // S (blue) cone missing + protanomaly, // L (red) cone deficient + deuteranomaly, // M (green) cone deficient (most common) + tritanomaly // S (blue) cone deficient + }; + + enum Mode { + simulation, + correction + }; + + Daltonizer(); + ~Daltonizer(); + + void setType(ColorBlindnessTypes type); + void setMode(Mode mode); + + // returns the color transform to apply in the shader + const mat4& operator()(); + +private: + void update(); + + ColorBlindnessTypes mType; + Mode mMode; + bool mDirty; + mat4 mColorTransform; +}; + +} /* namespace android */ +#endif /* SF_EFFECTS_DALTONIZER_H_ */ diff --git a/services/surfaceflinger/EventControlThread.cpp b/services/surfaceflinger/EventControlThread.cpp new file mode 100644 index 0000000..6504091 --- /dev/null +++ b/services/surfaceflinger/EventControlThread.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "EventControlThread.h" +#include "SurfaceFlinger.h" + +namespace android { + +EventControlThread::EventControlThread(const sp<SurfaceFlinger>& flinger): + mFlinger(flinger), + mVsyncEnabled(false) { +} + +void EventControlThread::setVsyncEnabled(bool enabled) { + Mutex::Autolock lock(mMutex); + mVsyncEnabled = enabled; + mCond.signal(); +} + +bool EventControlThread::threadLoop() { + Mutex::Autolock lock(mMutex); + + bool vsyncEnabled = mVsyncEnabled; + + mFlinger->eventControl(HWC_DISPLAY_PRIMARY, SurfaceFlinger::EVENT_VSYNC, + mVsyncEnabled); + + while (true) { + status_t err = mCond.wait(mMutex); + if (err != NO_ERROR) { + ALOGE("error waiting for new events: %s (%d)", + strerror(-err), err); + return false; + } + + if (vsyncEnabled != mVsyncEnabled) { + mFlinger->eventControl(HWC_DISPLAY_PRIMARY, + SurfaceFlinger::EVENT_VSYNC, mVsyncEnabled); + vsyncEnabled = mVsyncEnabled; + } + } + + return false; +} + +} // namespace android diff --git a/include/utils/SystemClock.h b/services/surfaceflinger/EventControlThread.h index d75264c..be6c53a 100644 --- a/include/utils/SystemClock.h +++ b/services/surfaceflinger/EventControlThread.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,20 +14,35 @@ * limitations under the License. */ -#ifndef ANDROID_UTILS_SYSTEMCLOCK_H -#define ANDROID_UTILS_SYSTEMCLOCK_H +#ifndef ANDROID_EVENTCONTROLTHREAD_H +#define ANDROID_EVENTCONTROLTHREAD_H -#include <stdint.h> -#include <sys/types.h> +#include <stddef.h> + +#include <utils/Mutex.h> +#include <utils/Thread.h> namespace android { -int setCurrentTimeMillis(int64_t millis); -int64_t uptimeMillis(); -int64_t elapsedRealtime(); -int64_t elapsedRealtimeNano(); +class SurfaceFlinger; + +class EventControlThread: public Thread { +public: + + EventControlThread(const sp<SurfaceFlinger>& flinger); + virtual ~EventControlThread() {} + + void setVsyncEnabled(bool enabled); + virtual bool threadLoop(); + +private: + sp<SurfaceFlinger> mFlinger; + bool mVsyncEnabled; -}; // namespace android + Mutex mMutex; + Condition mCond; +}; -#endif // ANDROID_UTILS_SYSTEMCLOCK_H +} +#endif // ANDROID_DISPSYNC_H diff --git a/services/surfaceflinger/EventLog/EventLog.cpp b/services/surfaceflinger/EventLog/EventLog.cpp new file mode 100644 index 0000000..47bab83 --- /dev/null +++ b/services/surfaceflinger/EventLog/EventLog.cpp @@ -0,0 +1,133 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <cutils/log.h> +#include <utils/String8.h> + +#include "EventLog.h" + +// --------------------------------------------------------------------------- +namespace android { +// --------------------------------------------------------------------------- + +ANDROID_SINGLETON_STATIC_INSTANCE(EventLog) + + +EventLog::EventLog() { +} + +void EventLog::doLogFrameDurations(const String8& window, + const int32_t* durations, size_t numDurations) { + EventLog::TagBuffer buffer(LOGTAG_SF_FRAME_DUR); + buffer.startList(1 + numDurations); + buffer.writeString8(window); + for (size_t i = 0; i < numDurations; i++) { + buffer.writeInt32(durations[i]); + } + buffer.endList(); + buffer.log(); +} + +void EventLog::logFrameDurations(const String8& window, + const int32_t* durations, size_t numDurations) { + EventLog::getInstance().doLogFrameDurations(window, durations, + numDurations); +} + +// --------------------------------------------------------------------------- + +EventLog::TagBuffer::TagBuffer(int32_t tag) + : mPos(0), mTag(tag), mOverflow(false) { +} + +void EventLog::TagBuffer::log() { + if (mOverflow) { + ALOGW("couldn't log to binary event log: overflow."); + } else if (android_bWriteLog(mTag, mStorage, mPos) < 0) { + ALOGE("couldn't log to EventLog: %s", strerror(errno)); + } + // purge the buffer + mPos = 0; + mOverflow = false; +} + +void EventLog::TagBuffer::startList(int8_t count) { + if (mOverflow) return; + const size_t needed = 1 + sizeof(count); + if (mPos + needed > STORAGE_MAX_SIZE) { + mOverflow = true; + return; + } + mStorage[mPos + 0] = EVENT_TYPE_LIST; + mStorage[mPos + 1] = count; + mPos += needed; +} + +void EventLog::TagBuffer::endList() { + if (mOverflow) return; + const size_t needed = 1; + if (mPos + needed > STORAGE_MAX_SIZE) { + mOverflow = true; + return; + } + mStorage[mPos + 0] = '\n'; + mPos += needed; +} + +void EventLog::TagBuffer::writeInt32(int32_t value) { + if (mOverflow) return; + const size_t needed = 1 + sizeof(value); + if (mPos + needed > STORAGE_MAX_SIZE) { + mOverflow = true; + return; + } + mStorage[mPos + 0] = EVENT_TYPE_INT; + memcpy(&mStorage[mPos + 1], &value, sizeof(value)); + mPos += needed; +} + +void EventLog::TagBuffer::writeInt64(int64_t value) { + if (mOverflow) return; + const size_t needed = 1 + sizeof(value); + if (mPos + needed > STORAGE_MAX_SIZE) { + mOverflow = true; + return; + } + mStorage[mPos + 0] = EVENT_TYPE_LONG; + memcpy(&mStorage[mPos + 1], &value, sizeof(value)); + mPos += needed; +} + +void EventLog::TagBuffer::writeString8(const String8& value) { + if (mOverflow) return; + const int32_t stringLen = value.length(); + const size_t needed = 1 + sizeof(int32_t) + stringLen; + if (mPos + needed > STORAGE_MAX_SIZE) { + mOverflow = true; + return; + } + mStorage[mPos + 0] = EVENT_TYPE_STRING; + memcpy(&mStorage[mPos + 1], &stringLen, sizeof(int32_t)); + memcpy(&mStorage[mPos + 5], value.string(), stringLen); + mPos += needed; +} + +// --------------------------------------------------------------------------- +}// namespace android + +// --------------------------------------------------------------------------- diff --git a/services/surfaceflinger/EventLog/EventLog.h b/services/surfaceflinger/EventLog/EventLog.h new file mode 100644 index 0000000..5207514 --- /dev/null +++ b/services/surfaceflinger/EventLog/EventLog.h @@ -0,0 +1,85 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdint.h> +#include <utils/Errors.h> +#include <utils/Singleton.h> + +#ifndef ANDROID_SF_EVENTLOG_H +#define ANDROID_SF_EVENTLOG_H + +// --------------------------------------------------------------------------- +namespace android { +// --------------------------------------------------------------------------- + +class String8; + +class EventLog : public Singleton<EventLog> { + +public: + static void logFrameDurations(const String8& window, + const int32_t* durations, size_t numDurations); + +protected: + EventLog(); + +private: + /* + * EventLogBuffer is a helper class to construct an in-memory event log + * tag. In this version the buffer is not dynamic, so write operation can + * fail if there is not enough space in the temporary buffer. + * Once constructed, the buffer can be logger by calling the log() + * method. + */ + + class TagBuffer { + enum { STORAGE_MAX_SIZE = 128 }; + int32_t mPos; + int32_t mTag; + bool mOverflow; + char mStorage[STORAGE_MAX_SIZE]; + public: + TagBuffer(int32_t tag); + + // starts list of items + void startList(int8_t count); + // terminates the list + void endList(); + // write a 32-bit integer + void writeInt32(int32_t value); + // write a 64-bit integer + void writeInt64(int64_t value); + // write a C string + void writeString8(const String8& value); + + // outputs the the buffer to the log + void log(); + }; + + friend class Singleton<EventLog>; + EventLog(const EventLog&); + EventLog& operator =(const EventLog&); + + enum { LOGTAG_SF_FRAME_DUR = 60100 }; + void doLogFrameDurations(const String8& window, const int32_t* durations, + size_t numDurations); +}; + +// --------------------------------------------------------------------------- +}// namespace android +// --------------------------------------------------------------------------- + +#endif /* ANDROID_SF_EVENTLOG_H */ diff --git a/services/surfaceflinger/EventLog/EventLogTags.logtags b/services/surfaceflinger/EventLog/EventLogTags.logtags new file mode 100644 index 0000000..791e0e4 --- /dev/null +++ b/services/surfaceflinger/EventLog/EventLogTags.logtags @@ -0,0 +1,41 @@ +# The entries in this file map a sparse set of log tag numbers to tag names. +# This is installed on the device, in /system/etc, and parsed by logcat. +# +# Tag numbers are decimal integers, from 0 to 2^31. (Let's leave the +# negative values alone for now.) +# +# Tag names are one or more ASCII letters and numbers or underscores, i.e. +# "[A-Z][a-z][0-9]_". Do not include spaces or punctuation (the former +# impacts log readability, the latter makes regex searches more annoying). +# +# Tag numbers and names are separated by whitespace. Blank lines and lines +# starting with '#' are ignored. +# +# Optionally, after the tag names can be put a description for the value(s) +# of the tag. Description are in the format +# (<name>|data type[|data unit]) +# Multiple values are separated by commas. +# +# The data type is a number from the following values: +# 1: int +# 2: long +# 3: string +# 4: list +# +# The data unit is a number taken from the following list: +# 1: Number of objects +# 2: Number of bytes +# 3: Number of milliseconds +# 4: Number of allocations +# 5: Id +# 6: Percent +# Default value for data of type int/long is 2 (bytes). +# +# See system/core/logcat/event.logtags for the master copy of the tags. + +# 60100 - 60199 reserved for surfaceflinger + +60100 sf_frame_dur (window|3),(dur0|1),(dur1|1),(dur2|1),(dur3|1),(dur4|1),(dur5|1),(dur6|1) + +# NOTE - the range 1000000-2000000 is reserved for partners and others who +# want to define their own log tags without conflicting with the core platform. diff --git a/services/surfaceflinger/EventThread.cpp b/services/surfaceflinger/EventThread.cpp index 4d0fc79..3528b62 100644 --- a/services/surfaceflinger/EventThread.cpp +++ b/services/surfaceflinger/EventThread.cpp @@ -36,12 +36,13 @@ namespace android { // --------------------------------------------------------------------------- -EventThread::EventThread(const sp<SurfaceFlinger>& flinger) - : mFlinger(flinger), +EventThread::EventThread(const sp<VSyncSource>& src) + : mVSyncSource(src), mUseSoftwareVSync(false), + mVsyncEnabled(false), mDebugVsyncEnabled(false) { - for (int32_t i=0 ; i<HWC_NUM_DISPLAY_TYPES ; i++) { + for (int32_t i=0 ; i<DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES ; i++) { mVSyncEvent[i].header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC; mVSyncEvent[i].header.id = 0; mVSyncEvent[i].header.timestamp = 0; @@ -110,27 +111,21 @@ void EventThread::onScreenAcquired() { } } - -void EventThread::onVSyncReceived(int type, nsecs_t timestamp) { - ALOGE_IF(type >= HWC_NUM_DISPLAY_TYPES, - "received vsync event for an invalid display (id=%d)", type); - +void EventThread::onVSyncEvent(nsecs_t timestamp) { Mutex::Autolock _l(mLock); - if (type < HWC_NUM_DISPLAY_TYPES) { - mVSyncEvent[type].header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC; - mVSyncEvent[type].header.id = type; - mVSyncEvent[type].header.timestamp = timestamp; - mVSyncEvent[type].vsync.count++; - mCondition.broadcast(); - } + mVSyncEvent[0].header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC; + mVSyncEvent[0].header.id = 0; + mVSyncEvent[0].header.timestamp = timestamp; + mVSyncEvent[0].vsync.count++; + mCondition.broadcast(); } void EventThread::onHotplugReceived(int type, bool connected) { - ALOGE_IF(type >= HWC_NUM_DISPLAY_TYPES, + ALOGE_IF(type >= DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES, "received hotplug event for an invalid display (id=%d)", type); Mutex::Autolock _l(mLock); - if (type < HWC_NUM_DISPLAY_TYPES) { + if (type < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) { DisplayEventReceiver::Event event; event.header.type = DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG; event.header.id = type; @@ -184,7 +179,7 @@ Vector< sp<EventThread::Connection> > EventThread::waitForEvent( size_t vsyncCount = 0; nsecs_t timestamp = 0; - for (int32_t i=0 ; i<HWC_NUM_DISPLAY_TYPES ; i++) { + for (int32_t i=0 ; i<DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES ; i++) { timestamp = mVSyncEvent[i].header.timestamp; if (timestamp) { // we have a vsync event to dispatch @@ -285,7 +280,7 @@ Vector< sp<EventThread::Connection> > EventThread::waitForEvent( // FIXME: how do we decide which display id the fake // vsync came from ? mVSyncEvent[0].header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC; - mVSyncEvent[0].header.id = HWC_DISPLAY_PRIMARY; + mVSyncEvent[0].header.id = DisplayDevice::DISPLAY_PRIMARY; mVSyncEvent[0].header.timestamp = systemTime(SYSTEM_TIME_MONOTONIC); mVSyncEvent[0].vsync.count++; } @@ -308,19 +303,26 @@ Vector< sp<EventThread::Connection> > EventThread::waitForEvent( void EventThread::enableVSyncLocked() { if (!mUseSoftwareVSync) { // never enable h/w VSYNC when screen is off - mFlinger->eventControl(HWC_DISPLAY_PRIMARY, SurfaceFlinger::EVENT_VSYNC, true); - mPowerHAL.vsyncHint(true); + if (!mVsyncEnabled) { + mVsyncEnabled = true; + mVSyncSource->setCallback(static_cast<VSyncSource::Callback*>(this)); + mVSyncSource->setVSyncEnabled(true); + mPowerHAL.vsyncHint(true); + } } mDebugVsyncEnabled = true; } void EventThread::disableVSyncLocked() { - mFlinger->eventControl(HWC_DISPLAY_PRIMARY, SurfaceFlinger::EVENT_VSYNC, false); - mPowerHAL.vsyncHint(false); - mDebugVsyncEnabled = false; + if (mVsyncEnabled) { + mVsyncEnabled = false; + mVSyncSource->setVSyncEnabled(false); + mPowerHAL.vsyncHint(false); + mDebugVsyncEnabled = false; + } } -void EventThread::dump(String8& result, char* buffer, size_t SIZE) const { +void EventThread::dump(String8& result) const { Mutex::Autolock _l(mLock); result.appendFormat("VSYNC state: %s\n", mDebugVsyncEnabled?"enabled":"disabled"); @@ -328,7 +330,7 @@ void EventThread::dump(String8& result, char* buffer, size_t SIZE) const { mUseSoftwareVSync?"enabled":"disabled"); result.appendFormat(" numListeners=%u,\n events-delivered: %u\n", mDisplayEventConnections.size(), - mVSyncEvent[HWC_DISPLAY_PRIMARY].vsync.count); + mVSyncEvent[DisplayDevice::DISPLAY_PRIMARY].vsync.count); for (size_t i=0 ; i<mDisplayEventConnections.size() ; i++) { sp<Connection> connection = mDisplayEventConnections.itemAt(i).promote(); diff --git a/services/surfaceflinger/EventThread.h b/services/surfaceflinger/EventThread.h index 1934f98..f6ab4a7 100644 --- a/services/surfaceflinger/EventThread.h +++ b/services/surfaceflinger/EventThread.h @@ -23,12 +23,11 @@ #include <gui/DisplayEventReceiver.h> #include <gui/IDisplayEventConnection.h> -#include <hardware/hwcomposer_defs.h> - #include <utils/Errors.h> #include <utils/threads.h> #include <utils/SortedVector.h> +#include "DisplayDevice.h" #include "DisplayHardware/PowerHAL.h" // --------------------------------------------------------------------------- @@ -40,7 +39,21 @@ class String8; // --------------------------------------------------------------------------- -class EventThread : public Thread { + +class VSyncSource : public virtual RefBase { +public: + class Callback: public virtual RefBase { + public: + virtual ~Callback() {} + virtual void onVSyncEvent(nsecs_t when) = 0; + }; + + virtual ~VSyncSource() {} + virtual void setVSyncEnabled(bool enable) = 0; + virtual void setCallback(const sp<Callback>& callback) = 0; +}; + +class EventThread : public Thread, private VSyncSource::Callback { class Connection : public BnDisplayEventConnection { public: Connection(const sp<EventThread>& eventThread); @@ -63,7 +76,7 @@ class EventThread : public Thread { public: - EventThread(const sp<SurfaceFlinger>& flinger); + EventThread(const sp<VSyncSource>& src); sp<Connection> createEventConnection() const; status_t registerDisplayEventConnection(const sp<Connection>& connection); @@ -77,25 +90,26 @@ public: // called after the screen is turned on from main thread void onScreenAcquired(); - // called when receiving a vsync event - void onVSyncReceived(int type, nsecs_t timestamp); + // called when receiving a hotplug event void onHotplugReceived(int type, bool connected); Vector< sp<EventThread::Connection> > waitForEvent( DisplayEventReceiver::Event* event); - void dump(String8& result, char* buffer, size_t SIZE) const; + void dump(String8& result) const; private: virtual bool threadLoop(); virtual void onFirstRef(); + virtual void onVSyncEvent(nsecs_t timestamp); + void removeDisplayEventConnection(const wp<Connection>& connection); void enableVSyncLocked(); void disableVSyncLocked(); // constants - sp<SurfaceFlinger> mFlinger; + sp<VSyncSource> mVSyncSource; PowerHAL mPowerHAL; mutable Mutex mLock; @@ -104,8 +118,9 @@ private: // protected by mLock SortedVector< wp<Connection> > mDisplayEventConnections; Vector< DisplayEventReceiver::Event > mPendingEvents; - DisplayEventReceiver::Event mVSyncEvent[HWC_DISPLAY_TYPES_SUPPORTED]; + DisplayEventReceiver::Event mVSyncEvent[DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES]; bool mUseSoftwareVSync; + bool mVsyncEnabled; // for debugging bool mDebugVsyncEnabled; diff --git a/services/surfaceflinger/FrameTracker.cpp b/services/surfaceflinger/FrameTracker.cpp index 9b55d44..d406672 100644 --- a/services/surfaceflinger/FrameTracker.cpp +++ b/services/surfaceflinger/FrameTracker.cpp @@ -17,17 +17,22 @@ // This is needed for stdint.h to define INT64_MAX in C++ #define __STDC_LIMIT_MACROS +#include <cutils/log.h> + #include <ui/Fence.h> #include <utils/String8.h> #include "FrameTracker.h" +#include "EventLog/EventLog.h" namespace android { FrameTracker::FrameTracker() : mOffset(0), - mNumFences(0) { + mNumFences(0), + mDisplayPeriod(0) { + resetFrameCountersLocked(); } void FrameTracker::setDesiredPresentTime(nsecs_t presentTime) { @@ -57,8 +62,18 @@ void FrameTracker::setActualPresentFence(const sp<Fence>& readyFence) { mNumFences++; } +void FrameTracker::setDisplayRefreshPeriod(nsecs_t displayPeriod) { + Mutex::Autolock lock(mMutex); + mDisplayPeriod = displayPeriod; +} + void FrameTracker::advanceFrame() { Mutex::Autolock lock(mMutex); + + // Update the statistic to include the frame we just finished. + updateStatsLocked(mOffset); + + // Advance to the next frame. mOffset = (mOffset+1) % NUM_FRAME_RECORDS; mFrameRecords[mOffset].desiredPresentTime = INT64_MAX; mFrameRecords[mOffset].frameReadyTime = INT64_MAX; @@ -98,12 +113,19 @@ void FrameTracker::clear() { mFrameRecords[mOffset].actualPresentTime = INT64_MAX; } +void FrameTracker::logAndResetStats(const String8& name) { + Mutex::Autolock lock(mMutex); + logStatsLocked(name); + resetFrameCountersLocked(); +} + void FrameTracker::processFencesLocked() const { FrameRecord* records = const_cast<FrameRecord*>(mFrameRecords); int& numFences = const_cast<int&>(mNumFences); for (int i = 1; i < NUM_FRAME_RECORDS && numFences > 0; i++) { size_t idx = (mOffset+NUM_FRAME_RECORDS-i) % NUM_FRAME_RECORDS; + bool updated = false; const sp<Fence>& rfence = records[idx].frameReadyFence; if (rfence != NULL) { @@ -111,6 +133,7 @@ void FrameTracker::processFencesLocked() const { if (records[idx].frameReadyTime < INT64_MAX) { records[idx].frameReadyFence = NULL; numFences--; + updated = true; } } @@ -120,11 +143,67 @@ void FrameTracker::processFencesLocked() const { if (records[idx].actualPresentTime < INT64_MAX) { records[idx].actualPresentFence = NULL; numFences--; + updated = true; + } + } + + if (updated) { + updateStatsLocked(idx); + } + } +} + +void FrameTracker::updateStatsLocked(size_t newFrameIdx) const { + int* numFrames = const_cast<int*>(mNumFrames); + + if (mDisplayPeriod > 0 && isFrameValidLocked(newFrameIdx)) { + size_t prevFrameIdx = (newFrameIdx+NUM_FRAME_RECORDS-1) % + NUM_FRAME_RECORDS; + + if (isFrameValidLocked(prevFrameIdx)) { + nsecs_t newPresentTime = + mFrameRecords[newFrameIdx].actualPresentTime; + nsecs_t prevPresentTime = + mFrameRecords[prevFrameIdx].actualPresentTime; + + nsecs_t duration = newPresentTime - prevPresentTime; + int numPeriods = int((duration + mDisplayPeriod/2) / + mDisplayPeriod); + + for (int i = 0; i < NUM_FRAME_BUCKETS-1; i++) { + int nextBucket = 1 << (i+1); + if (numPeriods < nextBucket) { + numFrames[i]++; + return; + } } + + // The last duration bucket is a catch-all. + numFrames[NUM_FRAME_BUCKETS-1]++; } } } +void FrameTracker::resetFrameCountersLocked() { + for (int i = 0; i < NUM_FRAME_BUCKETS; i++) { + mNumFrames[i] = 0; + } +} + +void FrameTracker::logStatsLocked(const String8& name) const { + for (int i = 0; i < NUM_FRAME_BUCKETS; i++) { + if (mNumFrames[i] > 0) { + EventLog::logFrameDurations(name, mNumFrames, NUM_FRAME_BUCKETS); + return; + } + } +} + +bool FrameTracker::isFrameValidLocked(size_t idx) const { + return mFrameRecords[idx].actualPresentTime > 0 && + mFrameRecords[idx].actualPresentTime < INT64_MAX; +} + void FrameTracker::dump(String8& result) const { Mutex::Autolock lock(mMutex); processFencesLocked(); diff --git a/services/surfaceflinger/FrameTracker.h b/services/surfaceflinger/FrameTracker.h index 3d122c4..9233247 100644 --- a/services/surfaceflinger/FrameTracker.h +++ b/services/surfaceflinger/FrameTracker.h @@ -43,6 +43,8 @@ public: // frame time history. enum { NUM_FRAME_RECORDS = 128 }; + enum { NUM_FRAME_BUCKETS = 7 }; + FrameTracker(); // setDesiredPresentTime sets the time at which the current frame @@ -68,12 +70,21 @@ public: // at which the current frame became visible to the user. void setActualPresentFence(const sp<Fence>& fence); + // setDisplayRefreshPeriod sets the display refresh period in nanoseconds. + // This is used to compute frame presentation duration statistics relative + // to this period. + void setDisplayRefreshPeriod(nsecs_t displayPeriod); + // advanceFrame advances the frame tracker to the next frame. void advanceFrame(); // clear resets all the tracked frame data to zero. void clear(); + // logAndResetStats dumps the current statistics to the binary event log + // and then resets the accumulated statistics to their initial values. + void logAndResetStats(const String8& name); + // dump appends the current frame display time history to the result string. void dump(String8& result) const; @@ -99,6 +110,21 @@ private: // change. This allows it to be called from the dump method. void processFencesLocked() const; + // updateStatsLocked updates the running statistics that are gathered + // about the frame times. + void updateStatsLocked(size_t newFrameIdx) const; + + // resetFrameCounteresLocked sets all elements of the mNumFrames array to + // 0. + void resetFrameCountersLocked(); + + // logStatsLocked dumps the current statistics to the binary event log. + void logStatsLocked(const String8& name) const; + + // isFrameValidLocked returns true if the data for the given frame index is + // valid and has all arrived (i.e. there are no oustanding fences). + bool isFrameValidLocked(size_t idx) const; + // mFrameRecords is the circular buffer storing the tracked data for each // frame. FrameRecord mFrameRecords[NUM_FRAME_RECORDS]; @@ -115,6 +141,17 @@ private: // doesn't grow with NUM_FRAME_RECORDS. int mNumFences; + // mNumFrames keeps a count of the number of frames with a duration in a + // particular range of vsync periods. Element n of the array stores the + // number of frames with duration in the half-inclusive range + // [2^n, 2^(n+1)). The last element of the array contains the count for + // all frames with duration greater than 2^(NUM_FRAME_BUCKETS-1). + int32_t mNumFrames[NUM_FRAME_BUCKETS]; + + // mDisplayPeriod is the display refresh period of the display for which + // this FrameTracker is gathering information. + nsecs_t mDisplayPeriod; + // mMutex is used to protect access to all member variables. mutable Mutex mMutex; }; diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 4779804..61af51f 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -36,14 +36,16 @@ #include <gui/Surface.h> #include "clz.h" +#include "Colorizer.h" #include "DisplayDevice.h" -#include "GLExtensions.h" #include "Layer.h" #include "SurfaceFlinger.h" #include "SurfaceTextureLayer.h" #include "DisplayHardware/HWComposer.h" +#include "RenderEngine/RenderEngine.h" + #define DEBUG_RESIZE 0 namespace android { @@ -62,7 +64,6 @@ Layer::Layer(SurfaceFlinger* flinger, const sp<Client>& client, mName("unnamed"), mDebug(false), mFormat(PIXEL_FORMAT_NONE), - mGLExtensions(GLExtensions::getInstance()), mOpaqueLayer(true), mTransactionFlags(0), mQueuedFrames(0), @@ -73,13 +74,15 @@ Layer::Layer(SurfaceFlinger* flinger, const sp<Client>& client, mFrameLatencyNeeded(false), mFiltering(false), mNeedsFiltering(false), + mMesh(Mesh::TRIANGLE_FAN, 4, 2, 2), mSecure(false), mProtectedByApp(false), mHasSurface(false), mClientRef(client) { mCurrentCrop.makeInvalid(); - glGenTextures(1, &mTextureName); + mFlinger->getRenderEngine().genTextures(1, &mTextureName); + mTexture.init(Texture::TEXTURE_EXTERNAL, mTextureName); uint32_t layerFlags = 0; if (flags & ISurfaceComposerClient::eHidden) @@ -103,18 +106,18 @@ Layer::Layer(SurfaceFlinger* flinger, const sp<Client>& client, // drawing state & current state are identical mDrawingState = mCurrentState; + + nsecs_t displayPeriod = + flinger->getHwComposer().getRefreshPeriod(HWC_DISPLAY_PRIMARY); + mFrameTracker.setDisplayRefreshPeriod(displayPeriod); } -void Layer::onFirstRef() -{ +void Layer::onFirstRef() { // Creates a custom BufferQueue for SurfaceFlingerConsumer to use - sp<BufferQueue> bq = new SurfaceTextureLayer(mFlinger); - mSurfaceFlingerConsumer = new SurfaceFlingerConsumer(mTextureName, true, - GL_TEXTURE_EXTERNAL_OES, false, bq); - + mBufferQueue = new SurfaceTextureLayer(mFlinger); + mSurfaceFlingerConsumer = new SurfaceFlingerConsumer(mBufferQueue, mTextureName); mSurfaceFlingerConsumer->setConsumerUsageBits(getEffectiveUsage(0)); mSurfaceFlingerConsumer->setFrameAvailableListener(this); - mSurfaceFlingerConsumer->setSynchronousMode(true); mSurfaceFlingerConsumer->setName(mName); #ifdef TARGET_DISABLE_TRIPLE_BUFFERING @@ -134,6 +137,7 @@ Layer::~Layer() { c->detachLayer(this); } mFlinger->deleteTextureAsync(mTextureName); + mFrameTracker.logAndResetStats(mName); } // --------------------------------------------------------------------------- @@ -164,21 +168,13 @@ void Layer::onRemoved() { // set-up // --------------------------------------------------------------------------- -String8 Layer::getName() const { +const String8& Layer::getName() const { return mName; } status_t Layer::setBuffers( uint32_t w, uint32_t h, PixelFormat format, uint32_t flags) { - // this surfaces pixel format - PixelFormatInfo info; - status_t err = getPixelFormatInfo(format, &info); - if (err) { - ALOGE("unsupported pixelformat %d", format); - return err; - } - uint32_t const maxSurfaceDims = min( mFlinger->getMaxTextureSize(), mFlinger->getMaxViewportDims()); @@ -231,19 +227,10 @@ sp<IBinder> Layer::getHandle() { return new Handle(mFlinger, this); } -sp<BufferQueue> Layer::getBufferQueue() const { - return mSurfaceFlingerConsumer->getBufferQueue(); +sp<IGraphicBufferProducer> Layer::getBufferQueue() const { + return mBufferQueue; } -//virtual sp<IGraphicBufferProducer> getSurfaceTexture() const { -// sp<IGraphicBufferProducer> res; -// sp<const Layer> that( mOwner.promote() ); -// if (that != NULL) { -// res = that->mSurfaceFlingerConsumer->getBufferQueue(); -// } -// return res; -//} - // --------------------------------------------------------------------------- // h/w composer set-up // --------------------------------------------------------------------------- @@ -265,43 +252,48 @@ Rect Layer::getContentCrop() const { return crop; } -uint32_t Layer::getContentTransform() const { - return mCurrentTransform; +static Rect reduce(const Rect& win, const Region& exclude) { + if (CC_LIKELY(exclude.isEmpty())) { + return win; + } + if (exclude.isRect()) { + return win.reduce(exclude.getBounds()); + } + return Region(win).subtract(exclude).getBounds(); } Rect Layer::computeBounds() const { - const Layer::State& s(drawingState()); + const Layer::State& s(getDrawingState()); Rect win(s.active.w, s.active.h); if (!s.active.crop.isEmpty()) { win.intersect(s.active.crop, &win); } - return win; + // subtract the transparent region and snap to the bounds + return reduce(win, s.activeTransparentRegion); } -Rect Layer::computeCrop(const sp<const DisplayDevice>& hw) const { - /* - * The way we compute the crop (aka. texture coordinates when we have a - * Layer) produces a different output from the GL code in - * drawWithOpenGL() due to HWC being limited to integers. The difference - * can be large if getContentTransform() contains a large scale factor. - * See comments in drawWithOpenGL() for more details. - */ - +FloatRect Layer::computeCrop(const sp<const DisplayDevice>& hw) const { // the content crop is the area of the content that gets scaled to the // layer's size. - Rect crop(getContentCrop()); + FloatRect crop(getContentCrop()); // the active.crop is the area of the window that gets cropped, but not // scaled in any ways. - const State& s(drawingState()); + const State& s(getDrawingState()); // apply the projection's clipping to the window crop in // layerstack space, and convert-back to layer space. - // if there are no window scaling (or content scaling) involved, - // this operation will map to full pixels in the buffer. - // NOTE: should we revert to GL composition if a scaling is involved - // since it cannot be represented in the HWC API? - Rect activeCrop(s.transform.transform(s.active.crop)); + // if there are no window scaling involved, this operation will map to full + // pixels in the buffer. + // FIXME: the 3 lines below can produce slightly incorrect clipping when we have + // a viewport clipping and a window transform. we should use floating point to fix this. + + Rect activeCrop(s.active.w, s.active.h); + if (!s.active.crop.isEmpty()) { + activeCrop = s.active.crop; + } + + activeCrop = s.transform.transform(activeCrop); activeCrop.intersect(hw->getViewport(), &activeCrop); activeCrop = s.transform.inverse().transform(activeCrop); @@ -309,11 +301,14 @@ Rect Layer::computeCrop(const sp<const DisplayDevice>& hw) const { // window's bounds activeCrop.intersect(Rect(s.active.w, s.active.h), &activeCrop); + // subtract the transparent region and snap to the bounds + activeCrop = reduce(activeCrop, s.activeTransparentRegion); + if (!activeCrop.isEmpty()) { // Transform the window crop to match the buffer coordinate system, // which means using the inverse of the current transform set on the // SurfaceFlingerConsumer. - uint32_t invTransform = getContentTransform(); + uint32_t invTransform = mCurrentTransform; int winWidth = s.active.w; int winHeight = s.active.h; if (invTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) { @@ -325,15 +320,14 @@ Rect Layer::computeCrop(const sp<const DisplayDevice>& hw) const { const Rect winCrop = activeCrop.transform( invTransform, s.active.w, s.active.h); - // the code below essentially performs a scaled intersection - // of crop and winCrop - float xScale = float(crop.width()) / float(winWidth); - float yScale = float(crop.height()) / float(winHeight); + // below, crop is intersected with winCrop expressed in crop's coordinate space + float xScale = crop.getWidth() / float(winWidth); + float yScale = crop.getHeight() / float(winHeight); - int insetL = int(ceilf( winCrop.left * xScale)); - int insetT = int(ceilf( winCrop.top * yScale)); - int insetR = int(ceilf((winWidth - winCrop.right ) * xScale)); - int insetB = int(ceilf((winHeight - winCrop.bottom) * yScale)); + float insetL = winCrop.left * xScale; + float insetT = winCrop.top * yScale; + float insetR = (winWidth - winCrop.right ) * xScale; + float insetB = (winHeight - winCrop.bottom) * yScale; crop.left += insetL; crop.top += insetT; @@ -357,7 +351,7 @@ void Layer::setGeometry( } // this gives us only the "orientation" component of the transform - const State& s(drawingState()); + const State& s(getDrawingState()); if (!isOpaque() || s.alpha != 0xFF) { layer.setBlending(mPremultipliedAlpha ? HWC_BLENDING_PREMULT : @@ -382,7 +376,21 @@ void Layer::setGeometry( */ const Transform bufferOrientation(mCurrentTransform); - const Transform transform(tr * s.transform * bufferOrientation); + Transform transform(tr * s.transform * bufferOrientation); + + if (mSurfaceFlingerConsumer->getTransformToDisplayInverse()) { + /* + * the code below applies the display's inverse transform to the buffer + */ + uint32_t invTransform = hw->getOrientationTransform(); + // calculate the inverse transform + if (invTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) { + invTransform ^= NATIVE_WINDOW_TRANSFORM_FLIP_V | + NATIVE_WINDOW_TRANSFORM_FLIP_H; + } + // and apply to the current transform + transform = transform * Transform(invTransform); + } // this gives us only the "orientation" component of the transform const uint32_t orientation = transform.getOrientation(); @@ -484,6 +492,8 @@ void Layer::onDraw(const sp<const DisplayDevice>& hw, const Region& clip) const bool blackOutLayer = isProtected() || (isSecure() && !hw->isSecure()); + RenderEngine& engine(mFlinger->getRenderEngine()); + if (!blackOutLayer) { // TODO: we could be more subtle with isFixedSize() const bool useFiltering = getFiltering() || needsFiltering(hw) || isFixedSize(); @@ -493,50 +503,55 @@ void Layer::onDraw(const sp<const DisplayDevice>& hw, const Region& clip) const mSurfaceFlingerConsumer->setFilteringEnabled(useFiltering); mSurfaceFlingerConsumer->getTransformMatrix(textureMatrix); - // Set things up for texturing. - glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTextureName); - GLenum filter = GL_NEAREST; - if (useFiltering) { - filter = GL_LINEAR; + if (mSurfaceFlingerConsumer->getTransformToDisplayInverse()) { + + /* + * the code below applies the display's inverse transform to the texture transform + */ + + // create a 4x4 transform matrix from the display transform flags + const mat4 flipH(-1,0,0,0, 0,1,0,0, 0,0,1,0, 1,0,0,1); + const mat4 flipV( 1,0,0,0, 0,-1,0,0, 0,0,1,0, 0,1,0,1); + const mat4 rot90( 0,1,0,0, -1,0,0,0, 0,0,1,0, 1,0,0,1); + + mat4 tr; + uint32_t transform = hw->getOrientationTransform(); + if (transform & NATIVE_WINDOW_TRANSFORM_ROT_90) + tr = tr * rot90; + if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_H) + tr = tr * flipH; + if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_V) + tr = tr * flipV; + + // calculate the inverse + tr = inverse(tr); + + // and finally apply it to the original texture matrix + const mat4 texTransform(mat4(static_cast<const float*>(textureMatrix)) * tr); + memcpy(textureMatrix, texTransform.asArray(), sizeof(textureMatrix)); } - glTexParameterx(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, filter); - glTexParameterx(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, filter); - glMatrixMode(GL_TEXTURE); - glLoadMatrixf(textureMatrix); - glMatrixMode(GL_MODELVIEW); - glDisable(GL_TEXTURE_2D); - glEnable(GL_TEXTURE_EXTERNAL_OES); + + // Set things up for texturing. + mTexture.setDimensions(mActiveBuffer->getWidth(), mActiveBuffer->getHeight()); + mTexture.setFiltering(useFiltering); + mTexture.setMatrix(textureMatrix); + + engine.setupLayerTexturing(mTexture); } else { - glBindTexture(GL_TEXTURE_2D, mFlinger->getProtectedTexName()); - glMatrixMode(GL_TEXTURE); - glLoadIdentity(); - glMatrixMode(GL_MODELVIEW); - glDisable(GL_TEXTURE_EXTERNAL_OES); - glEnable(GL_TEXTURE_2D); + engine.setupLayerBlackedOut(); } - drawWithOpenGL(hw, clip); - - glDisable(GL_TEXTURE_EXTERNAL_OES); - glDisable(GL_TEXTURE_2D); + engine.disableTexturing(); } void Layer::clearWithOpenGL(const sp<const DisplayDevice>& hw, const Region& clip, - GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) const + float red, float green, float blue, float alpha) const { - const uint32_t fbHeight = hw->getHeight(); - glColor4f(red,green,blue,alpha); - - glDisable(GL_TEXTURE_EXTERNAL_OES); - glDisable(GL_TEXTURE_2D); - glDisable(GL_BLEND); - - LayerMesh mesh; - computeGeometry(hw, &mesh); - - glVertexPointer(2, GL_FLOAT, 0, mesh.getVertices()); - glDrawArrays(GL_TRIANGLE_FAN, 0, mesh.getVertexCount()); + RenderEngine& engine(mFlinger->getRenderEngine()); + computeGeometry(hw, mMesh); + engine.setupFillWithColor(red, green, blue, alpha); + engine.drawMesh(mMesh); } void Layer::clearWithOpenGL( @@ -547,41 +562,9 @@ void Layer::clearWithOpenGL( void Layer::drawWithOpenGL( const sp<const DisplayDevice>& hw, const Region& clip) const { const uint32_t fbHeight = hw->getHeight(); - const State& s(drawingState()); - - GLenum src = mPremultipliedAlpha ? GL_ONE : GL_SRC_ALPHA; - if (CC_UNLIKELY(s.alpha < 0xFF)) { - const GLfloat alpha = s.alpha * (1.0f/255.0f); - if (mPremultipliedAlpha) { - glColor4f(alpha, alpha, alpha, alpha); - } else { - glColor4f(1, 1, 1, alpha); - } - glEnable(GL_BLEND); - glBlendFunc(src, GL_ONE_MINUS_SRC_ALPHA); - glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - } else { - glColor4f(1, 1, 1, 1); - glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); - if (!isOpaque()) { - glEnable(GL_BLEND); - glBlendFunc(src, GL_ONE_MINUS_SRC_ALPHA); - } else { - glDisable(GL_BLEND); - } - } - - LayerMesh mesh; - computeGeometry(hw, &mesh); - - // TODO: we probably want to generate the texture coords with the mesh - // here we assume that we only have 4 vertices - - struct TexCoords { - GLfloat u; - GLfloat v; - }; + const State& s(getDrawingState()); + computeGeometry(hw, mMesh); /* * NOTE: the way we compute the texture coordinates here produces @@ -592,38 +575,30 @@ void Layer::drawWithOpenGL( * * The GL code below is more logical (imho), and the difference with * HWC is due to a limitation of the HWC API to integers -- a question - * is suspend is wether we should ignore this problem or revert to + * is suspend is whether we should ignore this problem or revert to * GL composition when a buffer scaling is applied (maybe with some * minimal value)? Or, we could make GL behave like HWC -- but this feel * like more of a hack. */ const Rect win(computeBounds()); - GLfloat left = GLfloat(win.left) / GLfloat(s.active.w); - GLfloat top = GLfloat(win.top) / GLfloat(s.active.h); - GLfloat right = GLfloat(win.right) / GLfloat(s.active.w); - GLfloat bottom = GLfloat(win.bottom) / GLfloat(s.active.h); - - TexCoords texCoords[4]; - texCoords[0].u = left; - texCoords[0].v = top; - texCoords[1].u = left; - texCoords[1].v = bottom; - texCoords[2].u = right; - texCoords[2].v = bottom; - texCoords[3].u = right; - texCoords[3].v = top; - for (int i = 0; i < 4; i++) { - texCoords[i].v = 1.0f - texCoords[i].v; - } - - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glTexCoordPointer(2, GL_FLOAT, 0, texCoords); - glVertexPointer(2, GL_FLOAT, 0, mesh.getVertices()); - glDrawArrays(GL_TRIANGLE_FAN, 0, mesh.getVertexCount()); + float left = float(win.left) / float(s.active.w); + float top = float(win.top) / float(s.active.h); + float right = float(win.right) / float(s.active.w); + float bottom = float(win.bottom) / float(s.active.h); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - glDisable(GL_BLEND); + // TODO: we probably want to generate the texture coords with the mesh + // here we assume that we only have 4 vertices + Mesh::VertexArray<vec2> texCoords(mMesh.getTexCoordArray<vec2>()); + texCoords[0] = vec2(left, 1.0f - top); + texCoords[1] = vec2(left, 1.0f - bottom); + texCoords[2] = vec2(right, 1.0f - bottom); + texCoords[3] = vec2(right, 1.0f - top); + + RenderEngine& engine(mFlinger->getRenderEngine()); + engine.setupLayerBlending(mPremultipliedAlpha, isOpaque(), s.alpha); + engine.drawMesh(mMesh); + engine.disableBlending(); } void Layer::setFiltering(bool filtering) { @@ -641,38 +616,43 @@ bool Layer::getFiltering() const { // hardware.h, instead of using hard-coded values here. #define HARDWARE_IS_DEVICE_FORMAT(f) ((f) >= 0x100 && (f) <= 0x1FF) -bool Layer::getOpacityForFormat(uint32_t format) -{ +bool Layer::getOpacityForFormat(uint32_t format) { if (HARDWARE_IS_DEVICE_FORMAT(format)) { return true; } - PixelFormatInfo info; - status_t err = getPixelFormatInfo(PixelFormat(format), &info); - // in case of error (unknown format), we assume no blending - return (err || info.h_alpha <= info.l_alpha); + switch (format) { + case HAL_PIXEL_FORMAT_RGBA_8888: + case HAL_PIXEL_FORMAT_BGRA_8888: + case HAL_PIXEL_FORMAT_sRGB_A_8888: + return false; + } + // in all other case, we have no blending (also for unknown formats) + return true; } // ---------------------------------------------------------------------------- // local state // ---------------------------------------------------------------------------- -void Layer::computeGeometry(const sp<const DisplayDevice>& hw, LayerMesh* mesh) const +void Layer::computeGeometry(const sp<const DisplayDevice>& hw, Mesh& mesh) const { - const Layer::State& s(drawingState()); + const Layer::State& s(getDrawingState()); const Transform tr(hw->getTransform() * s.transform); const uint32_t hw_h = hw->getHeight(); Rect win(s.active.w, s.active.h); if (!s.active.crop.isEmpty()) { win.intersect(s.active.crop, &win); } - if (mesh) { - tr.transform(mesh->mVertices[0], win.left, win.top); - tr.transform(mesh->mVertices[1], win.left, win.bottom); - tr.transform(mesh->mVertices[2], win.right, win.bottom); - tr.transform(mesh->mVertices[3], win.right, win.top); - for (size_t i=0 ; i<4 ; i++) { - mesh->mVertices[i][1] = hw_h - mesh->mVertices[i][1]; - } + // subtract the transparent region and snap to the bounds + win = reduce(win, s.activeTransparentRegion); + + Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>()); + position[0] = tr.transform(win.left, win.top); + position[1] = tr.transform(win.left, win.bottom); + position[2] = tr.transform(win.right, win.bottom); + position[3] = tr.transform(win.right, win.top); + for (size_t i=0 ; i<4 ; i++) { + position[i].y = hw_h - position[i].y; } } @@ -731,11 +711,11 @@ void Layer::setVisibleNonTransparentRegion(const Region& uint32_t Layer::doTransaction(uint32_t flags) { ATRACE_CALL(); - const Layer::State& front(drawingState()); - const Layer::State& temp(currentState()); + const Layer::State& s(getDrawingState()); + const Layer::State& c(getCurrentState()); - const bool sizeChanged = (temp.requested.w != front.requested.w) || - (temp.requested.h != front.requested.h); + const bool sizeChanged = (c.requested.w != s.requested.w) || + (c.requested.h != s.requested.h); if (sizeChanged) { // the size changed, we need to ask our client to request a new buffer @@ -745,46 +725,46 @@ uint32_t Layer::doTransaction(uint32_t flags) { " requested={ wh={%4u,%4u} crop={%4d,%4d,%4d,%4d} (%4d,%4d) }}\n" " drawing={ active ={ wh={%4u,%4u} crop={%4d,%4d,%4d,%4d} (%4d,%4d) }\n" " requested={ wh={%4u,%4u} crop={%4d,%4d,%4d,%4d} (%4d,%4d) }}\n", - this, (const char*) getName(), mCurrentTransform, mCurrentScalingMode, - temp.active.w, temp.active.h, - temp.active.crop.left, - temp.active.crop.top, - temp.active.crop.right, - temp.active.crop.bottom, - temp.active.crop.getWidth(), - temp.active.crop.getHeight(), - temp.requested.w, temp.requested.h, - temp.requested.crop.left, - temp.requested.crop.top, - temp.requested.crop.right, - temp.requested.crop.bottom, - temp.requested.crop.getWidth(), - temp.requested.crop.getHeight(), - front.active.w, front.active.h, - front.active.crop.left, - front.active.crop.top, - front.active.crop.right, - front.active.crop.bottom, - front.active.crop.getWidth(), - front.active.crop.getHeight(), - front.requested.w, front.requested.h, - front.requested.crop.left, - front.requested.crop.top, - front.requested.crop.right, - front.requested.crop.bottom, - front.requested.crop.getWidth(), - front.requested.crop.getHeight()); + this, getName().string(), mCurrentTransform, mCurrentScalingMode, + c.active.w, c.active.h, + c.active.crop.left, + c.active.crop.top, + c.active.crop.right, + c.active.crop.bottom, + c.active.crop.getWidth(), + c.active.crop.getHeight(), + c.requested.w, c.requested.h, + c.requested.crop.left, + c.requested.crop.top, + c.requested.crop.right, + c.requested.crop.bottom, + c.requested.crop.getWidth(), + c.requested.crop.getHeight(), + s.active.w, s.active.h, + s.active.crop.left, + s.active.crop.top, + s.active.crop.right, + s.active.crop.bottom, + s.active.crop.getWidth(), + s.active.crop.getHeight(), + s.requested.w, s.requested.h, + s.requested.crop.left, + s.requested.crop.top, + s.requested.crop.right, + s.requested.crop.bottom, + s.requested.crop.getWidth(), + s.requested.crop.getHeight()); // record the new size, form this point on, when the client request // a buffer, it'll get the new size. mSurfaceFlingerConsumer->setDefaultBufferSize( - temp.requested.w, temp.requested.h); + c.requested.w, c.requested.h); } if (!isFixedSize()) { - const bool resizePending = (temp.requested.w != temp.active.w) || - (temp.requested.h != temp.active.h); + const bool resizePending = (c.requested.w != c.active.w) || + (c.requested.h != c.active.h); if (resizePending) { // don't let Layer::doTransaction update the drawing state @@ -804,23 +784,23 @@ uint32_t Layer::doTransaction(uint32_t flags) { // this is used by Layer, which special cases resizes. if (flags & eDontUpdateGeometryState) { } else { - Layer::State& editTemp(currentState()); - editTemp.active = temp.requested; + Layer::State& editCurrentState(getCurrentState()); + editCurrentState.active = c.requested; } - if (front.active != temp.active) { + if (s.active != c.active) { // invalidate and recompute the visible regions if needed flags |= Layer::eVisibleRegion; } - if (temp.sequence != front.sequence) { + if (c.sequence != s.sequence) { // invalidate and recompute the visible regions if needed flags |= eVisibleRegion; this->contentDirty = true; // we may use linear filtering, if the matrix scales us - const uint8_t type = temp.transform.getType(); - mNeedsFiltering = (!temp.transform.preserveRects() || + const uint8_t type = c.transform.getType(); + mNeedsFiltering = (!c.transform.preserveRects() || (type >= Transform::SCALE)); } @@ -977,11 +957,6 @@ Region Layer::latchBuffer(bool& recomputeVisibleRegions) const bool oldOpacity = isOpaque(); sp<GraphicBuffer> oldActiveBuffer = mActiveBuffer; - // signal another event if we have more frames pending - if (android_atomic_dec(&mQueuedFrames) > 1) { - mFlinger->signalLayerUpdate(); - } - struct Reject : public SurfaceFlingerConsumer::BufferRejecter { Layer::State& front; Layer::State& current; @@ -993,7 +968,7 @@ Region Layer::latchBuffer(bool& recomputeVisibleRegions) } virtual bool reject(const sp<GraphicBuffer>& buf, - const BufferQueue::BufferItem& item) { + const IGraphicBufferConsumer::BufferItem& item) { if (buf == NULL) { return false; } @@ -1056,6 +1031,8 @@ Region Layer::latchBuffer(bool& recomputeVisibleRegions) if (front.active.w != bufWidth || front.active.h != bufHeight) { // reject this buffer + //ALOGD("rejecting buffer: bufWidth=%d, bufHeight=%d, front.active.{w=%d, h=%d}", + // bufWidth, bufHeight, front.active.w, front.active.h); return true; } } @@ -1085,9 +1062,23 @@ Region Layer::latchBuffer(bool& recomputeVisibleRegions) }; - Reject r(mDrawingState, currentState(), recomputeVisibleRegions); + Reject r(mDrawingState, getCurrentState(), recomputeVisibleRegions); - if (mSurfaceFlingerConsumer->updateTexImage(&r) != NO_ERROR) { + status_t updateResult = mSurfaceFlingerConsumer->updateTexImage(&r); + if (updateResult == BufferQueue::PRESENT_LATER) { + // Producer doesn't want buffer to be displayed yet. Signal a + // layer update so we check again at the next opportunity. + mFlinger->signalLayerUpdate(); + return outDirtyRegion; + } + + // Decrement the queued-frames count. Signal another event if we + // have more frames pending. + if (android_atomic_dec(&mQueuedFrames) > 1) { + mFlinger->signalLayerUpdate(); + } + + if (updateResult != NO_ERROR) { // something happened! recomputeVisibleRegions = true; return outDirtyRegion; @@ -1135,15 +1126,12 @@ Region Layer::latchBuffer(bool& recomputeVisibleRegions) recomputeVisibleRegions = true; } - glTexParameterx(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameterx(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - // FIXME: postedRegion should be dirty & bounds - const Layer::State& front(drawingState()); - Region dirtyRegion(Rect(front.active.w, front.active.h)); + const Layer::State& s(getDrawingState()); + Region dirtyRegion(Rect(s.active.w, s.active.h)); // transform the dirty region to window-manager space - outDirtyRegion = (front.transform.transform(dirtyRegion)); + outDirtyRegion = (s.transform.transform(dirtyRegion)); } return outDirtyRegion; } @@ -1178,21 +1166,21 @@ void Layer::updateTransformHint(const sp<const DisplayDevice>& hw) const { // debugging // ---------------------------------------------------------------------------- -void Layer::dump(String8& result, char* buffer, size_t SIZE) const +void Layer::dump(String8& result, Colorizer& colorizer) const { - const Layer::State& s(drawingState()); + const Layer::State& s(getDrawingState()); - snprintf(buffer, SIZE, + colorizer.colorize(result, Colorizer::GREEN); + result.appendFormat( "+ %s %p (%s)\n", getTypeId(), this, getName().string()); - result.append(buffer); + colorizer.reset(result); s.activeTransparentRegion.dump(result, "transparentRegion"); visibleRegion.dump(result, "visibleRegion"); sp<Client> client(mClientRef.promote()); - snprintf(buffer, SIZE, - " " + result.appendFormat( " " "layerStack=%4d, z=%9d, pos=(%g,%g), size=(%4d,%4d), crop=(%4d,%4d,%4d,%4d), " "isOpaque=%1d, invalidate=%1d, " "alpha=0x%02x, flags=0x%08x, tr=[%.2f, %.2f][%.2f, %.2f]\n" @@ -1205,7 +1193,6 @@ void Layer::dump(String8& result, char* buffer, size_t SIZE) const s.transform[0][0], s.transform[0][1], s.transform[1][0], s.transform[1][1], client.get()); - result.append(buffer); sp<const GraphicBuffer> buf0(mActiveBuffer); uint32_t w0=0, h0=0, s0=0, f0=0; @@ -1215,26 +1202,19 @@ void Layer::dump(String8& result, char* buffer, size_t SIZE) const s0 = buf0->getStride(); f0 = buf0->format; } - snprintf(buffer, SIZE, + result.appendFormat( " " "format=%2d, activeBuffer=[%4ux%4u:%4u,%3X]," " queued-frames=%d, mRefreshPending=%d\n", mFormat, w0, h0, s0,f0, mQueuedFrames, mRefreshPending); - result.append(buffer); - if (mSurfaceFlingerConsumer != 0) { - mSurfaceFlingerConsumer->dump(result, " ", buffer, SIZE); + mSurfaceFlingerConsumer->dump(result, " "); } } - -void Layer::shortDump(String8& result, char* scratch, size_t size) const { - Layer::dump(result, scratch, size); -} - -void Layer::dumpStats(String8& result, char* buffer, size_t SIZE) const { +void Layer::dumpStats(String8& result) const { mFrameTracker.dump(result); } @@ -1242,6 +1222,10 @@ void Layer::clearStats() { mFrameTracker.clear(); } +void Layer::logFrameStats() { + mFrameTracker.logAndResetStats(mName); +} + // --------------------------------------------------------------------------- Layer::LayerCleaner::LayerCleaner(const sp<SurfaceFlinger>& flinger, @@ -1255,6 +1239,12 @@ Layer::LayerCleaner::~LayerCleaner() { } // --------------------------------------------------------------------------- +}; // namespace android +#if defined(__gl_h_) +#error "don't include gl/gl.h in this file" +#endif -}; // namespace android +#if defined(__gl2_h_) +#error "don't include gl2/gl2.h in this file" +#endif diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 2765db1..ef4a7e9 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -22,8 +22,6 @@ #include <EGL/egl.h> #include <EGL/eglext.h> -#include <GLES/gl.h> -#include <GLES/glext.h> #include <utils/RefBase.h> #include <utils/String8.h> @@ -45,16 +43,19 @@ #include "Transform.h" #include "DisplayHardware/HWComposer.h" +#include "DisplayHardware/FloatRect.h" +#include "RenderEngine/Mesh.h" +#include "RenderEngine/Texture.h" namespace android { // --------------------------------------------------------------------------- class Client; +class Colorizer; class DisplayDevice; class GraphicBuffer; class SurfaceFlinger; -class GLExtensions; // --------------------------------------------------------------------------- @@ -110,26 +111,11 @@ public: Region requestedTransparentRegion; }; - class LayerMesh { - friend class Layer; - GLfloat mVertices[4][2]; - size_t mNumVertices; - public: - LayerMesh() : - mNumVertices(4) { - } - GLfloat const* getVertices() const { - return &mVertices[0][0]; - } - size_t getVertexCount() const { - return mNumVertices; - } - }; - // ----------------------------------------------------------------------- Layer(SurfaceFlinger* flinger, const sp<Client>& client, const String8& name, uint32_t w, uint32_t h, uint32_t flags); + virtual ~Layer(); // the this layer's size and format @@ -146,89 +132,112 @@ public: bool setCrop(const Rect& crop); bool setLayerStack(uint32_t layerStack); - void commitTransaction(); - uint32_t getTransactionFlags(uint32_t flags); uint32_t setTransactionFlags(uint32_t flags); - void computeGeometry(const sp<const DisplayDevice>& hw, LayerMesh* mesh) const; + void computeGeometry(const sp<const DisplayDevice>& hw, Mesh& mesh) const; Rect computeBounds() const; sp<IBinder> getHandle(); - sp<BufferQueue> getBufferQueue() const; - String8 getName() const; + sp<IGraphicBufferProducer> getBufferQueue() const; + const String8& getName() const; // ----------------------------------------------------------------------- + // Virtuals virtual const char* getTypeId() const { return "Layer"; } - virtual void setGeometry(const sp<const DisplayDevice>& hw, + /* + * isOpaque - true if this surface is opaque + */ + virtual bool isOpaque() const; + + /* + * isSecure - true if this surface is secure, that is if it prevents + * screenshots or VNC servers. + */ + virtual bool isSecure() const { return mSecure; } + + /* + * isProtected - true if the layer may contain protected content in the + * GRALLOC_USAGE_PROTECTED sense. + */ + virtual bool isProtected() const; + + /* + * isVisible - true if this layer is visible, false otherwise + */ + virtual bool isVisible() const; + + /* + * isFixedSize - true if content has a fixed size + */ + virtual bool isFixedSize() const; + +protected: + /* + * onDraw - draws the surface. + */ + virtual void onDraw(const sp<const DisplayDevice>& hw, const Region& clip) const; + +public: + // ----------------------------------------------------------------------- + + void setGeometry(const sp<const DisplayDevice>& hw, HWComposer::HWCLayerInterface& layer); - virtual void setPerFrameData(const sp<const DisplayDevice>& hw, + void setPerFrameData(const sp<const DisplayDevice>& hw, HWComposer::HWCLayerInterface& layer); - virtual void setAcquireFence(const sp<const DisplayDevice>& hw, + void setAcquireFence(const sp<const DisplayDevice>& hw, HWComposer::HWCLayerInterface& layer); /* * called after page-flip */ - virtual void onLayerDisplayed(const sp<const DisplayDevice>& hw, + void onLayerDisplayed(const sp<const DisplayDevice>& hw, HWComposer::HWCLayerInterface* layer); /* * called before composition. * returns true if the layer has pending updates. */ - virtual bool onPreComposition(); + bool onPreComposition(); /* * called after composition. */ - virtual void onPostComposition(); + void onPostComposition(); /* * draw - performs some global clipping optimizations * and calls onDraw(). - * Typically this method is not overridden, instead implement onDraw() - * to perform the actual drawing. */ - virtual void draw(const sp<const DisplayDevice>& hw, const Region& clip) const; - virtual void draw(const sp<const DisplayDevice>& hw); - - /* - * onDraw - draws the surface. - */ - virtual void onDraw(const sp<const DisplayDevice>& hw, const Region& clip) const; - - /* - * needsLinearFiltering - true if this surface's state requires filtering - */ - virtual bool needsFiltering(const sp<const DisplayDevice>& hw) const; + void draw(const sp<const DisplayDevice>& hw, const Region& clip) const; + void draw(const sp<const DisplayDevice>& hw); /* * doTransaction - process the transaction. This is a good place to figure * out which attributes of the surface have changed. */ - virtual uint32_t doTransaction(uint32_t transactionFlags); + uint32_t doTransaction(uint32_t transactionFlags); /* * setVisibleRegion - called to set the new visible region. This gives * a chance to update the new visible region or record the fact it changed. */ - virtual void setVisibleRegion(const Region& visibleRegion); + void setVisibleRegion(const Region& visibleRegion); /* * setCoveredRegion - called when the covered region changes. The covered * region corresponds to any area of the surface that is covered * (transparently or not) by another surface. */ - virtual void setCoveredRegion(const Region& coveredRegion); + void setCoveredRegion(const Region& coveredRegion); /* * setVisibleNonTransparentRegion - called when the visible and * non-transparent region changes. */ - virtual void setVisibleNonTransparentRegion(const Region& + void setVisibleNonTransparentRegion(const Region& visibleNonTransparentRegion); /* @@ -237,57 +246,24 @@ public: * operation, so this should be set only if needed). Typically this is used * to figure out if the content or size of a surface has changed. */ - virtual Region latchBuffer(bool& recomputeVisibleRegions); - - /* - * isOpaque - true if this surface is opaque - */ - virtual bool isOpaque() const; - - /* - * isSecure - true if this surface is secure, that is if it prevents - * screenshots or VNC servers. - */ - virtual bool isSecure() const { return mSecure; } - - /* - * isProtected - true if the layer may contain protected content in the - * GRALLOC_USAGE_PROTECTED sense. - */ - virtual bool isProtected() const; - - /* - * isVisible - true if this layer is visible, false otherwise - */ - virtual bool isVisible() const; - - /* - * isFixedSize - true if content has a fixed size - */ - virtual bool isFixedSize() const; + Region latchBuffer(bool& recomputeVisibleRegions); /* * called with the state lock when the surface is removed from the * current list */ - virtual void onRemoved(); + void onRemoved(); // Updates the transform hint in our SurfaceFlingerConsumer to match // the current orientation of the display device. - virtual void updateTransformHint(const sp<const DisplayDevice>& hw) const; + void updateTransformHint(const sp<const DisplayDevice>& hw) const; /* * returns the rectangle that crops the content of the layer and scales it * to the layer's size. */ - virtual Rect getContentCrop() const; - - /* - * returns the transform bits (90 rotation / h-flip / v-flip) of the - * layer's content - */ - virtual uint32_t getContentTransform() const; + Rect getContentCrop() const; // ----------------------------------------------------------------------- @@ -298,16 +274,16 @@ public: // only for debugging inline const sp<GraphicBuffer>& getActiveBuffer() const { return mActiveBuffer; } - inline const State& drawingState() const { return mDrawingState; } - inline const State& currentState() const { return mCurrentState; } - inline State& currentState() { return mCurrentState; } + inline const State& getDrawingState() const { return mDrawingState; } + inline const State& getCurrentState() const { return mCurrentState; } + inline State& getCurrentState() { return mCurrentState; } /* always call base class first */ - virtual void dump(String8& result, char* scratch, size_t size) const; - virtual void shortDump(String8& result, char* scratch, size_t size) const; - virtual void dumpStats(String8& result, char* buffer, size_t SIZE) const; - virtual void clearStats(); + void dump(String8& result, Colorizer& colorizer) const; + void dumpStats(String8& result) const; + void clearStats(); + void logFrameStats(); protected: // constant @@ -333,15 +309,19 @@ private: // Interface implementation for SurfaceFlingerConsumer::FrameAvailableListener virtual void onFrameAvailable(); + void commitTransaction(); + + // needsLinearFiltering - true if this surface's state requires filtering + bool needsFiltering(const sp<const DisplayDevice>& hw) const; uint32_t getEffectiveUsage(uint32_t usage) const; - Rect computeCrop(const sp<const DisplayDevice>& hw) const; + FloatRect computeCrop(const sp<const DisplayDevice>& hw) const; bool isCropped() const; static bool getOpacityForFormat(uint32_t format); // drawing void clearWithOpenGL(const sp<const DisplayDevice>& hw, const Region& clip, - GLclampf r, GLclampf g, GLclampf b, GLclampf alpha) const; + float r, float g, float b, float alpha) const; void drawWithOpenGL(const sp<const DisplayDevice>& hw, const Region& clip) const; @@ -349,12 +329,12 @@ private: // constants sp<SurfaceFlingerConsumer> mSurfaceFlingerConsumer; - GLuint mTextureName; + sp<BufferQueue> mBufferQueue; + uint32_t mTextureName; bool mPremultipliedAlpha; String8 mName; mutable bool mDebug; PixelFormat mFormat; - const GLExtensions& mGLExtensions; bool mOpaqueLayer; // these are protected by an external lock @@ -378,6 +358,10 @@ private: bool mFiltering; // Whether filtering is needed b/c of the drawingstate bool mNeedsFiltering; + // The mesh used to draw the layer in GLES composition mode + mutable Mesh mMesh; + // The mesh used to draw the layer in GLES composition mode + mutable Texture mTexture; // page-flip thread (currently main thread) bool mSecure; // no screenshots diff --git a/services/surfaceflinger/LayerDim.cpp b/services/surfaceflinger/LayerDim.cpp index 36bafdb..4e82bab 100644 --- a/services/surfaceflinger/LayerDim.cpp +++ b/services/surfaceflinger/LayerDim.cpp @@ -18,9 +18,6 @@ #include <stdint.h> #include <sys/types.h> -#include <GLES/gl.h> -#include <GLES/glext.h> - #include <utils/Errors.h> #include <utils/Log.h> @@ -29,6 +26,7 @@ #include "LayerDim.h" #include "SurfaceFlinger.h" #include "DisplayDevice.h" +#include "RenderEngine/RenderEngine.h" namespace android { // --------------------------------------------------------------------------- @@ -43,35 +41,19 @@ LayerDim::~LayerDim() { void LayerDim::onDraw(const sp<const DisplayDevice>& hw, const Region& clip) const { - const State& s(drawingState()); + const State& s(getDrawingState()); if (s.alpha>0) { - const GLfloat alpha = s.alpha/255.0f; - const uint32_t fbHeight = hw->getHeight(); - glDisable(GL_TEXTURE_EXTERNAL_OES); - glDisable(GL_TEXTURE_2D); - - if (s.alpha == 0xFF) { - glDisable(GL_BLEND); - } else { - glEnable(GL_BLEND); - glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - } - - glColor4f(0, 0, 0, alpha); - - LayerMesh mesh; - computeGeometry(hw, &mesh); - - glVertexPointer(2, GL_FLOAT, 0, mesh.getVertices()); - glDrawArrays(GL_TRIANGLE_FAN, 0, mesh.getVertexCount()); - - glDisable(GL_BLEND); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); + Mesh mesh(Mesh::TRIANGLE_FAN, 4, 2); + computeGeometry(hw, mesh); + RenderEngine& engine(mFlinger->getRenderEngine()); + engine.setupDimLayerBlending(s.alpha); + engine.drawMesh(mesh); + engine.disableBlending(); } } bool LayerDim::isVisible() const { - const Layer::State& s(drawingState()); + const Layer::State& s(getDrawingState()); return !(s.flags & layer_state_t::eLayerHidden) && s.alpha; } diff --git a/services/surfaceflinger/LayerDim.h b/services/surfaceflinger/LayerDim.h index e19bf52..6561d7f 100644 --- a/services/surfaceflinger/LayerDim.h +++ b/services/surfaceflinger/LayerDim.h @@ -20,9 +20,6 @@ #include <stdint.h> #include <sys/types.h> -#include <EGL/egl.h> -#include <EGL/eglext.h> - #include "Layer.h" // --------------------------------------------------------------------------- @@ -36,13 +33,10 @@ public: const String8& name, uint32_t w, uint32_t h, uint32_t flags); virtual ~LayerDim(); + virtual const char* getTypeId() const { return "LayerDim"; } virtual void onDraw(const sp<const DisplayDevice>& hw, const Region& clip) const; virtual bool isOpaque() const { return false; } virtual bool isSecure() const { return false; } - virtual bool isProtectedByApp() const { return false; } - virtual bool isProtectedByDRM() const { return false; } - virtual const char* getTypeId() const { return "LayerDim"; } - virtual bool isFixedSize() const { return true; } virtual bool isVisible() const; }; diff --git a/services/surfaceflinger/RenderEngine/Description.cpp b/services/surfaceflinger/RenderEngine/Description.cpp new file mode 100644 index 0000000..1adcd1f --- /dev/null +++ b/services/surfaceflinger/RenderEngine/Description.cpp @@ -0,0 +1,92 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdint.h> +#include <string.h> + +#include <utils/TypeHelpers.h> + +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> + +#include "Description.h" + +namespace android { + +Description::Description() : + mUniformsDirty(true) { + mPlaneAlpha = 1.0f; + mPremultipliedAlpha = false; + mOpaque = true; + mTextureEnabled = false; + mColorMatrixEnabled = false; + + memset(mColor, 0, sizeof(mColor)); +} + +Description::~Description() { +} + +void Description::setPlaneAlpha(GLclampf planeAlpha) { + if (planeAlpha != mPlaneAlpha) { + mUniformsDirty = true; + mPlaneAlpha = planeAlpha; + } +} + +void Description::setPremultipliedAlpha(bool premultipliedAlpha) { + if (premultipliedAlpha != mPremultipliedAlpha) { + mPremultipliedAlpha = premultipliedAlpha; + } +} + +void Description::setOpaque(bool opaque) { + if (opaque != mOpaque) { + mOpaque = opaque; + } +} + +void Description::setTexture(const Texture& texture) { + mTexture = texture; + mTextureEnabled = true; + mUniformsDirty = true; +} + +void Description::disableTexture() { + mTextureEnabled = false; +} + +void Description::setColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) { + mColor[0] = red; + mColor[1] = green; + mColor[2] = blue; + mColor[3] = alpha; + mUniformsDirty = true; +} + +void Description::setProjectionMatrix(const mat4& mtx) { + mProjectionMatrix = mtx; + mUniformsDirty = true; +} + +void Description::setColorMatrix(const mat4& mtx) { + const mat4 identity; + mColorMatrix = mtx; + mColorMatrixEnabled = (mtx != identity); +} + + +} /* namespace android */ diff --git a/services/surfaceflinger/RenderEngine/Description.h b/services/surfaceflinger/RenderEngine/Description.h new file mode 100644 index 0000000..43b835f --- /dev/null +++ b/services/surfaceflinger/RenderEngine/Description.h @@ -0,0 +1,76 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <GLES2/gl2.h> +#include "Texture.h" + +#ifndef SF_RENDER_ENGINE_DESCRIPTION_H_ +#define SF_RENDER_ENGINE_DESCRIPTION_H_ + +namespace android { + +class Program; + +/* + * This holds the state of the rendering engine. This class is used + * to generate a corresponding GLSL program and set the appropriate + * uniform. + * + * Program and ProgramCache are friends and access the state directly + */ +class Description { + friend class Program; + friend class ProgramCache; + + // value of the plane-alpha, between 0 and 1 + GLclampf mPlaneAlpha; + // whether textures are premultiplied + bool mPremultipliedAlpha; + // whether this layer is marked as opaque + bool mOpaque; + + // Texture this layer uses + Texture mTexture; + bool mTextureEnabled; + + // color used when texturing is disabled + GLclampf mColor[4]; + // projection matrix + mat4 mProjectionMatrix; + + bool mColorMatrixEnabled; + mat4 mColorMatrix; + +public: + Description(); + ~Description(); + + void setPlaneAlpha(GLclampf planeAlpha); + void setPremultipliedAlpha(bool premultipliedAlpha); + void setOpaque(bool opaque); + void setTexture(const Texture& texture); + void disableTexture(); + void setColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha); + void setProjectionMatrix(const mat4& mtx); + void setColorMatrix(const mat4& mtx); + +private: + bool mUniformsDirty; +}; + +} /* namespace android */ + +#endif /* SF_RENDER_ENGINE_DESCRIPTION_H_ */ diff --git a/services/surfaceflinger/RenderEngine/GLES10RenderEngine.cpp b/services/surfaceflinger/RenderEngine/GLES10RenderEngine.cpp new file mode 100644 index 0000000..9a47568 --- /dev/null +++ b/services/surfaceflinger/RenderEngine/GLES10RenderEngine.cpp @@ -0,0 +1,61 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <GLES/gl.h> + +#include <cutils/compiler.h> + +#include "GLES10RenderEngine.h" + +// --------------------------------------------------------------------------- +namespace android { +// --------------------------------------------------------------------------- + +GLES10RenderEngine::~GLES10RenderEngine() { +} + +void GLES10RenderEngine::setupLayerBlending( + bool premultipliedAlpha, bool opaque, int alpha) { + // OpenGL ES 1.0 doesn't support texture combiners. + // This path doesn't properly handle opaque layers that have non-opaque + // alpha values. The alpha channel will be copied into the framebuffer or + // screenshot, so if the framebuffer or screenshot is blended on top of + // something else, whatever is below the window will incorrectly show + // through. + if (CC_UNLIKELY(alpha < 0xFF)) { + GLfloat floatAlpha = alpha * (1.0f / 255.0f); + if (premultipliedAlpha) { + glColor4f(floatAlpha, floatAlpha, floatAlpha, floatAlpha); + } else { + glColor4f(1.0f, 1.0f, 1.0f, floatAlpha); + } + glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + } else { + glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + } + + if (alpha < 0xFF || !opaque) { + glEnable(GL_BLEND); + glBlendFunc(premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA, + GL_ONE_MINUS_SRC_ALPHA); + } else { + glDisable(GL_BLEND); + } +} + +// --------------------------------------------------------------------------- +}; // namespace android +// --------------------------------------------------------------------------- diff --git a/include/utils/StopWatch.h b/services/surfaceflinger/RenderEngine/GLES10RenderEngine.h index 693dd3c..f9c7c04 100644 --- a/include/utils/StopWatch.h +++ b/services/surfaceflinger/RenderEngine/GLES10RenderEngine.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005 The Android Open Source Project + * Copyright 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,51 +14,27 @@ * limitations under the License. */ -#ifndef ANDROID_STOPWATCH_H -#define ANDROID_STOPWATCH_H + +#ifndef SF_GLES10RENDERENGINE_H_ +#define SF_GLES10RENDERENGINE_H_ #include <stdint.h> #include <sys/types.h> -#include <utils/Timers.h> +#include "GLES11RenderEngine.h" // --------------------------------------------------------------------------- - namespace android { +// --------------------------------------------------------------------------- -class StopWatch -{ -public: - StopWatch( const char *name, - int clock = SYSTEM_TIME_MONOTONIC, - uint32_t flags = 0); - ~StopWatch(); - - const char* name() const; - nsecs_t lap(); - nsecs_t elapsedTime() const; - - void reset(); - -private: - const char* mName; - int mClock; - uint32_t mFlags; - - struct lap_t { - nsecs_t soFar; - nsecs_t thisLap; - }; - - nsecs_t mStartTime; - lap_t mLaps[8]; - int mNumLaps; +class GLES10RenderEngine : public GLES11RenderEngine { + virtual ~GLES10RenderEngine(); +protected: + virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque, int alpha); }; - +// --------------------------------------------------------------------------- }; // namespace android - - // --------------------------------------------------------------------------- -#endif // ANDROID_STOPWATCH_H +#endif /* SF_GLES10RENDERENGINE_H_ */ diff --git a/services/surfaceflinger/RenderEngine/GLES11RenderEngine.cpp b/services/surfaceflinger/RenderEngine/GLES11RenderEngine.cpp new file mode 100644 index 0000000..521a5d2 --- /dev/null +++ b/services/surfaceflinger/RenderEngine/GLES11RenderEngine.cpp @@ -0,0 +1,258 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <GLES/gl.h> +#include <GLES/glext.h> + +#include <utils/String8.h> +#include <cutils/compiler.h> + +#include "GLES11RenderEngine.h" +#include "Mesh.h" +#include "Texture.h" + +// --------------------------------------------------------------------------- +namespace android { +// --------------------------------------------------------------------------- + +GLES11RenderEngine::GLES11RenderEngine() { + + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize); + glGetIntegerv(GL_MAX_VIEWPORT_DIMS, mMaxViewportDims); + + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + glPixelStorei(GL_PACK_ALIGNMENT, 4); + glEnableClientState(GL_VERTEX_ARRAY); + glShadeModel(GL_FLAT); + glDisable(GL_DITHER); + glDisable(GL_CULL_FACE); + + struct pack565 { + inline uint16_t operator() (int r, int g, int b) const { + return (r<<11)|(g<<5)|b; + } + } pack565; + + const uint16_t protTexData[] = { 0 }; + glGenTextures(1, &mProtectedTexName); + glBindTexture(GL_TEXTURE_2D, mProtectedTexName); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, + GL_RGB, GL_UNSIGNED_SHORT_5_6_5, protTexData); +} + +GLES11RenderEngine::~GLES11RenderEngine() { +} + + +size_t GLES11RenderEngine::getMaxTextureSize() const { + return mMaxTextureSize; +} + +size_t GLES11RenderEngine::getMaxViewportDims() const { + return + mMaxViewportDims[0] < mMaxViewportDims[1] ? + mMaxViewportDims[0] : mMaxViewportDims[1]; +} + +void GLES11RenderEngine::setViewportAndProjection( + size_t vpw, size_t vph, size_t w, size_t h, bool yswap) { + glViewport(0, 0, vpw, vph); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + // put the origin in the left-bottom corner + if (yswap) glOrthof(0, w, h, 0, 0, 1); + else glOrthof(0, w, 0, h, 0, 1); + glMatrixMode(GL_MODELVIEW); +} + +void GLES11RenderEngine::setupLayerBlending( + bool premultipliedAlpha, bool opaque, int alpha) { + GLenum combineRGB; + GLenum combineAlpha; + GLenum src0Alpha; + GLfloat envColor[4]; + + if (CC_UNLIKELY(alpha < 0xFF)) { + // Cv = premultiplied ? Cs*alpha : Cs + // Av = !opaque ? As*alpha : As + combineRGB = premultipliedAlpha ? GL_MODULATE : GL_REPLACE; + combineAlpha = !opaque ? GL_MODULATE : GL_REPLACE; + src0Alpha = GL_CONSTANT; + envColor[0] = alpha * (1.0f / 255.0f); + } else { + // Cv = Cs + // Av = opaque ? 1.0 : As + combineRGB = GL_REPLACE; + combineAlpha = GL_REPLACE; + src0Alpha = opaque ? GL_CONSTANT : GL_TEXTURE; + envColor[0] = 1.0f; + } + + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, combineRGB); + glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); + if (combineRGB == GL_MODULATE) { + glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_CONSTANT); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); + } + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, combineAlpha); + glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_ALPHA, src0Alpha); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); + if (combineAlpha == GL_MODULATE) { + glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA); + } + if (combineRGB == GL_MODULATE || src0Alpha == GL_CONSTANT) { + envColor[1] = envColor[0]; + envColor[2] = envColor[0]; + envColor[3] = envColor[0]; + glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, envColor); + } + + if (alpha < 0xFF || !opaque) { + glEnable(GL_BLEND); + glBlendFunc(premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA, + GL_ONE_MINUS_SRC_ALPHA); + } else { + glDisable(GL_BLEND); + } +} + +void GLES11RenderEngine::setupDimLayerBlending(int alpha) { + glDisable(GL_TEXTURE_EXTERNAL_OES); + glDisable(GL_TEXTURE_2D); + if (alpha == 0xFF) { + glDisable(GL_BLEND); + } else { + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + } + glColor4f(0, 0, 0, alpha/255.0f); +} + +void GLES11RenderEngine::setupLayerTexturing(const Texture& texture) { + GLuint target = texture.getTextureTarget(); + glBindTexture(target, texture.getTextureName()); + GLenum filter = GL_NEAREST; + if (texture.getFiltering()) { + filter = GL_LINEAR; + } + glTexParameterx(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterx(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameterx(target, GL_TEXTURE_MAG_FILTER, filter); + glTexParameterx(target, GL_TEXTURE_MIN_FILTER, filter); + glMatrixMode(GL_TEXTURE); + glLoadMatrixf(texture.getMatrix().asArray()); + glMatrixMode(GL_MODELVIEW); + glDisable(GL_TEXTURE_2D); + glEnable(GL_TEXTURE_EXTERNAL_OES); +} + +void GLES11RenderEngine::setupLayerBlackedOut() { + glBindTexture(GL_TEXTURE_2D, mProtectedTexName); + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + glDisable(GL_TEXTURE_EXTERNAL_OES); + glEnable(GL_TEXTURE_2D); +} + +void GLES11RenderEngine::disableTexturing() { + glDisable(GL_TEXTURE_EXTERNAL_OES); + glDisable(GL_TEXTURE_2D); +} + +void GLES11RenderEngine::disableBlending() { + glDisable(GL_BLEND); +} + +void GLES11RenderEngine::bindImageAsFramebuffer(EGLImageKHR image, + uint32_t* texName, uint32_t* fbName, uint32_t* status) { + GLuint tname, name; + // turn our EGLImage into a texture + glGenTextures(1, &tname); + glBindTexture(GL_TEXTURE_2D, tname); + glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)image); + + // create a Framebuffer Object to render into + glGenFramebuffersOES(1, &name); + glBindFramebufferOES(GL_FRAMEBUFFER_OES, name); + glFramebufferTexture2DOES(GL_FRAMEBUFFER_OES, + GL_COLOR_ATTACHMENT0_OES, GL_TEXTURE_2D, tname, 0); + + *status = glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES); + *texName = tname; + *fbName = name; +} + +void GLES11RenderEngine::unbindFramebuffer(uint32_t texName, uint32_t fbName) { + glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0); + glDeleteFramebuffersOES(1, &fbName); + glDeleteTextures(1, &texName); +} + +void GLES11RenderEngine::setupFillWithColor(float r, float g, float b, float a) { + glColor4f(r, g, b, a); + glDisable(GL_TEXTURE_EXTERNAL_OES); + glDisable(GL_TEXTURE_2D); + glDisable(GL_BLEND); +} + +void GLES11RenderEngine::drawMesh(const Mesh& mesh) { + if (mesh.getTexCoordsSize()) { + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer(mesh.getTexCoordsSize(), + GL_FLOAT, + mesh.getByteStride(), + mesh.getTexCoords()); + } + + glVertexPointer(mesh.getVertexSize(), + GL_FLOAT, + mesh.getByteStride(), + mesh.getPositions()); + + glDrawArrays(mesh.getPrimitive(), 0, mesh.getVertexCount()); + + if (mesh.getTexCoordsSize()) { + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + } +} + +void GLES11RenderEngine::beginGroup(const mat4& colorTransform) { + // doesn't do anything in GLES 1.1 +} + +void GLES11RenderEngine::endGroup() { + // doesn't do anything in GLES 1.1 +} + +void GLES11RenderEngine::dump(String8& result) { + RenderEngine::dump(result); +} + +// --------------------------------------------------------------------------- +}; // namespace android +// --------------------------------------------------------------------------- + +#if defined(__gl2_h_) +#error "don't include gl2/gl2.h in this file" +#endif diff --git a/services/surfaceflinger/RenderEngine/GLES11RenderEngine.h b/services/surfaceflinger/RenderEngine/GLES11RenderEngine.h new file mode 100644 index 0000000..cd53aab --- /dev/null +++ b/services/surfaceflinger/RenderEngine/GLES11RenderEngine.h @@ -0,0 +1,74 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef SF_GLES11RENDERENGINE_H_ +#define SF_GLES11RENDERENGINE_H_ + +#include <stdint.h> +#include <sys/types.h> + +#include <GLES/gl.h> + +#include "RenderEngine.h" + +// --------------------------------------------------------------------------- +namespace android { +// --------------------------------------------------------------------------- + +class String8; +class Mesh; +class Texture; + +class GLES11RenderEngine : public RenderEngine { + GLuint mProtectedTexName; + GLint mMaxViewportDims[2]; + GLint mMaxTextureSize; + + virtual void bindImageAsFramebuffer(EGLImageKHR image, + uint32_t* texName, uint32_t* fbName, uint32_t* status); + virtual void unbindFramebuffer(uint32_t texName, uint32_t fbName); + +public: + GLES11RenderEngine(); + +protected: + virtual ~GLES11RenderEngine(); + + virtual void dump(String8& result); + virtual void setViewportAndProjection(size_t vpw, size_t vph, size_t w, size_t h, bool yswap); + virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque, int alpha); + virtual void setupDimLayerBlending(int alpha); + virtual void setupLayerTexturing(const Texture& texture); + virtual void setupLayerBlackedOut(); + virtual void setupFillWithColor(float r, float g, float b, float a) ; + virtual void disableTexturing(); + virtual void disableBlending(); + + virtual void drawMesh(const Mesh& mesh); + + virtual void beginGroup(const mat4& colorTransform); + virtual void endGroup(); + + virtual size_t getMaxTextureSize() const; + virtual size_t getMaxViewportDims() const; +}; + +// --------------------------------------------------------------------------- +}; // namespace android +// --------------------------------------------------------------------------- + +#endif /* SF_GLES11RENDERENGINE_H_ */ diff --git a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp new file mode 100644 index 0000000..a2a6270 --- /dev/null +++ b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp @@ -0,0 +1,294 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> + +#include <utils/String8.h> +#include <utils/Trace.h> + +#include <cutils/compiler.h> + +#include "GLES20RenderEngine.h" +#include "Program.h" +#include "ProgramCache.h" +#include "Description.h" +#include "Mesh.h" +#include "Texture.h" + +// --------------------------------------------------------------------------- +namespace android { +// --------------------------------------------------------------------------- + +GLES20RenderEngine::GLES20RenderEngine() : + mVpWidth(0), mVpHeight(0) { + + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize); + glGetIntegerv(GL_MAX_VIEWPORT_DIMS, mMaxViewportDims); + + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + glPixelStorei(GL_PACK_ALIGNMENT, 4); + + struct pack565 { + inline uint16_t operator() (int r, int g, int b) const { + return (r<<11)|(g<<5)|b; + } + } pack565; + + const uint16_t protTexData[] = { 0 }; + glGenTextures(1, &mProtectedTexName); + glBindTexture(GL_TEXTURE_2D, mProtectedTexName); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, + GL_RGB, GL_UNSIGNED_SHORT_5_6_5, protTexData); + + //mColorBlindnessCorrection = M; +} + +GLES20RenderEngine::~GLES20RenderEngine() { +} + + +size_t GLES20RenderEngine::getMaxTextureSize() const { + return mMaxTextureSize; +} + +size_t GLES20RenderEngine::getMaxViewportDims() const { + return + mMaxViewportDims[0] < mMaxViewportDims[1] ? + mMaxViewportDims[0] : mMaxViewportDims[1]; +} + +void GLES20RenderEngine::setViewportAndProjection( + size_t vpw, size_t vph, size_t w, size_t h, bool yswap) { + mat4 m; + if (yswap) m = mat4::ortho(0, w, h, 0, 0, 1); + else m = mat4::ortho(0, w, 0, h, 0, 1); + + glViewport(0, 0, vpw, vph); + mState.setProjectionMatrix(m); + mVpWidth = vpw; + mVpHeight = vph; +} + +void GLES20RenderEngine::setupLayerBlending( + bool premultipliedAlpha, bool opaque, int alpha) { + + mState.setPremultipliedAlpha(premultipliedAlpha); + mState.setOpaque(opaque); + mState.setPlaneAlpha(alpha / 255.0f); + + if (alpha < 0xFF || !opaque) { + glEnable(GL_BLEND); + glBlendFunc(premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } else { + glDisable(GL_BLEND); + } +} + +void GLES20RenderEngine::setupDimLayerBlending(int alpha) { + mState.setPlaneAlpha(1.0f); + mState.setPremultipliedAlpha(true); + mState.setOpaque(false); + mState.setColor(0, 0, 0, alpha/255.0f); + mState.disableTexture(); + + if (alpha == 0xFF) { + glDisable(GL_BLEND); + } else { + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + } +} + +void GLES20RenderEngine::setupLayerTexturing(const Texture& texture) { + GLuint target = texture.getTextureTarget(); + glBindTexture(target, texture.getTextureName()); + GLenum filter = GL_NEAREST; + if (texture.getFiltering()) { + filter = GL_LINEAR; + } + glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, filter); + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, filter); + + mState.setTexture(texture); +} + +void GLES20RenderEngine::setupLayerBlackedOut() { + glBindTexture(GL_TEXTURE_2D, mProtectedTexName); + Texture texture(Texture::TEXTURE_2D, mProtectedTexName); + texture.setDimensions(1, 1); // FIXME: we should get that from somewhere + mState.setTexture(texture); +} + +void GLES20RenderEngine::disableTexturing() { + mState.disableTexture(); +} + +void GLES20RenderEngine::disableBlending() { + glDisable(GL_BLEND); +} + + +void GLES20RenderEngine::bindImageAsFramebuffer(EGLImageKHR image, + uint32_t* texName, uint32_t* fbName, uint32_t* status) { + GLuint tname, name; + // turn our EGLImage into a texture + glGenTextures(1, &tname); + glBindTexture(GL_TEXTURE_2D, tname); + glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)image); + + // create a Framebuffer Object to render into + glGenFramebuffers(1, &name); + glBindFramebuffer(GL_FRAMEBUFFER, name); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tname, 0); + + *status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + *texName = tname; + *fbName = name; +} + +void GLES20RenderEngine::unbindFramebuffer(uint32_t texName, uint32_t fbName) { + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glDeleteFramebuffers(1, &fbName); + glDeleteTextures(1, &texName); +} + +void GLES20RenderEngine::setupFillWithColor(float r, float g, float b, float a) { + mState.setPlaneAlpha(1.0f); + mState.setPremultipliedAlpha(true); + mState.setOpaque(false); + mState.setColor(r, g, b, a); + mState.disableTexture(); + glDisable(GL_BLEND); +} + +void GLES20RenderEngine::drawMesh(const Mesh& mesh) { + + ProgramCache::getInstance().useProgram(mState); + + if (mesh.getTexCoordsSize()) { + glEnableVertexAttribArray(Program::texCoords); + glVertexAttribPointer(Program::texCoords, + mesh.getTexCoordsSize(), + GL_FLOAT, GL_FALSE, + mesh.getByteStride(), + mesh.getTexCoords()); + } + + glVertexAttribPointer(Program::position, + mesh.getVertexSize(), + GL_FLOAT, GL_FALSE, + mesh.getByteStride(), + mesh.getPositions()); + + glDrawArrays(mesh.getPrimitive(), 0, mesh.getVertexCount()); + + if (mesh.getTexCoordsSize()) { + glDisableVertexAttribArray(Program::texCoords); + } +} + +void GLES20RenderEngine::beginGroup(const mat4& colorTransform) { + + GLuint tname, name; + // create the texture + glGenTextures(1, &tname); + glBindTexture(GL_TEXTURE_2D, tname); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, mVpWidth, mVpHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + + // create a Framebuffer Object to render into + glGenFramebuffers(1, &name); + glBindFramebuffer(GL_FRAMEBUFFER, name); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tname, 0); + + Group group; + group.texture = tname; + group.fbo = name; + group.width = mVpWidth; + group.height = mVpHeight; + group.colorTransform = colorTransform; + + mGroupStack.push(group); +} + +void GLES20RenderEngine::endGroup() { + + const Group group(mGroupStack.top()); + mGroupStack.pop(); + + // activate the previous render target + GLuint fbo = 0; + if (!mGroupStack.isEmpty()) { + fbo = mGroupStack.top().fbo; + } + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + + // set our state + Texture texture(Texture::TEXTURE_2D, group.texture); + texture.setDimensions(group.width, group.height); + glBindTexture(GL_TEXTURE_2D, group.texture); + + mState.setPlaneAlpha(1.0f); + mState.setPremultipliedAlpha(true); + mState.setOpaque(false); + mState.setTexture(texture); + mState.setColorMatrix(group.colorTransform); + glDisable(GL_BLEND); + + Mesh mesh(Mesh::TRIANGLE_FAN, 4, 2, 2); + Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>()); + Mesh::VertexArray<vec2> texCoord(mesh.getTexCoordArray<vec2>()); + position[0] = vec2(0, 0); + position[1] = vec2(group.width, 0); + position[2] = vec2(group.width, group.height); + position[3] = vec2(0, group.height); + texCoord[0] = vec2(0, 0); + texCoord[1] = vec2(1, 0); + texCoord[2] = vec2(1, 1); + texCoord[3] = vec2(0, 1); + drawMesh(mesh); + + // reset color matrix + mState.setColorMatrix(mat4()); + + // free our fbo and texture + glDeleteFramebuffers(1, &group.fbo); + glDeleteTextures(1, &group.texture); +} + +void GLES20RenderEngine::dump(String8& result) { + RenderEngine::dump(result); +} + +// --------------------------------------------------------------------------- +}; // namespace android +// --------------------------------------------------------------------------- + +#if defined(__gl_h_) +#error "don't include gl/gl.h in this file" +#endif diff --git a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.h b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.h new file mode 100644 index 0000000..8b67fcc --- /dev/null +++ b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.h @@ -0,0 +1,89 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef SF_GLES20RENDERENGINE_H_ +#define SF_GLES20RENDERENGINE_H_ + +#include <stdint.h> +#include <sys/types.h> + +#include <GLES2/gl2.h> + +#include "RenderEngine.h" +#include "ProgramCache.h" +#include "Description.h" + +// --------------------------------------------------------------------------- +namespace android { +// --------------------------------------------------------------------------- + +class String8; +class Mesh; +class Texture; + +class GLES20RenderEngine : public RenderEngine { + GLuint mProtectedTexName; + GLint mMaxViewportDims[2]; + GLint mMaxTextureSize; + GLuint mVpWidth; + GLuint mVpHeight; + + struct Group { + GLuint texture; + GLuint fbo; + GLuint width; + GLuint height; + mat4 colorTransform; + }; + + Description mState; + Vector<Group> mGroupStack; + + virtual void bindImageAsFramebuffer(EGLImageKHR image, + uint32_t* texName, uint32_t* fbName, uint32_t* status); + virtual void unbindFramebuffer(uint32_t texName, uint32_t fbName); + +public: + GLES20RenderEngine(); + +protected: + virtual ~GLES20RenderEngine(); + + virtual void dump(String8& result); + virtual void setViewportAndProjection(size_t vpw, size_t vph, size_t w, size_t h, bool yswap); + virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque, int alpha); + virtual void setupDimLayerBlending(int alpha); + virtual void setupLayerTexturing(const Texture& texture); + virtual void setupLayerBlackedOut(); + virtual void setupFillWithColor(float r, float g, float b, float a); + virtual void disableTexturing(); + virtual void disableBlending(); + + virtual void drawMesh(const Mesh& mesh); + + virtual void beginGroup(const mat4& colorTransform); + virtual void endGroup(); + + virtual size_t getMaxTextureSize() const; + virtual size_t getMaxViewportDims() const; +}; + +// --------------------------------------------------------------------------- +}; // namespace android +// --------------------------------------------------------------------------- + +#endif /* SF_GLES20RENDERENGINE_H_ */ diff --git a/services/surfaceflinger/GLExtensions.cpp b/services/surfaceflinger/RenderEngine/GLExtensions.cpp index e5fb083..76bbcc1 100644 --- a/services/surfaceflinger/GLExtensions.cpp +++ b/services/surfaceflinger/RenderEngine/GLExtensions.cpp @@ -26,10 +26,7 @@ namespace android { ANDROID_SINGLETON_STATIC_INSTANCE( GLExtensions ) GLExtensions::GLExtensions() - : mHaveTextureExternal(false), - mHaveNpot(false), - mHaveDirectTexture(false), - mHaveFramebufferObject(false) + : mHaveFramebufferObject(false) { } @@ -37,18 +34,12 @@ void GLExtensions::initWithGLStrings( GLubyte const* vendor, GLubyte const* renderer, GLubyte const* version, - GLubyte const* extensions, - char const* egl_vendor, - char const* egl_version, - char const* egl_extensions) + GLubyte const* extensions) { mVendor = (char const*)vendor; mRenderer = (char const*)renderer; mVersion = (char const*)version; mExtensions = (char const*)extensions; - mEglVendor = egl_vendor; - mEglVersion = egl_version; - mEglExtensions = egl_extensions; char const* curr = (char const*)extensions; char const* head = curr; @@ -61,39 +52,6 @@ void GLExtensions::initWithGLStrings( curr = head+1; } while (head); - curr = egl_extensions; - head = curr; - do { - head = strchr(curr, ' '); - String8 s(curr, head ? head-curr : strlen(curr)); - if (s.length()) { - mExtensionList.add(s); - } - curr = head+1; - } while (head); - -#ifdef EGL_ANDROID_image_native_buffer - if (hasExtension("GL_OES_EGL_image") && - (hasExtension("EGL_KHR_image_base") || hasExtension("EGL_KHR_image")) && - hasExtension("EGL_ANDROID_image_native_buffer")) - { - mHaveDirectTexture = true; - } -#else -#error "EGL_ANDROID_image_native_buffer not supported" -#endif - - if (hasExtension("GL_ARB_texture_non_power_of_two")) { - mHaveNpot = true; - } - - if (hasExtension("GL_OES_EGL_image_external")) { - mHaveTextureExternal = true; - } else if (strstr(mRenderer.string(), "Adreno")) { - // hack for Adreno 200 - mHaveTextureExternal = true; - } - if (hasExtension("GL_OES_framebuffer_object")) { mHaveFramebufferObject = true; } @@ -121,18 +79,5 @@ char const* GLExtensions::getExtension() const { return mExtensions.string(); } -char const* GLExtensions::getEglVendor() const { - return mEglVendor.string(); -} - -char const* GLExtensions::getEglVersion() const { - return mEglVersion.string(); -} - -char const* GLExtensions::getEglExtension() const { - return mEglExtensions.string(); -} - - // --------------------------------------------------------------------------- }; // namespace android diff --git a/services/surfaceflinger/GLExtensions.h b/services/surfaceflinger/RenderEngine/GLExtensions.h index c86c66a..d81ed2a 100644 --- a/services/surfaceflinger/GLExtensions.h +++ b/services/surfaceflinger/RenderEngine/GLExtensions.h @@ -36,18 +36,12 @@ class GLExtensions : public Singleton<GLExtensions> { friend class Singleton<GLExtensions>; - bool mHaveTextureExternal : 1; - bool mHaveNpot : 1; - bool mHaveDirectTexture : 1; bool mHaveFramebufferObject : 1; String8 mVendor; String8 mRenderer; String8 mVersion; String8 mExtensions; - String8 mEglVendor; - String8 mEglVersion; - String8 mEglExtensions; SortedVector<String8> mExtensionList; GLExtensions(const GLExtensions&); @@ -57,15 +51,6 @@ protected: GLExtensions(); public: - inline bool haveTextureExternal() const { - return mHaveTextureExternal; - } - inline bool haveNpot() const { - return mHaveNpot; - } - inline bool haveDirectTexture() const { - return mHaveDirectTexture; - } inline bool haveFramebufferObject() const { return mHaveFramebufferObject; @@ -75,20 +60,13 @@ public: GLubyte const* vendor, GLubyte const* renderer, GLubyte const* version, - GLubyte const* extensions, - char const* egl_vendor, - char const* egl_version, - char const* egl_extensions); + GLubyte const* extensions); char const* getVendor() const; char const* getRenderer() const; char const* getVersion() const; char const* getExtension() const; - char const* getEglVendor() const; - char const* getEglVersion() const; - char const* getEglExtension() const; - bool hasExtension(char const* extension) const; }; diff --git a/services/surfaceflinger/RenderEngine/Mesh.cpp b/services/surfaceflinger/RenderEngine/Mesh.cpp new file mode 100644 index 0000000..3f50cb0 --- /dev/null +++ b/services/surfaceflinger/RenderEngine/Mesh.cpp @@ -0,0 +1,73 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Mesh.h" + +namespace android { + +Mesh::Mesh(Primitive primitive, size_t vertexCount, size_t vertexSize, size_t texCoordSize) + : mVertexCount(vertexCount), mVertexSize(vertexSize), mTexCoordsSize(texCoordSize), + mPrimitive(primitive) +{ + mVertices = new float[(vertexSize + texCoordSize) * vertexCount]; + mStride = mVertexSize + mTexCoordsSize; +} + +Mesh::~Mesh() { + delete [] mVertices; +} + +Mesh::Primitive Mesh::getPrimitive() const { + return mPrimitive; +} + + +float const* Mesh::getPositions() const { + return mVertices; +} +float* Mesh::getPositions() { + return mVertices; +} + +float const* Mesh::getTexCoords() const { + return mVertices + mVertexSize; +} +float* Mesh::getTexCoords() { + return mVertices + mVertexSize; +} + + +size_t Mesh::getVertexCount() const { + return mVertexCount; +} + +size_t Mesh::getVertexSize() const { + return mVertexSize; +} + +size_t Mesh::getTexCoordsSize() const { + return mTexCoordsSize; +} + +size_t Mesh::getByteStride() const { + return mStride*sizeof(float); +} + +size_t Mesh::getStride() const { + return mStride; +} + +} /* namespace android */ diff --git a/services/surfaceflinger/RenderEngine/Mesh.h b/services/surfaceflinger/RenderEngine/Mesh.h new file mode 100644 index 0000000..b6d42b0 --- /dev/null +++ b/services/surfaceflinger/RenderEngine/Mesh.h @@ -0,0 +1,99 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SF_RENDER_ENGINE_MESH_H +#define SF_RENDER_ENGINE_MESH_H + +#include <stdint.h> + +namespace android { + +class Mesh { +public: + enum Primitive { + TRIANGLES = 0x0004, // GL_TRIANGLES + TRIANGLE_STRIP = 0x0005, // GL_TRIANGLE_STRIP + TRIANGLE_FAN = 0x0006 // GL_TRIANGLE_FAN + }; + + Mesh(Primitive primitive, size_t vertexCount, size_t vertexSize, size_t texCoordsSize = 0); + ~Mesh(); + + /* + * VertexArray handles the stride automatically. + */ + template <typename TYPE> + class VertexArray { + friend class Mesh; + float* mData; + size_t mStride; + VertexArray(float* data, size_t stride) : mData(data), mStride(stride) { } + public: + TYPE& operator[](size_t index) { + return *reinterpret_cast<TYPE*>(&mData[index*mStride]); + } + TYPE const& operator[](size_t index) const { + return *reinterpret_cast<TYPE const*>(&mData[index*mStride]); + } + }; + + template <typename TYPE> + VertexArray<TYPE> getPositionArray() { return VertexArray<TYPE>(getPositions(), mStride); } + + template <typename TYPE> + VertexArray<TYPE> getTexCoordArray() { return VertexArray<TYPE>(getTexCoords(), mStride); } + + Primitive getPrimitive() const; + + // returns a pointer to the vertices positions + float const* getPositions() const; + + // returns a pointer to the vertices texture coordinates + float const* getTexCoords() const; + + // number of vertices in this mesh + size_t getVertexCount() const; + + // dimension of vertices + size_t getVertexSize() const; + + // dimension of texture coordinates + size_t getTexCoordsSize() const; + + // return stride in bytes + size_t getByteStride() const; + + // return stride in floats + size_t getStride() const; + +private: + Mesh(const Mesh&); + Mesh& operator = (const Mesh&); + Mesh const& operator = (const Mesh&) const; + + float* getPositions(); + float* getTexCoords(); + float* mVertices; + size_t mVertexCount; + size_t mVertexSize; + size_t mTexCoordsSize; + size_t mStride; + Primitive mPrimitive; +}; + + +} /* namespace android */ +#endif /* SF_RENDER_ENGINE_MESH_H */ diff --git a/services/surfaceflinger/RenderEngine/Program.cpp b/services/surfaceflinger/RenderEngine/Program.cpp new file mode 100644 index 0000000..4a7fb58 --- /dev/null +++ b/services/surfaceflinger/RenderEngine/Program.cpp @@ -0,0 +1,148 @@ +/*Gluint + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdint.h> + +#include <log/log.h> + +#include "Program.h" +#include "ProgramCache.h" +#include "Description.h" +#include <utils/String8.h> + +namespace android { + +Program::Program(const ProgramCache::Key& needs, const char* vertex, const char* fragment) + : mInitialized(false) { + GLuint vertexId = buildShader(vertex, GL_VERTEX_SHADER); + GLuint fragmentId = buildShader(fragment, GL_FRAGMENT_SHADER); + GLuint programId = glCreateProgram(); + glAttachShader(programId, vertexId); + glAttachShader(programId, fragmentId); + glBindAttribLocation(programId, position, "position"); + glBindAttribLocation(programId, texCoords, "texCoords"); + glLinkProgram(programId); + + GLint status; + glGetProgramiv(programId, GL_LINK_STATUS, &status); + if (status != GL_TRUE) { + ALOGE("Error while linking shaders:"); + GLint infoLen = 0; + glGetProgramiv(programId, GL_INFO_LOG_LENGTH, &infoLen); + if (infoLen > 1) { + GLchar log[infoLen]; + glGetProgramInfoLog(programId, infoLen, 0, &log[0]); + ALOGE("%s", log); + } + glDetachShader(programId, vertexId); + glDetachShader(programId, fragmentId); + glDeleteShader(vertexId); + glDeleteShader(fragmentId); + glDeleteProgram(programId); + } else { + mProgram = programId; + mVertexShader = vertexId; + mFragmentShader = fragmentId; + mInitialized = true; + + mColorMatrixLoc = glGetUniformLocation(programId, "colorMatrix"); + mProjectionMatrixLoc = glGetUniformLocation(programId, "projection"); + mTextureMatrixLoc = glGetUniformLocation(programId, "texture"); + mSamplerLoc = glGetUniformLocation(programId, "sampler"); + mColorLoc = glGetUniformLocation(programId, "color"); + mAlphaPlaneLoc = glGetUniformLocation(programId, "alphaPlane"); + + // set-up the default values for our uniforms + glUseProgram(programId); + const GLfloat m[16] = {1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 }; + glUniformMatrix4fv(mProjectionMatrixLoc, 1, GL_FALSE, m); + glEnableVertexAttribArray(0); + } +} + +Program::~Program() { +} + +bool Program::isValid() const { + return mInitialized; +} + +void Program::use() { + glUseProgram(mProgram); +} + +GLuint Program::getAttrib(const char* name) const { + // TODO: maybe use a local cache + return glGetAttribLocation(mProgram, name); +} + +GLint Program::getUniform(const char* name) const { + // TODO: maybe use a local cache + return glGetUniformLocation(mProgram, name); +} + +GLuint Program::buildShader(const char* source, GLenum type) { + GLuint shader = glCreateShader(type); + glShaderSource(shader, 1, &source, 0); + glCompileShader(shader); + GLint status; + glGetShaderiv(shader, GL_COMPILE_STATUS, &status); + if (status != GL_TRUE) { + // Some drivers return wrong values for GL_INFO_LOG_LENGTH + // use a fixed size instead + GLchar log[512]; + glGetShaderInfoLog(shader, sizeof(log), 0, log); + ALOGE("Error while compiling shader: \n%s\n%s", source, log); + glDeleteShader(shader); + return 0; + } + return shader; +} + +String8& Program::dumpShader(String8& result, GLenum type) { + GLuint shader = GL_FRAGMENT_SHADER ? mFragmentShader : mVertexShader; + GLint l; + glGetShaderiv(shader, GL_SHADER_SOURCE_LENGTH, &l); + char* src = new char[l]; + glGetShaderSource(shader, l, NULL, src); + result.append(src); + delete [] src; + return result; +} + +void Program::setUniforms(const Description& desc) { + + // TODO: we should have a mechanism here to not always reset uniforms that + // didn't change for this program. + + if (mSamplerLoc >= 0) { + glUniform1i(mSamplerLoc, 0); + glUniformMatrix4fv(mTextureMatrixLoc, 1, GL_FALSE, desc.mTexture.getMatrix().asArray()); + } + if (mAlphaPlaneLoc >= 0) { + glUniform1f(mAlphaPlaneLoc, desc.mPlaneAlpha); + } + if (mColorLoc >= 0) { + glUniform4fv(mColorLoc, 1, desc.mColor); + } + if (mColorMatrixLoc >= 0) { + glUniformMatrix4fv(mColorMatrixLoc, 1, GL_FALSE, desc.mColorMatrix.asArray()); + } + // these uniforms are always present + glUniformMatrix4fv(mProjectionMatrixLoc, 1, GL_FALSE, desc.mProjectionMatrix.asArray()); +} + +} /* namespace android */ diff --git a/services/surfaceflinger/RenderEngine/Program.h b/services/surfaceflinger/RenderEngine/Program.h new file mode 100644 index 0000000..36bd120 --- /dev/null +++ b/services/surfaceflinger/RenderEngine/Program.h @@ -0,0 +1,92 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SF_RENDER_ENGINE_PROGRAM_H +#define SF_RENDER_ENGINE_PROGRAM_H + +#include <stdint.h> + +#include <GLES2/gl2.h> + +#include "Description.h" +#include "ProgramCache.h" + +namespace android { + +class String8; + +/* + * Abstracts a GLSL program comprising a vertex and fragment shader + */ +class Program { +public: + // known locations for position and texture coordinates + enum { position=0, texCoords=1 }; + + Program(const ProgramCache::Key& needs, const char* vertex, const char* fragment); + ~Program(); + + /* whether this object is usable */ + bool isValid() const; + + /* Binds this program to the GLES context */ + void use(); + + /* Returns the location of the specified attribute */ + GLuint getAttrib(const char* name) const; + + /* Returns the location of the specified uniform */ + GLint getUniform(const char* name) const; + + /* set-up uniforms from the description */ + void setUniforms(const Description& desc); + + +private: + GLuint buildShader(const char* source, GLenum type); + String8& dumpShader(String8& result, GLenum type); + + // whether the initialization succeeded + bool mInitialized; + + // Name of the OpenGL program and shaders + GLuint mProgram; + GLuint mVertexShader; + GLuint mFragmentShader; + + /* location of the projection matrix uniform */ + GLint mProjectionMatrixLoc; + + /* location of the color matrix uniform */ + GLint mColorMatrixLoc; + + /* location of the texture matrix uniform */ + GLint mTextureMatrixLoc; + + /* location of the sampler uniform */ + GLint mSamplerLoc; + + /* location of the alpha plane uniform */ + GLint mAlphaPlaneLoc; + + /* location of the color uniform */ + GLint mColorLoc; +}; + + +} /* namespace android */ + +#endif /* SF_RENDER_ENGINE_PROGRAM_H */ diff --git a/services/surfaceflinger/RenderEngine/ProgramCache.cpp b/services/surfaceflinger/RenderEngine/ProgramCache.cpp new file mode 100644 index 0000000..09b0ddc --- /dev/null +++ b/services/surfaceflinger/RenderEngine/ProgramCache.cpp @@ -0,0 +1,221 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> + +#include <utils/String8.h> + +#include "ProgramCache.h" +#include "Program.h" +#include "Description.h" + +namespace android { +// ----------------------------------------------------------------------------------------------- + + +/* + * A simple formatter class to automatically add the endl and + * manage the indentation. + */ + +class Formatter; +static Formatter& indent(Formatter& f); +static Formatter& dedent(Formatter& f); + +class Formatter { + String8 mString; + int mIndent; + typedef Formatter& (*FormaterManipFunc)(Formatter&); + friend Formatter& indent(Formatter& f); + friend Formatter& dedent(Formatter& f); +public: + Formatter() : mIndent(0) {} + + String8 getString() const { + return mString; + } + + friend Formatter& operator << (Formatter& out, const char* in) { + for (int i=0 ; i<out.mIndent ; i++) { + out.mString.append(" "); + } + out.mString.append(in); + out.mString.append("\n"); + return out; + } + friend inline Formatter& operator << (Formatter& out, const String8& in) { + return operator << (out, in.string()); + } + friend inline Formatter& operator<<(Formatter& to, FormaterManipFunc func) { + return (*func)(to); + } +}; +Formatter& indent(Formatter& f) { + f.mIndent++; + return f; +} +Formatter& dedent(Formatter& f) { + f.mIndent--; + return f; +} + +// ----------------------------------------------------------------------------------------------- + +ANDROID_SINGLETON_STATIC_INSTANCE(ProgramCache) + + +ProgramCache::ProgramCache() { +} + +ProgramCache::~ProgramCache() { +} + +ProgramCache::Key ProgramCache::computeKey(const Description& description) { + Key needs; + needs.set(Key::TEXTURE_MASK, + !description.mTextureEnabled ? Key::TEXTURE_OFF : + description.mTexture.getTextureTarget() == GL_TEXTURE_EXTERNAL_OES ? Key::TEXTURE_EXT : + description.mTexture.getTextureTarget() == GL_TEXTURE_2D ? Key::TEXTURE_2D : + Key::TEXTURE_OFF) + .set(Key::PLANE_ALPHA_MASK, + (description.mPlaneAlpha < 1) ? Key::PLANE_ALPHA_LT_ONE : Key::PLANE_ALPHA_EQ_ONE) + .set(Key::BLEND_MASK, + description.mPremultipliedAlpha ? Key::BLEND_PREMULT : Key::BLEND_NORMAL) + .set(Key::OPACITY_MASK, + description.mOpaque ? Key::OPACITY_OPAQUE : Key::OPACITY_TRANSLUCENT) + .set(Key::COLOR_MATRIX_MASK, + description.mColorMatrixEnabled ? Key::COLOR_MATRIX_ON : Key::COLOR_MATRIX_OFF); + return needs; +} + +String8 ProgramCache::generateVertexShader(const Key& needs) { + Formatter vs; + if (needs.isTexturing()) { + vs << "attribute vec4 texCoords;" + << "varying vec2 outTexCoords;"; + } + vs << "attribute vec4 position;" + << "uniform mat4 projection;" + << "uniform mat4 texture;" + << "void main(void) {" << indent + << "gl_Position = projection * position;"; + if (needs.isTexturing()) { + vs << "outTexCoords = (texture * texCoords).st;"; + } + vs << dedent << "}"; + return vs.getString(); +} + +String8 ProgramCache::generateFragmentShader(const Key& needs) { + Formatter fs; + if (needs.getTextureTarget() == Key::TEXTURE_EXT) { + fs << "#extension GL_OES_EGL_image_external : require"; + } + + // default precision is required-ish in fragment shaders + fs << "precision mediump float;"; + + if (needs.getTextureTarget() == Key::TEXTURE_EXT) { + fs << "uniform samplerExternalOES sampler;" + << "varying vec2 outTexCoords;"; + } else if (needs.getTextureTarget() == Key::TEXTURE_2D) { + fs << "uniform sampler2D sampler;" + << "varying vec2 outTexCoords;"; + } else if (needs.getTextureTarget() == Key::TEXTURE_OFF) { + fs << "uniform vec4 color;"; + } + if (needs.hasPlaneAlpha()) { + fs << "uniform float alphaPlane;"; + } + if (needs.hasColorMatrix()) { + fs << "uniform mat4 colorMatrix;"; + } + fs << "void main(void) {" << indent; + if (needs.isTexturing()) { + fs << "gl_FragColor = texture2D(sampler, outTexCoords);"; + } else { + fs << "gl_FragColor = color;"; + } + if (needs.isOpaque()) { + fs << "gl_FragColor.a = 1.0;"; + } + if (needs.hasPlaneAlpha()) { + // modulate the alpha value with planeAlpha + if (needs.isPremultiplied()) { + // ... and the color too if we're premultiplied + fs << "gl_FragColor *= alphaPlane;"; + } else { + fs << "gl_FragColor.a *= alphaPlane;"; + } + } + + if (needs.hasColorMatrix()) { + if (!needs.isOpaque() && needs.isPremultiplied()) { + // un-premultiply if needed before linearization + fs << "gl_FragColor.rgb = gl_FragColor.rgb/gl_FragColor.a;"; + } + fs << "gl_FragColor.rgb = pow(gl_FragColor.rgb, vec3(2.2));"; + fs << "gl_FragColor = colorMatrix*gl_FragColor;"; + fs << "gl_FragColor.rgb = pow(gl_FragColor.rgb, vec3(1.0 / 2.2));"; + if (!needs.isOpaque() && needs.isPremultiplied()) { + // and re-premultiply if needed after gamma correction + fs << "gl_FragColor.rgb = gl_FragColor.rgb*gl_FragColor.a;"; + } + } + + fs << dedent << "}"; + return fs.getString(); +} + +Program* ProgramCache::generateProgram(const Key& needs) { + // vertex shader + String8 vs = generateVertexShader(needs); + + // fragment shader + String8 fs = generateFragmentShader(needs); + + Program* program = new Program(needs, vs.string(), fs.string()); + return program; +} + +void ProgramCache::useProgram(const Description& description) { + + // generate the key for the shader based on the description + Key needs(computeKey(description)); + + // look-up the program in the cache + Program* program = mCache.valueFor(needs); + if (program == NULL) { + // we didn't find our program, so generate one... + nsecs_t time = -systemTime(); + program = generateProgram(needs); + mCache.add(needs, program); + time += systemTime(); + + //ALOGD(">>> generated new program: needs=%08X, time=%u ms (%d programs)", + // needs.mNeeds, uint32_t(ns2ms(time)), mCache.size()); + } + + // here we have a suitable program for this description + if (program->isValid()) { + program->use(); + program->setUniforms(description); + } +} + + +} /* namespace android */ diff --git a/services/surfaceflinger/RenderEngine/ProgramCache.h b/services/surfaceflinger/RenderEngine/ProgramCache.h new file mode 100644 index 0000000..e8b9dcd --- /dev/null +++ b/services/surfaceflinger/RenderEngine/ProgramCache.h @@ -0,0 +1,134 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SF_RENDER_ENGINE_PROGRAMCACHE_H +#define SF_RENDER_ENGINE_PROGRAMCACHE_H + +#include <GLES2/gl2.h> + +#include <utils/Singleton.h> +#include <utils/KeyedVector.h> +#include <utils/TypeHelpers.h> + +#include "Description.h" + +namespace android { + +class Description; +class Program; +class String8; + +/* + * This class generates GLSL programs suitable to handle a given + * Description. It's responsible for figuring out what to + * generate from a Description. + * It also maintains a cache of these Programs. + */ +class ProgramCache : public Singleton<ProgramCache> { +public: + /* + * Key is used to retrieve a Program in the cache. + * A Key is generated from a Description. + */ + class Key { + friend class ProgramCache; + typedef uint32_t key_t; + key_t mKey; + public: + enum { + BLEND_PREMULT = 0x00000001, + BLEND_NORMAL = 0x00000000, + BLEND_MASK = 0x00000001, + + OPACITY_OPAQUE = 0x00000002, + OPACITY_TRANSLUCENT = 0x00000000, + OPACITY_MASK = 0x00000002, + + PLANE_ALPHA_LT_ONE = 0x00000004, + PLANE_ALPHA_EQ_ONE = 0x00000000, + PLANE_ALPHA_MASK = 0x00000004, + + TEXTURE_OFF = 0x00000000, + TEXTURE_EXT = 0x00000008, + TEXTURE_2D = 0x00000010, + TEXTURE_MASK = 0x00000018, + + COLOR_MATRIX_OFF = 0x00000000, + COLOR_MATRIX_ON = 0x00000020, + COLOR_MATRIX_MASK = 0x00000020, + }; + + inline Key() : mKey(0) { } + inline Key(const Key& rhs) : mKey(rhs.mKey) { } + + inline Key& set(key_t mask, key_t value) { + mKey = (mKey & ~mask) | value; + return *this; + } + + inline bool isTexturing() const { + return (mKey & TEXTURE_MASK) != TEXTURE_OFF; + } + inline int getTextureTarget() const { + return (mKey & TEXTURE_MASK); + } + inline bool isPremultiplied() const { + return (mKey & BLEND_MASK) == BLEND_PREMULT; + } + inline bool isOpaque() const { + return (mKey & OPACITY_MASK) == OPACITY_OPAQUE; + } + inline bool hasPlaneAlpha() const { + return (mKey & PLANE_ALPHA_MASK) == PLANE_ALPHA_LT_ONE; + } + inline bool hasColorMatrix() const { + return (mKey & COLOR_MATRIX_MASK) == COLOR_MATRIX_ON; + } + + // this is the definition of a friend function -- not a method of class Needs + friend inline int strictly_order_type(const Key& lhs, const Key& rhs) { + return (lhs.mKey < rhs.mKey) ? 1 : 0; + } + }; + + ProgramCache(); + ~ProgramCache(); + + // useProgram lookup a suitable program in the cache or generates one + // if none can be found. + void useProgram(const Description& description); + +private: + // compute a cache Key from a Description + static Key computeKey(const Description& description); + // generates a program from the Key + static Program* generateProgram(const Key& needs); + // generates the vertex shader from the Key + static String8 generateVertexShader(const Key& needs); + // generates the fragment shader from the Key + static String8 generateFragmentShader(const Key& needs); + + // Key/Value map used for caching Programs. Currently the cache + // is never shrunk. + DefaultKeyedVector<Key, Program*> mCache; +}; + + +ANDROID_BASIC_TYPES_TRAITS(ProgramCache::Key) + +} /* namespace android */ + +#endif /* SF_RENDER_ENGINE_PROGRAMCACHE_H */ diff --git a/services/surfaceflinger/RenderEngine/RenderEngine.cpp b/services/surfaceflinger/RenderEngine/RenderEngine.cpp new file mode 100644 index 0000000..ba82cad --- /dev/null +++ b/services/surfaceflinger/RenderEngine/RenderEngine.cpp @@ -0,0 +1,239 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <cutils/log.h> +#include <ui/Rect.h> +#include <ui/Region.h> + +#include "RenderEngine.h" +#include "GLES10RenderEngine.h" +#include "GLES11RenderEngine.h" +#include "GLES20RenderEngine.h" +#include "GLExtensions.h" +#include "Mesh.h" + +// --------------------------------------------------------------------------- +namespace android { +// --------------------------------------------------------------------------- + +RenderEngine* RenderEngine::create(EGLDisplay display, EGLConfig config) { + EGLint renderableType = 0; + EGLint contextClientVersion = 0; + + // query the renderable type, setting the EGL_CONTEXT_CLIENT_VERSION accordingly + if (!eglGetConfigAttrib(display, config, EGL_RENDERABLE_TYPE, &renderableType)) { + LOG_ALWAYS_FATAL("can't query EGLConfig RENDERABLE_TYPE"); + } + + if (renderableType & EGL_OPENGL_ES2_BIT) { + contextClientVersion = 2; + } else if (renderableType & EGL_OPENGL_ES_BIT) { + contextClientVersion = 1; + } else { + LOG_ALWAYS_FATAL("no supported EGL_RENDERABLE_TYPEs"); + } + + // Also create our EGLContext + EGLint contextAttributes[] = { + EGL_CONTEXT_CLIENT_VERSION, contextClientVersion, // MUST be first +#ifdef EGL_IMG_context_priority +#ifdef HAS_CONTEXT_PRIORITY +#warning "using EGL_IMG_context_priority" + EGL_CONTEXT_PRIORITY_LEVEL_IMG, EGL_CONTEXT_PRIORITY_HIGH_IMG, +#endif +#endif + EGL_NONE, EGL_NONE + }; + EGLContext ctxt = eglCreateContext(display, config, NULL, contextAttributes); + + // if can't create a GL context, we can only abort. + LOG_ALWAYS_FATAL_IF(ctxt==EGL_NO_CONTEXT, "EGLContext creation failed"); + + + // now figure out what version of GL did we actually get + // NOTE: a dummy surface is not needed if KHR_create_context is supported + + EGLint attribs[] = { EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE, EGL_NONE }; + EGLSurface dummy = eglCreatePbufferSurface(display, config, attribs); + LOG_ALWAYS_FATAL_IF(dummy==EGL_NO_SURFACE, "can't create dummy pbuffer"); + EGLBoolean success = eglMakeCurrent(display, dummy, dummy, ctxt); + LOG_ALWAYS_FATAL_IF(!success, "can't make dummy pbuffer current"); + + GLExtensions& extensions(GLExtensions::getInstance()); + extensions.initWithGLStrings( + glGetString(GL_VENDOR), + glGetString(GL_RENDERER), + glGetString(GL_VERSION), + glGetString(GL_EXTENSIONS)); + + GlesVersion version = parseGlesVersion( extensions.getVersion() ); + + // initialize the renderer while GL is current + + RenderEngine* engine = NULL; + switch (version) { + case GLES_VERSION_1_0: + engine = new GLES10RenderEngine(); + break; + case GLES_VERSION_1_1: + engine = new GLES11RenderEngine(); + break; + case GLES_VERSION_2_0: + case GLES_VERSION_3_0: + engine = new GLES20RenderEngine(); + break; + } + engine->setEGLContext(ctxt); + + ALOGI("OpenGL ES informations:"); + ALOGI("vendor : %s", extensions.getVendor()); + ALOGI("renderer : %s", extensions.getRenderer()); + ALOGI("version : %s", extensions.getVersion()); + ALOGI("extensions: %s", extensions.getExtension()); + ALOGI("GL_MAX_TEXTURE_SIZE = %d", engine->getMaxTextureSize()); + ALOGI("GL_MAX_VIEWPORT_DIMS = %d", engine->getMaxViewportDims()); + + eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglDestroySurface(display, dummy); + + return engine; +} + +RenderEngine::RenderEngine() : mEGLContext(EGL_NO_CONTEXT) { +} + +RenderEngine::~RenderEngine() { +} + +void RenderEngine::setEGLContext(EGLContext ctxt) { + mEGLContext = ctxt; +} + +EGLContext RenderEngine::getEGLContext() const { + return mEGLContext; +} + +void RenderEngine::checkErrors() const { + do { + // there could be more than one error flag + GLenum error = glGetError(); + if (error == GL_NO_ERROR) + break; + ALOGE("GL error 0x%04x", int(error)); + } while (true); +} + +RenderEngine::GlesVersion RenderEngine::parseGlesVersion(const char* str) { + int major, minor; + if (sscanf(str, "OpenGL ES-CM %d.%d", &major, &minor) != 2) { + if (sscanf(str, "OpenGL ES %d.%d", &major, &minor) != 2) { + ALOGW("Unable to parse GL_VERSION string: \"%s\"", str); + return GLES_VERSION_1_0; + } + } + + if (major == 1 && minor == 0) return GLES_VERSION_1_0; + if (major == 1 && minor >= 1) return GLES_VERSION_1_1; + if (major == 2 && minor >= 0) return GLES_VERSION_2_0; + if (major == 3 && minor >= 0) return GLES_VERSION_3_0; + + ALOGW("Unrecognized OpenGL ES version: %d.%d", major, minor); + return GLES_VERSION_1_0; +} + +void RenderEngine::fillRegionWithColor(const Region& region, uint32_t height, + float red, float green, float blue, float alpha) { + size_t c; + Rect const* r = region.getArray(&c); + Mesh mesh(Mesh::TRIANGLES, c*6, 2); + Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>()); + for (size_t i=0 ; i<c ; i++, r++) { + position[i*6 + 0].x = r->left; + position[i*6 + 0].y = height - r->top; + position[i*6 + 1].x = r->left; + position[i*6 + 1].y = height - r->bottom; + position[i*6 + 2].x = r->right; + position[i*6 + 2].y = height - r->bottom; + position[i*6 + 3].x = r->left; + position[i*6 + 3].y = height - r->top; + position[i*6 + 4].x = r->right; + position[i*6 + 4].y = height - r->bottom; + position[i*6 + 5].x = r->right; + position[i*6 + 5].y = height - r->top; + } + setupFillWithColor(red, green, blue, alpha); + drawMesh(mesh); +} + +void RenderEngine::clearWithColor(float red, float green, float blue, float alpha) { + glClearColor(red, green, blue, alpha); + glClear(GL_COLOR_BUFFER_BIT); +} + +void RenderEngine::setScissor( + uint32_t left, uint32_t bottom, uint32_t right, uint32_t top) { + glScissor(left, bottom, right, top); + glEnable(GL_SCISSOR_TEST); +} + +void RenderEngine::disableScissor() { + glDisable(GL_SCISSOR_TEST); +} + +void RenderEngine::genTextures(size_t count, uint32_t* names) { + glGenTextures(count, names); +} + +void RenderEngine::deleteTextures(size_t count, uint32_t const* names) { + glDeleteTextures(count, names); +} + +void RenderEngine::readPixels(size_t l, size_t b, size_t w, size_t h, uint32_t* pixels) { + glReadPixels(l, b, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixels); +} + +void RenderEngine::dump(String8& result) { + const GLExtensions& extensions(GLExtensions::getInstance()); + result.appendFormat("GLES: %s, %s, %s\n", + extensions.getVendor(), + extensions.getRenderer(), + extensions.getVersion()); + result.appendFormat("%s\n", extensions.getExtension()); +} + +// --------------------------------------------------------------------------- + +RenderEngine::BindImageAsFramebuffer::BindImageAsFramebuffer( + RenderEngine& engine, EGLImageKHR image) : mEngine(engine) +{ + mEngine.bindImageAsFramebuffer(image, &mTexName, &mFbName, &mStatus); + + ALOGE_IF(mStatus != GL_FRAMEBUFFER_COMPLETE_OES, + "glCheckFramebufferStatusOES error %d", mStatus); +} + +RenderEngine::BindImageAsFramebuffer::~BindImageAsFramebuffer() { + // back to main framebuffer + mEngine.unbindFramebuffer(mTexName, mFbName); +} + +status_t RenderEngine::BindImageAsFramebuffer::getStatus() const { + return mStatus == GL_FRAMEBUFFER_COMPLETE_OES ? NO_ERROR : BAD_VALUE; +} + +// --------------------------------------------------------------------------- +}; // namespace android +// --------------------------------------------------------------------------- diff --git a/services/surfaceflinger/RenderEngine/RenderEngine.h b/services/surfaceflinger/RenderEngine/RenderEngine.h new file mode 100644 index 0000000..3c7f9ab --- /dev/null +++ b/services/surfaceflinger/RenderEngine/RenderEngine.h @@ -0,0 +1,117 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef SF_RENDERENGINE_H_ +#define SF_RENDERENGINE_H_ + +#include <stdint.h> +#include <sys/types.h> + +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <ui/mat4.h> + +// --------------------------------------------------------------------------- +namespace android { +// --------------------------------------------------------------------------- + +class String8; +class Rect; +class Region; +class Mesh; +class Texture; + +class RenderEngine { + enum GlesVersion { + GLES_VERSION_1_0 = 0x10000, + GLES_VERSION_1_1 = 0x10001, + GLES_VERSION_2_0 = 0x20000, + GLES_VERSION_3_0 = 0x30000, + }; + static GlesVersion parseGlesVersion(const char* str); + + EGLContext mEGLContext; + void setEGLContext(EGLContext ctxt); + + virtual void bindImageAsFramebuffer(EGLImageKHR image, uint32_t* texName, uint32_t* fbName, uint32_t* status) = 0; + virtual void unbindFramebuffer(uint32_t texName, uint32_t fbName) = 0; + +protected: + RenderEngine(); + virtual ~RenderEngine() = 0; + +public: + static RenderEngine* create(EGLDisplay display, EGLConfig config); + + // dump the extension strings. always call the base class. + virtual void dump(String8& result); + + // helpers + void clearWithColor(float red, float green, float blue, float alpha); + void fillRegionWithColor(const Region& region, uint32_t height, + float red, float green, float blue, float alpha); + + // common to all GL versions + void setScissor(uint32_t left, uint32_t bottom, uint32_t right, uint32_t top); + void disableScissor(); + void genTextures(size_t count, uint32_t* names); + void deleteTextures(size_t count, uint32_t const* names); + void readPixels(size_t l, size_t b, size_t w, size_t h, uint32_t* pixels); + + class BindImageAsFramebuffer { + RenderEngine& mEngine; + uint32_t mTexName, mFbName; + uint32_t mStatus; + public: + BindImageAsFramebuffer(RenderEngine& engine, EGLImageKHR image); + ~BindImageAsFramebuffer(); + int getStatus() const; + }; + + // set-up + virtual void checkErrors() const; + virtual void setViewportAndProjection(size_t vpw, size_t vph, size_t w, size_t h, bool yswap) = 0; + virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque, int alpha) = 0; + virtual void setupDimLayerBlending(int alpha) = 0; + virtual void setupLayerTexturing(const Texture& texture) = 0; + virtual void setupLayerBlackedOut() = 0; + virtual void setupFillWithColor(float r, float g, float b, float a) = 0; + + virtual void disableTexturing() = 0; + virtual void disableBlending() = 0; + + // drawing + virtual void drawMesh(const Mesh& mesh) = 0; + + // grouping + // creates a color-transform group, everything drawn in the group will be + // transformed by the given color transform when endGroup() is called. + virtual void beginGroup(const mat4& colorTransform) = 0; + virtual void endGroup() = 0; + + // queries + virtual size_t getMaxTextureSize() const = 0; + virtual size_t getMaxViewportDims() const = 0; + + EGLContext getEGLContext() const; +}; + +// --------------------------------------------------------------------------- +}; // namespace android +// --------------------------------------------------------------------------- + +#endif /* SF_RENDERENGINE_H_ */ diff --git a/services/surfaceflinger/RenderEngine/Texture.cpp b/services/surfaceflinger/RenderEngine/Texture.cpp new file mode 100644 index 0000000..8875b6d --- /dev/null +++ b/services/surfaceflinger/RenderEngine/Texture.cpp @@ -0,0 +1,79 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <string.h> + +#include "Texture.h" + +namespace android { + +Texture::Texture() : + mTextureName(0), mTextureTarget(TEXTURE_2D), + mWidth(0), mHeight(0), mFiltering(false) { +} + +Texture::Texture(Target textureTarget, uint32_t textureName) : + mTextureName(textureName), mTextureTarget(textureTarget), + mWidth(0), mHeight(0), mFiltering(false) { +} + +void Texture::init(Target textureTarget, uint32_t textureName) { + mTextureName = textureName; + mTextureTarget = textureTarget; +} + +Texture::~Texture() { +} + + +void Texture::setMatrix(float const* matrix) { + mTextureMatrix = mat4(matrix); +} + +void Texture::setFiltering(bool enabled) { + mFiltering = enabled; +} + +void Texture::setDimensions(size_t width, size_t height) { + mWidth = width; + mHeight = height; +} + +uint32_t Texture::getTextureName() const { + return mTextureName; +} + +uint32_t Texture::getTextureTarget() const { + return mTextureTarget; +} + +const mat4& Texture::getMatrix() const { + return mTextureMatrix; +} + +bool Texture::getFiltering() const { + return mFiltering; +} + +size_t Texture::getWidth() const { + return mWidth; +} + +size_t Texture::getHeight() const { + return mHeight; +} + +} /* namespace android */ diff --git a/services/surfaceflinger/RenderEngine/Texture.h b/services/surfaceflinger/RenderEngine/Texture.h new file mode 100644 index 0000000..8cf85fc --- /dev/null +++ b/services/surfaceflinger/RenderEngine/Texture.h @@ -0,0 +1,56 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdint.h> +#include <ui/mat4.h> + +#ifndef SF_RENDER_ENGINE_TEXTURE_H +#define SF_RENDER_ENGINE_TEXTURE_H + +namespace android { + +class Texture { + uint32_t mTextureName; + uint32_t mTextureTarget; + size_t mWidth; + size_t mHeight; + bool mFiltering; + mat4 mTextureMatrix; + +public: + enum Target { TEXTURE_2D = 0x0DE1, TEXTURE_EXTERNAL = 0x8D65 }; + + Texture(); + Texture(Target textureTarget, uint32_t textureName); + ~Texture(); + + void init(Target textureTarget, uint32_t textureName); + + void setMatrix(float const* matrix); + void setFiltering(bool enabled); + void setDimensions(size_t width, size_t height); + + uint32_t getTextureName() const; + uint32_t getTextureTarget() const; + + const mat4& getMatrix() const; + bool getFiltering() const; + size_t getWidth() const; + size_t getHeight() const; +}; + +} /* namespace android */ +#endif /* SF_RENDER_ENGINE_TEXTURE_H */ diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index ef0d521..9d94c87 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -23,7 +23,6 @@ #include <dlfcn.h> #include <EGL/egl.h> -#include <GLES/gl.h> #include <cutils/log.h> #include <cutils/properties.h> @@ -55,12 +54,14 @@ #include <private/android_filesystem_config.h> #include <private/gui/SyncFeatures.h> +#include "Client.h" #include "clz.h" +#include "Colorizer.h" #include "DdmConnection.h" #include "DisplayDevice.h" -#include "Client.h" +#include "DispSync.h" +#include "EventControlThread.h" #include "EventThread.h" -#include "GLExtensions.h" #include "Layer.h" #include "LayerDim.h" #include "SurfaceFlinger.h" @@ -69,11 +70,56 @@ #include "DisplayHardware/HWComposer.h" #include "DisplayHardware/VirtualDisplaySurface.h" +#include "Effects/Daltonizer.h" + +#include "RenderEngine/RenderEngine.h" +#include <cutils/compiler.h> + #define DISPLAY_COUNT 1 +/* + * DEBUG_SCREENSHOTS: set to true to check that screenshots are not all + * black pixels. + */ +#define DEBUG_SCREENSHOTS false + EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name); namespace android { + +// This works around the lack of support for the sync framework on some +// devices. +#ifdef RUNNING_WITHOUT_SYNC_FRAMEWORK +static const bool runningWithoutSyncFramework = true; +#else +static const bool runningWithoutSyncFramework = false; +#endif + +// This is the phase offset in nanoseconds of the software vsync event +// relative to the vsync event reported by HWComposer. The software vsync +// event is when SurfaceFlinger and Choreographer-based applications run each +// frame. +// +// This phase offset allows adjustment of the minimum latency from application +// wake-up (by Choregographer) time to the time at which the resulting window +// image is displayed. This value may be either positive (after the HW vsync) +// or negative (before the HW vsync). Setting it to 0 will result in a +// minimum latency of two vsync periods because the app and SurfaceFlinger +// will run just after the HW vsync. Setting it to a positive number will +// result in the minimum latency being: +// +// (2 * VSYNC_PERIOD - (vsyncPhaseOffsetNs % VSYNC_PERIOD)) +// +// Note that reducing this latency makes it more likely for the applications +// to not have their window content image ready in time. When this happens +// the latency will end up being an additional vsync period, and animations +// will hiccup. Therefore, this latency should be tuned somewhat +// conservatively (or at least with awareness of the trade-off being made). +static const int64_t vsyncPhaseOffsetNs = VSYNC_EVENT_PHASE_OFFSET_NS; + +// This is the phase offset at which SurfaceFlinger's composition runs. +static const int64_t sfVsyncPhaseOffsetNs = SF_VSYNC_EVENT_PHASE_OFFSET_NS; + // --------------------------------------------------------------------------- const String16 sHardwareTest("android.permission.HARDWARE_TEST"); @@ -84,12 +130,13 @@ const String16 sDump("android.permission.DUMP"); // --------------------------------------------------------------------------- SurfaceFlinger::SurfaceFlinger() - : BnSurfaceComposer(), Thread(false), + : BnSurfaceComposer(), mTransactionFlags(0), mTransactionPending(false), mAnimTransactionPending(false), mLayersRemoved(false), mRepaintEverything(0), + mRenderEngine(NULL), mBootTime(systemTime()), mVisibleRegionsDirty(false), mHwWorkListDirty(false), @@ -102,7 +149,10 @@ SurfaceFlinger::SurfaceFlinger() mLastSwapBufferTime(0), mDebugInTransaction(0), mLastTransactionTime(0), - mBootFinished(false) + mBootFinished(false), + mPrimaryHWVsyncEnabled(false), + mHWVsyncAvailable(false), + mDaltonize(false) { ALOGI("SurfaceFlinger is starting"); @@ -130,14 +180,8 @@ SurfaceFlinger::SurfaceFlinger() void SurfaceFlinger::onFirstRef() { mEventQueue.init(this); - - run("SurfaceFlinger", PRIORITY_URGENT_DISPLAY); - - // Wait for the main thread to be done with its initialization - mReadyToRunBarrier.wait(); } - SurfaceFlinger::~SurfaceFlinger() { EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); @@ -195,6 +239,25 @@ sp<IBinder> SurfaceFlinger::createDisplay(const String8& displayName, return token; } +void SurfaceFlinger::destroyDisplay(const sp<IBinder>& display) { + Mutex::Autolock _l(mStateLock); + + ssize_t idx = mCurrentState.displays.indexOfKey(display); + if (idx < 0) { + ALOGW("destroyDisplay: invalid display token"); + return; + } + + const DisplayDeviceState& info(mCurrentState.displays.valueAt(idx)); + if (!info.isVirtualDisplay()) { + ALOGE("destroyDisplay called for non-virtual display"); + return; + } + + mCurrentState.displays.removeItemsAt(idx); + setTransactionFlags(eDisplayTransactionNeeded); +} + void SurfaceFlinger::createBuiltinDisplayLocked(DisplayDevice::DisplayType type) { ALOGW_IF(mBuiltinDisplays[type], "Overwriting display token for display type %d", type); @@ -206,7 +269,7 @@ void SurfaceFlinger::createBuiltinDisplayLocked(DisplayDevice::DisplayType type) } sp<IBinder> SurfaceFlinger::getBuiltInDisplay(int32_t id) { - if (uint32_t(id) >= DisplayDevice::NUM_DISPLAY_TYPES) { + if (uint32_t(id) >= DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) { ALOGE("getDefaultDisplay: id=%d is not a valid default display id", id); return NULL; } @@ -239,19 +302,20 @@ void SurfaceFlinger::bootFinished() property_set("service.bootanim.exit", "1"); } -void SurfaceFlinger::deleteTextureAsync(GLuint texture) { +void SurfaceFlinger::deleteTextureAsync(uint32_t texture) { class MessageDestroyGLTexture : public MessageBase { - GLuint texture; + RenderEngine& engine; + uint32_t texture; public: - MessageDestroyGLTexture(GLuint texture) - : texture(texture) { + MessageDestroyGLTexture(RenderEngine& engine, uint32_t texture) + : engine(engine), texture(texture) { } virtual bool handler() { - glDeleteTextures(1, &texture); + engine.deleteTextures(1, &texture); return true; } }; - postMessageAsync(new MessageDestroyGLTexture(texture)); + postMessageAsync(new MessageDestroyGLTexture(getRenderEngine(), texture)); } status_t SurfaceFlinger::selectConfigForAttribute( @@ -340,142 +404,112 @@ public: operator EGLint const* () const { return &mList.keyAt(0).v; } }; -EGLConfig SurfaceFlinger::selectEGLConfig(EGLDisplay display, EGLint nativeVisualId) { +status_t SurfaceFlinger::selectEGLConfig(EGLDisplay display, EGLint nativeVisualId, + EGLint renderableType, EGLConfig* config) { // select our EGLConfig. It must support EGL_RECORDABLE_ANDROID if // it is to be used with WIFI displays - EGLConfig config; - EGLint dummy; status_t err; + EGLint wantedAttribute; + EGLint wantedAttributeValue; EGLAttributeVector attribs; - attribs[EGL_SURFACE_TYPE] = EGL_WINDOW_BIT; - attribs[EGL_RECORDABLE_ANDROID] = EGL_TRUE; - attribs[EGL_FRAMEBUFFER_TARGET_ANDROID] = EGL_TRUE; - attribs[EGL_RED_SIZE] = 8; - attribs[EGL_GREEN_SIZE] = 8; - attribs[EGL_BLUE_SIZE] = 8; - - err = selectConfigForAttribute(display, attribs, EGL_NONE, EGL_NONE, &config); - if (!err) - goto success; - - // maybe we failed because of EGL_FRAMEBUFFER_TARGET_ANDROID - ALOGW("no suitable EGLConfig found, trying without EGL_FRAMEBUFFER_TARGET_ANDROID"); - attribs.remove(EGL_FRAMEBUFFER_TARGET_ANDROID); - err = selectConfigForAttribute(display, attribs, - EGL_NATIVE_VISUAL_ID, nativeVisualId, &config); - if (!err) - goto success; - - // maybe we failed because of EGL_RECORDABLE_ANDROID - ALOGW("no suitable EGLConfig found, trying without EGL_RECORDABLE_ANDROID"); - attribs.remove(EGL_RECORDABLE_ANDROID); - err = selectConfigForAttribute(display, attribs, - EGL_NATIVE_VISUAL_ID, nativeVisualId, &config); - if (!err) - goto success; - - // allow less than 24-bit color; the non-gpu-accelerated emulator only - // supports 16-bit color - ALOGW("no suitable EGLConfig found, trying with 16-bit color allowed"); - attribs.remove(EGL_RED_SIZE); - attribs.remove(EGL_GREEN_SIZE); - attribs.remove(EGL_BLUE_SIZE); - err = selectConfigForAttribute(display, attribs, - EGL_NATIVE_VISUAL_ID, nativeVisualId, &config); - if (!err) - goto success; - - // this EGL is too lame for Android - ALOGE("no suitable EGLConfig found, giving up"); - - return 0; - -success: - if (eglGetConfigAttrib(display, config, EGL_CONFIG_CAVEAT, &dummy)) - ALOGW_IF(dummy == EGL_SLOW_CONFIG, "EGL_SLOW_CONFIG selected!"); - return config; -} - -EGLContext SurfaceFlinger::createGLContext(EGLDisplay display, EGLConfig config) { - // Also create our EGLContext - EGLint contextAttributes[] = { -#ifdef EGL_IMG_context_priority -#ifdef HAS_CONTEXT_PRIORITY -#warning "using EGL_IMG_context_priority" - EGL_CONTEXT_PRIORITY_LEVEL_IMG, EGL_CONTEXT_PRIORITY_HIGH_IMG, -#endif -#endif - EGL_NONE, EGL_NONE - }; - EGLContext ctxt = eglCreateContext(display, config, NULL, contextAttributes); - ALOGE_IF(ctxt==EGL_NO_CONTEXT, "EGLContext creation failed"); - return ctxt; -} - -void SurfaceFlinger::initializeGL(EGLDisplay display) { - GLExtensions& extensions(GLExtensions::getInstance()); - extensions.initWithGLStrings( - glGetString(GL_VENDOR), - glGetString(GL_RENDERER), - glGetString(GL_VERSION), - glGetString(GL_EXTENSIONS), - eglQueryString(display, EGL_VENDOR), - eglQueryString(display, EGL_VERSION), - eglQueryString(display, EGL_EXTENSIONS)); - - glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize); - glGetIntegerv(GL_MAX_VIEWPORT_DIMS, mMaxViewportDims); - - glPixelStorei(GL_UNPACK_ALIGNMENT, 4); - glPixelStorei(GL_PACK_ALIGNMENT, 4); - glEnableClientState(GL_VERTEX_ARRAY); - glShadeModel(GL_FLAT); - glDisable(GL_DITHER); - glDisable(GL_CULL_FACE); - - struct pack565 { - inline uint16_t operator() (int r, int g, int b) const { - return (r<<11)|(g<<5)|b; - } - } pack565; - - const uint16_t protTexData[] = { pack565(0x03, 0x03, 0x03) }; - glGenTextures(1, &mProtectedTexName); - glBindTexture(GL_TEXTURE_2D, mProtectedTexName); - glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, - GL_RGB, GL_UNSIGNED_SHORT_5_6_5, protTexData); + if (renderableType) { + attribs[EGL_RENDERABLE_TYPE] = renderableType; + attribs[EGL_RECORDABLE_ANDROID] = EGL_TRUE; + attribs[EGL_SURFACE_TYPE] = EGL_WINDOW_BIT|EGL_PBUFFER_BIT; + attribs[EGL_FRAMEBUFFER_TARGET_ANDROID] = EGL_TRUE; + attribs[EGL_RED_SIZE] = 8; + attribs[EGL_GREEN_SIZE] = 8; + attribs[EGL_BLUE_SIZE] = 8; + wantedAttribute = EGL_NONE; + wantedAttributeValue = EGL_NONE; - // print some debugging info - EGLint r,g,b,a; - eglGetConfigAttrib(display, mEGLConfig, EGL_RED_SIZE, &r); - eglGetConfigAttrib(display, mEGLConfig, EGL_GREEN_SIZE, &g); - eglGetConfigAttrib(display, mEGLConfig, EGL_BLUE_SIZE, &b); - eglGetConfigAttrib(display, mEGLConfig, EGL_ALPHA_SIZE, &a); - ALOGI("EGL informations:"); - ALOGI("vendor : %s", extensions.getEglVendor()); - ALOGI("version : %s", extensions.getEglVersion()); - ALOGI("extensions: %s", extensions.getEglExtension()); - ALOGI("Client API: %s", eglQueryString(display, EGL_CLIENT_APIS)?:"Not Supported"); - ALOGI("EGLSurface: %d-%d-%d-%d, config=%p", r, g, b, a, mEGLConfig); - ALOGI("OpenGL ES informations:"); - ALOGI("vendor : %s", extensions.getVendor()); - ALOGI("renderer : %s", extensions.getRenderer()); - ALOGI("version : %s", extensions.getVersion()); - ALOGI("extensions: %s", extensions.getExtension()); - ALOGI("GL_MAX_TEXTURE_SIZE = %d", mMaxTextureSize); - ALOGI("GL_MAX_VIEWPORT_DIMS = %d x %d", mMaxViewportDims[0], mMaxViewportDims[1]); + } else { + // if no renderable type specified, fallback to a simplified query + wantedAttribute = EGL_NATIVE_VISUAL_ID; + wantedAttributeValue = nativeVisualId; + } + + err = selectConfigForAttribute(display, attribs, wantedAttribute, + wantedAttributeValue, config); + if (err == NO_ERROR) { + EGLint caveat; + if (eglGetConfigAttrib(display, *config, EGL_CONFIG_CAVEAT, &caveat)) + ALOGW_IF(caveat == EGL_SLOW_CONFIG, "EGL_SLOW_CONFIG selected!"); + } + return err; } -status_t SurfaceFlinger::readyToRun() -{ +class DispSyncSource : public VSyncSource, private DispSync::Callback { +public: + DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync) : + mValue(0), + mPhaseOffset(phaseOffset), + mTraceVsync(traceVsync), + mDispSync(dispSync) {} + + virtual ~DispSyncSource() {} + + virtual void setVSyncEnabled(bool enable) { + // Do NOT lock the mutex here so as to avoid any mutex ordering issues + // with locking it in the onDispSyncEvent callback. + if (enable) { + status_t err = mDispSync->addEventListener(mPhaseOffset, + static_cast<DispSync::Callback*>(this)); + if (err != NO_ERROR) { + ALOGE("error registering vsync callback: %s (%d)", + strerror(-err), err); + } + ATRACE_INT("VsyncOn", 1); + } else { + status_t err = mDispSync->removeEventListener( + static_cast<DispSync::Callback*>(this)); + if (err != NO_ERROR) { + ALOGE("error unregistering vsync callback: %s (%d)", + strerror(-err), err); + } + ATRACE_INT("VsyncOn", 0); + } + } + + virtual void setCallback(const sp<VSyncSource::Callback>& callback) { + Mutex::Autolock lock(mMutex); + mCallback = callback; + } + +private: + virtual void onDispSyncEvent(nsecs_t when) { + sp<VSyncSource::Callback> callback; + { + Mutex::Autolock lock(mMutex); + callback = mCallback; + + if (mTraceVsync) { + mValue = (mValue + 1) % 2; + ATRACE_INT("VSYNC", mValue); + } + } + + if (callback != NULL) { + callback->onVSyncEvent(when); + } + } + + int mValue; + + const nsecs_t mPhaseOffset; + const bool mTraceVsync; + + DispSync* mDispSync; + sp<VSyncSource::Callback> mCallback; + Mutex mMutex; +}; + +void SurfaceFlinger::init() { ALOGI( "SurfaceFlinger's main thread ready to run. " "Initializing graphics H/W..."); + status_t err; Mutex::Autolock _l(mStateLock); // initialize EGL for the default display @@ -487,10 +521,46 @@ status_t SurfaceFlinger::readyToRun() mHwc = new HWComposer(this, *static_cast<HWComposer::EventHandler *>(this)); - // initialize the config and context - EGLint format = mHwc->getVisualID(); - mEGLConfig = selectEGLConfig(mEGLDisplay, format); - mEGLContext = createGLContext(mEGLDisplay, mEGLConfig); + // First try to get an ES2 config + err = selectEGLConfig(mEGLDisplay, mHwc->getVisualID(), EGL_OPENGL_ES2_BIT, + &mEGLConfig); + + if (err != NO_ERROR) { + // If ES2 fails, try ES1 + err = selectEGLConfig(mEGLDisplay, mHwc->getVisualID(), + EGL_OPENGL_ES_BIT, &mEGLConfig); + } + + if (err != NO_ERROR) { + // still didn't work, probably because we're on the emulator... + // try a simplified query + ALOGW("no suitable EGLConfig found, trying a simpler query"); + err = selectEGLConfig(mEGLDisplay, mHwc->getVisualID(), 0, &mEGLConfig); + } + + if (err != NO_ERROR) { + // this EGL is too lame for android + LOG_ALWAYS_FATAL("no suitable EGLConfig found, giving up"); + } + + // print some debugging info + EGLint r,g,b,a; + eglGetConfigAttrib(mEGLDisplay, mEGLConfig, EGL_RED_SIZE, &r); + eglGetConfigAttrib(mEGLDisplay, mEGLConfig, EGL_GREEN_SIZE, &g); + eglGetConfigAttrib(mEGLDisplay, mEGLConfig, EGL_BLUE_SIZE, &b); + eglGetConfigAttrib(mEGLDisplay, mEGLConfig, EGL_ALPHA_SIZE, &a); + ALOGI("EGL informations:"); + ALOGI("vendor : %s", eglQueryString(mEGLDisplay, EGL_VENDOR)); + ALOGI("version : %s", eglQueryString(mEGLDisplay, EGL_VERSION)); + ALOGI("extensions: %s", eglQueryString(mEGLDisplay, EGL_EXTENSIONS)); + ALOGI("Client API: %s", eglQueryString(mEGLDisplay, EGL_CLIENT_APIS)?:"Not Supported"); + ALOGI("EGLSurface: %d-%d-%d-%d, config=%p", r, g, b, a, mEGLConfig); + + // get a RenderEngine for the given display / config (can't fail) + mRenderEngine = RenderEngine::create(mEGLDisplay, mEGLConfig); + + // retrieve the EGL context that was selected/created + mEGLContext = mRenderEngine->getEGLContext(); // figure out which format we got eglGetConfigAttrib(mEGLDisplay, mEGLConfig, @@ -500,7 +570,7 @@ status_t SurfaceFlinger::readyToRun() "couldn't create EGLContext"); // initialize our non-virtual displays - for (size_t i=0 ; i<DisplayDevice::NUM_DISPLAY_TYPES ; i++) { + for (size_t i=0 ; i<DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES ; i++) { DisplayDevice::DisplayType type((DisplayDevice::DisplayType)i); // set-up the displays that are already connected if (mHwc->isConnected(i) || type==DisplayDevice::DISPLAY_PRIMARY) { @@ -509,9 +579,11 @@ status_t SurfaceFlinger::readyToRun() createBuiltinDisplayLocked(type); wp<IBinder> token = mBuiltinDisplays[i]; + sp<BufferQueue> bq = new BufferQueue(new GraphicBufferAlloc()); + sp<FramebufferSurface> fbs = new FramebufferSurface(*mHwc, i, bq); sp<DisplayDevice> hw = new DisplayDevice(this, type, allocateHwcDisplayId(type), isSecure, token, - new FramebufferSurface(*mHwc, i), + fbs, bq, mEGLConfig); if (i > DisplayDevice::DISPLAY_PRIMARY) { // FIXME: currently we don't get blank/unblank requests @@ -524,39 +596,39 @@ status_t SurfaceFlinger::readyToRun() } } - // we need a GL context current in a few places, when initializing - // OpenGL ES (see below), or creating a layer, - // or when a texture is (asynchronously) destroyed, and for that - // we need a valid surface, so it's convenient to use the main display - // for that. - sp<const DisplayDevice> hw(getDefaultDisplayDevice()); - - // initialize OpenGL ES - DisplayDevice::makeCurrent(mEGLDisplay, hw, mEGLContext); - initializeGL(mEGLDisplay); + // make the GLContext current so that we can create textures when creating Layers + // (which may happens before we render something) + getDefaultDisplayDevice()->makeCurrent(mEGLDisplay, mEGLContext); // start the EventThread - mEventThread = new EventThread(this); - mEventQueue.setEventThread(mEventThread); + sp<VSyncSource> vsyncSrc = new DispSyncSource(&mPrimaryDispSync, + vsyncPhaseOffsetNs, true); + mEventThread = new EventThread(vsyncSrc); + sp<VSyncSource> sfVsyncSrc = new DispSyncSource(&mPrimaryDispSync, + sfVsyncPhaseOffsetNs, false); + mSFEventThread = new EventThread(sfVsyncSrc); + mEventQueue.setEventThread(mSFEventThread); - // initialize our drawing state - mDrawingState = mCurrentState; + mEventControlThread = new EventControlThread(this); + mEventControlThread->run("EventControl", PRIORITY_URGENT_DISPLAY); + // set a fake vsync period if there is no HWComposer + if (mHwc->initCheck() != NO_ERROR) { + mPrimaryDispSync.setPeriod(16666667); + } - // We're now ready to accept clients... - mReadyToRunBarrier.open(); + // initialize our drawing state + mDrawingState = mCurrentState; // set initial conditions (e.g. unblank default device) initializeDisplays(); // start boot animation startBootAnim(); - - return NO_ERROR; } int32_t SurfaceFlinger::allocateHwcDisplayId(DisplayDevice::DisplayType type) { - return (uint32_t(type) < DisplayDevice::NUM_DISPLAY_TYPES) ? + return (uint32_t(type) < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) ? type : mHwc->allocateDisplayId(); } @@ -566,13 +638,12 @@ void SurfaceFlinger::startBootAnim() { property_set("ctl.start", "bootanim"); } -uint32_t SurfaceFlinger::getMaxTextureSize() const { - return mMaxTextureSize; +size_t SurfaceFlinger::getMaxTextureSize() const { + return mRenderEngine->getMaxTextureSize(); } -uint32_t SurfaceFlinger::getMaxViewportDims() const { - return mMaxViewportDims[0] < mMaxViewportDims[1] ? - mMaxViewportDims[0] : mMaxViewportDims[1]; +size_t SurfaceFlinger::getMaxViewportDims() const { + return mRenderEngine->getMaxViewportDims(); } // ---------------------------------------------------------------------------- @@ -586,7 +657,7 @@ bool SurfaceFlinger::authenticateSurfaceTexture( status_t SurfaceFlinger::getDisplayInfo(const sp<IBinder>& display, DisplayInfo* info) { int32_t type = NAME_NOT_FOUND; - for (int i=0 ; i<DisplayDevice::NUM_DISPLAY_TYPES ; i++) { + for (int i=0 ; i<DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES ; i++) { if (display == mBuiltinDisplays[i]) { type = i; break; @@ -637,7 +708,6 @@ status_t SurfaceFlinger::getDisplayInfo(const sp<IBinder>& display, DisplayInfo* // TODO: this needs to go away (currently needed only by webkit) sp<const DisplayDevice> hw(getDefaultDisplayDevice()); info->orientation = hw->getOrientation(); - getPixelFormatInfo(hw->getFormat(), &info->pixelFormatInfo); } else { // TODO: where should this value come from? static const int TV_DENSITY = 213; @@ -695,22 +765,73 @@ status_t SurfaceFlinger::postMessageSync(const sp<MessageBase>& msg, return res; } -bool SurfaceFlinger::threadLoop() { - waitForEvent(); - return true; +void SurfaceFlinger::run() { + do { + waitForEvent(); + } while (true); } -void SurfaceFlinger::onVSyncReceived(int type, nsecs_t timestamp) { - if (mEventThread == NULL) { - // This is a temporary workaround for b/7145521. A non-null pointer - // does not mean EventThread has finished initializing, so this - // is not a correct fix. - ALOGW("WARNING: EventThread not started, ignoring vsync"); +void SurfaceFlinger::enableHardwareVsync() { + Mutex::Autolock _l(mHWVsyncLock); + if (!mPrimaryHWVsyncEnabled && mHWVsyncAvailable) { + mPrimaryDispSync.beginResync(); + //eventControl(HWC_DISPLAY_PRIMARY, SurfaceFlinger::EVENT_VSYNC, true); + mEventControlThread->setVsyncEnabled(true); + mPrimaryHWVsyncEnabled = true; + } +} + +void SurfaceFlinger::resyncToHardwareVsync(bool makeAvailable) { + Mutex::Autolock _l(mHWVsyncLock); + + if (makeAvailable) { + mHWVsyncAvailable = true; + } else if (!mHWVsyncAvailable) { + ALOGE("resyncToHardwareVsync called when HW vsync unavailable"); return; } - if (uint32_t(type) < DisplayDevice::NUM_DISPLAY_TYPES) { - // we should only receive DisplayDevice::DisplayType from the vsync callback - mEventThread->onVSyncReceived(type, timestamp); + + const nsecs_t period = + getHwComposer().getRefreshPeriod(HWC_DISPLAY_PRIMARY); + + mPrimaryDispSync.reset(); + mPrimaryDispSync.setPeriod(period); + + if (!mPrimaryHWVsyncEnabled) { + mPrimaryDispSync.beginResync(); + //eventControl(HWC_DISPLAY_PRIMARY, SurfaceFlinger::EVENT_VSYNC, true); + mEventControlThread->setVsyncEnabled(true); + mPrimaryHWVsyncEnabled = true; + } +} + +void SurfaceFlinger::disableHardwareVsync(bool makeUnavailable) { + Mutex::Autolock _l(mHWVsyncLock); + if (mPrimaryHWVsyncEnabled) { + //eventControl(HWC_DISPLAY_PRIMARY, SurfaceFlinger::EVENT_VSYNC, false); + mEventControlThread->setVsyncEnabled(false); + mPrimaryDispSync.endResync(); + mPrimaryHWVsyncEnabled = false; + } + if (makeUnavailable) { + mHWVsyncAvailable = false; + } +} + +void SurfaceFlinger::onVSyncReceived(int type, nsecs_t timestamp) { + bool needsHwVsync = false; + + { // Scope for the lock + Mutex::Autolock _l(mHWVsyncLock); + if (type == 0 && mPrimaryHWVsyncEnabled) { + needsHwVsync = mPrimaryDispSync.addResyncSample(timestamp); + } + } + + if (needsHwVsync) { + enableHardwareVsync(); + } else { + disableHardwareVsync(false); } } @@ -723,7 +844,7 @@ void SurfaceFlinger::onHotplugReceived(int type, bool connected) { return; } - if (uint32_t(type) < DisplayDevice::NUM_DISPLAY_TYPES) { + if (uint32_t(type) < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) { Mutex::Autolock _l(mStateLock); if (connected) { createBuiltinDisplayLocked((DisplayDevice::DisplayType)type); @@ -738,6 +859,7 @@ void SurfaceFlinger::onHotplugReceived(int type, bool connected) { } void SurfaceFlinger::eventControl(int disp, int event, int enabled) { + ATRACE_CALL(); getHwComposer().eventControl(disp, event, enabled); } @@ -797,24 +919,10 @@ void SurfaceFlinger::doDebugFlashRegions() doComposeSurfaces(hw, Region(hw->bounds())); // and draw the dirty region - glDisable(GL_TEXTURE_EXTERNAL_OES); - glDisable(GL_TEXTURE_2D); - glDisable(GL_BLEND); - glColor4f(1, 0, 1, 1); const int32_t height = hw->getHeight(); - Region::const_iterator it = dirtyRegion.begin(); - Region::const_iterator const end = dirtyRegion.end(); - while (it != end) { - const Rect& r = *it++; - GLfloat vertices[][2] = { - { (GLfloat) r.left, (GLfloat) (height - r.top) }, - { (GLfloat) r.left, (GLfloat) (height - r.bottom) }, - { (GLfloat) r.right, (GLfloat) (height - r.bottom) }, - { (GLfloat) r.right, (GLfloat) (height - r.top) } - }; - glVertexPointer(2, GL_FLOAT, 0, vertices); - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - } + RenderEngine& engine(getRenderEngine()); + engine.fillRegionWithColor(dirtyRegion, height, 1, 0, 1, 1); + hw->compositionComplete(); hw->swapBuffers(getHwComposer()); } @@ -837,10 +945,10 @@ void SurfaceFlinger::doDebugFlashRegions() void SurfaceFlinger::preComposition() { bool needExtraInvalidate = false; - const LayerVector& currentLayers(mDrawingState.layersSortedByZ); - const size_t count = currentLayers.size(); + const LayerVector& layers(mDrawingState.layersSortedByZ); + const size_t count = layers.size(); for (size_t i=0 ; i<count ; i++) { - if (currentLayers[i]->onPreComposition()) { + if (layers[i]->onPreComposition()) { needExtraInvalidate = true; } } @@ -851,17 +959,33 @@ void SurfaceFlinger::preComposition() void SurfaceFlinger::postComposition() { - const LayerVector& currentLayers(mDrawingState.layersSortedByZ); - const size_t count = currentLayers.size(); + const LayerVector& layers(mDrawingState.layersSortedByZ); + const size_t count = layers.size(); for (size_t i=0 ; i<count ; i++) { - currentLayers[i]->onPostComposition(); + layers[i]->onPostComposition(); + } + + const HWComposer& hwc = getHwComposer(); + sp<Fence> presentFence = hwc.getDisplayFence(HWC_DISPLAY_PRIMARY); + + if (presentFence->isValid()) { + if (mPrimaryDispSync.addPresentFence(presentFence)) { + enableHardwareVsync(); + } else { + disableHardwareVsync(false); + } + } + + if (runningWithoutSyncFramework) { + const sp<const DisplayDevice> hw(getDefaultDisplayDevice()); + if (hw->isScreenAcquired()) { + enableHardwareVsync(); + } } if (mAnimCompositionPending) { mAnimCompositionPending = false; - const HWComposer& hwc = getHwComposer(); - sp<Fence> presentFence = hwc.getDisplayFence(HWC_DISPLAY_PRIMARY); if (presentFence->isValid()) { mAnimFrameTracker.setActualPresentFence(presentFence); } else { @@ -881,7 +1005,7 @@ void SurfaceFlinger::rebuildLayerStacks() { mVisibleRegionsDirty = false; invalidateHwcGeometry(); - const LayerVector& currentLayers(mDrawingState.layersSortedByZ); + const LayerVector& layers(mDrawingState.layersSortedByZ); for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) { Region opaqueRegion; Region dirtyRegion; @@ -890,13 +1014,13 @@ void SurfaceFlinger::rebuildLayerStacks() { const Transform& tr(hw->getTransform()); const Rect bounds(hw->getBounds()); if (hw->canDraw()) { - SurfaceFlinger::computeVisibleRegions(currentLayers, + SurfaceFlinger::computeVisibleRegions(layers, hw->getLayerStack(), dirtyRegion, opaqueRegion); - const size_t count = currentLayers.size(); + const size_t count = layers.size(); for (size_t i=0 ; i<count ; i++) { - const sp<Layer>& layer(currentLayers[i]); - const Layer::State& s(layer->drawingState()); + const sp<Layer>& layer(layers[i]); + const Layer::State& s(layer->getDrawingState()); if (s.layerStack == hw->getLayerStack()) { Region drawRegion(tr.transform( layer->visibleNonTransparentRegion)); @@ -916,6 +1040,10 @@ void SurfaceFlinger::rebuildLayerStacks() { } void SurfaceFlinger::setUpHWComposer() { + for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) { + mDisplays[dpy]->beginFrame(); + } + HWComposer& hwc(getHwComposer()); if (hwc.initCheck() == NO_ERROR) { // build the h/w work list @@ -934,7 +1062,7 @@ void SurfaceFlinger::setUpHWComposer() { for (size_t i=0 ; cur!=end && i<count ; ++i, ++cur) { const sp<Layer>& layer(currentLayers[i]); layer->setGeometry(hw, *cur); - if (mDebugDisableHWC || mDebugRegion) { + if (mDebugDisableHWC || mDebugRegion || mDaltonize) { cur->setSkip(true); } } @@ -966,6 +1094,11 @@ void SurfaceFlinger::setUpHWComposer() { status_t err = hwc.prepare(); ALOGE_IF(err, "HWComposer::prepare failed (%s)", strerror(-err)); + + for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) { + sp<const DisplayDevice> hw(mDisplays[dpy]); + hw->prepareFrame(hwc); + } } } @@ -1004,12 +1137,17 @@ void SurfaceFlinger::postFramebuffer() // EGL spec says: // "surface must be bound to the calling thread's current context, // for the current rendering API." - DisplayDevice::makeCurrent(mEGLDisplay, - getDefaultDisplayDevice(), mEGLContext); + getDefaultDisplayDevice()->makeCurrent(mEGLDisplay, mEGLContext); } hwc.commit(); } + // make the default display current because the VirtualDisplayDevice code cannot + // deal with dequeueBuffer() being called outside of the composition loop; however + // the code below can call glFlush() which is allowed (and does in some case) call + // dequeueBuffer(). + getDefaultDisplayDevice()->makeCurrent(mEGLDisplay, mEGLContext); + for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) { sp<const DisplayDevice> hw(mDisplays[dpy]); const Vector< sp<Layer> >& currentLayers(hw->getVisibleLayersSortedByZ()); @@ -1031,12 +1169,23 @@ void SurfaceFlinger::postFramebuffer() mLastSwapBufferTime = systemTime() - now; mDebugInSwapBuffers = 0; + + uint32_t flipCount = getDefaultDisplayDevice()->getPageFlipCount(); + if (flipCount % LOG_FRAME_STATS_PERIOD == 0) { + logFrameStats(); + } } void SurfaceFlinger::handleTransaction(uint32_t transactionFlags) { ATRACE_CALL(); + // here we keep a copy of the drawing state (that is the state that's + // going to be overwritten by handleTransactionLocked()) outside of + // mStateLock so that the side-effects of the State assignment + // don't happen with mStateLock held (which can cause deadlocks). + State drawingState(mDrawingState); + Mutex::Autolock _l(mStateLock); const nsecs_t now = systemTime(); mDebugInTransaction = now; @@ -1106,11 +1255,11 @@ void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags) // be sure that nothing associated with this display // is current. const sp<const DisplayDevice> defaultDisplay(getDefaultDisplayDevice()); - DisplayDevice::makeCurrent(mEGLDisplay, defaultDisplay, mEGLContext); + defaultDisplay->makeCurrent(mEGLDisplay, mEGLContext); sp<DisplayDevice> hw(getDisplayDevice(draw.keyAt(i))); if (hw != NULL) hw->disconnect(getHwComposer()); - if (draw[i].type < DisplayDevice::NUM_DISPLAY_TYPES) + if (draw[i].type < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) mEventThread->onHotplugReceived(draw[i].type, false); mDisplays.removeItem(draw.keyAt(i)); } else { @@ -1158,16 +1307,29 @@ void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags) const DisplayDeviceState& state(curr[i]); sp<DisplaySurface> dispSurface; + sp<IGraphicBufferProducer> producer; + sp<BufferQueue> bq = new BufferQueue(new GraphicBufferAlloc()); + int32_t hwcDisplayId = -1; if (state.isVirtualDisplay()) { // Virtual displays without a surface are dormant: // they have external state (layer stack, projection, // etc.) but no internal state (i.e. a DisplayDevice). if (state.surface != NULL) { + hwcDisplayId = allocateHwcDisplayId(state.type); - dispSurface = new VirtualDisplaySurface( - *mHwc, hwcDisplayId, state.surface, + sp<VirtualDisplaySurface> vds = new VirtualDisplaySurface( + *mHwc, hwcDisplayId, state.surface, bq, state.displayName); + + dispSurface = vds; + if (hwcDisplayId >= 0) { + producer = vds; + } else { + // There won't be any interaction with HWC for this virtual display, + // so the GLES driver can pass buffers directly to the sink. + producer = state.surface; + } } } else { ALOGE_IF(state.surface!=NULL, @@ -1177,14 +1339,15 @@ void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags) hwcDisplayId = allocateHwcDisplayId(state.type); // for supported (by hwc) displays we provide our // own rendering surface - dispSurface = new FramebufferSurface(*mHwc, state.type); + dispSurface = new FramebufferSurface(*mHwc, state.type, bq); + producer = bq; } const wp<IBinder>& display(curr.keyAt(i)); if (dispSurface != NULL) { sp<DisplayDevice> hw = new DisplayDevice(this, state.type, hwcDisplayId, state.isSecure, - display, dispSurface, mEGLConfig); + display, dispSurface, producer, mEGLConfig); hw->setLayerStack(state.layerStack); hw->setProjection(state.orientation, state.viewport, state.frame); @@ -1232,7 +1395,7 @@ void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags) // layerStack first (so we don't have to traverse the list // of displays for every layer). const sp<Layer>& layer(currentLayers[i]); - uint32_t layerStack = layer->drawingState().layerStack; + uint32_t layerStack = layer->getDrawingState().layerStack; if (i==0 || currentlayerStack != layerStack) { currentlayerStack = layerStack; // figure out if this layerstack is mirrored @@ -1269,8 +1432,8 @@ void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags) * Perform our own transaction if needed */ - const LayerVector& previousLayers(mDrawingState.layersSortedByZ); - if (currentLayers.size() > previousLayers.size()) { + const LayerVector& layers(mDrawingState.layersSortedByZ); + if (currentLayers.size() > layers.size()) { // layers have been added mVisibleRegionsDirty = true; } @@ -1280,15 +1443,15 @@ void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags) if (mLayersRemoved) { mLayersRemoved = false; mVisibleRegionsDirty = true; - const size_t count = previousLayers.size(); + const size_t count = layers.size(); for (size_t i=0 ; i<count ; i++) { - const sp<Layer>& layer(previousLayers[i]); + const sp<Layer>& layer(layers[i]); if (currentLayers.indexOf(layer) < 0) { // this layer is not visible anymore // TODO: we could traverse the tree from front to back and // compute the actual visible region // TODO: we could cache the transformed region - const Layer::State& s(layer->drawingState()); + const Layer::State& s(layer->getDrawingState()); Region visibleReg = s.transform.transform( Region(Rect(s.active.w, s.active.h))); invalidateLayerStack(s.layerStack, visibleReg); @@ -1336,7 +1499,7 @@ void SurfaceFlinger::computeVisibleRegions( const sp<Layer>& layer = currentLayers[i]; // start with the whole surface at its current location - const Layer::State& s(layer->drawingState()); + const Layer::State& s(layer->getDrawingState()); // only consider the layers on the given layer stack if (s.layerStack != layerStack) @@ -1473,12 +1636,12 @@ void SurfaceFlinger::handlePageFlip() Region dirtyRegion; bool visibleRegions = false; - const LayerVector& currentLayers(mDrawingState.layersSortedByZ); - const size_t count = currentLayers.size(); + const LayerVector& layers(mDrawingState.layersSortedByZ); + const size_t count = layers.size(); for (size_t i=0 ; i<count ; i++) { - const sp<Layer>& layer(currentLayers[i]); + const sp<Layer>& layer(layers[i]); const Region dirty(layer->latchBuffer(visibleRegions)); - const Layer::State& s(layer->drawingState()); + const Layer::State& s(layer->getDrawingState()); invalidateLayerStack(s.layerStack, dirty); } @@ -1519,7 +1682,14 @@ void SurfaceFlinger::doDisplayComposition(const sp<const DisplayDevice>& hw, } } - doComposeSurfaces(hw, dirtyRegion); + if (CC_LIKELY(!mDaltonize)) { + doComposeSurfaces(hw, dirtyRegion); + } else { + RenderEngine& engine(getRenderEngine()); + engine.beginGroup(mDaltonizer()); + doComposeSurfaces(hw, dirtyRegion); + engine.endGroup(); + } // update the swap region and clear the dirty region hw->swapRegion.orSelf(dirtyRegion); @@ -1530,33 +1700,29 @@ void SurfaceFlinger::doDisplayComposition(const sp<const DisplayDevice>& hw, void SurfaceFlinger::doComposeSurfaces(const sp<const DisplayDevice>& hw, const Region& dirty) { + RenderEngine& engine(getRenderEngine()); const int32_t id = hw->getHwcDisplayId(); HWComposer& hwc(getHwComposer()); HWComposer::LayerListIterator cur = hwc.begin(id); const HWComposer::LayerListIterator end = hwc.end(id); - const bool hasGlesComposition = hwc.hasGlesComposition(id) || (cur==end); + bool hasGlesComposition = hwc.hasGlesComposition(id); if (hasGlesComposition) { - if (!DisplayDevice::makeCurrent(mEGLDisplay, hw, mEGLContext)) { + if (!hw->makeCurrent(mEGLDisplay, mEGLContext)) { ALOGW("DisplayDevice::makeCurrent failed. Aborting surface composition for display %s", hw->getDisplayName().string()); return; } - // set the frame buffer - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - // Never touch the framebuffer if we don't have any framebuffer layers const bool hasHwcComposition = hwc.hasHwcComposition(id); if (hasHwcComposition) { // when using overlays, we assume a fully transparent framebuffer // NOTE: we could reduce how much we need to clear, for instance // remove where there are opaque FB layers. however, on some - // GPUs doing a "clean slate" glClear might be more efficient. + // GPUs doing a "clean slate" clear might be more efficient. // We'll revisit later if needed. - glClearColor(0, 0, 0, 0); - glClear(GL_COLOR_BUFFER_BIT); + engine.clearWithColor(0, 0, 0, 0); } else { // we start with the whole screen area const Region bounds(hw->getBounds()); @@ -1589,11 +1755,11 @@ void SurfaceFlinger::doComposeSurfaces(const sp<const DisplayDevice>& hw, const // scissor doesn't match the screen's dimensions, so we // need to clear everything outside of it and enable // the GL scissor so we don't draw anything where we shouldn't - const GLint height = hw->getHeight(); - glScissor(scissor.left, height - scissor.bottom, - scissor.getWidth(), scissor.getHeight()); + // enable scissor for this frame - glEnable(GL_SCISSOR_TEST); + const uint32_t height = hw->getHeight(); + engine.setScissor(scissor.left, height - scissor.bottom, + scissor.getWidth(), scissor.getHeight()); } } } @@ -1613,9 +1779,10 @@ void SurfaceFlinger::doComposeSurfaces(const sp<const DisplayDevice>& hw, const if (!clip.isEmpty()) { switch (cur->getCompositionType()) { case HWC_OVERLAY: { + const Layer::State& state(layer->getDrawingState()); if ((cur->getHints() & HWC_HINT_CLEAR_FB) && i - && layer->isOpaque() + && layer->isOpaque() && (state.alpha == 0xFF) && hasGlesComposition) { // never clear the very first layer since we're // guaranteed the FB is already cleared @@ -1650,31 +1817,13 @@ void SurfaceFlinger::doComposeSurfaces(const sp<const DisplayDevice>& hw, const } // disable scissor at the end of the frame - glDisable(GL_SCISSOR_TEST); + engine.disableScissor(); } -void SurfaceFlinger::drawWormhole(const sp<const DisplayDevice>& hw, - const Region& region) const -{ - glDisable(GL_TEXTURE_EXTERNAL_OES); - glDisable(GL_TEXTURE_2D); - glDisable(GL_BLEND); - glColor4f(0,0,0,0); - +void SurfaceFlinger::drawWormhole(const sp<const DisplayDevice>& hw, const Region& region) const { const int32_t height = hw->getHeight(); - Region::const_iterator it = region.begin(); - Region::const_iterator const end = region.end(); - while (it != end) { - const Rect& r = *it++; - GLfloat vertices[][2] = { - { (GLfloat) r.left, (GLfloat) (height - r.top) }, - { (GLfloat) r.left, (GLfloat) (height - r.bottom) }, - { (GLfloat) r.right, (GLfloat) (height - r.bottom) }, - { (GLfloat) r.right, (GLfloat) (height - r.top) } - }; - glVertexPointer(2, GL_FLOAT, 0, vertices); - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - } + RenderEngine& engine(getRenderEngine()); + engine.fillRegionWithColor(region, height, 0, 0, 0, 0); } void SurfaceFlinger::addClientLayer(const sp<Client>& client, @@ -1691,8 +1840,7 @@ void SurfaceFlinger::addClientLayer(const sp<Client>& client, mGraphicBufferProducerList.add(gbc->asBinder()); } -status_t SurfaceFlinger::removeLayer(const sp<Layer>& layer) -{ +status_t SurfaceFlinger::removeLayer(const sp<Layer>& layer) { Mutex::Autolock _l(mStateLock); ssize_t index = mCurrentState.layersSortedByZ.remove(layer); if (index >= 0) { @@ -1704,18 +1852,15 @@ status_t SurfaceFlinger::removeLayer(const sp<Layer>& layer) return status_t(index); } -uint32_t SurfaceFlinger::peekTransactionFlags(uint32_t flags) -{ +uint32_t SurfaceFlinger::peekTransactionFlags(uint32_t flags) { return android_atomic_release_load(&mTransactionFlags); } -uint32_t SurfaceFlinger::getTransactionFlags(uint32_t flags) -{ +uint32_t SurfaceFlinger::getTransactionFlags(uint32_t flags) { return android_atomic_and(~flags, &mTransactionFlags) & flags; } -uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags) -{ +uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags) { uint32_t old = android_atomic_or(flags, &mTransactionFlags); if ((old & flags)==0) { // wake the server up signalTransaction(); @@ -2033,6 +2178,10 @@ void SurfaceFlinger::onInitializeDisplays() { displays.add(d); setTransactionState(state, displays, 0); onScreenAcquired(getDefaultDisplayDevice()); + + const nsecs_t period = + getHwComposer().getRefreshPeriod(HWC_DISPLAY_PRIMARY); + mAnimFrameTracker.setDisplayRefreshPeriod(period); } void SurfaceFlinger::initializeDisplays() { @@ -2060,13 +2209,15 @@ void SurfaceFlinger::onScreenAcquired(const sp<const DisplayDevice>& hw) { hw->acquireScreen(); int32_t type = hw->getDisplayType(); - if (type < DisplayDevice::NUM_DISPLAY_TYPES) { + if (type < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) { // built-in display, tell the HWC getHwComposer().acquire(type); if (type == DisplayDevice::DISPLAY_PRIMARY) { // FIXME: eventthread only knows about the main display right now mEventThread->onScreenAcquired(); + + resyncToHardwareVsync(true); } } mVisibleRegionsDirty = true; @@ -2082,8 +2233,10 @@ void SurfaceFlinger::onScreenReleased(const sp<const DisplayDevice>& hw) { hw->releaseScreen(); int32_t type = hw->getDisplayType(); - if (type < DisplayDevice::NUM_DISPLAY_TYPES) { + if (type < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) { if (type == DisplayDevice::DISPLAY_PRIMARY) { + disableHardwareVsync(true); // also cancels any in-progress resync + // FIXME: eventthread only knows about the main display right now mEventThread->onScreenReleased(); } @@ -2106,7 +2259,7 @@ void SurfaceFlinger::unblank(const sp<IBinder>& display) { const sp<DisplayDevice> hw(mFlinger.getDisplayDevice(mDisplay)); if (hw == NULL) { ALOGE("Attempt to unblank null display %p", mDisplay.get()); - } else if (hw->getDisplayType() >= DisplayDevice::NUM_DISPLAY_TYPES) { + } else if (hw->getDisplayType() >= DisplayDevice::DISPLAY_VIRTUAL) { ALOGW("Attempt to unblank virtual display"); } else { mFlinger.onScreenAcquired(hw); @@ -2129,7 +2282,7 @@ void SurfaceFlinger::blank(const sp<IBinder>& display) { const sp<DisplayDevice> hw(mFlinger.getDisplayDevice(mDisplay)); if (hw == NULL) { ALOGE("Attempt to blank null display %p", mDisplay.get()); - } else if (hw->getDisplayType() >= DisplayDevice::NUM_DISPLAY_TYPES) { + } else if (hw->getDisplayType() >= DisplayDevice::DISPLAY_VIRTUAL) { ALOGW("Attempt to blank virtual display"); } else { mFlinger.onScreenReleased(hw); @@ -2145,19 +2298,15 @@ void SurfaceFlinger::blank(const sp<IBinder>& display) { status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args) { - const size_t SIZE = 4096; - char buffer[SIZE]; String8 result; - IPCThreadState* ipc = IPCThreadState::self(); const int pid = ipc->getCallingPid(); const int uid = ipc->getCallingUid(); if ((uid != AID_SHELL) && !PermissionCache::checkPermission(sDump, pid, uid)) { - snprintf(buffer, SIZE, "Permission Denial: " + result.appendFormat("Permission Denial: " "can't dump SurfaceFlinger from pid=%d, uid=%d\n", pid, uid); - result.append(buffer); } else { // Try to get the main lock, but don't insist if we can't // (this would indicate SF is stuck, but we want to be able to @@ -2168,10 +2317,9 @@ status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args) } const bool locked(retry >= 0); if (!locked) { - snprintf(buffer, SIZE, + result.append( "SurfaceFlinger appears to be unresponsive, " "dumping anyways (no locks held)\n"); - result.append(buffer); } bool dumpAll = true; @@ -2181,27 +2329,27 @@ status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args) if ((index < numArgs) && (args[index] == String16("--list"))) { index++; - listLayersLocked(args, index, result, buffer, SIZE); + listLayersLocked(args, index, result); dumpAll = false; } if ((index < numArgs) && (args[index] == String16("--latency"))) { index++; - dumpStatsLocked(args, index, result, buffer, SIZE); + dumpStatsLocked(args, index, result); dumpAll = false; } if ((index < numArgs) && (args[index] == String16("--latency-clear"))) { index++; - clearStatsLocked(args, index, result, buffer, SIZE); + clearStatsLocked(args, index, result); dumpAll = false; } } if (dumpAll) { - dumpAllLocked(result, buffer, SIZE); + dumpAllLocked(args, index, result); } if (locked) { @@ -2213,19 +2361,18 @@ status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args) } void SurfaceFlinger::listLayersLocked(const Vector<String16>& args, size_t& index, - String8& result, char* buffer, size_t SIZE) const + String8& result) const { const LayerVector& currentLayers = mCurrentState.layersSortedByZ; const size_t count = currentLayers.size(); for (size_t i=0 ; i<count ; i++) { const sp<Layer>& layer(currentLayers[i]); - snprintf(buffer, SIZE, "%s\n", layer->getName().string()); - result.append(buffer); + result.appendFormat("%s\n", layer->getName().string()); } } void SurfaceFlinger::dumpStatsLocked(const Vector<String16>& args, size_t& index, - String8& result, char* buffer, size_t SIZE) const + String8& result) const { String8 name; if (index < args.size()) { @@ -2245,14 +2392,14 @@ void SurfaceFlinger::dumpStatsLocked(const Vector<String16>& args, size_t& index for (size_t i=0 ; i<count ; i++) { const sp<Layer>& layer(currentLayers[i]); if (name == layer->getName()) { - layer->dumpStats(result, buffer, SIZE); + layer->dumpStats(result); } } } } void SurfaceFlinger::clearStatsLocked(const Vector<String16>& args, size_t& index, - String8& result, char* buffer, size_t SIZE) + String8& result) { String8 name; if (index < args.size()) { @@ -2272,6 +2419,19 @@ void SurfaceFlinger::clearStatsLocked(const Vector<String16>& args, size_t& inde mAnimFrameTracker.clear(); } +// This should only be called from the main thread. Otherwise it would need +// the lock and should use mCurrentState rather than mDrawingState. +void SurfaceFlinger::logFrameStats() { + const LayerVector& drawingLayers = mDrawingState.layersSortedByZ; + const size_t count = drawingLayers.size(); + for (size_t i=0 ; i<count ; i++) { + const sp<Layer>& layer(drawingLayers[i]); + layer->logFrameStats(); + } + + mAnimFrameTracker.logAndResetStats(String8("<win-anim>")); +} + /*static*/ void SurfaceFlinger::appendSfConfigString(String8& result) { static const char* config = @@ -2292,9 +2452,18 @@ void SurfaceFlinger::clearStatsLocked(const Vector<String16>& args, size_t& inde result.append(config); } -void SurfaceFlinger::dumpAllLocked( - String8& result, char* buffer, size_t SIZE) const +void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index, + String8& result) const { + bool colorize = false; + if (index < args.size() + && (args[index] == String16("--color"))) { + colorize = true; + index++; + } + + Colorizer colorizer(colorize); + // figure out if we're stuck somewhere const nsecs_t now = systemTime(); const nsecs_t inSwapBuffers(mDebugInSwapBuffers); @@ -2305,13 +2474,18 @@ void SurfaceFlinger::dumpAllLocked( /* * Dump library configuration. */ + + colorizer.bold(result); result.append("Build configuration:"); + colorizer.reset(result); appendSfConfigString(result); appendUiConfigString(result); appendGuiConfigString(result); result.append("\n"); + colorizer.bold(result); result.append("Sync configuration: "); + colorizer.reset(result); result.append(SyncFeatures::getInstance().toString()); result.append("\n"); @@ -2320,56 +2494,50 @@ void SurfaceFlinger::dumpAllLocked( */ const LayerVector& currentLayers = mCurrentState.layersSortedByZ; const size_t count = currentLayers.size(); - snprintf(buffer, SIZE, "Visible layers (count = %d)\n", count); - result.append(buffer); + colorizer.bold(result); + result.appendFormat("Visible layers (count = %d)\n", count); + colorizer.reset(result); for (size_t i=0 ; i<count ; i++) { const sp<Layer>& layer(currentLayers[i]); - layer->dump(result, buffer, SIZE); + layer->dump(result, colorizer); } /* * Dump Display state */ - snprintf(buffer, SIZE, "Displays (%d entries)\n", mDisplays.size()); - result.append(buffer); + colorizer.bold(result); + result.appendFormat("Displays (%d entries)\n", mDisplays.size()); + colorizer.reset(result); for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) { const sp<const DisplayDevice>& hw(mDisplays[dpy]); - hw->dump(result, buffer, SIZE); + hw->dump(result); } /* * Dump SurfaceFlinger global state */ - snprintf(buffer, SIZE, "SurfaceFlinger global state:\n"); - result.append(buffer); + colorizer.bold(result); + result.append("SurfaceFlinger global state:\n"); + colorizer.reset(result); HWComposer& hwc(getHwComposer()); sp<const DisplayDevice> hw(getDefaultDisplayDevice()); - const GLExtensions& extensions(GLExtensions::getInstance()); - snprintf(buffer, SIZE, "EGL implementation : %s\n", + colorizer.bold(result); + result.appendFormat("EGL implementation : %s\n", eglQueryStringImplementationANDROID(mEGLDisplay, EGL_VERSION)); - result.append(buffer); - snprintf(buffer, SIZE, "%s\n", + colorizer.reset(result); + result.appendFormat("%s\n", eglQueryStringImplementationANDROID(mEGLDisplay, EGL_EXTENSIONS)); - result.append(buffer); - snprintf(buffer, SIZE, "GLES: %s, %s, %s\n", - extensions.getVendor(), - extensions.getRenderer(), - extensions.getVersion()); - result.append(buffer); - snprintf(buffer, SIZE, "%s\n", extensions.getExtension()); - result.append(buffer); + mRenderEngine->dump(result); hw->undefinedRegion.dump(result, "undefinedRegion"); - snprintf(buffer, SIZE, - " orientation=%d, canDraw=%d\n", + result.appendFormat(" orientation=%d, canDraw=%d\n", hw->getOrientation(), hw->canDraw()); - result.append(buffer); - snprintf(buffer, SIZE, + result.appendFormat( " last eglSwapBuffers() time: %f us\n" " last transaction time : %f us\n" " transaction-flags : %08x\n" @@ -2387,31 +2555,28 @@ void SurfaceFlinger::dumpAllLocked( hwc.getDpiY(HWC_DISPLAY_PRIMARY), mEGLNativeVisualId, !mGpuToCpuSupported); - result.append(buffer); - snprintf(buffer, SIZE, " eglSwapBuffers time: %f us\n", + result.appendFormat(" eglSwapBuffers time: %f us\n", inSwapBuffersDuration/1000.0); - result.append(buffer); - snprintf(buffer, SIZE, " transaction time: %f us\n", + result.appendFormat(" transaction time: %f us\n", inTransactionDuration/1000.0); - result.append(buffer); /* * VSYNC state */ - mEventThread->dump(result, buffer, SIZE); + mEventThread->dump(result); /* * Dump HWComposer state */ - snprintf(buffer, SIZE, "h/w composer state:\n"); - result.append(buffer); - snprintf(buffer, SIZE, " h/w composer %s and %s\n", + colorizer.bold(result); + result.append("h/w composer state:\n"); + colorizer.reset(result); + result.appendFormat(" h/w composer %s and %s\n", hwc.initCheck()==NO_ERROR ? "present" : "not present", - (mDebugDisableHWC || mDebugRegion) ? "disabled" : "enabled"); - result.append(buffer); - hwc.dump(result, buffer, SIZE); + (mDebugDisableHWC || mDebugRegion || mDaltonize) ? "disabled" : "enabled"); + hwc.dump(result); /* * Dump gralloc state @@ -2555,6 +2720,24 @@ status_t SurfaceFlinger::onTransact( Mutex::Autolock _l(mStateLock); sp<const DisplayDevice> hw(getDefaultDisplayDevice()); reply->writeInt32(hw->getPageFlipCount()); + return NO_ERROR; + } + case 1014: { + // daltonize + n = data.readInt32(); + switch (n % 10) { + case 1: mDaltonizer.setType(Daltonizer::protanomaly); break; + case 2: mDaltonizer.setType(Daltonizer::deuteranomaly); break; + case 3: mDaltonizer.setType(Daltonizer::tritanomaly); break; + } + if (n >= 10) { + mDaltonizer.setMode(Daltonizer::correction); + } else { + mDaltonizer.setMode(Daltonizer::simulation); + } + mDaltonize = n > 0; + invalidateHwcGeometry(); + repaintEverything(); } return NO_ERROR; } @@ -2646,16 +2829,17 @@ public: } void exit(status_t result) { + this->result = result; exitPending = true; looper->sendMessage(this, Message(MSG_EXIT)); } }; + status_t SurfaceFlinger::captureScreen(const sp<IBinder>& display, const sp<IGraphicBufferProducer>& producer, uint32_t reqWidth, uint32_t reqHeight, - uint32_t minLayerZ, uint32_t maxLayerZ, - bool isCpuConsumer) { + uint32_t minLayerZ, uint32_t maxLayerZ) { if (CC_UNLIKELY(display == 0)) return BAD_VALUE; @@ -2663,6 +2847,17 @@ status_t SurfaceFlinger::captureScreen(const sp<IBinder>& display, if (CC_UNLIKELY(producer == 0)) return BAD_VALUE; + // if we have secure windows on this display, never allow the screen capture + // unless the producer interface is local (i.e.: we can take a screenshot for + // ourselves). + if (!producer->asBinder()->localBinder()) { + Mutex::Autolock _l(mStateLock); + sp<const DisplayDevice> hw(getDisplayDevice(display)); + if (hw->getSecureLayerVisible()) { + ALOGW("FB is protected: PERMISSION_DENIED"); + return PERMISSION_DENIED; + } + } class MessageCaptureScreen : public MessageBase { SurfaceFlinger* flinger; @@ -2670,18 +2865,16 @@ status_t SurfaceFlinger::captureScreen(const sp<IBinder>& display, sp<IGraphicBufferProducer> producer; uint32_t reqWidth, reqHeight; uint32_t minLayerZ,maxLayerZ; - bool useReadPixels; status_t result; public: MessageCaptureScreen(SurfaceFlinger* flinger, const sp<IBinder>& display, const sp<IGraphicBufferProducer>& producer, uint32_t reqWidth, uint32_t reqHeight, - uint32_t minLayerZ, uint32_t maxLayerZ, bool useReadPixels) + uint32_t minLayerZ, uint32_t maxLayerZ) : flinger(flinger), display(display), producer(producer), reqWidth(reqWidth), reqHeight(reqHeight), minLayerZ(minLayerZ), maxLayerZ(maxLayerZ), - useReadPixels(useReadPixels), result(PERMISSION_DENIED) { } @@ -2691,13 +2884,8 @@ status_t SurfaceFlinger::captureScreen(const sp<IBinder>& display, virtual bool handler() { Mutex::Autolock _l(flinger->mStateLock); sp<const DisplayDevice> hw(flinger->getDisplayDevice(display)); - if (!useReadPixels) { - result = flinger->captureScreenImplLocked(hw, - producer, reqWidth, reqHeight, minLayerZ, maxLayerZ); - } else { - result = flinger->captureScreenImplCpuConsumerLocked(hw, - producer, reqWidth, reqHeight, minLayerZ, maxLayerZ); - } + result = flinger->captureScreenImplLocked(hw, + producer, reqWidth, reqHeight, minLayerZ, maxLayerZ); static_cast<GraphicProducerWrapper*>(producer->asBinder().get())->exit(result); return true; } @@ -2710,25 +2898,6 @@ status_t SurfaceFlinger::captureScreen(const sp<IBinder>& display, // scheduled at this time, this will end-up being a no-op as well. mEventQueue.invalidateTransactionNow(); - bool useReadPixels = false; - if (isCpuConsumer) { - bool formatSupportedBytBitmap = - (mEGLNativeVisualId == HAL_PIXEL_FORMAT_RGBA_8888) || - (mEGLNativeVisualId == HAL_PIXEL_FORMAT_RGBX_8888); - if (formatSupportedBytBitmap == false) { - // the pixel format we have is not compatible with - // Bitmap.java, which is the likely client of this API, - // so we just revert to glReadPixels() in that case. - useReadPixels = true; - } - if (mGpuToCpuSupported == false) { - // When we know the GL->CPU path works, we can call - // captureScreenImplLocked() directly, instead of using the - // glReadPixels() workaround. - useReadPixels = true; - } - } - // this creates a "fake" BBinder which will serve as a "fake" remote // binder to receive the marshaled calls and forward them to the // real remote (a BpGraphicBufferProducer) @@ -2738,8 +2907,7 @@ status_t SurfaceFlinger::captureScreen(const sp<IBinder>& display, // which does the marshaling work forwards to our "fake remote" above. sp<MessageBase> msg = new MessageCaptureScreen(this, display, IGraphicBufferProducer::asInterface( wrapper ), - reqWidth, reqHeight, minLayerZ, maxLayerZ, - useReadPixels); + reqWidth, reqHeight, minLayerZ, maxLayerZ); status_t res = postMessageAsync(msg); if (res == NO_ERROR) { @@ -2756,37 +2924,28 @@ void SurfaceFlinger::renderScreenImplLocked( bool yswap) { ATRACE_CALL(); + RenderEngine& engine(getRenderEngine()); // get screen geometry const uint32_t hw_w = hw->getWidth(); const uint32_t hw_h = hw->getHeight(); - const bool filtering = reqWidth != hw_w || reqWidth != hw_h; // make sure to clear all GL error flags - while ( glGetError() != GL_NO_ERROR ) ; + engine.checkErrors(); // set-up our viewport - glViewport(0, 0, reqWidth, reqHeight); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - if (yswap) glOrthof(0, hw_w, hw_h, 0, 0, 1); - else glOrthof(0, hw_w, 0, hw_h, 0, 1); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); + engine.setViewportAndProjection(reqWidth, reqHeight, hw_w, hw_h, yswap); + engine.disableTexturing(); // redraw the screen entirely... - glDisable(GL_SCISSOR_TEST); - glClearColor(0,0,0,1); - glClear(GL_COLOR_BUFFER_BIT); - glDisable(GL_TEXTURE_EXTERNAL_OES); - glDisable(GL_TEXTURE_2D); + engine.clearWithColor(0, 0, 0, 1); const LayerVector& layers( mDrawingState.layersSortedByZ ); const size_t count = layers.size(); for (size_t i=0 ; i<count ; ++i) { const sp<Layer>& layer(layers[i]); - const Layer::State& state(layer->drawingState()); + const Layer::State& state(layer->getDrawingState()); if (state.layerStack == hw->getLayerStack()) { if (state.z >= minLayerZ && state.z <= maxLayerZ) { if (layer->isVisible()) { @@ -2800,6 +2959,7 @@ void SurfaceFlinger::renderScreenImplLocked( // compositionComplete is needed for older driver hw->compositionComplete(); + hw->setViewportAndProjection(); } @@ -2815,82 +2975,6 @@ status_t SurfaceFlinger::captureScreenImplLocked( const uint32_t hw_w = hw->getWidth(); const uint32_t hw_h = hw->getHeight(); - // if we have secure windows on this display, never allow the screen capture - if (hw->getSecureLayerVisible()) { - ALOGW("FB is protected: PERMISSION_DENIED"); - return PERMISSION_DENIED; - } - - if ((reqWidth > hw_w) || (reqHeight > hw_h)) { - ALOGE("size mismatch (%d, %d) > (%d, %d)", - reqWidth, reqHeight, hw_w, hw_h); - return BAD_VALUE; - } - - reqWidth = (!reqWidth) ? hw_w : reqWidth; - reqHeight = (!reqHeight) ? hw_h : reqHeight; - - // Create a surface to render into - sp<Surface> surface = new Surface(producer); - ANativeWindow* const window = surface.get(); - - // set the buffer size to what the user requested - native_window_set_buffers_user_dimensions(window, reqWidth, reqHeight); - - // and create the corresponding EGLSurface - EGLSurface eglSurface = eglCreateWindowSurface( - mEGLDisplay, mEGLConfig, window, NULL); - if (eglSurface == EGL_NO_SURFACE) { - ALOGE("captureScreenImplLocked: eglCreateWindowSurface() failed 0x%4x", - eglGetError()); - return BAD_VALUE; - } - - if (!eglMakeCurrent(mEGLDisplay, eglSurface, eglSurface, mEGLContext)) { - ALOGE("captureScreenImplLocked: eglMakeCurrent() failed 0x%4x", - eglGetError()); - eglDestroySurface(mEGLDisplay, eglSurface); - return BAD_VALUE; - } - - renderScreenImplLocked(hw, reqWidth, reqHeight, minLayerZ, maxLayerZ, false); - - // and finishing things up... - if (eglSwapBuffers(mEGLDisplay, eglSurface) != EGL_TRUE) { - ALOGE("captureScreenImplLocked: eglSwapBuffers() failed 0x%4x", - eglGetError()); - eglDestroySurface(mEGLDisplay, eglSurface); - return BAD_VALUE; - } - - eglDestroySurface(mEGLDisplay, eglSurface); - - return NO_ERROR; -} - - -status_t SurfaceFlinger::captureScreenImplCpuConsumerLocked( - const sp<const DisplayDevice>& hw, - const sp<IGraphicBufferProducer>& producer, - uint32_t reqWidth, uint32_t reqHeight, - uint32_t minLayerZ, uint32_t maxLayerZ) -{ - ATRACE_CALL(); - - if (!GLExtensions::getInstance().haveFramebufferObject()) { - return INVALID_OPERATION; - } - - // get screen geometry - const uint32_t hw_w = hw->getWidth(); - const uint32_t hw_h = hw->getHeight(); - - // if we have secure windows on this display, never allow the screen capture - if (hw->getSecureLayerVisible()) { - ALOGW("FB is protected: PERMISSION_DENIED"); - return PERMISSION_DENIED; - } - if ((reqWidth > hw_w) || (reqHeight > hw_h)) { ALOGE("size mismatch (%d, %d) > (%d, %d)", reqWidth, reqHeight, hw_w, hw_h); @@ -2900,72 +2984,122 @@ status_t SurfaceFlinger::captureScreenImplCpuConsumerLocked( reqWidth = (!reqWidth) ? hw_w : reqWidth; reqHeight = (!reqHeight) ? hw_h : reqHeight; - GLuint tname; - glGenRenderbuffersOES(1, &tname); - glBindRenderbufferOES(GL_RENDERBUFFER_OES, tname); - glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_RGBA8_OES, reqWidth, reqHeight); + // create a surface (because we're a producer, and we need to + // dequeue/queue a buffer) + sp<Surface> sur = new Surface(producer, false); + ANativeWindow* window = sur.get(); - // create a FBO - GLuint name; - glGenFramebuffersOES(1, &name); - glBindFramebufferOES(GL_FRAMEBUFFER_OES, name); - glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, - GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, tname); + status_t result = NO_ERROR; + if (native_window_api_connect(window, NATIVE_WINDOW_API_EGL) == NO_ERROR) { + uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN | + GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE; + + int err = 0; + err = native_window_set_buffers_dimensions(window, reqWidth, reqHeight); + err |= native_window_set_scaling_mode(window, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW); + err |= native_window_set_buffers_format(window, HAL_PIXEL_FORMAT_RGBA_8888); + err |= native_window_set_usage(window, usage); + + if (err == NO_ERROR) { + ANativeWindowBuffer* buffer; + /* TODO: Once we have the sync framework everywhere this can use + * server-side waits on the fence that dequeueBuffer returns. + */ + result = native_window_dequeue_buffer_and_wait(window, &buffer); + if (result == NO_ERROR) { + // create an EGLImage from the buffer so we can later + // turn it into a texture + EGLImageKHR image = eglCreateImageKHR(mEGLDisplay, EGL_NO_CONTEXT, + EGL_NATIVE_BUFFER_ANDROID, buffer, NULL); + if (image != EGL_NO_IMAGE_KHR) { + // this binds the given EGLImage as a framebuffer for the + // duration of this scope. + RenderEngine::BindImageAsFramebuffer imageBond(getRenderEngine(), image); + if (imageBond.getStatus() == NO_ERROR) { + // this will in fact render into our dequeued buffer + // via an FBO, which means we didn't have to create + // an EGLSurface and therefore we're not + // dependent on the context's EGLConfig. + renderScreenImplLocked(hw, reqWidth, reqHeight, + minLayerZ, maxLayerZ, true); + + // Create a sync point and wait on it, so we know the buffer is + // ready before we pass it along. We can't trivially call glFlush(), + // so we use a wait flag instead. + // TODO: pass a sync fd to queueBuffer() and let the consumer wait. + EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_FENCE_KHR, NULL); + if (sync != EGL_NO_SYNC_KHR) { + EGLint result = eglClientWaitSyncKHR(mEGLDisplay, sync, + EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, 2000000000 /*2 sec*/); + EGLint eglErr = eglGetError(); + eglDestroySyncKHR(mEGLDisplay, sync); + if (result == EGL_TIMEOUT_EXPIRED_KHR) { + ALOGW("captureScreen: fence wait timed out"); + } else { + ALOGW_IF(eglErr != EGL_SUCCESS, + "captureScreen: error waiting on EGL fence: %#x", eglErr); + } + } else { + ALOGW("captureScreen: error creating EGL fence: %#x", eglGetError()); + // not fatal + } - GLenum status = glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES); + if (DEBUG_SCREENSHOTS) { + uint32_t* pixels = new uint32_t[reqWidth*reqHeight]; + getRenderEngine().readPixels(0, 0, reqWidth, reqHeight, pixels); + checkScreenshot(reqWidth, reqHeight, reqWidth, pixels, + hw, minLayerZ, maxLayerZ); + delete [] pixels; + } - status_t result = NO_ERROR; - if (status == GL_FRAMEBUFFER_COMPLETE_OES) { - - renderScreenImplLocked(hw, reqWidth, reqHeight, minLayerZ, maxLayerZ, true); - - // Below we render the screenshot into the - // CpuConsumer using glReadPixels from our FBO. - // Some older drivers don't support the GL->CPU path so we - // have to wrap it with a CPU->CPU path, which is what - // glReadPixels essentially is. - - sp<Surface> sur = new Surface(producer); - ANativeWindow* window = sur.get(); - - if (native_window_api_connect(window, NATIVE_WINDOW_API_CPU) == NO_ERROR) { - int err = 0; - err = native_window_set_buffers_dimensions(window, reqWidth, reqHeight); - err |= native_window_set_buffers_format(window, HAL_PIXEL_FORMAT_RGBA_8888); - err |= native_window_set_usage(window, - GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN); - - if (err == NO_ERROR) { - ANativeWindowBuffer* buffer; - if (native_window_dequeue_buffer_and_wait(window, &buffer) == NO_ERROR) { - sp<GraphicBuffer> buf = static_cast<GraphicBuffer*>(buffer); - void* vaddr; - if (buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, &vaddr) == NO_ERROR) { - glReadPixels(0, 0, buffer->stride, reqHeight, - GL_RGBA, GL_UNSIGNED_BYTE, vaddr); - buf->unlock(); + } else { + ALOGE("got GL_FRAMEBUFFER_COMPLETE_OES error while taking screenshot"); + result = INVALID_OPERATION; } - window->queueBuffer(window, buffer, -1); + // destroy our image + eglDestroyImageKHR(mEGLDisplay, image); + } else { + result = BAD_VALUE; } + window->queueBuffer(window, buffer, -1); } - native_window_api_disconnect(window, NATIVE_WINDOW_API_CPU); + } else { + result = BAD_VALUE; } - - } else { - ALOGE("got GL_FRAMEBUFFER_COMPLETE_OES while taking screenshot"); - result = INVALID_OPERATION; + native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL); } - // back to main framebuffer - glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0); - glDeleteRenderbuffersOES(1, &tname); - glDeleteFramebuffersOES(1, &name); - - DisplayDevice::setViewportAndProjection(hw); - return result; } +void SurfaceFlinger::checkScreenshot(size_t w, size_t s, size_t h, void const* vaddr, + const sp<const DisplayDevice>& hw, uint32_t minLayerZ, uint32_t maxLayerZ) { + if (DEBUG_SCREENSHOTS) { + for (size_t y=0 ; y<h ; y++) { + uint32_t const * p = (uint32_t const *)vaddr + y*s; + for (size_t x=0 ; x<w ; x++) { + if (p[x] != 0xFF000000) return; + } + } + ALOGE("*** we just took a black screenshot ***\n" + "requested minz=%d, maxz=%d, layerStack=%d", + minLayerZ, maxLayerZ, hw->getLayerStack()); + const LayerVector& layers( mDrawingState.layersSortedByZ ); + const size_t count = layers.size(); + for (size_t i=0 ; i<count ; ++i) { + const sp<Layer>& layer(layers[i]); + const Layer::State& state(layer->getDrawingState()); + const bool visible = (state.layerStack == hw->getLayerStack()) + && (state.z >= minLayerZ && state.z <= maxLayerZ) + && (layer->isVisible()); + ALOGE("%c index=%d, name=%s, layerStack=%d, z=%d, visible=%d, flags=%x, alpha=%x", + visible ? '+' : '-', + i, layer->getName().string(), state.layerStack, state.z, + layer->isVisible(), state.flags, state.alpha); + } + } +} + // --------------------------------------------------------------------------- SurfaceFlinger::LayerVector::LayerVector() { @@ -2982,13 +3116,13 @@ int SurfaceFlinger::LayerVector::do_compare(const void* lhs, const sp<Layer>& l(*reinterpret_cast<const sp<Layer>*>(lhs)); const sp<Layer>& r(*reinterpret_cast<const sp<Layer>*>(rhs)); - uint32_t ls = l->currentState().layerStack; - uint32_t rs = r->currentState().layerStack; + uint32_t ls = l->getCurrentState().layerStack; + uint32_t rs = r->getCurrentState().layerStack; if (ls != rs) return ls - rs; - uint32_t lz = l->currentState().z; - uint32_t rz = r->currentState().z; + uint32_t lz = l->getCurrentState().z; + uint32_t rz = r->getCurrentState().z; if (lz != rz) return lz - rz; @@ -3010,3 +3144,12 @@ SurfaceFlinger::DisplayDeviceState::DisplayDeviceState(DisplayDevice::DisplayTyp // --------------------------------------------------------------------------- }; // namespace android + + +#if defined(__gl_h_) +#error "don't include gl/gl.h in this file" +#endif + +#if defined(__gl2_h_) +#error "don't include gl2/gl2.h in this file" +#endif diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 739099c..f08e66a 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -21,7 +21,10 @@ #include <sys/types.h> #include <EGL/egl.h> -#include <GLES/gl.h> + +/* + * NOTE: Make sure this file doesn't include anything from <gl/ > or <gl2/ > + */ #include <cutils/compiler.h> @@ -32,7 +35,6 @@ #include <utils/SortedVector.h> #include <utils/threads.h> -#include <binder/BinderService.h> #include <binder/IMemory.h> #include <ui/PixelFormat.h> @@ -46,10 +48,12 @@ #include "Barrier.h" #include "DisplayDevice.h" +#include "DispSync.h" #include "FrameTracker.h" #include "MessageQueue.h" #include "DisplayHardware/HWComposer.h" +#include "Effects/Daltonizer.h" namespace android { @@ -62,6 +66,8 @@ class IGraphicBufferAlloc; class Layer; class LayerDim; class Surface; +class RenderEngine; +class EventControlThread; // --------------------------------------------------------------------------- @@ -72,30 +78,32 @@ enum { eTransactionMask = 0x07 }; -class SurfaceFlinger : public BinderService<SurfaceFlinger>, - public BnSurfaceComposer, +class SurfaceFlinger : public BnSurfaceComposer, private IBinder::DeathRecipient, - private Thread, private HWComposer::EventHandler { public: - static char const* getServiceName() { + static char const* getServiceName() ANDROID_API { return "SurfaceFlinger"; } - SurfaceFlinger(); + SurfaceFlinger() ANDROID_API; + + // must be called before clients can connect + void init() ANDROID_API; + + // starts SurfaceFlinger main loop in the current thread + void run() ANDROID_API; enum { EVENT_VSYNC = HWC_EVENT_VSYNC }; // post an asynchronous message to the main thread - status_t postMessageAsync(const sp<MessageBase>& msg, nsecs_t reltime = 0, - uint32_t flags = 0); + status_t postMessageAsync(const sp<MessageBase>& msg, nsecs_t reltime = 0, uint32_t flags = 0); // post a synchronous message to the main thread - status_t postMessageSync(const sp<MessageBase>& msg, nsecs_t reltime = 0, - uint32_t flags = 0); + status_t postMessageSync(const sp<MessageBase>& msg, nsecs_t reltime = 0, uint32_t flags = 0); // force full composition on all displays void repaintEverything(); @@ -106,7 +114,7 @@ public: } // utility function to delete a texture on the main thread - void deleteTextureAsync(GLuint texture); + void deleteTextureAsync(uint32_t texture); // enable/disable h/w composer event // TODO: this should be made accessible only to EventThread @@ -121,12 +129,20 @@ public: // TODO: this should be made accessible only to HWComposer const Vector< sp<Layer> >& getLayerSortedByZForHwcDisplay(int id); + RenderEngine& getRenderEngine() const { + return *mRenderEngine; + } + private: friend class Client; friend class DisplayEventConnection; friend class Layer; friend class SurfaceTextureLayer; + // This value is specified in number of frames. Log frame stats at most + // every half hour. + enum { LOG_FRAME_STATS_PERIOD = 30*60*60 }; + // We're reference counted, never destroy SurfaceFlinger directly virtual ~SurfaceFlinger(); @@ -175,6 +191,7 @@ private: virtual sp<ISurfaceComposerClient> createConnection(); virtual sp<IGraphicBufferAlloc> createGraphicBufferAlloc(); virtual sp<IBinder> createDisplay(const String8& displayName, bool secure); + virtual void destroyDisplay(const sp<IBinder>& display); virtual sp<IBinder> getBuiltInDisplay(int32_t id); virtual void setTransactionState(const Vector<ComposerState>& state, const Vector<DisplayState>& displays, uint32_t flags); @@ -185,7 +202,7 @@ private: virtual status_t captureScreen(const sp<IBinder>& display, const sp<IGraphicBufferProducer>& producer, uint32_t reqWidth, uint32_t reqHeight, - uint32_t minLayerZ, uint32_t maxLayerZ, bool isCpuConsumer); + uint32_t minLayerZ, uint32_t maxLayerZ); // called when screen needs to turn off virtual void blank(const sp<IBinder>& display); // called when screen is turning back on @@ -198,10 +215,8 @@ private: virtual void binderDied(const wp<IBinder>& who); /* ------------------------------------------------------------------------ - * Thread interface + * RefBase interface */ - virtual bool threadLoop(); - virtual status_t readyToRun(); virtual void onFirstRef(); /* ------------------------------------------------------------------------ @@ -244,8 +259,7 @@ private: uint32_t peekTransactionFlags(uint32_t flags); uint32_t setTransactionFlags(uint32_t flags); void commitTransaction(); - uint32_t setClientStateLocked(const sp<Client>& client, - const layer_state_t& s); + uint32_t setClientStateLocked(const sp<Client>& client, const layer_state_t& s); uint32_t setDisplayStateLocked(const DisplayState& s); /* ------------------------------------------------------------------------ @@ -300,23 +314,15 @@ private: uint32_t reqWidth, uint32_t reqHeight, uint32_t minLayerZ, uint32_t maxLayerZ); - status_t captureScreenImplCpuConsumerLocked( - const sp<const DisplayDevice>& hw, - const sp<IGraphicBufferProducer>& producer, - uint32_t reqWidth, uint32_t reqHeight, - uint32_t minLayerZ, uint32_t maxLayerZ); - - /* ------------------------------------------------------------------------ * EGL */ static status_t selectConfigForAttribute(EGLDisplay dpy, EGLint const* attrs, EGLint attribute, EGLint value, EGLConfig* outConfig); - static EGLConfig selectEGLConfig(EGLDisplay disp, EGLint visualId); - static EGLContext createGLContext(EGLDisplay disp, EGLConfig config); - void initializeGL(EGLDisplay display); - uint32_t getMaxTextureSize() const; - uint32_t getMaxViewportDims() const; + static status_t selectEGLConfig(EGLDisplay disp, EGLint visualId, + EGLint renderableType, EGLConfig* config); + size_t getMaxTextureSize() const; + size_t getMaxViewportDims() const; /* ------------------------------------------------------------------------ * Display and layer stack management @@ -364,35 +370,37 @@ private: void setUpHWComposer(); void doComposition(); void doDebugFlashRegions(); - void doDisplayComposition(const sp<const DisplayDevice>& hw, - const Region& dirtyRegion); - void doComposeSurfaces(const sp<const DisplayDevice>& hw, - const Region& dirty); + void doDisplayComposition(const sp<const DisplayDevice>& hw, const Region& dirtyRegion); + void doComposeSurfaces(const sp<const DisplayDevice>& hw, const Region& dirty); void postFramebuffer(); - void drawWormhole(const sp<const DisplayDevice>& hw, - const Region& region) const; - GLuint getProtectedTexName() const { - return mProtectedTexName; - } + void drawWormhole(const sp<const DisplayDevice>& hw, const Region& region) const; /* ------------------------------------------------------------------------ * Display management */ + /* ------------------------------------------------------------------------ + * VSync + */ + void enableHardwareVsync(); + void disableHardwareVsync(bool makeUnavailable); + void resyncToHardwareVsync(bool makeAvailable); /* ------------------------------------------------------------------------ * Debugging & dumpsys */ - void listLayersLocked(const Vector<String16>& args, size_t& index, - String8& result, char* buffer, size_t SIZE) const; - void dumpStatsLocked(const Vector<String16>& args, size_t& index, - String8& result, char* buffer, size_t SIZE) const; - void clearStatsLocked(const Vector<String16>& args, size_t& index, - String8& result, char* buffer, size_t SIZE); - void dumpAllLocked(String8& result, char* buffer, size_t SIZE) const; + void listLayersLocked(const Vector<String16>& args, size_t& index, String8& result) const; + void dumpStatsLocked(const Vector<String16>& args, size_t& index, String8& result) const; + void clearStatsLocked(const Vector<String16>& args, size_t& index, String8& result); + void dumpAllLocked(const Vector<String16>& args, size_t& index, String8& result) const; bool startDdmConnection(); static void appendSfConfigString(String8& result); + void checkScreenshot(size_t w, size_t s, size_t h, void const* vaddr, + const sp<const DisplayDevice>& hw, + uint32_t minLayerZ, uint32_t maxLayerZ); + + void logFrameStats(); /* ------------------------------------------------------------------------ * Attributes @@ -416,17 +424,17 @@ private: // constant members (no synchronization needed for access) HWComposer* mHwc; - GLuint mProtectedTexName; + RenderEngine* mRenderEngine; nsecs_t mBootTime; bool mGpuToCpuSupported; sp<EventThread> mEventThread; - GLint mMaxViewportDims[2]; - GLint mMaxTextureSize; + sp<EventThread> mSFEventThread; + sp<EventControlThread> mEventControlThread; EGLContext mEGLContext; EGLConfig mEGLConfig; EGLDisplay mEGLDisplay; EGLint mEGLNativeVisualId; - sp<IBinder> mBuiltinDisplays[DisplayDevice::NUM_DISPLAY_TYPES]; + sp<IBinder> mBuiltinDisplays[DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES]; // Can only accessed from the main thread, these members // don't need synchronization @@ -452,21 +460,26 @@ private: // these are thread safe mutable MessageQueue mEventQueue; - mutable Barrier mReadyToRunBarrier; FrameTracker mAnimFrameTracker; + DispSync mPrimaryDispSync; // protected by mDestroyedLayerLock; mutable Mutex mDestroyedLayerLock; Vector<Layer const *> mDestroyedLayers; + // protected by mHWVsyncLock + Mutex mHWVsyncLock; + bool mPrimaryHWVsyncEnabled; + bool mHWVsyncAvailable; + /* ------------------------------------------------------------------------ * Feature prototyping */ - sp<IBinder> mExtDisplayToken; + Daltonizer mDaltonizer; + bool mDaltonize; }; -// --------------------------------------------------------------------------- }; // namespace android #endif // ANDROID_SURFACE_FLINGER_H diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.cpp b/services/surfaceflinger/SurfaceFlingerConsumer.cpp index 2869250..6dc093e 100644 --- a/services/surfaceflinger/SurfaceFlingerConsumer.cpp +++ b/services/surfaceflinger/SurfaceFlingerConsumer.cpp @@ -50,12 +50,12 @@ status_t SurfaceFlingerConsumer::updateTexImage(BufferRejecter* rejecter) // Acquire the next buffer. // In asynchronous mode the list is guaranteed to be one buffer // deep, while in synchronous mode we use the oldest buffer. - err = acquireBufferLocked(&item); + err = acquireBufferLocked(&item, computeExpectedPresent()); if (err != NO_ERROR) { if (err == BufferQueue::NO_BUFFER_AVAILABLE) { - // This variant of updateTexImage does not guarantee that the - // texture is bound, so no need to call glBindTexture. err = NO_ERROR; + } else if (err == BufferQueue::PRESENT_LATER) { + // return the error, without logging } else { ALOGE("updateTexImage: acquire failed: %s (%d)", strerror(-err), err); @@ -69,12 +69,12 @@ status_t SurfaceFlingerConsumer::updateTexImage(BufferRejecter* rejecter) // reject buffers which have the wrong size int buf = item.mBuf; if (rejecter && rejecter->reject(mSlots[buf].mGraphicBuffer, item)) { - releaseBufferLocked(buf, EGL_NO_SYNC_KHR); + releaseBufferLocked(buf, mSlots[buf].mGraphicBuffer, EGL_NO_SYNC_KHR); return NO_ERROR; } // Release the previous buffer. - err = releaseAndUpdateLocked(item); + err = updateAndReleaseLocked(item); if (err != NO_ERROR) { return err; } @@ -99,6 +99,61 @@ status_t SurfaceFlingerConsumer::bindTextureImage() return bindTextureImageLocked(); } +status_t SurfaceFlingerConsumer::acquireBufferLocked( + BufferQueue::BufferItem *item, nsecs_t presentWhen) { + status_t result = GLConsumer::acquireBufferLocked(item, presentWhen); + if (result == NO_ERROR) { + mTransformToDisplayInverse = item->mTransformToDisplayInverse; + } + return result; +} + +bool SurfaceFlingerConsumer::getTransformToDisplayInverse() const { + return mTransformToDisplayInverse; +} + +// We need to determine the time when a buffer acquired now will be +// displayed. This can be calculated: +// time when previous buffer's actual-present fence was signaled +// + current display refresh rate * HWC latency +// + a little extra padding +// +// Buffer producers are expected to set their desired presentation time +// based on choreographer time stamps, which (coming from vsync events) +// will be slightly later then the actual-present timing. If we get a +// desired-present time that is unintentionally a hair after the next +// vsync, we'll hold the frame when we really want to display it. We +// want to use an expected-presentation time that is slightly late to +// avoid this sort of edge case. +nsecs_t SurfaceFlingerConsumer::computeExpectedPresent() +{ + // Don't yet have an easy way to get actual buffer flip time for + // the specific display, so use the current time. This is typically + // 1.3ms past the vsync event time. + const nsecs_t prevVsync = systemTime(CLOCK_MONOTONIC); + + // Given a SurfaceFlinger reference, and information about what display + // we're destined for, we could query the HWC for the refresh rate. This + // could change over time, e.g. we could switch to 24fps for a movie. + // For now, assume 60fps. + //const nsecs_t vsyncPeriod = + // getHwComposer().getRefreshPeriod(HWC_DISPLAY_PRIMARY); + const nsecs_t vsyncPeriod = 16700000; + + // The HWC doesn't currently have a way to report additional latency. + // Assume that whatever we submit now will appear on the next flip, + // i.e. 1 frame of latency w.r.t. the previous flip. + const uint32_t hwcLatency = 1; + + // A little extra padding to compensate for slack between actual vsync + // time and vsync event receipt. Currently not needed since we're + // using "now" instead of a vsync time. + const nsecs_t extraPadding = 0; + + // Total it up. + return prevVsync + hwcLatency * vsyncPeriod + extraPadding; +} + // --------------------------------------------------------------------------- }; // namespace android diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.h b/services/surfaceflinger/SurfaceFlingerConsumer.h index 22eec81..688ad32 100644 --- a/services/surfaceflinger/SurfaceFlingerConsumer.h +++ b/services/surfaceflinger/SurfaceFlingerConsumer.h @@ -27,22 +27,21 @@ namespace android { */ class SurfaceFlingerConsumer : public GLConsumer { public: - SurfaceFlingerConsumer(GLuint tex, bool allowSynchronousMode = true, - GLenum texTarget = GL_TEXTURE_EXTERNAL_OES, bool useFenceSync = true, - const sp<BufferQueue> &bufferQueue = 0) - : GLConsumer(tex, allowSynchronousMode, texTarget, useFenceSync, - bufferQueue) + SurfaceFlingerConsumer(const sp<BufferQueue>& bq, uint32_t tex) + : GLConsumer(bq, tex, GLConsumer::TEXTURE_EXTERNAL, false) {} class BufferRejecter { friend class SurfaceFlingerConsumer; virtual bool reject(const sp<GraphicBuffer>& buf, - const BufferQueue::BufferItem& item) = 0; + const IGraphicBufferConsumer::BufferItem& item) = 0; protected: virtual ~BufferRejecter() { } }; + virtual status_t acquireBufferLocked(BufferQueue::BufferItem *item, nsecs_t presentWhen); + // This version of updateTexImage() takes a functor that may be used to // reject the newly acquired buffer. Unlike the GLConsumer version, // this does not guarantee that the buffer has been bound to the GL @@ -51,6 +50,17 @@ public: // See GLConsumer::bindTextureImageLocked(). status_t bindTextureImage(); + + // must be called from SF main thread + bool getTransformToDisplayInverse() const; + +private: + nsecs_t computeExpectedPresent(); + + // Indicates this buffer must be transformed by the inverse transform of the screen + // it is displayed onto. This is applied after GLConsumer::mCurrentTransform. + // This must be set/read from SurfaceFlinger's main thread. + bool mTransformToDisplayInverse; }; // ---------------------------------------------------------------------------- diff --git a/services/surfaceflinger/SurfaceTextureLayer.cpp b/services/surfaceflinger/SurfaceTextureLayer.cpp index d0f0dae..9d79ce2 100644 --- a/services/surfaceflinger/SurfaceTextureLayer.cpp +++ b/services/surfaceflinger/SurfaceTextureLayer.cpp @@ -28,7 +28,7 @@ namespace android { SurfaceTextureLayer::SurfaceTextureLayer(const sp<SurfaceFlinger>& flinger) - : BufferQueue(true), flinger(flinger) { + : BufferQueue(), flinger(flinger) { } SurfaceTextureLayer::~SurfaceTextureLayer() { @@ -48,34 +48,8 @@ SurfaceTextureLayer::~SurfaceTextureLayer() { return true; } }; - flinger->postMessageAsync( new MessageCleanUpList(flinger, this) ); -} - -status_t SurfaceTextureLayer::connect(int api, QueueBufferOutput* output) { - status_t err = BufferQueue::connect(api, output); - if (err == NO_ERROR) { - switch(api) { - case NATIVE_WINDOW_API_MEDIA: - case NATIVE_WINDOW_API_CAMERA: - // Camera preview and videos are rate-limited on the producer - // side. If enabled for this build, we use async mode to always - // show the most recent frame at the cost of requiring an - // additional buffer. -#ifndef NEVER_DEFAULT_TO_ASYNC_MODE - err = setSynchronousMode(false); - break; -#endif - // fall through to set synchronous mode when not defaulting to - // async mode. - default: - err = setSynchronousMode(true); - break; - } - if (err != NO_ERROR) { - disconnect(api); - } - } - return err; + flinger->postMessageAsync( + new MessageCleanUpList(flinger, static_cast<BnGraphicBufferProducer*>(this)) ); } // --------------------------------------------------------------------------- diff --git a/services/surfaceflinger/SurfaceTextureLayer.h b/services/surfaceflinger/SurfaceTextureLayer.h index 13cff2f..5f5e4ef 100644 --- a/services/surfaceflinger/SurfaceTextureLayer.h +++ b/services/surfaceflinger/SurfaceTextureLayer.h @@ -38,10 +38,6 @@ class SurfaceTextureLayer : public BufferQueue { public: SurfaceTextureLayer(const sp<SurfaceFlinger>& flinger); virtual ~SurfaceTextureLayer(); - - // After calling the superclass connect(), set or clear synchronous - // mode appropriately for the specified API. - virtual status_t connect(int api, QueueBufferOutput* output); }; // --------------------------------------------------------------------------- diff --git a/services/surfaceflinger/Transform.cpp b/services/surfaceflinger/Transform.cpp index 315720e..3456abf 100644 --- a/services/surfaceflinger/Transform.cpp +++ b/services/surfaceflinger/Transform.cpp @@ -83,8 +83,8 @@ Transform Transform::operator * (const Transform& rhs) const return r; } -float const* Transform::operator [] (int i) const { - return mMatrix[i].v; +const vec3& Transform::operator [] (size_t i) const { + return mMatrix[i]; } bool Transform::transformed() const { @@ -173,7 +173,7 @@ status_t Transform::set(uint32_t flags, float w, float h) return NO_ERROR; } -Transform::vec2 Transform::transform(const vec2& v) const { +vec2 Transform::transform(const vec2& v) const { vec2 r; const mat33& M(mMatrix); r[0] = M[0][0]*v[0] + M[1][0]*v[1] + M[2][0]; @@ -181,7 +181,7 @@ Transform::vec2 Transform::transform(const vec2& v) const { return r; } -Transform::vec3 Transform::transform(const vec3& v) const { +vec3 Transform::transform(const vec3& v) const { vec3 r; const mat33& M(mMatrix); r[0] = M[0][0]*v[0] + M[1][0]*v[1] + M[2][0]*v[2]; @@ -190,12 +190,9 @@ Transform::vec3 Transform::transform(const vec3& v) const { return r; } -void Transform::transform(float* point, int x, int y) const +vec2 Transform::transform(int x, int y) const { - vec2 v(x, y); - v = transform(v); - point[0] = v[0]; - point[1] = v[1]; + return transform(vec2(x,y)); } Rect Transform::makeBounds(int w, int h) const diff --git a/services/surfaceflinger/Transform.h b/services/surfaceflinger/Transform.h index c4efade..c2f0010 100644 --- a/services/surfaceflinger/Transform.h +++ b/services/surfaceflinger/Transform.h @@ -22,6 +22,8 @@ #include <ui/Point.h> #include <ui/Rect.h> +#include <ui/vec2.h> +#include <ui/vec3.h> #include <hardware/hardware.h> @@ -63,7 +65,7 @@ public: uint32_t getType() const; uint32_t getOrientation() const; - float const* operator [] (int i) const; // returns column i + const vec3& operator [] (size_t i) const; // returns column i float tx() const; float ty() const; @@ -75,7 +77,7 @@ public: // transform data Rect makeBounds(int w, int h) const; - void transform(float* point, int x, int y) const; + vec2 transform(int x, int y) const; Region transform(const Region& reg) const; Rect transform(const Rect& bounds) const; Transform operator * (const Transform& rhs) const; @@ -86,24 +88,6 @@ public: void dump(const char* name) const; private: - struct vec3 { - float v[3]; - inline vec3() { } - inline vec3(float a, float b, float c) { - v[0] = a; v[1] = b; v[2] = c; - } - inline float operator [] (int i) const { return v[i]; } - inline float& operator [] (int i) { return v[i]; } - }; - struct vec2 { - float v[2]; - inline vec2() { } - inline vec2(float a, float b) { - v[0] = a; v[1] = b; - } - inline float operator [] (int i) const { return v[i]; } - inline float& operator [] (int i) { return v[i]; } - }; struct mat33 { vec3 v[3]; inline const vec3& operator [] (int i) const { return v[i]; } diff --git a/services/surfaceflinger/main_surfaceflinger.cpp b/services/surfaceflinger/main_surfaceflinger.cpp new file mode 100644 index 0000000..b161480 --- /dev/null +++ b/services/surfaceflinger/main_surfaceflinger.cpp @@ -0,0 +1,58 @@ +/* + * 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. + */ + +#if defined(HAVE_PTHREADS) +#include <sys/resource.h> +#endif + +#include <cutils/sched_policy.h> +#include <binder/IServiceManager.h> +#include <binder/IPCThreadState.h> +#include <binder/ProcessState.h> +#include <binder/IServiceManager.h> +#include "SurfaceFlinger.h" + +using namespace android; + +int main(int argc, char** argv) { + // When SF is launched in its own process, limit the number of + // binder threads to 4. + ProcessState::self()->setThreadPoolMaxThreadCount(4); + + // start the thread pool + sp<ProcessState> ps(ProcessState::self()); + ps->startThreadPool(); + + // instantiate surfaceflinger + sp<SurfaceFlinger> flinger = new SurfaceFlinger(); + +#if defined(HAVE_PTHREADS) + setpriority(PRIO_PROCESS, 0, PRIORITY_URGENT_DISPLAY); +#endif + set_sched_policy(0, SP_FOREGROUND); + + // initialize before clients can connect + flinger->init(); + + // publish surface flinger + sp<IServiceManager> sm(defaultServiceManager()); + sm->addService(String16(SurfaceFlinger::getServiceName()), flinger, false); + + // run in this thread + flinger->run(); + + return 0; +} |