am 9f476fd0: Merge "Add a symbol to represent MNC=0"
* commit '9f476fd08079701d1ad0f7c591667b6e673ed38e':
Add a symbol to represent MNC=0
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 @@
{ "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 @@
{ 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/dumpstate.c b/cmds/dumpstate/dumpstate.c
index 89bea91..b8f505e 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 @@
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();
@@ -278,6 +279,16 @@
run_command("DUMPSYS", 60, "dumpsys", NULL);
printf("========================================================\n");
+ printf("== Checkins\n");
+ printf("========================================================\n");
+
+ run_command("CHECKIN BATTERYSTATS", 30, "dumpsys", "batterystats", "--checkin", 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/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 @@
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 @@
}
}
+ 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/GLHelper.cpp b/cmds/flatland/GLHelper.cpp
index 4f7697f..3928039 100644
--- a/cmds/flatland/GLHelper.cpp
+++ b/cmds/flatland/GLHelper.cpp
@@ -198,9 +198,9 @@
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 @@
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 @@
},
},
+ { "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 @@
},
},
+ { "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 @@
},
},
},
+
+ { "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..84ad204 100644
--- a/cmds/installd/commands.c
+++ b/cmds/installd/commands.c
@@ -108,11 +108,11 @@
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 @@
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)
{
char pkgdir[PKG_PATH_MAX];
char applibdir[PKG_PATH_MAX];
@@ -192,10 +192,10 @@
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;
}
@@ -262,10 +262,10 @@
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 @@
}
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 @@
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 @@
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 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 @@
}
}
- 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 @@
}
-/* a simpler version of dexOptGenerateCacheFileName() */
int create_cache_path(char path[PKG_PATH_MAX], const char *src)
{
char *tmp;
@@ -580,7 +579,7 @@
}
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 @@
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 @@
{
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 @@
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 @@
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 @@
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 c918633..1904408 100644
--- a/cmds/installd/installd.c
+++ b/cmds/installd/installd.c
@@ -83,7 +83,7 @@
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);
@@ -108,7 +108,7 @@
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])
@@ -198,7 +198,7 @@
unsigned short count;
int ret = -1;
-// ALOGI("execute('%s')\n", cmd);
+ // ALOGI("execute('%s')\n", cmd);
/* default reply is "" */
reply[0] = 0;
@@ -240,7 +240,7 @@
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..635b07c 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(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 @@
/* 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);
+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, 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_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 @@
/**
* 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 @@
}
/**
- * 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 @@
}
/**
- * 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_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 @@
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 @@
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/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/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/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 @@
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..32c5c0a 100644
--- a/include/android/sensor.h
+++ b/include/android/sensor.h
@@ -114,14 +114,20 @@
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;
+ };
+ union {
+ uint64_t data[8];
+ uint64_t step_counter;
+ } u64;
};
int32_t reserved1[4];
} ASensorEvent;
diff --git a/include/batteryservice/BatteryService.h b/include/batteryservice/BatteryService.h
new file mode 100644
index 0000000..855262b
--- /dev/null
+++ b/include/batteryservice/BatteryService.h
@@ -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.
+ */
+
+#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 batteryTemperature;
+ String8 batteryTechnology;
+
+ status_t writeToParcel(Parcel* parcel) const;
+ status_t readFromParcel(Parcel* parcel);
+};
+
+}; // namespace android
+
+#endif // ANDROID_BATTERYSERVICE_H
diff --git a/include/batteryservice/IBatteryPropertiesListener.h b/include/batteryservice/IBatteryPropertiesListener.h
new file mode 100644
index 0000000..b02d8e9
--- /dev/null
+++ b/include/batteryservice/IBatteryPropertiesListener.h
@@ -0,0 +1,45 @@
+/*
+ * 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_IBATTERYPROPERTIESLISTENER_H
+#define ANDROID_IBATTERYPROPERTIESLISTENER_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,
+};
+
+// ----------------------------------------------------------------------------
+
+class IBatteryPropertiesListener : public IInterface {
+public:
+ DECLARE_META_INTERFACE(BatteryPropertiesListener);
+
+ virtual void batteryPropertiesChanged(struct BatteryProperties props) = 0;
+};
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
+#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 @@
}
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
similarity index 96%
rename from include/utils/BufferedTextOutput.h
rename to 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/binder/Debug.h b/include/binder/Debug.h
new file mode 100644
index 0000000..f6a3355
--- /dev/null
+++ b/include/binder/Debug.h
@@ -0,0 +1,49 @@
+/*
+ * 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_BINDER_DEBUG_H
+#define ANDROID_BINDER_DEBUG_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+const char* stringForIndent(int32_t indentLevel);
+
+typedef void (*debugPrintFunc)(void* cookie, const char* txt);
+
+void printTypeCode(uint32_t typeCode,
+ debugPrintFunc func = 0, void* cookie = 0);
+
+void printHexData(int32_t indent, const void *buf, size_t length,
+ size_t bytesPerLine=16, int32_t singleLineBytesCutoff=16,
+ size_t alignment=0, bool cArrayStyle=false,
+ debugPrintFunc func = 0, void* cookie = 0);
+
+#ifdef __cplusplus
+}
+#endif
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+
+#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 @@
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 @@
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 @@
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 @@
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 @@
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/utils/TextOutput.h b/include/binder/TextOutput.h
similarity index 96%
rename from include/utils/TextOutput.h
rename to 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, 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/BufferItemConsumer.h b/include/gui/BufferItemConsumer.h
index 98b450c..9370e81 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 @@
// 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 @@
//
// 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
diff --git a/include/gui/BufferQueue.h b/include/gui/BufferQueue.h
index 6c1b691..cfce40d 100644
--- a/include/gui/BufferQueue.h
+++ b/include/gui/BufferQueue.h
@@ -39,7 +39,7 @@
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.
@@ -97,11 +97,9 @@
// 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();
// Query native window attributes. The "what" values are enumerated in
@@ -169,7 +167,7 @@
//
// 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 +195,6 @@
// 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 +204,7 @@
// 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(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
@@ -229,18 +218,19 @@
// 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;
+ virtual void dump(String8& result, const char* prefix) const;
// public facing structure for BufferSlot
struct BufferItem {
- BufferItem()
- :
+ BufferItem() :
mTransform(0),
mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
mTimestamp(0),
mFrameNumber(0),
- mBuf(INVALID_BUFFER_SLOT) {
+ mBuf(INVALID_BUFFER_SLOT),
+ mIsDroppable(false),
+ mAcquireCalled(false) {
mCrop.makeInvalid();
}
// mGraphicBuffer points to the buffer allocated for this slot, or is NULL
@@ -269,6 +259,16 @@
// mFence is a fence that will signal when the buffer is idle.
sp<Fence> mFence;
+
+ // 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;
};
// The following public functions are the consumer-facing interface
@@ -280,12 +280,18 @@
// 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.
+ 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,16 +300,19 @@
//
// 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,
+ 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);
+ status_t consumerConnect(const sp<ConsumerListener>& consumer, bool controlledByApp);
// consumerDisconnect disconnects a consumer from the BufferQueue. All
// buffers will be freed and the BufferQueue is placed in the "abandoned"
@@ -331,15 +340,18 @@
// The count must be between 2 and NUM_BUFFER_SLOTS, inclusive.
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().
+ 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;
-
// setConsumerName sets the name used in logging
void setConsumerName(const String8& name);
@@ -359,6 +371,7 @@
// NATIVE_WINDOW_TRANSFORM_ROT_90. The default is 0 (no transform).
status_t setTransformHint(uint32_t hint);
+
private:
// freeBufferLocked frees the GraphicBuffer and sync resources for the
// given slot.
@@ -368,47 +381,39 @@
// 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 +421,10 @@
: 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 +483,6 @@
// 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).
@@ -576,12 +562,18 @@
// to NULL and is written by consumerConnect and consumerDisconnect.
sp<ConsumerListener> 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 +584,7 @@
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 +605,7 @@
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
diff --git a/include/gui/ConsumerBase.h b/include/gui/ConsumerBase.h
index 8a7545d..7b58bc5 100644
--- a/include/gui/ConsumerBase.h
+++ b/include/gui/ConsumerBase.h
@@ -73,7 +73,7 @@
// 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.
@@ -87,7 +87,9 @@
// ConsumerBase constructs a new ConsumerBase object to consume image
// buffers from the given BufferQueue.
- ConsumerBase(const sp<BufferQueue> &bufferQueue);
+ // The controlledByApp flag indicates that this consumer is under the application's
+ // control.
+ ConsumerBase(const sp<BufferQueue> &bufferQueue, 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
@@ -143,8 +145,7 @@
// 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 +154,8 @@
// 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(BufferQueue::BufferItem *item,
+ nsecs_t presentWhen);
// releaseBufferLocked relinquishes control over a buffer, returning that
// control to the BufferQueue.
@@ -161,17 +163,23 @@
// 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 +193,9 @@
// 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
diff --git a/include/gui/CpuConsumer.h b/include/gui/CpuConsumer.h
index bf9918e..2890350 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 @@
// 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<BufferQueue>& bq,
+ uint32_t maxLockedBuffers, bool controlledByApp = false);
virtual ~CpuConsumer();
@@ -73,6 +75,18 @@
// 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
diff --git a/include/gui/DummyConsumer.h b/include/gui/DummyConsumer.h
deleted file mode 100644
index 08e8ec8..0000000
--- a/include/gui/DummyConsumer.h
+++ /dev/null
@@ -1,46 +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_GUI_DUMMYCONSUMER_H
-#define ANDROID_GUI_DUMMYCONSUMER_H
-
-#include <gui/BufferQueue.h>
-
-namespace android {
-// ----------------------------------------------------------------------------
-
-
-// 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();
-
-};
-
-// ----------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_GUI_DUMMYCONSUMER_H
diff --git a/include/gui/GLConsumer.h b/include/gui/GLConsumer.h
index 1ef54f5..ac4a832 100644
--- a/include/gui/GLConsumer.h
+++ b/include/gui/GLConsumer.h
@@ -87,10 +87,7 @@
// 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);
+ 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 +98,13 @@
// 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
@@ -180,10 +184,6 @@
// 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,7 +193,6 @@
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.
@@ -236,20 +235,22 @@
// 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 +258,7 @@
// 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
@@ -422,6 +423,10 @@
// It is set to false by detachFromContext, and then set to true again by
// attachToContext.
bool mAttached;
+
+ // mReleasedTexImageBuffer is a dummy buffer used when in single buffer
+ // mode and releaseTexImage() has been called
+ sp<GraphicBuffer> mReleasedTexImageBuffer;
};
// ----------------------------------------------------------------------------
diff --git a/include/gui/IGraphicBufferProducer.h b/include/gui/IGraphicBufferProducer.h
index 29c7ff3..9677962 100644
--- a/include/gui/IGraphicBufferProducer.h
+++ b/include/gui/IGraphicBufferProducer.h
@@ -84,7 +84,10 @@
// 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,6 +99,8 @@
// 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.
@@ -103,17 +108,18 @@
struct QueueBufferInput : public Flattenable {
inline QueueBufferInput(const Parcel& parcel);
inline QueueBufferInput(int64_t timestamp,
- const Rect& crop, int scalingMode, uint32_t transform,
- sp<Fence> fence)
+ const Rect& crop, int scalingMode, uint32_t transform, bool async,
+ const sp<Fence>& fence)
: timestamp(timestamp), crop(crop), scalingMode(scalingMode),
- transform(transform), fence(fence) { }
+ transform(transform), async(async), fence(fence) { }
inline void deflate(int64_t* outTimestamp, Rect* outCrop,
- int* outScalingMode, uint32_t* outTransform,
+ int* outScalingMode, uint32_t* outTransform, bool* outAsync,
sp<Fence>* outFence) const {
*outTimestamp = timestamp;
*outCrop = crop;
*outScalingMode = scalingMode;
*outTransform = transform;
+ *outAsync = bool(async);
*outFence = fence;
}
@@ -130,6 +136,7 @@
Rect crop;
int scalingMode;
uint32_t transform;
+ int async;
sp<Fence> fence;
};
@@ -171,13 +178,6 @@
// '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.
@@ -188,7 +188,7 @@
// 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;
+ virtual status_t connect(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/Surface.h b/include/gui/Surface.h
index c25847c..2f7406e 100644
--- a/include/gui/Surface.h
+++ b/include/gui/Surface.h
@@ -61,8 +61,11 @@
* 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 +231,14 @@
// 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/input/IInputFlinger.h b/include/input/IInputFlinger.h
new file mode 100644
index 0000000..79ff12a
--- /dev/null
+++ b/include/input/IInputFlinger.h
@@ -0,0 +1,54 @@
+/*
+ * 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 _LIBINPUT_IINPUT_FLINGER_H
+#define _LIBINPUT_IINPUT_FLINGER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <binder/IInterface.h>
+
+namespace android {
+
+/*
+ * This class defines the Binder IPC interface for accessing various
+ * InputFlinger features.
+ */
+class IInputFlinger : public IInterface {
+public:
+ DECLARE_META_INTERFACE(InputFlinger);
+
+ virtual status_t doSomething() = 0;
+};
+
+
+/**
+ * Binder implementation.
+ */
+class BnInputFlinger : public BnInterface<IInputFlinger> {
+public:
+ enum {
+ DO_SOMETHING_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
+ };
+
+ virtual status_t onTransact(uint32_t code, const Parcel& data,
+ Parcel* reply, uint32_t flags = 0);
+};
+
+} // namespace android
+
+#endif // _LIBINPUT_IINPUT_FLINGER_H
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/hardware/HDCPAPI.h b/include/media/hardware/HDCPAPI.h
index 147448e..b3f4222 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 {
@@ -90,6 +91,20 @@
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..ac67f94 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 @@
//
// 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 @@
//
// 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,13 @@
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 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 @@
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 @@
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 @@
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..e21e6a8 100644
--- a/include/powermanager/IPowerManager.h
+++ b/include/powermanager/IPowerManager.h
@@ -30,7 +30,8 @@
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 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 @@
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/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 @@
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 @@
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 @@
// 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 @@
// 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 @@
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 @@
// (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/utils/BasicHashtable.h b/include/utils/BasicHashtable.h
index 7a6c96c..c235d62 100644
--- a/include/utils/BasicHashtable.h
+++ b/include/utils/BasicHashtable.h
@@ -52,6 +52,7 @@
BasicHashtableImpl(size_t entrySize, bool hasTrivialDestructor,
size_t minimumInitialCapacity, float loadFactor);
BasicHashtableImpl(const BasicHashtableImpl& other);
+ virtual ~BasicHashtableImpl();
void dispose();
diff --git a/include/utils/BitSet.h b/include/utils/BitSet.h
index e189d0c..19c03d1 100644
--- a/include/utils/BitSet.h
+++ b/include/utils/BitSet.h
@@ -101,6 +101,20 @@
inline bool operator== (const BitSet32& other) const { return value == other.value; }
inline bool operator!= (const BitSet32& other) const { return value != other.value; }
+ inline BitSet32 operator& (const BitSet32& other) const {
+ return BitSet32(value & other.value);
+ }
+ inline BitSet32& operator&= (const BitSet32& other) {
+ value &= other.value;
+ return *this;
+ }
+ inline BitSet32 operator| (const BitSet32& other) const {
+ return BitSet32(value | other.value);
+ }
+ inline BitSet32& operator|= (const BitSet32& other) {
+ value |= other.value;
+ return *this;
+ }
};
ANDROID_BASIC_TYPES_TRAITS(BitSet32)
diff --git a/include/utils/Debug.h b/include/utils/Debug.h
index d9ed32d..08893bd 100644
--- a/include/utils/Debug.h
+++ b/include/utils/Debug.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ANDROID_DEBUG_H
-#define ANDROID_DEBUG_H
+#ifndef ANDROID_UTILS_DEBUG_H
+#define ANDROID_UTILS_DEBUG_H
#include <stdint.h>
#include <sys/types.h>
@@ -43,28 +43,6 @@
#endif
// ---------------------------------------------------------------------------
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-const char* stringForIndent(int32_t indentLevel);
-
-typedef void (*debugPrintFunc)(void* cookie, const char* txt);
-
-void printTypeCode(uint32_t typeCode,
- debugPrintFunc func = 0, void* cookie = 0);
-
-void printHexData(int32_t indent, const void *buf, size_t length,
- size_t bytesPerLine=16, int32_t singleLineBytesCutoff=16,
- size_t alignment=0, bool cArrayStyle=false,
- debugPrintFunc func = 0, void* cookie = 0);
-
-#ifdef __cplusplus
-}
-#endif
-
-// ---------------------------------------------------------------------------
}; // namespace android
-#endif // ANDROID_DEBUG_H
+#endif // ANDROID_UTILS_DEBUG_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/Looper.h b/include/utils/Looper.h
index d4a0067..2e0651a 100644
--- a/include/utils/Looper.h
+++ b/include/utils/Looper.h
@@ -297,6 +297,13 @@
void removeMessages(const sp<MessageHandler>& handler, int what);
/**
+ * Return whether this looper's thread is currently idling -- that is, whether it
+ * stopped waiting for more work to do. Note that this is intrinsically racy, since
+ * its state can change before you get the result back.
+ */
+ bool isIdling() const;
+
+ /**
* 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.
@@ -353,6 +360,10 @@
Vector<MessageEnvelope> mMessageEnvelopes; // guarded by mLock
bool mSendingMessage; // guarded by mLock
+ // Whether we are currently waiting for work. Not protected by a lock,
+ // any use of it is racy anyway.
+ volatile bool mIdling;
+
int mEpollFd; // immutable
// Locked list of file descriptor monitoring requests.
diff --git a/include/utils/LruCache.h b/include/utils/LruCache.h
index 302b929..053bfaf 100644
--- a/include/utils/LruCache.h
+++ b/include/utils/LruCache.h
@@ -18,12 +18,19 @@
#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.
+/**
+ * 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 TKey, typename TValue>
class LruCache {
diff --git a/include/utils/String16.h b/include/utils/String16.h
index fe06c57..d131bfc 100644
--- a/include/utils/String16.h
+++ b/include/utils/String16.h
@@ -41,7 +41,16 @@
class String16
{
public:
+ /* use String16(StaticLinkage) if you're statically linking against
+ * libutils and declaring an empty static String16, e.g.:
+ *
+ * static String16 sAStaticEmptyString(String16::kEmptyString);
+ * static String16 sAnotherStaticEmptyString(sAStaticEmptyString);
+ */
+ enum StaticLinkage { kEmptyString };
+
String16();
+ explicit String16(StaticLinkage);
String16(const String16& o);
String16(const String16& o,
size_t len,
@@ -117,8 +126,6 @@
// 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.
diff --git a/include/utils/String8.h b/include/utils/String8.h
index 335e7f1..ef59470 100644
--- a/include/utils/String8.h
+++ b/include/utils/String8.h
@@ -37,7 +37,16 @@
class String8
{
public:
+ /* use String8(StaticLinkage) if you're statically linking against
+ * libutils and declaring an empty static String8, e.g.:
+ *
+ * static String8 sAStaticEmptyString(String8::kEmptyString);
+ * static String8 sAnotherStaticEmptyString(sAStaticEmptyString);
+ */
+ enum StaticLinkage { kEmptyString };
+
String8();
+ explicit String8(StaticLinkage);
String8(const String8& o);
explicit String8(const char* o);
explicit String8(const char* o, size_t numChars);
@@ -224,8 +233,6 @@
// 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.
diff --git a/include/utils/StringArray.h b/include/utils/StringArray.h
deleted file mode 100644
index c2445871..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
index 49fa3a8..aba9577 100644
--- a/include/utils/StrongPointer.h
+++ b/include/utils/StrongPointer.h
@@ -26,9 +26,6 @@
// ---------------------------------------------------------------------------
namespace android {
-class TextOutput;
-TextOutput& printStrongPointer(TextOutput& to, const void* val);
-
template<typename T> class wp;
// ---------------------------------------------------------------------------
@@ -58,9 +55,8 @@
// ---------------------------------------------------------------------------
-template <typename T>
-class sp
-{
+template<typename T>
+class sp {
public:
inline sp() : m_ptr(0) { }
@@ -110,92 +106,93 @@
#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);
- }
+ : 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);
- }
+ : 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);
+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);
+ : m_ptr(other.m_ptr) {
+ if (m_ptr)
+ m_ptr->incStrong(this);
}
template<typename T>
-sp<T>& sp<T>::operator = (const sp<T>& other) {
+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);
+ 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);
+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)
-{
+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);
+ 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);
+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)
-{
+template<typename T>
+void sp<T>::force_set(T* other) {
other->forceIncStrong(this);
m_ptr = other;
}
template<typename T>
-void sp<T>::clear()
-{
+void sp<T>::clear() {
if (m_ptr) {
m_ptr->decStrong(this);
m_ptr = 0;
@@ -207,12 +204,6 @@
m_ptr = ptr;
}
-template <typename T>
-inline TextOutput& operator<<(TextOutput& to, const sp<T>& val)
-{
- return printStrongPointer(to, val.get());
-}
-
}; // namespace android
// ---------------------------------------------------------------------------
diff --git a/include/utils/SystemClock.h b/include/utils/SystemClock.h
index d75264c..01db340 100644
--- a/include/utils/SystemClock.h
+++ b/include/utils/SystemClock.h
@@ -22,7 +22,6 @@
namespace android {
-int setCurrentTimeMillis(int64_t millis);
int64_t uptimeMillis();
int64_t elapsedRealtime();
int64_t elapsedRealtimeNano();
diff --git a/include/utils/ThreadDefs.h b/include/utils/ThreadDefs.h
index a8f8eb3..9711c13 100644
--- a/include/utils/ThreadDefs.h
+++ b/include/utils/ThreadDefs.h
@@ -20,6 +20,7 @@
#include <stdint.h>
#include <sys/types.h>
#include <system/graphics.h>
+#include <system/thread_defs.h>
// ---------------------------------------------------------------------------
// C API
@@ -32,53 +33,6 @@
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
diff --git a/include/utils/Timers.h b/include/utils/Timers.h
index 92f66c9..d015421 100644
--- a/include/utils/Timers.h
+++ b/include/utils/Timers.h
@@ -103,43 +103,4 @@
} // 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/Trace.h b/include/utils/Trace.h
index 49578c4..6ee343d 100644
--- a/include/utils/Trace.h
+++ b/include/utils/Trace.h
@@ -17,6 +17,8 @@
#ifndef ANDROID_TRACE_H
#define ANDROID_TRACE_H
+#ifdef HAVE_ANDROID_OS
+
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
@@ -57,4 +59,11 @@
}; // namespace android
+#else // HAVE_ANDROID_OS
+
+#define ATRACE_NAME(...)
+#define ATRACE_CALL()
+
+#endif // HAVE_ANDROID_OS
+
#endif // ANDROID_TRACE_H
diff --git a/include/utils/VectorImpl.h b/include/utils/VectorImpl.h
index 9bc50e6..21ad71c 100644
--- a/include/utils/VectorImpl.h
+++ b/include/utils/VectorImpl.h
@@ -105,16 +105,6 @@
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);
@@ -166,16 +156,6 @@
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;
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/misc.h b/include/utils/misc.h
index f1aa432..6cccec3 100644
--- a/include/utils/misc.h
+++ b/include/utils/misc.h
@@ -20,7 +20,6 @@
#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 */
@@ -30,26 +29,6 @@
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();
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 @@
AppOpsManager.cpp \
Binder.cpp \
BpBinder.cpp \
+ BufferedTextOutput.cpp \
+ Debug.cpp \
IAppOpsCallback.cpp \
IAppOpsService.cpp \
IInterface.cpp \
@@ -30,7 +32,8 @@
Parcel.cpp \
PermissionCache.cpp \
ProcessState.cpp \
- Static.cpp
+ Static.cpp \
+ TextOutput.cpp \
LOCAL_PATH:= $(call my-dir)
@@ -44,5 +47,6 @@
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::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
similarity index 98%
rename from libs/utils/BufferedTextOutput.cpp
rename to 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
similarity index 96%
rename from libs/utils/Debug.cpp
rename to 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 @@
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 @@
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 @@
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 @@
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 @@
} 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 @@
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 @@
return err;
}
-int IPCThreadState::getCallingPid()
+int IPCThreadState::getCallingPid() const
{
return mCallingPid;
}
-int IPCThreadState::getCallingUid()
+int IPCThreadState::getCallingUid() const
{
return mCallingUid;
}
@@ -417,6 +418,60 @@
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 @@
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 @@
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 @@
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 @@
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 @@
{
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/Parcel.cpp b/libs/binder/Parcel.cpp
index c7bdcbc..359742c 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>
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 @@
// 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
similarity index 91%
rename from libs/utils/TextOutput.cpp
rename to 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 @@
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..f627e5d 100644
--- a/libs/gui/Android.mk
+++ b/libs/gui/Android.mk
@@ -8,7 +8,6 @@
ConsumerBase.cpp \
CpuConsumer.cpp \
DisplayEventReceiver.cpp \
- DummyConsumer.cpp \
GLConsumer.cpp \
GraphicBufferAlloc.cpp \
GuiConfig.cpp \
diff --git a/libs/gui/BufferItemConsumer.cpp b/libs/gui/BufferItemConsumer.cpp
index 7db1b84..0f818b7 100644
--- a/libs/gui/BufferItemConsumer.cpp
+++ b/libs/gui/BufferItemConsumer.cpp
@@ -29,12 +29,11 @@
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);
}
@@ -47,14 +46,15 @@
mBufferQueue->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 @@
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)",
diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp
index b4c7231..45488ff 100644
--- a/libs/gui/BufferQueue.cpp
+++ b/libs/gui/BufferQueue.cpp
@@ -30,6 +30,7 @@
#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 +64,15 @@
}
}
-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 +101,8 @@
}
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 +111,6 @@
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;
@@ -156,21 +153,21 @@
}
// 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 +175,10 @@
}
// 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,7 +212,7 @@
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);
@@ -237,15 +232,11 @@
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 +246,7 @@
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 +270,6 @@
usage |= mConsumerUsageBits;
int found = -1;
- int dequeuedCount = 0;
bool tryAgain = true;
while (tryAgain) {
if (mAbandoned) {
@@ -287,7 +277,16 @@
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 +300,28 @@
// 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 +340,7 @@
// 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 +354,16 @@
// 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);
}
}
@@ -418,6 +432,7 @@
return NO_INIT;
}
+ mSlots[*outBuf].mFrameNumber = ~0;
mSlots[*outBuf].mGraphicBuffer = graphicBuffer;
}
}
@@ -435,44 +450,13 @@
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 +466,47 @@
uint32_t transform;
int scalingMode;
int64_t timestamp;
+ bool async;
sp<Fence> fence;
- input.deflate(×tamp, &crop, &scalingMode, &transform, &fence);
+ input.deflate(×tamp, &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;
{ // 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 +521,12 @@
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 +537,48 @@
return -EINVAL;
}
- if (mSynchronousMode) {
- // In synchronous mode we queue all buffers in a FIFO.
- mQueue.push_back(buf);
-
- // Synchronous mode always signals that an additional frame should
- // be consumed.
- 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;
- } 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;
- }
- }
-
- 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;
+ BufferItem item;
+ item.mAcquireCalled = mSlots[buf].mAcquireCalled;
+ item.mGraphicBuffer = mSlots[buf].mGraphicBuffer;
+ item.mCrop = crop;
+ item.mTransform = transform;
+ item.mScalingMode = scalingMode;
+ item.mTimestamp = timestamp;
+ item.mFrameNumber = mFrameCounter;
+ item.mBuf = buf;
+ item.mFence = fence;
+ item.mIsDroppable = mDequeueBufferCannotBlock || async;
+
+ if (mQueue.empty()) {
+ // when the queue is empty, we can ignore "mDequeueBufferCannotBlock", and
+ // simply queue this buffer.
+ mQueue.push_back(item);
+ listener = mConsumerListener;
+ } else {
+ // 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 {
+ mQueue.push_back(item);
+ listener = mConsumerListener;
+ }
+ }
+
mBufferHasBeenQueued = true;
mDequeueCondition.broadcast();
@@ -601,10 +605,9 @@
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,7 +623,7 @@
mDequeueCondition.broadcast();
}
-status_t BufferQueue::connect(int api, QueueBufferOutput* output) {
+status_t BufferQueue::connect(int api, bool producerControlledByApp, QueueBufferOutput* output) {
ATRACE_CALL();
ST_LOGV("connect: api=%d", api);
Mutex::Autolock lock(mMutex);
@@ -657,6 +660,7 @@
}
mBufferHasBeenQueued = false;
+ mDequeueBufferCannotBlock = mConsumerControlledByApp && producerControlledByApp;
return err;
}
@@ -683,7 +687,7 @@
case NATIVE_WINDOW_API_MEDIA:
case NATIVE_WINDOW_API_CAMERA:
if (mConnectedApi == api) {
- drainQueueAndFreeBuffersLocked();
+ freeAllBuffersLocked();
mConnectedApi = NO_CONNECTED_API;
mDequeueCondition.broadcast();
listener = mConsumerListener;
@@ -707,36 +711,35 @@
return err;
}
-void BufferQueue::dump(String8& result) const
-{
- char buffer[1024];
- BufferQueue::dump(result, "", buffer, 1024);
+void BufferQueue::dump(String8& result) const {
+ BufferQueue::dump(result, "");
}
-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 +753,30 @@
}
} 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)
- );
- result.append(buffer);
-
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)
+ );
+
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 +801,13 @@
}
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 presentWhen) {
ATRACE_CALL();
Mutex::Autolock _l(mMutex);
@@ -827,58 +830,108 @@
// 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());
+ int buf = front->mBuf;
- if (mSlots[buf].mAcquireCalled) {
- buffer->mGraphicBuffer = NULL;
- } else {
- buffer->mGraphicBuffer = mSlots[buf].mGraphicBuffer;
- }
- 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;
+ // Compare the buffer's desired presentation time to the predicted
+ // actual display time.
+ //
+ // The "presentWhen" argument indicates when the buffer is expected
+ // to be presented on-screen. If the buffer's desired-present time
+ // is earlier (less) than presentWhen, meaning it'll be displayed
+ // on time or possibly late, we acquire and return it. If we don't want
+ // to display it until after the presentWhen time, we return PRESENT_LATER
+ // without acquiring it.
+ //
+ // To be safe, we don't refuse to acquire the buffer if presentWhen is
+ // more than one second in the future beyond the desired present time
+ // (i.e. we'd be holding the buffer for a really long time).
+ const int MAX_FUTURE_NSEC = 1000000000ULL;
+ nsecs_t desiredPresent = front->mTimestamp;
+ if (presentWhen != 0 && desiredPresent > presentWhen &&
+ desiredPresent - presentWhen < MAX_FUTURE_NSEC)
+ {
+ ST_LOGV("pts defer: des=%lld when=%lld (%lld) now=%lld",
+ desiredPresent, presentWhen, desiredPresent - presentWhen,
+ systemTime(CLOCK_MONOTONIC));
+ return PRESENT_LATER;
+ }
+ if (presentWhen != 0) {
+ ST_LOGV("pts accept: %p[%d] sig=%lld des=%lld when=%lld (%lld)",
+ mSlots, buf, mSlots[buf].mFence->getSignalTime(),
+ desiredPresent, presentWhen, desiredPresent - presentWhen);
+ }
+ *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,7 +946,8 @@
return NO_ERROR;
}
-status_t BufferQueue::consumerConnect(const sp<ConsumerListener>& consumerListener) {
+status_t BufferQueue::consumerConnect(const sp<ConsumerListener>& consumerListener,
+ bool controlledByApp) {
ST_LOGV("consumerConnect");
Mutex::Autolock lock(mMutex);
@@ -907,6 +961,7 @@
}
mConsumerListener = consumerListener;
+ mConsumerControlledByApp = controlledByApp;
return NO_ERROR;
}
@@ -943,14 +998,24 @@
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 +1035,17 @@
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 +1061,26 @@
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);
- }
- }
+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;
+
+ // 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;
+
+ return mMaxAcquiredBufferCount;
}
-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::getMinMaxBufferCountLocked(bool async) const {
+ return getMinUndequeuedBufferCount(async) + 1;
}
-status_t BufferQueue::drainQueueAndFreeBuffersLocked() {
- status_t err = drainQueueLocked();
- if (err == NO_ERROR) {
- if (mQueue.empty()) {
- freeAllBuffersLocked();
- } else {
- freeAllBuffersExceptHeadLocked();
- }
- }
- return err;
-}
-
-int BufferQueue::getMinMaxBufferCountLocked() const {
- return getMinUndequeuedBufferCountLocked() + 1;
-}
-
-int BufferQueue::getMinUndequeuedBufferCountLocked() const {
- return mSynchronousMode ? mMaxAcquiredBufferCount :
- mMaxAcquiredBufferCount + 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,6 +1105,22 @@
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):
mConsumerListener(consumerListener) {}
diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp
index 4937b17..cd94ce1 100644
--- a/libs/gui/ConsumerBase.cpp
+++ b/libs/gui/ConsumerBase.cpp
@@ -51,7 +51,7 @@
return android_atomic_inc(&globalCounter);
}
-ConsumerBase::ConsumerBase(const sp<BufferQueue>& bufferQueue) :
+ConsumerBase::ConsumerBase(const sp<BufferQueue>& bufferQueue, bool controlledByApp) :
mAbandoned(false),
mBufferQueue(bufferQueue) {
// Choose a name using the PID and a process-unique ID.
@@ -66,7 +66,7 @@
listener = static_cast<BufferQueue::ConsumerListener*>(this);
proxy = new BufferQueue::ProxyConsumerListener(listener);
- status_t err = mBufferQueue->consumerConnect(proxy);
+ status_t err = mBufferQueue->consumerConnect(proxy, controlledByApp);
if (err != NO_ERROR) {
CB_LOGE("ConsumerBase: error connecting to BufferQueue: %s (%d)",
strerror(-err), err);
@@ -95,6 +95,7 @@
CB_LOGV("freeBufferLocked: slotIndex=%d", slotIndex);
mSlots[slotIndex].mGraphicBuffer = 0;
mSlots[slotIndex].mFence = Fence::NO_FENCE;
+ mSlots[slotIndex].mFrameNumber = 0;
}
// Used for refactoring, should not be in final interface
@@ -165,28 +166,25 @@
}
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);
+ mBufferQueue->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 = mBufferQueue->acquireBuffer(item, presentWhen);
if (err != NO_ERROR) {
return err;
}
@@ -195,21 +193,31 @@
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 +237,20 @@
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 = mBufferQueue->releaseBuffer(slot, mSlots[slot].mFrameNumber,
+ display, eglFence, mSlots[slot].mFence);
if (err == BufferQueue::STALE_BUFFER_SLOT) {
freeBufferLocked(slot);
}
@@ -243,4 +260,13 @@
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..b8c00af 100644
--- a/libs/gui/CpuConsumer.cpp
+++ b/libs/gui/CpuConsumer.cpp
@@ -30,15 +30,15 @@
namespace android {
-CpuConsumer::CpuConsumer(uint32_t maxLockedBuffers, bool synchronousMode) :
- ConsumerBase(new BufferQueue(true) ),
+CpuConsumer::CpuConsumer(const sp<BufferQueue>& 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);
}
@@ -55,6 +55,18 @@
mBufferQueue->setConsumerName(name);
}
+status_t CpuConsumer::setDefaultBufferSize(uint32_t width, uint32_t height)
+{
+ Mutex::Autolock _l(mMutex);
+ return mBufferQueue->setDefaultBufferSize(width, height);
+}
+
+status_t CpuConsumer::setDefaultBufferFormat(uint32_t defaultFormat)
+{
+ Mutex::Autolock _l(mMutex);
+ return mBufferQueue->setDefaultBufferFormat(defaultFormat);
+}
+
status_t CpuConsumer::lockNextBuffer(LockedBuffer *nativeBuffer) {
status_t err;
@@ -67,7 +79,7 @@
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 +201,9 @@
// 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/DummyConsumer.cpp b/libs/gui/DummyConsumer.cpp
deleted file mode 100644
index be47e0e..0000000
--- a/libs/gui/DummyConsumer.cpp
+++ /dev/null
@@ -1,43 +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 "DummyConsumer"
-// #define LOG_NDEBUG 0
-
-#include <gui/DummyConsumer.h>
-
-#include <utils/Log.h>
-#include <utils/String8.h>
-
-namespace android {
-
-DummyConsumer::DummyConsumer() {
- ALOGV("DummyConsumer");
-}
-
-DummyConsumer::~DummyConsumer() {
- ALOGV("~DummyConsumer");
-}
-
-void DummyConsumer::onFrameAvailable() {
- ALOGV("onFrameAvailable");
-}
-
-void DummyConsumer::onBuffersReleased() {
- ALOGV("onBuffersReleased");
-}
-
-}; // namespace android
diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp
index dc46a51..b8a3d28 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>
@@ -49,6 +50,12 @@
#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 = { 11, 8,
+ "__X_____X_____X___X_____XXXXXXX___XX_XXX_XX_XXXXXXXXXXXX_XXXXXXX_XX_X_____X_X___XX_XX___" };
+
// Transform matrices
static float mtxIdentity[16] = {
1, 0, 0, 0,
@@ -79,15 +86,8 @@
GLConsumer::GLConsumer(const sp<BufferQueue>& bq, GLuint tex,
- GLenum texTarget, bool useFenceSync) :
- ConsumerBase(bq),
- mUseFenceSync(useFenceSync),
- mTexTarget(texTarget) {}
-
-
-GLConsumer::GLConsumer(GLuint tex, bool allowSynchronousMode,
- GLenum texTarget, bool useFenceSync, const sp<BufferQueue> &bufferQueue) :
- ConsumerBase(bufferQueue == 0 ? new BufferQueue(allowSynchronousMode) : bufferQueue),
+ GLenum texTarget, bool useFenceSync, bool isControlledByApp) :
+ ConsumerBase(bq, isControlledByApp),
mCurrentTransform(0),
mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
mCurrentFence(Fence::NO_FENCE),
@@ -146,7 +146,7 @@
// 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 +161,7 @@
}
// Release the previous buffer.
- err = releaseAndUpdateLocked(item);
+ err = updateAndReleaseLocked(item);
if (err != NO_ERROR) {
// We always bind the texture.
glBindTexture(mTexTarget, mTexName);
@@ -172,8 +172,83 @@
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 = checkAndUpdateEglStateLocked();
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ // Update the GLConsumer state.
+ int buf = mCurrentTexture;
+ if (buf != BufferQueue::INVALID_BUFFER_SLOT) {
+
+ ST_LOGV("releaseTexImage:(slot=%d", buf);
+
+ // 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;
+ }
+
+ 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;
+ }
+
+ if (CC_UNLIKELY(mReleasedTexImageBuffer == NULL)) {
+ // The first time, create the debug texture in case the application
+ // continues to use it.
+ sp<GraphicBuffer> buffer = new GraphicBuffer(11, 8, 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*11+x] == 'X') ? 0xFF000000 : 0xFFFFFFFF;
+ }
+ bits += w;
+ }
+ buffer->unlock();
+ mReleasedTexImageBuffer = buffer;
+ }
+
+ mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT;
+ mCurrentTextureBuf = mReleasedTexImageBuffer;
+ mCurrentCrop.makeInvalid();
+ mCurrentTransform = 0;
+ mCurrentScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE;
+ mCurrentTimestamp = 0;
+ mCurrentFence = Fence::NO_FENCE;
+
+ // bind a dummy texture
+ glBindTexture(mTexTarget, mTexName);
+ bindUnslottedBufferLocked(mEglDisplay);
+ }
+
+ return NO_ERROR;
+}
+
+status_t GLConsumer::acquireBufferLocked(BufferQueue::BufferItem *item,
+ nsecs_t presentWhen) {
+ status_t err = ConsumerBase::acquireBufferLocked(item, presentWhen);
if (err != NO_ERROR) {
return err;
}
@@ -195,21 +270,25 @@
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;
}
@@ -232,7 +311,7 @@
if (mEglSlots[buf].mEglImage == EGL_NO_IMAGE_KHR) {
EGLImageKHR image = createImage(mEglDisplay, mSlots[buf].mGraphicBuffer);
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;
}
@@ -244,21 +323,25 @@
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 [?]
@@ -341,7 +424,8 @@
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);
@@ -510,7 +594,8 @@
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);
@@ -652,8 +737,6 @@
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;
@@ -840,11 +923,6 @@
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) {
@@ -887,25 +965,16 @@
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);
-}
-
-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/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index 63d7628..2e561df 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -37,7 +37,6 @@
QUEUE_BUFFER,
CANCEL_BUFFER,
QUERY,
- SET_SYNCHRONOUS_MODE,
CONNECT,
DISCONNECT,
};
@@ -81,10 +80,11 @@
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);
@@ -142,22 +142,11 @@
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(int api, bool producerControlledByApp, QueueBufferOutput* output) {
Parcel data, reply;
data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
data.writeInt32(api);
+ data.writeInt32(producerControlledByApp);
status_t result = remote()->transact(CONNECT, data, &reply);
if (result != NO_ERROR) {
return result;
@@ -209,13 +198,14 @@
} 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) {
@@ -252,20 +242,14 @@
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);
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(api, producerControlledByApp, output);
reply->writeInt32(res);
return NO_ERROR;
} break;
@@ -292,6 +276,7 @@
+ sizeof(crop)
+ sizeof(scalingMode)
+ sizeof(transform)
+ + sizeof(async)
+ fence->getFlattenedSize();
}
@@ -309,6 +294,7 @@
memcpy(p, &crop, sizeof(crop)); p += sizeof(crop);
memcpy(p, &scalingMode, sizeof(scalingMode)); p += sizeof(scalingMode);
memcpy(p, &transform, sizeof(transform)); p += sizeof(transform);
+ memcpy(p, &async, sizeof(async)); p += sizeof(async);
err = fence->flatten(p, size - (p - (char*)buffer), fds, count);
return err;
}
@@ -322,6 +308,7 @@
memcpy(&crop, p, sizeof(crop)); p += sizeof(crop);
memcpy(&scalingMode, p, sizeof(scalingMode)); p += sizeof(scalingMode);
memcpy(&transform, p, sizeof(transform)); p += sizeof(transform);
+ memcpy(&async, p, sizeof(async)); p += sizeof(async);
fence = new Fence();
err = fence->unflatten(p, size - (p - (const char*)buffer), fds, count);
return err;
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index a616c1e..998ea8a 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 @@
mTransformHint = 0;
mConsumerRunningBehind = false;
mConnectedToCpu = false;
+ mProducerControlledByApp = true;
+ mSwapIntervalZero = false;
}
Surface::~Surface() {
@@ -160,7 +163,6 @@
// 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,9 +170,9 @@
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,
@@ -182,7 +184,7 @@
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)"
@@ -278,7 +280,7 @@
sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE);
IGraphicBufferProducer::QueueBufferOutput output;
IGraphicBufferProducer::QueueBufferInput input(timestamp, crop, mScalingMode,
- mTransform, fence);
+ mTransform, mSwapIntervalZero, fence);
status_t err = mGraphicBufferProducer->queueBuffer(i, input, &output);
if (err != OK) {
ALOGE("queueBuffer: error queuing buffer to SurfaceTexture, %d", err);
@@ -486,7 +488,7 @@
ALOGV("Surface::connect");
Mutex::Autolock lock(mMutex);
IGraphicBufferProducer::QueueBufferOutput output;
- int err = mGraphicBufferProducer->connect(api, &output);
+ int err = mGraphicBufferProducer->connect(api, mProducerControlledByApp, &output);
if (err == NO_ERROR) {
uint32_t numPendingBuffers = 0;
output.deflate(&mDefaultWidth, &mDefaultHeight, &mTransformHint,
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index f345df8..94f21b6 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -633,7 +633,8 @@
sp<CpuConsumer> ScreenshotClient::getCpuConsumer() const {
if (mCpuConsumer == NULL) {
- mCpuConsumer = new CpuConsumer(1);
+ sp<BufferQueue> bq = new BufferQueue();
+ mCpuConsumer = new CpuConsumer(bq, 1);
mCpuConsumer->setName(String8("ScreenshotClient"));
}
return mCpuConsumer;
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index 62d215b..b691fc1 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -62,40 +62,40 @@
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(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);
+ 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, 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..f8a35b4 100644
--- a/libs/gui/tests/CpuConsumer_test.cpp
+++ b/libs/gui/tests/CpuConsumer_test.cpp
@@ -66,7 +66,8 @@
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());
diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp
index 7376b4c..1de9c27 100644
--- a/libs/gui/tests/SurfaceTextureClient_test.cpp
+++ b/libs/gui/tests/SurfaceTextureClient_test.cpp
@@ -40,7 +40,8 @@
ALOGV("Begin test: %s.%s", testInfo->test_case_name(),
testInfo->name());
- mST = new GLConsumer(123);
+ sp<BufferQueue> bq = new BufferQueue();
+ mST = new GLConsumer(bq, 123);
mSTC = new Surface(mST->getBufferQueue());
mANW = mSTC;
@@ -337,7 +338,7 @@
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 +346,7 @@
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 +361,6 @@
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 +381,6 @@
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 +401,6 @@
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 +426,6 @@
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 +445,6 @@
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 +523,6 @@
};
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 +656,6 @@
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,7 +708,8 @@
ASSERT_NE(EGL_NO_CONTEXT, mEglContext);
for (int i = 0; i < NUM_SURFACE_TEXTURES; i++) {
- sp<GLConsumer> st(new GLConsumer(i));
+ sp<BufferQueue> bq = new BufferQueue();
+ sp<GLConsumer> st(new GLConsumer(bq, i));
sp<Surface> stc(new Surface(st->getBufferQueue()));
mEglSurfaces[i] = eglCreateWindowSurface(mEglDisplay, myConfig,
static_cast<ANativeWindow*>(stc.get()), NULL);
diff --git a/libs/gui/tests/SurfaceTexture_test.cpp b/libs/gui/tests/SurfaceTexture_test.cpp
index dd6c435..e6d87db 100644
--- a/libs/gui/tests/SurfaceTexture_test.cpp
+++ b/libs/gui/tests/SurfaceTexture_test.cpp
@@ -385,7 +385,8 @@
virtual void SetUp() {
GLTest::SetUp();
- mGlConsumer = new GLConsumer(TEX_ID);
+ sp<BufferQueue> bq = new BufferQueue();
+ mGlConsumer = new GLConsumer(bq, TEX_ID);
mSurface = new Surface(mGlConsumer->getBufferQueue());
mANW = mSurface.get();
@@ -479,7 +480,8 @@
virtual void SetUp() {
GLTest::SetUp();
- mST = new GLConsumer(TEX_ID);
+ sp<BufferQueue> bq = new BufferQueue();
+ mST = new GLConsumer(bq, TEX_ID);
mSTC = new Surface(mST->getBufferQueue());
mANW = mSTC;
mTextureRenderer = new TextureRenderer(TEX_ID, mST);
@@ -942,7 +944,6 @@
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 +1210,8 @@
sp<ANativeWindow> mANW;
};
- ASSERT_EQ(OK, mST->setSynchronousMode(true));
-
sp<DisconnectWaiter> dw(new DisconnectWaiter());
- mST->getBufferQueue()->consumerConnect(dw);
+ mST->getBufferQueue()->consumerConnect(dw, false);
sp<Thread> pt(new ProducerThread(mANW));
@@ -1235,8 +1234,6 @@
// 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 +1253,6 @@
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 +1265,6 @@
}
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 +1297,6 @@
// 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 +1406,6 @@
Mutex mMutex;
};
- ASSERT_EQ(OK, mST->setSynchronousMode(true));
ASSERT_EQ(OK, mST->setDefaultMaxBufferCount(2));
sp<Thread> pt(new ProducerThread(mANW));
@@ -1808,32 +1798,6 @@
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 +2247,6 @@
}
};
- ASSERT_EQ(OK, mST->setSynchronousMode(true));
ASSERT_EQ(OK, mST->setDefaultMaxBufferCount(2));
runProducerThread(new PT());
@@ -2824,7 +2787,6 @@
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..953f6f9 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -87,7 +87,8 @@
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(),
diff --git a/libs/input/Android.mk b/libs/input/Android.mk
new file mode 100644
index 0000000..944ac7f
--- /dev/null
+++ b/libs/input/Android.mk
@@ -0,0 +1,79 @@
+# 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) \
+ IInputFlinger.cpp \
+ 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/IInputFlinger.cpp b/libs/input/IInputFlinger.cpp
new file mode 100644
index 0000000..e009731
--- /dev/null
+++ b/libs/input/IInputFlinger.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 <stdint.h>
+#include <sys/types.h>
+
+#include <binder/Parcel.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+
+#include <input/IInputFlinger.h>
+
+
+namespace android {
+
+class BpInputFlinger : public BpInterface<IInputFlinger> {
+public:
+ BpInputFlinger(const sp<IBinder>& impl) :
+ BpInterface<IInputFlinger>(impl) { }
+
+ virtual status_t doSomething() {
+ Parcel data, reply;
+ data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor());
+ remote()->transact(BnInputFlinger::DO_SOMETHING_TRANSACTION, data, &reply);
+ return reply.readInt32();
+ }
+};
+
+IMPLEMENT_META_INTERFACE(InputFlinger, "android.input.IInputFlinger");
+
+
+status_t BnInputFlinger::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
+ switch(code) {
+ case DO_SOMETHING_TRANSACTION: {
+ CHECK_INTERFACE(IInputFlinger, data, reply);
+ reply->writeInt32(0);
+ break;
+ }
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+ return NO_ERROR;
+}
+
+};
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..d6507f4
--- /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("debug.inputconsumer.resample", value, NULL);
+ if (length > 0) {
+ if (!strcmp("0", value)) {
+ return false;
+ }
+ if (strcmp("1", value)) {
+ ALOGD("Unrecognized property value for 'debug.inputconsumer.resample'. "
+ "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) {
+ 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/input/tests/Android.mk b/libs/input/tests/Android.mk
new file mode 100644
index 0000000..c62dff1
--- /dev/null
+++ b/libs/input/tests/Android.mk
@@ -0,0 +1,33 @@
+# Build the unit tests.
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+# Build the unit tests.
+test_src_files := \
+ InputChannel_test.cpp \
+ InputEvent_test.cpp \
+ InputPublisherAndConsumer_test.cpp
+
+shared_libraries := \
+ libinput \
+ libcutils \
+ libutils \
+ libbinder \
+ libui \
+ libstlport
+
+static_libraries := \
+ libgtest \
+ libgtest_main
+
+$(foreach file,$(test_src_files), \
+ $(eval include $(CLEAR_VARS)) \
+ $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
+ $(eval LOCAL_STATIC_LIBRARIES := $(static_libraries)) \
+ $(eval LOCAL_SRC_FILES := $(file)) \
+ $(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/input/tests/TestHelpers.h b/libs/input/tests/TestHelpers.h
new file mode 100644
index 0000000..fe87bb9
--- /dev/null
+++ b/libs/input/tests/TestHelpers.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 TESTHELPERS_H
+#define TESTHELPERS_H
+
+#include <unistd.h>
+
+#include <utils/threads.h>
+
+namespace android {
+
+class Pipe {
+public:
+ int sendFd;
+ int receiveFd;
+
+ Pipe() {
+ int fds[2];
+ ::pipe(fds);
+
+ receiveFd = fds[0];
+ sendFd = fds[1];
+ }
+
+ ~Pipe() {
+ if (sendFd != -1) {
+ ::close(sendFd);
+ }
+
+ if (receiveFd != -1) {
+ ::close(receiveFd);
+ }
+ }
+
+ status_t writeSignal() {
+ ssize_t nWritten = ::write(sendFd, "*", 1);
+ return nWritten == 1 ? 0 : -errno;
+ }
+
+ status_t readSignal() {
+ char buf[1];
+ ssize_t nRead = ::read(receiveFd, buf, 1);
+ return nRead == 1 ? 0 : nRead == 0 ? -EPIPE : -errno;
+ }
+};
+
+class DelayedTask : public Thread {
+ int mDelayMillis;
+
+public:
+ DelayedTask(int delayMillis) : mDelayMillis(delayMillis) { }
+
+protected:
+ virtual ~DelayedTask() { }
+
+ virtual void doTask() = 0;
+
+ virtual bool threadLoop() {
+ usleep(mDelayMillis * 1000);
+ doTask();
+ return false;
+ }
+};
+
+} // namespace android
+
+#endif // TESTHELPERS_H
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;
- }
- return size;
-}
-
-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 bytesPerPixel(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 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 BAD_VALUE;
+}
- size_t numEntries;
- const Info *i = gGetPixelFormatTable(&numEntries) + format;
- bool valid = uint32_t(format) < numEntries;
- if (!valid) {
- return BAD_INDEX;
+ssize_t bitsPerPixel(PixelFormat format) {
+ switch (format) {
+ 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;
}
-
- 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 @@
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 @@
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 @@
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 @@
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..623f8ed 100644
--- a/libs/ui/Region.cpp
+++ b/libs/ui/Region.cpp
@@ -697,7 +697,7 @@
size_t count = reg.mStorage.size();
Rect* rects = reg.mStorage.editArray();
while (count) {
- rects->translate(dx, dy);
+ rects->offsetBy(dx, dy);
rects++;
count--;
}
diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk
index cbfe7bd..abf4b2e 100644
--- a/libs/utils/Android.mk
+++ b/libs/utils/Android.mk
@@ -20,9 +20,7 @@
commonSources:= \
BasicHashtable.cpp \
BlobCache.cpp \
- BufferedTextOutput.cpp \
CallStack.cpp \
- Debug.cpp \
FileMap.cpp \
Flattenable.cpp \
JenkinsHash.cpp \
@@ -36,18 +34,12 @@
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)
@@ -74,9 +66,7 @@
LOCAL_SRC_FILES += Looper.cpp
endif
LOCAL_MODULE:= libutils
-LOCAL_STATIC_LIBRARIES := libz
-LOCAL_C_INCLUDES := \
- external/zlib
+LOCAL_STATIC_LIBRARIES := liblog
LOCAL_CFLAGS += $(host_commonCflags)
LOCAL_LDLIBS += $(host_commonLdlibs)
include $(BUILD_HOST_STATIC_LIBRARY)
@@ -90,15 +80,13 @@
LOCAL_SRC_FILES += Looper.cpp
endif
LOCAL_MODULE:= lib64utils
-LOCAL_STATIC_LIBRARIES := libz
-LOCAL_C_INCLUDES := \
- external/zlib
+LOCAL_STATIC_LIBRARIES := liblog
LOCAL_CFLAGS += $(host_commonCflags) -m64
LOCAL_LDLIBS += $(host_commonLdlibs)
include $(BUILD_HOST_STATIC_LIBRARY)
-# For the device
+# For the device, static
# =====================================================
include $(CLEAR_VARS)
@@ -123,14 +111,28 @@
LOCAL_LDLIBS += -lpthread
+LOCAL_STATIC_LIBRARIES := \
+ libcutils
+
LOCAL_SHARED_LIBRARIES := \
- liblog \
- libcutils \
- libdl \
- libcorkscrew \
- libz
+ libcorkscrew \
+ liblog \
+ libdl
LOCAL_MODULE:= libutils
+include $(BUILD_STATIC_LIBRARY)
+
+# For the device, shared
+# =====================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE:= libutils
+LOCAL_WHOLE_STATIC_LIBRARIES := libutils
+LOCAL_SHARED_LIBRARIES := \
+ liblog \
+ libcutils \
+ libdl \
+ libcorkscrew
+
include $(BUILD_SHARED_LIBRARY)
# Include subdirectory makefiles
diff --git a/libs/utils/BasicHashtable.cpp b/libs/utils/BasicHashtable.cpp
index fd51b7b..491d9e9 100644
--- a/libs/utils/BasicHashtable.cpp
+++ b/libs/utils/BasicHashtable.cpp
@@ -42,6 +42,10 @@
}
}
+BasicHashtableImpl::~BasicHashtableImpl()
+{
+}
+
void BasicHashtableImpl::dispose() {
if (mBuckets) {
releaseBuckets(mBuckets, mBucketCount);
diff --git a/libs/utils/CleanSpec.mk b/libs/utils/CleanSpec.mk
new file mode 100644
index 0000000..c3c5651
--- /dev/null
+++ b/libs/utils/CleanSpec.mk
@@ -0,0 +1,51 @@
+# 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.
+#
+
+# If you don't need to do a full clean build but would like to touch
+# a file or delete some intermediate files, add a clean step to the end
+# of the list. These steps will only be run once, if they haven't been
+# run before.
+#
+# E.g.:
+# $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
+# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates)
+#
+# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
+# files that are missing or have been moved.
+#
+# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
+# Use $(OUT_DIR) to refer to the "out" directory.
+#
+# If you need to re-do something that's already mentioned, just copy
+# the command and add it to the bottom of the list. E.g., if a change
+# that you made last week required touching a file and a change you
+# made today requires touching the same file, just copy the old
+# touch step and add it to the end of the list.
+#
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
+
+# For example:
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
+#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
+#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
+
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
+$(call add-clean-step, rm -rf $(HOST_OUT)/obj/STATIC_LIBRARIES/libutils_intermediates/import_includes)
+$(call add-clean-step, rm -rf $(HOST_OUT)/obj/STATIC_LIBRARIES/lib64utils_intermediates/import_includes)
diff --git a/libs/utils/Looper.cpp b/libs/utils/Looper.cpp
index a5e6645..c51df2d 100644
--- a/libs/utils/Looper.cpp
+++ b/libs/utils/Looper.cpp
@@ -84,6 +84,8 @@
LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking. errno=%d",
errno);
+ mIdling = false;
+
// 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);
@@ -214,9 +216,15 @@
mResponses.clear();
mResponseIndex = 0;
+ // We are about to idle.
+ mIdling = true;
+
struct epoll_event eventItems[EPOLL_MAX_EVENTS];
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
+ // No longer idling.
+ mIdling = false;
+
// Acquire lock.
mLock.lock();
@@ -558,4 +566,8 @@
} // release lock
}
+bool Looper::isIdling() const {
+ return mIdling;
+}
+
} // namespace android
diff --git a/libs/utils/RefBase.cpp b/libs/utils/RefBase.cpp
index e538f68..f398a82 100644
--- a/libs/utils/RefBase.cpp
+++ b/libs/utils/RefBase.cpp
@@ -23,7 +23,6 @@
#include <utils/CallStack.h>
#include <utils/Log.h>
#include <utils/threads.h>
-#include <utils/TextOutput.h>
#include <stdlib.h>
#include <stdio.h>
@@ -648,19 +647,4 @@
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/Static.cpp b/libs/utils/Static.cpp
index 624e917..3ed07a1 100644
--- a/libs/utils/Static.cpp
+++ b/libs/utils/Static.cpp
@@ -17,13 +17,16 @@
// 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 {
+// For String8.cpp
+extern void initialize_string8();
+extern void terminate_string8();
+
+// For String16.cpp
+extern void initialize_string16();
+extern void terminate_string16();
+
class LibUtilsFirstStatics
{
public:
@@ -43,49 +46,4 @@
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/String16.cpp b/libs/utils/String16.cpp
index 94e072f..b09b728 100644
--- a/libs/utils/String16.cpp
+++ b/libs/utils/String16.cpp
@@ -20,11 +20,8 @@
#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>
@@ -96,6 +93,19 @@
{
}
+String16::String16(StaticLinkage)
+ : mString(0)
+{
+ // this constructor is used when we can't rely on the static-initializers
+ // having run. In this case we always allocate an empty string. It's less
+ // efficient than using getEmptyString(), but we assume it's uncommon.
+
+ char16_t* data = static_cast<char16_t*>(
+ SharedBuffer::alloc(sizeof(char16_t))->data());
+ data[0] = 0;
+ mString = data;
+}
+
String16::String16(const String16& o)
: mString(o.mString)
{
@@ -409,10 +419,4 @@
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
index 562f026..e852d77 100644
--- a/libs/utils/String8.cpp
+++ b/libs/utils/String8.cpp
@@ -20,11 +20,8 @@
#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>
/*
@@ -46,6 +43,8 @@
extern int gDarwinCantLoadAllObjects;
int gDarwinIsReallyAnnoying;
+void initialize_string8();
+
static inline char* getEmptyString()
{
gEmptyStringBuf->acquire();
@@ -143,6 +142,19 @@
{
}
+String8::String8(StaticLinkage)
+ : mString(0)
+{
+ // this constructor is used when we can't rely on the static-initializers
+ // having run. In this case we always allocate an empty string. It's less
+ // efficient than using getEmptyString(), but we assume it's uncommon.
+
+ char* data = static_cast<char*>(
+ SharedBuffer::alloc(sizeof(char))->data());
+ data[0] = 0;
+ mString = data;
+}
+
String8::String8(const String8& o)
: mString(o.mString)
{
@@ -450,12 +462,6 @@
utf8_to_utf32(mString, length(), dst);
}
-TextOutput& operator<<(TextOutput& to, const String8& val)
-{
- to << val.string();
- return to;
-}
-
// ---------------------------------------------------------------------------
// Path functions
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
index ec2d82e..4b74889 100644
--- a/libs/utils/SystemClock.cpp
+++ b/libs/utils/SystemClock.cpp
@@ -36,63 +36,11 @@
#include <utils/Timers.h>
#define LOG_TAG "SystemClock"
-#include "utils/Log.h"
+#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()
diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp
index 7b877e0..ff74914 100644
--- a/libs/utils/Threads.cpp
+++ b/libs/utils/Threads.cpp
@@ -21,7 +21,6 @@
#include <utils/Log.h>
#include <cutils/sched_policy.h>
-#include <cutils/properties.h>
#include <stdio.h>
#include <stdlib.h>
@@ -68,20 +67,6 @@
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;
@@ -97,15 +82,10 @@
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 (prio >= ANDROID_PRIORITY_BACKGROUND) {
+ set_sched_policy(0, SP_BACKGROUND);
+ } else {
+ set_sched_policy(0, SP_FOREGROUND);
}
if (name) {
@@ -337,20 +317,10 @@
#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 (pri >= ANDROID_PRIORITY_BACKGROUND) {
+ rc = set_sched_policy(tid, SP_BACKGROUND);
+ } else if (getpriority(PRIO_PROCESS, tid) >= ANDROID_PRIORITY_BACKGROUND) {
+ rc = set_sched_policy(tid, SP_FOREGROUND);
}
if (rc) {
diff --git a/libs/utils/Timers.cpp b/libs/utils/Timers.cpp
index d4f8516..5293cd2 100644
--- a/libs/utils/Timers.cpp
+++ b/libs/utils/Timers.cpp
@@ -70,65 +70,3 @@
}
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/VectorImpl.cpp b/libs/utils/VectorImpl.cpp
index 70f49de..5a79647 100644
--- a/libs/utils/VectorImpl.cpp
+++ b/libs/utils/VectorImpl.cpp
@@ -504,15 +504,6 @@
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)
@@ -628,16 +619,6 @@
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
index 445a23a..58eb499 100644
--- a/libs/utils/misc.cpp
+++ b/libs/utils/misc.cpp
@@ -25,7 +25,6 @@
#include <sys/stat.h>
#include <string.h>
#include <errno.h>
-#include <assert.h>
#include <stdio.h>
#if defined(HAVE_PTHREADS)
@@ -38,56 +37,6 @@
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;
diff --git a/libs/utils/tests/Android.mk b/libs/utils/tests/Android.mk
index a2ca9c8..caedaff 100644
--- a/libs/utils/tests/Android.mk
+++ b/libs/utils/tests/Android.mk
@@ -6,12 +6,12 @@
test_src_files := \
BasicHashtable_test.cpp \
BlobCache_test.cpp \
+ BitSet_test.cpp \
Looper_test.cpp \
LruCache_test.cpp \
String8_test.cpp \
Unicode_test.cpp \
- Vector_test.cpp \
- ZipFileRO_test.cpp
+ Vector_test.cpp
shared_libraries := \
libz \
diff --git a/libs/utils/tests/BitSet_test.cpp b/libs/utils/tests/BitSet_test.cpp
new file mode 100644
index 0000000..752e56d
--- /dev/null
+++ b/libs/utils/tests/BitSet_test.cpp
@@ -0,0 +1,87 @@
+/*
+ * 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 "BitSet_test"
+
+#include <utils/BitSet.h>
+#include <cutils/log.h>
+#include <gtest/gtest.h>
+#include <unistd.h>
+
+namespace android {
+
+class BitSetTest : public testing::Test {
+protected:
+ BitSet32 b1;
+ BitSet32 b2;
+ virtual void TearDown() {
+ b1.clear();
+ b2.clear();
+ }
+};
+
+
+TEST_F(BitSetTest, BitWiseOr) {
+ b1.markBit(2);
+ b2.markBit(4);
+
+ BitSet32 tmp = b1 | b2;
+ EXPECT_EQ(tmp.count(), 2u);
+ EXPECT_TRUE(tmp.hasBit(2) && tmp.hasBit(4));
+ // Check that the operator is symmetric
+ EXPECT_TRUE((b2 | b1) == (b1 | b2));
+
+ b1 |= b2;
+ EXPECT_EQ(b1.count(), 2u);
+ EXPECT_TRUE(b1.hasBit(2) && b1.hasBit(4));
+ EXPECT_TRUE(b2.hasBit(4) && b2.count() == 1u);
+}
+TEST_F(BitSetTest, BitWiseAnd_Disjoint) {
+ b1.markBit(2);
+ b1.markBit(4);
+ b1.markBit(6);
+
+ BitSet32 tmp = b1 & b2;
+ EXPECT_TRUE(tmp.isEmpty());
+ // Check that the operator is symmetric
+ EXPECT_TRUE((b2 & b1) == (b1 & b2));
+
+ b2 &= b1;
+ EXPECT_TRUE(b2.isEmpty());
+ EXPECT_EQ(b1.count(), 3u);
+ EXPECT_TRUE(b1.hasBit(2) && b1.hasBit(4) && b1.hasBit(6));
+}
+
+TEST_F(BitSetTest, BitWiseAnd_NonDisjoint) {
+ b1.markBit(2);
+ b1.markBit(4);
+ b1.markBit(6);
+ b2.markBit(3);
+ b2.markBit(6);
+ b2.markBit(9);
+
+ BitSet32 tmp = b1 & b2;
+ EXPECT_EQ(tmp.count(), 1u);
+ EXPECT_TRUE(tmp.hasBit(6));
+ // Check that the operator is symmetric
+ EXPECT_TRUE((b2 & b1) == (b1 & b2));
+
+ b1 &= b2;
+ EXPECT_EQ(b1.count(), 1u);
+ EXPECT_EQ(b2.count(), 3u);
+ EXPECT_TRUE(b2.hasBit(3) && b2.hasBit(6) && b2.hasBit(9));
+}
+} // 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/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 @@
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/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index 56550b3..beaa560 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 @@
/*
- * 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 @@
// ----------------------------------------------------------------------------
-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 @@
// ----------------------------------------------------------------------------
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* 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 @@
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::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";
+ 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"
+ };
- 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;
+ // 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,7 +371,7 @@
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
@@ -344,7 +409,7 @@
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 +422,7 @@
api++;
}
}
-
+
if (mask & GLESv1_CM) {
init_api(dso, gl_names,
(__eglMustCastToProperFunctionPointerType*)
@@ -371,7 +436,7 @@
&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 @@
void* dso[3];
};
- String8 mDriverTag;
getProcAddressType getProcAddress;
public:
@@ -63,7 +62,7 @@
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..86637dc 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -230,9 +230,6 @@
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();
@@ -341,42 +338,11 @@
// ----------------------------------------------------------------------------
-#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..6c285d3 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -866,9 +866,7 @@
}
if (found) {
-#if USE_FAST_TLS_KEY
addr = gExtensionForwarders[slot];
-#endif
sGLExtentionMap.add(name, addr);
sGLExtentionSlot++;
}
@@ -1199,6 +1197,11 @@
{
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 +1209,7 @@
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_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 @@
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 @@
}
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 @@
}
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 @@
}
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 @@
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 @@
#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 @@
: \
);
- #elif defined(__mips__)
+#elif defined(__mips__)
#define API_ENTRY(_api) __attribute__((noinline)) _api
@@ -88,27 +86,21 @@
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)
- #define GL_EXTENSION(_n)
-
- #warning "eglGetProcAddress() partially supported"
-
+ #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 @@
#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 @@
"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 @@
: \
);
- #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 * 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 @@
#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 @@
"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 @@
: \
);
- #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" 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_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 @@
#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 @@
Jamie Gennis
Andy McFadden
+ Jesse Hall
Contact
@@ -21,7 +22,7 @@
Version
- Version 2, April 1, 2013
+ Version 3, June 26, 2013
Number
@@ -92,7 +93,9 @@
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 @@
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/tests/EGLTest/EGL_test.cpp b/opengl/tests/EGLTest/EGL_test.cpp
index c0daba2..86bbb84 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 @@
};
EXPECT_TRUE(eglChooseConfig(mEglDisplay, attrs, &config, 1, &numConfigs));
+ struct DummyConsumer : public BufferQueue::ConsumerListener {
+ 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 @@
{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 @@
{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 @@
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 @@
{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.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 "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 @@
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 @@
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/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 @@
@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 @@
@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 @@
@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 @@
@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/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 @@
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..ab636a9
--- /dev/null
+++ b/services/batteryservice/BatteryProperties.cpp
@@ -0,0 +1,60 @@
+/*
+ * 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();
+ 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(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/inputflinger/Android.mk b/services/inputflinger/Android.mk
new file mode 100644
index 0000000..e32d38a
--- /dev/null
+++ b/services/inputflinger/Android.mk
@@ -0,0 +1,50 @@
+# 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)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ InputFlinger.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libbinder \
+ libcutils \
+ libinput \
+ liblog \
+ libutils
+
+LOCAL_CFLAGS += -fvisibility=hidden
+
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
+
+LOCAL_MODULE := libinputflinger
+
+include $(BUILD_SHARED_LIBRARY)
+
+########################################################################
+# build input flinger executable
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ main.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libbinder \
+ libinputflinger \
+ libutils
+
+LOCAL_MODULE := inputflinger
+
+include $(BUILD_EXECUTABLE)
diff --git a/services/inputflinger/InputFlinger.cpp b/services/inputflinger/InputFlinger.cpp
new file mode 100644
index 0000000..9ea6ce5
--- /dev/null
+++ b/services/inputflinger/InputFlinger.cpp
@@ -0,0 +1,89 @@
+/*
+ * 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 "InputFlinger"
+
+#include "InputFlinger.h"
+
+#include <stdint.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+
+#include <binder/IPCThreadState.h>
+#include <binder/PermissionCache.h>
+#include <cutils/log.h>
+#include <private/android_filesystem_config.h>
+
+namespace android {
+
+const String16 sAccessInputFlingerPermission("android.permission.ACCESS_INPUT_FLINGER");
+const String16 sDumpPermission("android.permission.DUMP");
+
+
+InputFlinger::InputFlinger() :
+ BnInputFlinger() {
+ ALOGI("InputFlinger is starting");
+}
+
+InputFlinger::~InputFlinger() {
+}
+
+status_t InputFlinger::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
+ switch (code) {
+ case DO_SOMETHING_TRANSACTION:
+ const IPCThreadState* ipc = IPCThreadState::self();
+ const int pid = ipc->getCallingPid();
+ const int uid = ipc->getCallingUid();
+ if (!PermissionCache::checkPermission(sAccessInputFlingerPermission, pid, uid)) {
+ ALOGE("Permission Denial: "
+ "can't access InputFlinger from pid=%d, uid=%d", pid, uid);
+ return PERMISSION_DENIED;
+ }
+ break;
+ }
+
+ return BnInputFlinger::onTransact(code, data, reply, flags);
+}
+
+status_t InputFlinger::dump(int fd, const Vector<String16>& args) {
+ String8 result;
+ const IPCThreadState* ipc = IPCThreadState::self();
+ const int pid = ipc->getCallingPid();
+ const int uid = ipc->getCallingUid();
+ if ((uid != AID_SHELL)
+ && !PermissionCache::checkPermission(sDumpPermission, pid, uid)) {
+ result.appendFormat("Permission Denial: "
+ "can't dump SurfaceFlinger from pid=%d, uid=%d\n", pid, uid);
+ } else {
+ dumpInternal(result);
+ }
+ write(fd, result.string(), result.size());
+ return OK;
+}
+
+void InputFlinger::dumpInternal(String8& result) {
+ result.append("INPUT FLINGER (dumpsys inputflinger)\n");
+ result.append("... nothing here yet...\n");
+}
+
+status_t InputFlinger::doSomething() {
+ ALOGI("Did something...");
+ return OK;
+}
+
+}; // namespace android
diff --git a/services/inputflinger/InputFlinger.h b/services/inputflinger/InputFlinger.h
new file mode 100644
index 0000000..731ab17
--- /dev/null
+++ b/services/inputflinger/InputFlinger.h
@@ -0,0 +1,54 @@
+/*
+ * 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_INPUT_FLINGER_H
+#define ANDROID_INPUT_FLINGER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <cutils/compiler.h>
+#include <input/IInputFlinger.h>
+#include <utils/String8.h>
+#include <utils/String16.h>
+
+namespace android {
+
+class InputFlinger : public BnInputFlinger {
+public:
+ static char const* getServiceName() ANDROID_API {
+ return "inputflinger";
+ }
+
+ InputFlinger() ANDROID_API;
+
+ // IBinder interface
+ virtual status_t onTransact(uint32_t code,
+ const Parcel& data, Parcel* reply, uint32_t flags);
+ virtual status_t dump(int fd, const Vector<String16>& args);
+
+ // IInputFlinger interface
+ virtual status_t doSomething();
+
+private:
+ virtual ~InputFlinger();
+
+ void dumpInternal(String8& result);
+};
+
+} // namespace android
+
+#endif // ANDROID_INPUT_FLINGER_H
diff --git a/cmds/sensorservice/main_sensorservice.cpp b/services/inputflinger/main.cpp
similarity index 76%
copy from cmds/sensorservice/main_sensorservice.cpp
copy to services/inputflinger/main.cpp
index 8610627..3209a62 100644
--- a/cmds/sensorservice/main_sensorservice.cpp
+++ b/services/inputflinger/main.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 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.
@@ -15,11 +15,12 @@
*/
#include <binder/BinderService.h>
-#include <SensorService.h>
+#include "InputFlinger.h"
using namespace android;
int main(int argc, char** argv) {
- SensorService::publishAndJoinThreadPool();
+ ProcessState::self()->setThreadPoolMaxThreadCount(4);
+ BinderService<InputFlinger>::publishAndJoinThreadPool(true);
return 0;
}
diff --git a/services/powermanager/IPowerManager.cpp b/services/powermanager/IPowerManager.cpp
index 0265df3..3f5b81e 100644
--- a/services/powermanager/IPowerManager.cpp
+++ b/services/powermanager/IPowerManager.cpp
@@ -41,7 +41,8 @@
{
}
- 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,6 +50,7 @@
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);
}
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 @@
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 @@
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 @@
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..09f60a9 100644
--- a/services/sensorservice/CorrectedGyroSensor.cpp
+++ b/services/sensorservice/CorrectedGyroSensor.cpp
@@ -69,7 +69,7 @@
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 @@
// 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..0bf20db 100644
--- a/services/sensorservice/GravitySensor.cpp
+++ b/services/sensorservice/GravitySensor.cpp
@@ -77,7 +77,7 @@
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..25ae473 100644
--- a/services/sensorservice/LinearAccelerationSensor.cpp
+++ b/services/sensorservice/LinearAccelerationSensor.cpp
@@ -62,7 +62,7 @@
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..b146332 100644
--- a/services/sensorservice/OrientationSensor.cpp
+++ b/services/sensorservice/OrientationSensor.cpp
@@ -33,6 +33,9 @@
: 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,
@@ -73,7 +76,7 @@
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..725deb4 100644
--- a/services/sensorservice/RotationVectorSensor.cpp
+++ b/services/sensorservice/RotationVectorSensor.cpp
@@ -63,7 +63,7 @@
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;
@@ -112,7 +112,7 @@
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..2fa5dbd 100644
--- a/services/sensorservice/SensorDevice.cpp
+++ b/services/sensorservice/SensorDevice.cpp
@@ -65,30 +65,26 @@
}
}
-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)={ ",
+ result.appendFormat("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",
+ result.appendFormat("%4.1f%s",
info.rates.valueAt(j) / 1e6f,
j<info.rates.size()-1 ? ", " : "");
- result.append(buffer);
}
- snprintf(buffer, SIZE, " }, selected=%4.1f ms\n", info.delay / 1e6f);
- result.append(buffer);
+ result.appendFormat(" }, selected=%4.1f ms\n", info.delay / 1e6f);
}
}
diff --git a/services/sensorservice/SensorDevice.h b/services/sensorservice/SensorDevice.h
index 227dab6..b50e205 100644
--- a/services/sensorservice/SensorDevice.h
+++ b/services/sensorservice/SensorDevice.h
@@ -57,7 +57,7 @@
status_t activate(void* ident, int handle, int enabled);
status_t setDelay(void* ident, int handle, int64_t ns);
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..4014477 100644
--- a/services/sensorservice/SensorFusion.cpp
+++ b/services/sensorservice/SensorFusion.cpp
@@ -28,6 +28,7 @@
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 @@
}
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);
@@ -125,14 +136,14 @@
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 +152,6 @@
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..432adbc 100644
--- a/services/sensorservice/SensorFusion.h
+++ b/services/sensorservice/SensorFusion.h
@@ -44,7 +44,7 @@
Sensor mGyro;
Fusion mFusion;
bool mEnabled;
- float mGyroRate;
+ float mEstimatedGyroRate;
nsecs_t mTargetDelayNs;
nsecs_t mGyroTime;
vec4_t mAttitude;
@@ -60,7 +60,7 @@
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 +68,7 @@
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/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 6481584..e3d2a60 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -93,6 +93,7 @@
orientationIndex = i;
break;
case SENSOR_TYPE_GYROSCOPE:
+ case SENSOR_TYPE_GYROSCOPE_UNCALIBRATED:
hasGyro = true;
break;
case SENSOR_TYPE_GRAVITY:
@@ -108,54 +109,44 @@
// registered)
const SensorFusion& fusion(SensorFusion::getInstance());
- 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).
-
- registerVirtualSensor( new RotationVectorSensor() );
- registerVirtualSensor( new GravitySensor(list, count) );
- registerVirtualSensor( new LinearAccelerationSensor(list, count) );
-
- // these are optional
- registerVirtualSensor( new OrientationSensor() );
- registerVirtualSensor( new CorrectedGyroSensor(list, count) );
- }
-
// build the sensor list returned to users
mUserSensorList = mSensorList;
if (hasGyro) {
+ Sensor aSensor;
+
+ // Add Android virtual sensors if they're not already
+ // available in the HAL
+
+ aSensor = registerVirtualSensor( new RotationVectorSensor() );
+ if (virtualSensorsNeeds & (1<<SENSOR_TYPE_ROTATION_VECTOR)) {
+ mUserSensorList.add(aSensor);
+ }
+
+ aSensor = registerVirtualSensor( new GravitySensor(list, count) );
+ if (virtualSensorsNeeds & (1<<SENSOR_TYPE_GRAVITY)) {
+ mUserSensorList.add(aSensor);
+ }
+
+ aSensor = registerVirtualSensor( new LinearAccelerationSensor(list, count) );
+ if (virtualSensorsNeeds & (1<<SENSOR_TYPE_LINEAR_ACCELERATION)) {
+ mUserSensorList.add(aSensor);
+ }
+
+ 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() );
}
- 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);
- }
- }
-
// 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;
run("SensorService", PRIORITY_URGENT_DISPLAY);
mInitCheck = NO_ERROR;
@@ -163,7 +154,7 @@
}
}
-void SensorService::registerSensor(SensorInterface* s)
+Sensor SensorService::registerSensor(SensorInterface* s)
{
sensors_event_t event;
memset(&event, 0, sizeof(event));
@@ -175,12 +166,15 @@
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 +187,77 @@
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);
- }
- SensorFusion::getInstance().dump(result, buffer, SIZE);
- SensorDevice::getInstance().dump(result, buffer, SIZE);
+ s.getHandle());
- snprintf(buffer, SIZE, "%d active connections\n",
- mActiveConnections.size());
- result.append(buffer);
- snprintf(buffer, SIZE, "Active sensors:\n");
- result.append(buffer);
+ if (s.getMinDelay() > 0) {
+ result.appendFormat(
+ "maxRate=%7.2fHz | ", 1e6f / s.getMinDelay());
+ } else {
+ result.append(s.getMinDelay() == 0
+ ? "on-demand | "
+ : "one-shot | ");
+ }
+
+ 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);
+ SensorDevice::getInstance().dump(result);
+
+ result.appendFormat("%d active connections\n", mActiveConnections.size());
+ 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);
}
}
write(fd, result.string(), result.size());
@@ -246,7 +270,8 @@
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) {
@@ -283,7 +308,7 @@
// 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 +360,7 @@
// 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;
@@ -423,18 +448,6 @@
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];
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index fcdbc7d..69e5dbb 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -50,12 +50,13 @@
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 nsecs_t MINIMUM_EVENTS_PERIOD = 1000000; // 1000 Hz
+ static const char* WAKE_LOCK_NAME;
- SensorService();
+ static char const* getServiceName() ANDROID_API { return "sensorservice"; }
+ SensorService() ANDROID_API;
virtual ~SensorService();
virtual void onFirstRef();
@@ -110,17 +111,16 @@
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;
@@ -140,8 +140,6 @@
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 disable(const sp<SensorEventConnection>& connection, int handle);
diff --git a/cmds/sensorservice/main_sensorservice.cpp b/services/sensorservice/main_sensorservice.cpp
similarity index 96%
rename from cmds/sensorservice/main_sensorservice.cpp
rename to 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..8dbb9c5 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -6,7 +6,6 @@
DisplayDevice.cpp \
EventThread.cpp \
FrameTracker.cpp \
- GLExtensions.cpp \
Layer.cpp \
LayerDim.cpp \
MessageQueue.cpp \
@@ -18,6 +17,13 @@
DisplayHardware/HWComposer.cpp \
DisplayHardware/PowerHAL.cpp \
DisplayHardware/VirtualDisplaySurface.cpp \
+ EventLog/EventLogTags.logtags \
+ EventLog/EventLog.cpp \
+ RenderEngine/GLExtensions.cpp \
+ RenderEngine/RenderEngine.cpp \
+ RenderEngine/GLES10RenderEngine.cpp \
+ RenderEngine/GLES11RenderEngine.cpp
+
LOCAL_CFLAGS:= -DLOG_TAG=\"SurfaceFlinger\"
LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
@@ -30,7 +36,6 @@
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 +46,8 @@
LOCAL_CFLAGS += -DNUM_FRAMEBUFFER_SURFACE_BUFFERS=$(NUM_FRAMEBUFFER_SURFACE_BUFFERS)
endif
+LOCAL_CFLAGS += -fvisibility=hidden
+
LOCAL_SHARED_LIBRARIES := \
libcutils \
liblog \
@@ -58,6 +65,22 @@
include $(BUILD_SHARED_LIBRARY)
###############################################################
+# build surfaceflinger's executable
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ main_surfaceflinger.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libsurfaceflinger \
+ 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/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/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 68b0b7f..c67f4d8 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.
*
@@ -189,7 +171,7 @@
void DisplayDevice::flip(const Region& dirty) const
{
- checkGLErrors();
+ mFlinger->getRenderEngine().checkErrors();
EGLDisplay dpy = mDisplay;
EGLSurface surface = mSurface;
@@ -206,6 +188,25 @@
mPageFlipCount++;
}
+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
@@ -246,28 +247,22 @@
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);
+ 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);
}
// ----------------------------------------------------------------------------
@@ -416,7 +411,7 @@
mScissor = mGlobalTransform.transform(viewport);
if (mScissor.isEmpty()) {
- mScissor.set(getBounds());
+ mScissor = getBounds();
}
mOrientation = orientation;
@@ -424,9 +419,9 @@
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 +438,6 @@
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..748be1a 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -119,6 +119,8 @@
int32_t getHwcDisplayId() const { return mHwcDisplayId; }
const wp<IBinder>& getDisplayToken() const { return mDisplayToken; }
+ status_t prepareFrame(const HWComposer& hwc) const;
+
void swapBuffers(HWComposer& hwc) const;
status_t compositionComplete() const;
@@ -133,10 +135,8 @@
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 +153,7 @@
* Debugging
*/
uint32_t getPageFlipCount() const;
- void dump(String8& result, char* buffer, size_t SIZE) const;
+ void dump(String8& result) const;
private:
/*
diff --git a/services/surfaceflinger/DisplayHardware/DisplaySurface.h b/services/surfaceflinger/DisplayHardware/DisplaySurface.h
index 2eca3cb..b0f460d 100644
--- a/services/surfaceflinger/DisplayHardware/DisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/DisplaySurface.h
@@ -32,6 +32,18 @@
public:
virtual sp<IGraphicBufferProducer> getIGraphicBufferProducer() const = 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
// older drivers for synchronization.
diff --git a/services/surfaceflinger/DisplayHardware/FloatRect.h b/services/surfaceflinger/DisplayHardware/FloatRect.h
new file mode 100644
index 0000000..b08a951
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/FloatRect.h
@@ -0,0 +1,42 @@
+/*
+ * 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_SF_FLOAT_RECT
+#define ANDROID_SF_FLOAT_RECT
+
+#include <utils/TypeHelpers.h>
+
+namespace android {
+
+class FloatRect
+{
+public:
+ float left;
+ float top;
+ float right;
+ float bottom;
+
+ inline FloatRect() { }
+ inline FloatRect(const Rect& other)
+ : left(other.left), top(other.top), right(other.right), bottom(other.bottom) { }
+
+ 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..419b81c 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
@@ -51,7 +51,7 @@
*/
FramebufferSurface::FramebufferSurface(HWComposer& hwc, int disp) :
- ConsumerBase(new BufferQueue(true, new GraphicBufferAlloc())),
+ ConsumerBase(new BufferQueue(new GraphicBufferAlloc())),
mDisplayType(disp),
mCurrentBufferSlot(-1),
mCurrentBuffer(0),
@@ -64,7 +64,6 @@
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);
}
@@ -72,6 +71,10 @@
return getBufferQueue();
}
+status_t FramebufferSurface::prepareFrame(CompositionType compositionType) {
+ return NO_ERROR;
+}
+
status_t FramebufferSurface::advanceFrame() {
// Once we remove FB HAL support, we can call nextBuffer() from here
// instead of using onFrameAvailable(). No real benefit, except it'll be
@@ -83,7 +86,7 @@
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 +106,9 @@
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 +147,8 @@
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 +179,10 @@
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..92a7f9b 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
@@ -41,6 +41,7 @@
virtual sp<IGraphicBufferProducer> getIGraphicBufferProducer() const;
+ virtual status_t prepareFrame(CompositionType compositionType);
virtual status_t compositionComplete();
virtual status_t advanceFrame();
virtual void onFrameCommitted();
@@ -55,8 +56,7 @@
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..f6256f9 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,9 +48,9 @@
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)
+#ifndef HWC_DEVICE_API_VERSION_1_3
+#define HWC_DEVICE_API_VERSION_1_3 HARDWARE_DEVICE_API_VERSION_2(1, 3, HWC_HEADER_VERSION)
+#endif
#define MIN_HWC_HEADER_VERSION HWC_HEADER_VERSION
@@ -156,8 +154,8 @@
// 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
+ if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_3)) {
+ // 1.3 adds support for virtual displays
mNumDisplays = MAX_DISPLAYS;
} else if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) {
// 1.1 adds support for multiple displays
@@ -550,9 +548,6 @@
// 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 +580,7 @@
}
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)) {
@@ -883,10 +878,25 @@
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 +972,7 @@
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);
@@ -1030,6 +1040,8 @@
}
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..0462bcc 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -47,6 +47,7 @@
class GraphicBuffer;
class Fence;
+class FloatRect;
class Region;
class String8;
class SurfaceFlinger;
@@ -158,7 +159,7 @@
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 +276,7 @@
friend class VSyncThread;
// for debugging ----------------------------------------------------------
- void dump(String8& out, char* scratch, size_t SIZE) const;
+ void dump(String8& out) const;
private:
void loadHwcModule();
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index 2838b23..57cb361 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -14,27 +14,89 @@
* 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)
+: ConsumerBase(new BufferQueue()),
+ 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] = mBufferQueue;
+
+ 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());
+ mBufferQueue->setConsumerName(ConsumerBase::mName);
+ mBufferQueue->setConsumerUsageBits(GRALLOC_USAGE_HW_COMPOSER);
+ mBufferQueue->setDefaultBufferSize(sinkWidth, sinkHeight);
+ mBufferQueue->setDefaultMaxBufferCount(2);
}
VirtualDisplaySurface::~VirtualDisplaySurface() {
}
sp<IGraphicBufferProducer> VirtualDisplaySurface::getIGraphicBufferProducer() const {
- return mSink;
+ if (mDisplayId >= 0) {
+ return static_cast<IGraphicBufferProducer*>(
+ const_cast<VirtualDisplaySurface*>(this));
+ } else {
+ // There won't be any interaction with HWC for this virtual display,
+ // so the GLES driver can pass buffers directly to the sink.
+ return mSource[SOURCE_SINK];
+ }
+}
+
+status_t VirtualDisplaySurface::prepareFrame(CompositionType compositionType) {
+ if (mDisplayId < 0)
+ return NO_ERROR;
+
+ VDS_LOGW_IF(mDbgState != DBG_STATE_IDLE,
+ "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 +104,317 @@
}
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;
+
+ status_t result;
+ sp<Fence> outFence;
+ if (mCompositionType != COMPOSITION_GLES) {
+ // Dequeue an output buffer from the sink
+ uint32_t transformHint, numPendingBuffers;
+ mQueueBufferOutput.deflate(&mSinkBufferWidth, &mSinkBufferHeight,
+ &transformHint, &numPendingBuffers);
+ int sslot;
+ result = dequeueBuffer(SOURCE_SINK, 0, &sslot, &outFence, false);
+ if (result < 0)
+ return result;
+ mOutputProducerSlot = mapSource2ProducerSlot(SOURCE_SINK, sslot);
+ }
+
+ if (mCompositionType == COMPOSITION_HWC) {
+ // We just dequeued the output buffer, use it for FB as well
+ mFbProducerSlot = mOutputProducerSlot;
+ mFbFence = outFence;
+ } else if (mCompositionType == COMPOSITION_GLES) {
+ mOutputProducerSlot = mFbProducerSlot;
+ outFence = mFbFence;
+ } else {
+ // mFbFence and mFbProducerSlot were set in queueBuffer,
+ // and mOutputProducerSlot and outFence were set above when dequeueing
+ // the sink buffer.
+ }
+
+ 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());
+
+ result = mHwc.fbPost(mDisplayId, mFbFence, fbBuffer);
+ if (result == NO_ERROR) {
+ result = mHwc.setOutputBuffer(mDisplayId, outFence, outBuffer);
+ }
+
+ return result;
}
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(),
+ Rect(mSinkBufferWidth, mSinkBufferHeight),
+ NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, false, 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, bool async) {
+ 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_LOGV("dequeueBuffer %dx%d fmt=%d usage=%#x", w, h, format, usage);
+
+ mProducerUsage = usage | GRALLOC_USAGE_HW_COMPOSER;
+ Source source = fbSourceForCompositionType(mCompositionType);
+ if (source == SOURCE_SINK) {
+ mSinkBufferWidth = w;
+ mSinkBufferHeight = h;
+ }
+
+ int sslot;
+ status_t result = dequeueBuffer(source, format, &sslot, fence, async);
+ 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 = mBufferQueue->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;
+ Rect crop;
+ int scalingMode;
+ uint32_t transform;
+ bool async;
+ input.deflate(×tamp, &crop, &scalingMode, &transform,
+ &async, &mFbFence);
+
+ mFbProducerSlot = pslot;
+ }
+
+ *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(int api, bool producerControlledByApp,
+ QueueBufferOutput* output) {
+ QueueBufferOutput qbo;
+ status_t result = mSource[SOURCE_SINK]->connect(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;
+ mFbProducerSlot = -1;
+ mOutputProducerSlot = -1;
+}
+
+// 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..dc9655b 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,186 @@
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,
+ private BnGraphicBufferProducer,
+ private ConsumerBase {
public:
VirtualDisplaySurface(HWComposer& hwc, int32_t dispId,
const sp<IGraphicBufferProducer>& sink,
const String8& name);
+ //
+ // DisplaySurface interface
+ //
virtual sp<IGraphicBufferProducer> getIGraphicBufferProducer() const;
-
+ 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(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, bool async);
+ void updateQueueBufferOutput(const QueueBufferOutput& qbo);
+ void resetPerFrameState();
+
+ // 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;
+
+ // 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 | 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,
+ // no buffer dequeued, but we know the buffer source for the frame
+ 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/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..4126c8a 100644
--- a/services/surfaceflinger/EventThread.cpp
+++ b/services/surfaceflinger/EventThread.cpp
@@ -320,7 +320,7 @@
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");
diff --git a/services/surfaceflinger/EventThread.h b/services/surfaceflinger/EventThread.h
index 1934f98..35ac0c8 100644
--- a/services/surfaceflinger/EventThread.h
+++ b/services/surfaceflinger/EventThread.h
@@ -84,7 +84,7 @@
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();
@@ -104,7 +104,7 @@
// protected by mLock
SortedVector< wp<Connection> > mDisplayEventConnections;
Vector< DisplayEventReceiver::Event > mPendingEvents;
- DisplayEventReceiver::Event mVSyncEvent[HWC_DISPLAY_TYPES_SUPPORTED];
+ DisplayEventReceiver::Event mVSyncEvent[HWC_NUM_DISPLAY_TYPES];
bool mUseSoftwareVSync;
// for debugging
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 @@
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 @@
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 @@
if (records[idx].frameReadyTime < INT64_MAX) {
records[idx].frameReadyFence = NULL;
numFences--;
+ updated = true;
}
}
@@ -120,11 +143,67 @@
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 @@
// 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 @@
// 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 @@
// 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 @@
// 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/GLExtensions.cpp b/services/surfaceflinger/GLExtensions.cpp
deleted file mode 100644
index e5fb083..0000000
--- a/services/surfaceflinger/GLExtensions.cpp
+++ /dev/null
@@ -1,138 +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 <stdlib.h>
-#include <stdio.h>
-#include <stdint.h>
-
-#include "GLExtensions.h"
-
-namespace android {
-// ---------------------------------------------------------------------------
-
-ANDROID_SINGLETON_STATIC_INSTANCE( GLExtensions )
-
-GLExtensions::GLExtensions()
- : mHaveTextureExternal(false),
- mHaveNpot(false),
- mHaveDirectTexture(false),
- mHaveFramebufferObject(false)
-{
-}
-
-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)
-{
- 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;
- do {
- head = strchr(curr, ' ');
- String8 s(curr, head ? head-curr : strlen(curr));
- if (s.length()) {
- mExtensionList.add(s);
- }
- 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;
- }
-}
-
-bool GLExtensions::hasExtension(char const* extension) const
-{
- const String8 s(extension);
- return mExtensionList.indexOf(s) >= 0;
-}
-
-char const* GLExtensions::getVendor() const {
- return mVendor.string();
-}
-
-char const* GLExtensions::getRenderer() const {
- return mRenderer.string();
-}
-
-char const* GLExtensions::getVersion() const {
- return mVersion.string();
-}
-
-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/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 4779804..401b0f3 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 @@
mName("unnamed"),
mDebug(false),
mFormat(PIXEL_FORMAT_NONE),
- mGLExtensions(GLExtensions::getInstance()),
mOpaqueLayer(true),
mTransactionFlags(0),
mQueuedFrames(0),
@@ -103,18 +104,21 @@
// drawing state & current state are identical
mDrawingState = mCurrentState;
+
+ nsecs_t displayPeriod =
+ flinger->getHwComposer().getRefreshPeriod(HWC_DISPLAY_PRIMARY);
+ mFrameTracker.setDisplayRefreshPeriod(displayPeriod);
}
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);
+ mSurfaceFlingerConsumer = new SurfaceFlingerConsumer(bq, mTextureName,
+ GL_TEXTURE_EXTERNAL_OES, false);
mSurfaceFlingerConsumer->setConsumerUsageBits(getEffectiveUsage(0));
mSurfaceFlingerConsumer->setFrameAvailableListener(this);
- mSurfaceFlingerConsumer->setSynchronousMode(true);
mSurfaceFlingerConsumer->setName(mName);
#ifdef TARGET_DISABLE_TRIPLE_BUFFERING
@@ -134,6 +138,7 @@
c->detachLayer(this);
}
mFlinger->deleteTextureAsync(mTextureName);
+ mFrameTracker.logAndResetStats(mName);
}
// ---------------------------------------------------------------------------
@@ -164,21 +169,13 @@
// 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());
@@ -235,15 +232,6 @@
return mSurfaceFlingerConsumer->getBufferQueue();
}
-//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,42 +253,41 @@
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?
+ // 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.transform.transform(s.active.crop));
activeCrop.intersect(hw->getViewport(), &activeCrop);
activeCrop = s.transform.inverse().transform(activeCrop);
@@ -309,11 +296,14 @@
// 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 +315,14 @@
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 +346,7 @@
}
// 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 :
@@ -484,6 +473,8 @@
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();
@@ -494,49 +485,24 @@
mSurfaceFlingerConsumer->getTransformMatrix(textureMatrix);
// Set things up for texturing.
- glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTextureName);
- GLenum filter = GL_NEAREST;
- if (useFiltering) {
- filter = GL_LINEAR;
- }
- 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);
+ engine.setupLayerTexturing(mTextureName, useFiltering, textureMatrix);
} 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
{
- 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());
+ mFlinger->getRenderEngine().clearWithColor(
+ mesh.getVertices(), mesh.getVertexCount(),
+ red, green, blue, alpha);
}
void Layer::clearWithOpenGL(
@@ -547,42 +513,11 @@
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);
- }
- }
+ const State& s(getDrawingState());
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;
- };
-
-
/*
* NOTE: the way we compute the texture coordinates here produces
* different results than when we take the HWC path -- in the later case
@@ -604,26 +539,25 @@
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;
+ // TODO: we probably want to generate the texture coords with the mesh
+ // here we assume that we only have 4 vertices
+ float texCoords[4][2];
+ texCoords[0][0] = left;
+ texCoords[0][1] = top;
+ texCoords[1][0] = left;
+ texCoords[1][1] = bottom;
+ texCoords[2][0] = right;
+ texCoords[2][1] = bottom;
+ texCoords[3][0] = right;
+ texCoords[3][1] = top;
for (int i = 0; i < 4; i++) {
- texCoords[i].v = 1.0f - texCoords[i].v;
+ texCoords[i][1] = 1.0f - texCoords[i][1];
}
- 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());
-
- glDisableClientState(GL_TEXTURE_COORD_ARRAY);
- glDisable(GL_BLEND);
+ RenderEngine& engine(mFlinger->getRenderEngine());
+ engine.setupLayerBlending(mPremultipliedAlpha, isOpaque(), s.alpha);
+ engine.drawMesh2D(mesh.getVertices(), texCoords, mesh.getVertexCount());
+ engine.disableBlending();
}
void Layer::setFiltering(bool filtering) {
@@ -641,15 +575,17 @@
// 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:
+ return false;
+ }
+ // in all other case, we have no blending (also for unknown formats)
+ return true;
}
// ----------------------------------------------------------------------------
@@ -658,13 +594,15 @@
void Layer::computeGeometry(const sp<const DisplayDevice>& hw, LayerMesh* 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);
}
+ // subtract the transparent region and snap to the bounds
+ win = reduce(win, s.activeTransparentRegion);
if (mesh) {
tr.transform(mesh->mVertices[0], win.left, win.top);
tr.transform(mesh->mVertices[1], win.left, win.bottom);
@@ -731,11 +669,11 @@
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 +683,46 @@
" 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 +742,23 @@
// 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 +915,6 @@
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;
@@ -1085,9 +1018,23 @@
};
- 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 +1082,12 @@
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 +1122,21 @@
// 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 +1149,6 @@
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 +1158,19 @@
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 +1178,10 @@
mFrameTracker.clear();
}
+void Layer::logFrameStats() {
+ mFrameTracker.logAndResetStats(mName);
+}
+
// ---------------------------------------------------------------------------
Layer::LayerCleaner::LayerCleaner(const sp<SurfaceFlinger>& flinger,
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 2765db1..9093116 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,17 @@
#include "Transform.h"
#include "DisplayHardware/HWComposer.h"
+#include "DisplayHardware/FloatRect.h"
namespace android {
// ---------------------------------------------------------------------------
class Client;
+class Colorizer;
class DisplayDevice;
class GraphicBuffer;
class SurfaceFlinger;
-class GLExtensions;
// ---------------------------------------------------------------------------
@@ -112,14 +111,15 @@
class LayerMesh {
friend class Layer;
- GLfloat mVertices[4][2];
+ typedef GLfloat float2[2];
+ float2 mVertices[4];
size_t mNumVertices;
public:
LayerMesh() :
mNumVertices(4) {
}
- GLfloat const* getVertices() const {
- return &mVertices[0][0];
+ float2 const* getVertices() const {
+ return mVertices;
}
size_t getVertexCount() const {
return mNumVertices;
@@ -130,6 +130,7 @@
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,8 +147,6 @@
bool setCrop(const Rect& crop);
bool setLayerStack(uint32_t layerStack);
- void commitTransaction();
-
uint32_t getTransactionFlags(uint32_t flags);
uint32_t setTransactionFlags(uint32_t flags);
@@ -156,89 +155,13 @@
sp<IBinder> getHandle();
sp<BufferQueue> getBufferQueue() const;
- String8 getName() const;
+ const String8& getName() const;
// -----------------------------------------------------------------------
+ // Virtuals
virtual const char* getTypeId() const { return "Layer"; }
- virtual void setGeometry(const sp<const DisplayDevice>& hw,
- HWComposer::HWCLayerInterface& layer);
- virtual void setPerFrameData(const sp<const DisplayDevice>& hw,
- HWComposer::HWCLayerInterface& layer);
- virtual void setAcquireFence(const sp<const DisplayDevice>& hw,
- HWComposer::HWCLayerInterface& layer);
-
- /*
- * called after page-flip
- */
- virtual void onLayerDisplayed(const sp<const DisplayDevice>& hw,
- HWComposer::HWCLayerInterface* layer);
-
- /*
- * called before composition.
- * returns true if the layer has pending updates.
- */
- virtual bool onPreComposition();
-
- /*
- * called after composition.
- */
- virtual 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;
-
- /*
- * 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);
-
- /*
- * 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);
-
- /*
- * 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);
-
- /*
- * setVisibleNonTransparentRegion - called when the visible and
- * non-transparent region changes.
- */
- virtual void setVisibleNonTransparentRegion(const Region&
- visibleNonTransparentRegion);
-
- /*
- * latchBuffer - called each time the screen is redrawn and returns whether
- * the visible regions need to be recomputed (this is a fairly heavy
- * 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
*/
@@ -266,28 +189,96 @@
*/
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);
+ void setPerFrameData(const sp<const DisplayDevice>& hw,
+ HWComposer::HWCLayerInterface& layer);
+ void setAcquireFence(const sp<const DisplayDevice>& hw,
+ HWComposer::HWCLayerInterface& layer);
+
+ /*
+ * called after page-flip
+ */
+ void onLayerDisplayed(const sp<const DisplayDevice>& hw,
+ HWComposer::HWCLayerInterface* layer);
+
+ /*
+ * called before composition.
+ * returns true if the layer has pending updates.
+ */
+ bool onPreComposition();
+
+ /*
+ * called after composition.
+ */
+ void onPostComposition();
+
+ /*
+ * draw - performs some global clipping optimizations
+ * and calls onDraw().
+ */
+ 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.
+ */
+ 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.
+ */
+ 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.
+ */
+ void setCoveredRegion(const Region& coveredRegion);
+
+ /*
+ * setVisibleNonTransparentRegion - called when the visible and
+ * non-transparent region changes.
+ */
+ void setVisibleNonTransparentRegion(const Region&
+ visibleNonTransparentRegion);
+
+ /*
+ * latchBuffer - called each time the screen is redrawn and returns whether
+ * the visible regions need to be recomputed (this is a fairly heavy
+ * 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.
+ */
+ 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 +289,16 @@
// 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,9 +324,13 @@
// 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);
@@ -354,7 +349,6 @@
String8 mName;
mutable bool mDebug;
PixelFormat mFormat;
- const GLExtensions& mGLExtensions;
bool mOpaqueLayer;
// these are protected by an external lock
diff --git a/services/surfaceflinger/LayerDim.cpp b/services/surfaceflinger/LayerDim.cpp
index 36bafdb..062ad46 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 @@
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);
+ RenderEngine& engine(mFlinger->getRenderEngine());
+ engine.setupDimLayerBlending(s.alpha);
+ engine.drawMesh2D(mesh.getVertices(), NULL, mesh.getVertexCount());
+ 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 @@
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/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/services/surfaceflinger/RenderEngine/GLES10RenderEngine.h b/services/surfaceflinger/RenderEngine/GLES10RenderEngine.h
new file mode 100644
index 0000000..f9c7c04
--- /dev/null
+++ b/services/surfaceflinger/RenderEngine/GLES10RenderEngine.h
@@ -0,0 +1,40 @@
+/*
+ * 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_GLES10RENDERENGINE_H_
+#define SF_GLES10RENDERENGINE_H_
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include "GLES11RenderEngine.h"
+
+// ---------------------------------------------------------------------------
+namespace android {
+// ---------------------------------------------------------------------------
+
+class GLES10RenderEngine : public GLES11RenderEngine {
+ virtual ~GLES10RenderEngine();
+protected:
+ virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque, int alpha);
+};
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+// ---------------------------------------------------------------------------
+
+#endif /* SF_GLES10RENDERENGINE_H_ */
diff --git a/services/surfaceflinger/RenderEngine/GLES11RenderEngine.cpp b/services/surfaceflinger/RenderEngine/GLES11RenderEngine.cpp
new file mode 100644
index 0000000..19499c9
--- /dev/null
+++ b/services/surfaceflinger/RenderEngine/GLES11RenderEngine.cpp
@@ -0,0 +1,217 @@
+/*
+ * 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 <utils/String8.h>
+#include <cutils/compiler.h>
+
+#include "GLES11RenderEngine.h"
+#include "GLExtensions.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[] = { 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);
+}
+
+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 w, size_t h) {
+ 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 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 ? alpha*As : 1.0
+ 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(size_t textureName,
+ bool useFiltering, const float* textureMatrix) {
+ glBindTexture(GL_TEXTURE_EXTERNAL_OES, textureName);
+ GLenum filter = GL_NEAREST;
+ if (useFiltering) {
+ filter = GL_LINEAR;
+ }
+ 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);
+ 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);
+}
+
+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::clearWithColor(const float vertices[][2] , size_t count,
+ float red, float green, float blue, float alpha) {
+ glColor4f(red, green, blue, alpha);
+ glDisable(GL_TEXTURE_EXTERNAL_OES);
+ glDisable(GL_TEXTURE_2D);
+ glDisable(GL_BLEND);
+ glVertexPointer(2, GL_FLOAT, 0, vertices);
+ glDrawArrays(GL_TRIANGLE_FAN, 0, count);
+}
+
+void GLES11RenderEngine::drawMesh2D(
+ const float vertices[][2], const float texCoords[][2], size_t count) {
+ if (texCoords) {
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
+ }
+ glVertexPointer(2, GL_FLOAT, 0, vertices);
+ glDrawArrays(GL_TRIANGLE_FAN, 0, count);
+ if (texCoords) {
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ }
+}
+
+void GLES11RenderEngine::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());
+}
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+// ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/RenderEngine/GLES11RenderEngine.h b/services/surfaceflinger/RenderEngine/GLES11RenderEngine.h
new file mode 100644
index 0000000..15054bd
--- /dev/null
+++ b/services/surfaceflinger/RenderEngine/GLES11RenderEngine.h
@@ -0,0 +1,67 @@
+/*
+ * 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 GLES11RenderEngine : public RenderEngine {
+ GLuint mProtectedTexName;
+ GLint mMaxViewportDims[2];
+ GLint mMaxTextureSize;
+
+public:
+ GLES11RenderEngine();
+
+protected:
+ virtual ~GLES11RenderEngine();
+
+ virtual void dump(String8& result);
+ virtual void setViewportAndProjection(size_t w, size_t h);
+ virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque, int alpha);
+ virtual void setupDimLayerBlending(int alpha);
+ virtual void setupLayerTexturing(size_t textureName, bool useFiltering, const float* textureMatrix);
+ virtual void setupLayerBlackedOut();
+ virtual void disableTexturing();
+ virtual void disableBlending();
+
+ virtual void clearWithColor(const float vertices[][2], size_t count,
+ float red, float green, float blue, float alpha);
+
+ virtual void drawMesh2D(const float vertices[][2], const float texCoords[][2], size_t count);
+
+ virtual size_t getMaxTextureSize() const;
+ virtual size_t getMaxViewportDims() const;
+};
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+// ---------------------------------------------------------------------------
+
+#endif /* SF_GLES11RENDERENGINE_H_ */
diff --git a/services/surfaceflinger/RenderEngine/GLExtensions.cpp b/services/surfaceflinger/RenderEngine/GLExtensions.cpp
new file mode 100644
index 0000000..76bbcc1
--- /dev/null
+++ b/services/surfaceflinger/RenderEngine/GLExtensions.cpp
@@ -0,0 +1,83 @@
+/*
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+
+#include "GLExtensions.h"
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+ANDROID_SINGLETON_STATIC_INSTANCE( GLExtensions )
+
+GLExtensions::GLExtensions()
+ : mHaveFramebufferObject(false)
+{
+}
+
+void GLExtensions::initWithGLStrings(
+ GLubyte const* vendor,
+ GLubyte const* renderer,
+ GLubyte const* version,
+ GLubyte const* extensions)
+{
+ mVendor = (char const*)vendor;
+ mRenderer = (char const*)renderer;
+ mVersion = (char const*)version;
+ mExtensions = (char const*)extensions;
+
+ char const* curr = (char const*)extensions;
+ char const* 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);
+
+ if (hasExtension("GL_OES_framebuffer_object")) {
+ mHaveFramebufferObject = true;
+ }
+}
+
+bool GLExtensions::hasExtension(char const* extension) const
+{
+ const String8 s(extension);
+ return mExtensionList.indexOf(s) >= 0;
+}
+
+char const* GLExtensions::getVendor() const {
+ return mVendor.string();
+}
+
+char const* GLExtensions::getRenderer() const {
+ return mRenderer.string();
+}
+
+char const* GLExtensions::getVersion() const {
+ return mVersion.string();
+}
+
+char const* GLExtensions::getExtension() const {
+ return mExtensions.string();
+}
+
+// ---------------------------------------------------------------------------
+}; // namespace android
diff --git a/services/surfaceflinger/GLExtensions.h b/services/surfaceflinger/RenderEngine/GLExtensions.h
similarity index 73%
rename from services/surfaceflinger/GLExtensions.h
rename to services/surfaceflinger/RenderEngine/GLExtensions.h
index c86c66a..d81ed2a 100644
--- a/services/surfaceflinger/GLExtensions.h
+++ b/services/surfaceflinger/RenderEngine/GLExtensions.h
@@ -36,18 +36,12 @@
{
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 @@
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 @@
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/RenderEngine.cpp b/services/surfaceflinger/RenderEngine/RenderEngine.cpp
new file mode 100644
index 0000000..cb77e38
--- /dev/null
+++ b/services/surfaceflinger/RenderEngine/RenderEngine.cpp
@@ -0,0 +1,145 @@
+/*
+ * 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 "RenderEngine.h"
+#include "GLES10RenderEngine.h"
+#include "GLES11RenderEngine.h"
+#include "GLExtensions.h"
+
+// ---------------------------------------------------------------------------
+namespace android {
+// ---------------------------------------------------------------------------
+
+RenderEngine* RenderEngine::create(EGLDisplay display, EGLConfig config) {
+ // Also create our EGLContext
+ EGLint contextAttributes[] = {
+// EGL_CONTEXT_CLIENT_VERSION, 2,
+#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 (ctxt == EGL_NO_CONTEXT) {
+ // maybe ES 2.x is not supported
+ ALOGW("can't create an ES 2.x context, trying 1.x");
+ ctxt = eglCreateContext(display, config, NULL, contextAttributes + 2);
+ }
+
+ // 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;
+}
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+// ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/RenderEngine/RenderEngine.h b/services/surfaceflinger/RenderEngine/RenderEngine.h
new file mode 100644
index 0000000..e43bfa4
--- /dev/null
+++ b/services/surfaceflinger/RenderEngine/RenderEngine.h
@@ -0,0 +1,78 @@
+/*
+ * 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>
+
+// ---------------------------------------------------------------------------
+namespace android {
+// ---------------------------------------------------------------------------
+
+class String8;
+
+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);
+
+protected:
+ RenderEngine();
+ virtual ~RenderEngine() = 0;
+
+public:
+ static RenderEngine* create(EGLDisplay display, EGLConfig config);
+
+ virtual void checkErrors() const;
+
+ virtual void dump(String8& result) = 0;
+ virtual void setViewportAndProjection(size_t w, size_t h) = 0;
+ virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque, int alpha) = 0;
+ virtual void setupDimLayerBlending(int alpha) = 0;
+ virtual void setupLayerTexturing(size_t textureName, bool useFiltering, const float* textureMatrix) = 0;
+ virtual void setupLayerBlackedOut() = 0;
+
+ virtual void disableTexturing() = 0;
+ virtual void disableBlending() = 0;
+
+ virtual void clearWithColor(const float vertices[][2], size_t count,
+ float red, float green, float blue, float alpha) = 0;
+
+ virtual void drawMesh2D(const float vertices[][2], const float texCoords[][2], size_t count) = 0;
+
+ 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/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index ef0d521..b0e4002 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -24,6 +24,7 @@
#include <EGL/egl.h>
#include <GLES/gl.h>
+#include <GLES/glext.h>
#include <cutils/log.h>
#include <cutils/properties.h>
@@ -55,12 +56,12 @@
#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 "EventThread.h"
-#include "GLExtensions.h"
#include "Layer.h"
#include "LayerDim.h"
#include "SurfaceFlinger.h"
@@ -69,8 +70,16 @@
#include "DisplayHardware/HWComposer.h"
#include "DisplayHardware/VirtualDisplaySurface.h"
+#include "RenderEngine/RenderEngine.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 {
@@ -90,6 +99,7 @@
mAnimTransactionPending(false),
mLayersRemoved(false),
mRepaintEverything(0),
+ mRenderEngine(NULL),
mBootTime(systemTime()),
mVisibleRegionsDirty(false),
mHwWorkListDirty(false),
@@ -348,7 +358,9 @@
status_t err;
EGLAttributeVector attribs;
- attribs[EGL_SURFACE_TYPE] = EGL_WINDOW_BIT;
+ // TODO: enable ES2
+ //attribs[EGL_RENDERABLE_TYPE] = EGL_OPENGL_ES_BIT | EGL_OPENGL_ES2_BIT;
+ attribs[EGL_SURFACE_TYPE] = EGL_WINDOW_BIT | EGL_PBUFFER_BIT;
attribs[EGL_RECORDABLE_ANDROID] = EGL_TRUE;
attribs[EGL_FRAMEBUFFER_TARGET_ANDROID] = EGL_TRUE;
attribs[EGL_RED_SIZE] = 8;
@@ -359,25 +371,12 @@
if (!err)
goto success;
- // maybe we failed because of EGL_FRAMEBUFFER_TARGET_ANDROID
- ALOGW("no suitable EGLConfig found, trying without EGL_FRAMEBUFFER_TARGET_ANDROID");
+ // this didn't work, probably because we're on the emulator...
+ // try a simplified query
+ ALOGW("no suitable EGLConfig found, trying a simpler query");
+ attribs.remove(EGL_RENDERABLE_TYPE);
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);
@@ -387,8 +386,7 @@
goto success;
// this EGL is too lame for Android
- ALOGE("no suitable EGLConfig found, giving up");
-
+ LOG_ALWAYS_FATAL("no suitable EGLConfig found, giving up");
return 0;
success:
@@ -397,79 +395,6 @@
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);
-
- // 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]);
-}
status_t SurfaceFlinger::readyToRun()
{
@@ -487,10 +412,27 @@
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);
+ // initialize the config and context (can't fail)
+ mEGLConfig = selectEGLConfig(mEGLDisplay, mHwc->getVisualID());
+
+ // 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,
@@ -524,17 +466,6 @@
}
}
- // 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);
-
// start the EventThread
mEventThread = new EventThread(this);
mEventQueue.setEventThread(mEventThread);
@@ -542,7 +473,6 @@
// initialize our drawing state
mDrawingState = mCurrentState;
-
// We're now ready to accept clients...
mReadyToRunBarrier.open();
@@ -566,13 +496,12 @@
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();
}
// ----------------------------------------------------------------------------
@@ -637,7 +566,6 @@
// 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;
@@ -837,10 +765,10 @@
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,10 +779,10 @@
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();
}
if (mAnimCompositionPending) {
@@ -881,7 +809,7 @@
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 +818,13 @@
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));
@@ -966,6 +894,11 @@
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,8 +937,7 @@
// 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();
}
@@ -1031,12 +963,23 @@
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,7 +1049,7 @@
// 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());
@@ -1232,7 +1175,7 @@
// 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 +1212,8 @@
* 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 +1223,15 @@
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 +1279,7 @@
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 +1416,12 @@
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);
}
@@ -1537,7 +1480,7 @@
const bool hasGlesComposition = hwc.hasGlesComposition(id) || (cur==end);
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;
@@ -2033,6 +1976,10 @@
displays.add(d);
setTransactionState(state, displays, 0);
onScreenAcquired(getDefaultDisplayDevice());
+
+ const nsecs_t period =
+ getHwComposer().getRefreshPeriod(HWC_DISPLAY_PRIMARY);
+ mAnimFrameTracker.setDisplayRefreshPeriod(period);
}
void SurfaceFlinger::initializeDisplays() {
@@ -2145,19 +2092,15 @@
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 +2111,9 @@
}
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 +2123,27 @@
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 +2155,18 @@
}
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 +2186,14 @@
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 +2213,19 @@
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 +2246,18 @@
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 +2268,18 @@
/*
* 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 +2288,50 @@
*/
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 +2349,28 @@
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);
+ hwc.dump(result);
/*
* Dump gralloc state
@@ -2646,16 +2605,18 @@
}
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) {
+ bool useReadPixels) {
if (CC_UNLIKELY(display == 0))
return BAD_VALUE;
@@ -2663,7 +2624,6 @@
if (CC_UNLIKELY(producer == 0))
return BAD_VALUE;
-
class MessageCaptureScreen : public MessageBase {
SurfaceFlinger* flinger;
sp<IBinder> display;
@@ -2691,13 +2651,10 @@
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);
- }
+ bool useReadPixels = this->useReadPixels && !flinger->mGpuToCpuSupported;
+ result = flinger->captureScreenImplLocked(hw,
+ producer, reqWidth, reqHeight, minLayerZ, maxLayerZ,
+ useReadPixels);
static_cast<GraphicProducerWrapper*>(producer->asBinder().get())->exit(result);
return true;
}
@@ -2710,25 +2667,6 @@
// 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)
@@ -2786,7 +2724,7 @@
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()) {
@@ -2807,7 +2745,8 @@
const sp<const DisplayDevice>& hw,
const sp<IGraphicBufferProducer>& producer,
uint32_t reqWidth, uint32_t reqHeight,
- uint32_t minLayerZ, uint32_t maxLayerZ)
+ uint32_t minLayerZ, uint32_t maxLayerZ,
+ bool useReadPixels)
{
ATRACE_CALL();
@@ -2827,145 +2766,141 @@
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);
- return BAD_VALUE;
- }
-
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 FBO
- GLuint name;
- glGenFramebuffersOES(1, &name);
- glBindFramebufferOES(GL_FRAMEBUFFER_OES, name);
- glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES,
- GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, tname);
-
- GLenum status = glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES);
+ // create a surface (because we're a producer, and we need to
+ // dequeue/queue a buffer)
+ sp<Surface> sur = new Surface(producer);
+ ANativeWindow* window = sur.get();
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();
- }
- window->queueBuffer(window, buffer, -1);
- }
- }
- native_window_api_disconnect(window, NATIVE_WINDOW_API_CPU);
+ 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;
+ if (!useReadPixels) {
+ usage = GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
}
- } else {
- ALOGE("got GL_FRAMEBUFFER_COMPLETE_OES while taking screenshot");
- result = INVALID_OPERATION;
+ 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, 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) {
+ GLuint tname, name;
+ if (!useReadPixels) {
+ // 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);
+ } else {
+ // since we're going to use glReadPixels() anyways,
+ // use an intermediate renderbuffer instead
+ glGenRenderbuffersOES(1, &tname);
+ glBindRenderbufferOES(GL_RENDERBUFFER_OES, tname);
+ glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_RGBA8_OES, reqWidth, reqHeight);
+ // create a FBO to render into
+ glGenFramebuffersOES(1, &name);
+ glBindFramebufferOES(GL_FRAMEBUFFER_OES, name);
+ glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES,
+ GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, tname);
+ }
+
+ GLenum status = glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES);
+ if (status == GL_FRAMEBUFFER_COMPLETE_OES) {
+ // 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);
+
+ if (useReadPixels) {
+ 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);
+ checkScreenshot(buf, vaddr, hw, minLayerZ, maxLayerZ);
+ buf->unlock();
+ }
+ }
+ } else {
+ ALOGE("got GL_FRAMEBUFFER_COMPLETE_OES error while taking screenshot");
+ result = INVALID_OPERATION;
+ }
+
+ // back to main framebuffer
+ glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);
+ glDeleteFramebuffersOES(1, &name);
+ if (!useReadPixels) {
+ glDeleteTextures(1, &tname);
+ } else {
+ glDeleteRenderbuffersOES(1, &tname);
+ }
+ // destroy our image
+ eglDestroyImageKHR(mEGLDisplay, image);
+ } else {
+ result = BAD_VALUE;
+ }
+ window->queueBuffer(window, buffer, -1);
+ }
+ } else {
+ result = BAD_VALUE;
+ }
+ 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);
+ hw->setViewportAndProjection();
return result;
}
+void SurfaceFlinger::checkScreenshot(const sp<GraphicBuffer>& buf, void const* vaddr,
+ const sp<const DisplayDevice>& hw,
+ uint32_t minLayerZ, uint32_t maxLayerZ) {
+ if (DEBUG_SCREENSHOTS) {
+ for (ssize_t y=0 ; y<buf->height ; y++) {
+ uint32_t const * p = (uint32_t const *)vaddr + y*buf->stride;
+ for (ssize_t x=0 ; x<buf->width ; 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 +2917,13 @@
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;
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 739099c..7099b35 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -21,7 +21,7 @@
#include <sys/types.h>
#include <EGL/egl.h>
-#include <GLES/gl.h>
+#include <GLES/gl.h> // needed for GLuint
#include <cutils/compiler.h>
@@ -62,6 +62,7 @@
class Layer;
class LayerDim;
class Surface;
+class RenderEngine;
// ---------------------------------------------------------------------------
@@ -79,11 +80,11 @@
private HWComposer::EventHandler
{
public:
- static char const* getServiceName() {
+ static char const* getServiceName() ANDROID_API {
return "SurfaceFlinger";
}
- SurfaceFlinger();
+ SurfaceFlinger() ANDROID_API;
enum {
EVENT_VSYNC = HWC_EVENT_VSYNC
@@ -121,12 +122,20 @@
// 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();
@@ -298,14 +307,8 @@
const sp<const DisplayDevice>& hw,
const sp<IGraphicBufferProducer>& producer,
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);
-
+ uint32_t minLayerZ, uint32_t maxLayerZ,
+ bool useReadPixels);
/* ------------------------------------------------------------------------
* EGL
@@ -313,10 +316,8 @@
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;
+ size_t getMaxTextureSize() const;
+ size_t getMaxViewportDims() const;
/* ------------------------------------------------------------------------
* Display and layer stack management
@@ -372,9 +373,6 @@
void postFramebuffer();
void drawWormhole(const sp<const DisplayDevice>& hw,
const Region& region) const;
- GLuint getProtectedTexName() const {
- return mProtectedTexName;
- }
/* ------------------------------------------------------------------------
* Display management
@@ -385,14 +383,20 @@
* Debugging & dumpsys
*/
void listLayersLocked(const Vector<String16>& args, size_t& index,
- String8& result, char* buffer, size_t SIZE) const;
+ String8& result) const;
void dumpStatsLocked(const Vector<String16>& args, size_t& index,
- String8& result, char* buffer, size_t SIZE) const;
+ String8& result) 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;
+ String8& result);
+ void dumpAllLocked(const Vector<String16>& args, size_t& index,
+ String8& result) const;
bool startDdmConnection();
static void appendSfConfigString(String8& result);
+ void checkScreenshot(const sp<GraphicBuffer>& buf, void const* vaddr,
+ const sp<const DisplayDevice>& hw,
+ uint32_t minLayerZ, uint32_t maxLayerZ);
+
+ void logFrameStats();
/* ------------------------------------------------------------------------
* Attributes
@@ -416,12 +420,10 @@
// 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;
EGLContext mEGLContext;
EGLConfig mEGLConfig;
EGLDisplay mEGLDisplay;
diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.cpp b/services/surfaceflinger/SurfaceFlingerConsumer.cpp
index 2869250..e95e057 100644
--- a/services/surfaceflinger/SurfaceFlingerConsumer.cpp
+++ b/services/surfaceflinger/SurfaceFlingerConsumer.cpp
@@ -50,12 +50,14 @@
// 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 +71,12 @@
// 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 +101,48 @@
return bindTextureImageLocked();
}
+// 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..5de6d12 100644
--- a/services/surfaceflinger/SurfaceFlingerConsumer.h
+++ b/services/surfaceflinger/SurfaceFlingerConsumer.h
@@ -27,11 +27,10 @@
*/
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, GLuint tex,
+ GLenum texTarget = GL_TEXTURE_EXTERNAL_OES,
+ bool useFenceSync = true)
+ : GLConsumer(bq, tex, texTarget, useFenceSync)
{}
class BufferRejecter {
@@ -51,6 +50,9 @@
// See GLConsumer::bindTextureImageLocked().
status_t bindTextureImage();
+
+private:
+ nsecs_t computeExpectedPresent();
};
// ----------------------------------------------------------------------------
diff --git a/services/surfaceflinger/SurfaceTextureLayer.cpp b/services/surfaceflinger/SurfaceTextureLayer.cpp
index d0f0dae..b76dc0c 100644
--- a/services/surfaceflinger/SurfaceTextureLayer.cpp
+++ b/services/surfaceflinger/SurfaceTextureLayer.cpp
@@ -28,7 +28,7 @@
SurfaceTextureLayer::SurfaceTextureLayer(const sp<SurfaceFlinger>& flinger)
- : BufferQueue(true), flinger(flinger) {
+ : BufferQueue(), flinger(flinger) {
}
SurfaceTextureLayer::~SurfaceTextureLayer() {
@@ -51,32 +51,5 @@
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;
-}
-
// ---------------------------------------------------------------------------
}; // namespace android
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 @@
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/cmds/surfaceflinger/main_surfaceflinger.cpp b/services/surfaceflinger/main_surfaceflinger.cpp
similarity index 96%
rename from cmds/surfaceflinger/main_surfaceflinger.cpp
rename to services/surfaceflinger/main_surfaceflinger.cpp
index ce7fde0..8503d4e 100644
--- a/cmds/surfaceflinger/main_surfaceflinger.cpp
+++ b/services/surfaceflinger/main_surfaceflinger.cpp
@@ -15,7 +15,7 @@
*/
#include <binder/BinderService.h>
-#include <SurfaceFlinger.h>
+#include "SurfaceFlinger.h"
using namespace android;