am 8b931916: (-s ours) am f4f2bd21: Tweak for forward compatibility
* commit '8b9319162d4f574fc45f88e6f9e28e62a4b5da78':
Tweak for forward compatibility
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index 1911839..6d6f77e 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" },
diff --git a/cmds/dumpstate/dumpstate.c b/cmds/dumpstate/dumpstate.c
index 89bea91..ed362d7 100644
--- a/cmds/dumpstate/dumpstate.c
+++ b/cmds/dumpstate/dumpstate.c
@@ -278,6 +278,15 @@
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 NETSTATS", 30, "dumpsys", "netstats", "--checkin", NULL);
+ run_command("CHECKIN PROCSTATS", 30, "dumpsys", "procstats", "--checkin", 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/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..131e03f 100644
--- a/cmds/installd/commands.c
+++ b/cmds/installd/commands.c
@@ -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,33 @@
{
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 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 dalvik.vm.lib */
+ property_get("dalvik.vm.lib", dalvik_vm_lib, "libdvm.so");
+ ALOGV("dalvik.vm.lib=%s\n", dalvik_vm_lib);
+
+ /* 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 +689,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 +717,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 +734,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(dalvik_vm_lib, "libdvm", 6) == 0) {
+ run_dexopt(zip_fd, out_fd, apk_path, out_path, dexopt_flags);
+ } else if (strncmp(dalvik_vm_lib, "libart", 6) == 0) {
+ run_dex2oat(zip_fd, out_fd, apk_path, out_path, dexopt_flags);
+ } else {
+ exit(69); /* Unexpected 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..87f900a 100644
--- a/cmds/installd/installd.c
+++ b/cmds/installd/installd.c
@@ -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/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/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/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 98%
rename from include/utils/BufferedTextOutput.h
rename to include/binder/BufferedTextOutput.h
index 69c6240..adf3c32 100644
--- a/include/utils/BufferedTextOutput.h
+++ b/include/binder/BufferedTextOutput.h
@@ -17,7 +17,7 @@
#ifndef ANDROID_BUFFEREDTEXTOUTPUT_H
#define ANDROID_BUFFEREDTEXTOUTPUT_H
-#include <utils/TextOutput.h>
+#include <binder/TextOutput.h>
#include <utils/threads.h>
#include <cutils/uio.h>
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/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/gui/BufferQueue.h b/include/gui/BufferQueue.h
index 6c1b691..766fa0f 100644
--- a/include/gui/BufferQueue.h
+++ b/include/gui/BufferQueue.h
@@ -229,7 +229,7 @@
// 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 {
@@ -240,7 +240,8 @@
mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
mTimestamp(0),
mFrameNumber(0),
- mBuf(INVALID_BUFFER_SLOT) {
+ mBuf(INVALID_BUFFER_SLOT),
+ mAcquireCalled(false) {
mCrop.makeInvalid();
}
// mGraphicBuffer points to the buffer allocated for this slot, or is NULL
@@ -269,6 +270,9 @@
// mFence is a fence that will signal when the buffer is idle.
sp<Fence> mFence;
+
+ // Indicates whether this buffer has been seen by a consumer yet
+ bool mAcquireCalled;
};
// The following public functions are the consumer-facing interface
@@ -285,7 +289,7 @@
// 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,7 +298,8 @@
//
// 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
@@ -368,10 +373,6 @@
// 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
@@ -410,20 +411,20 @@
// connected, mDequeueCondition must be broadcast.
int getMaxBufferCountLocked() 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 {
BufferSlot()
: 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).
@@ -592,7 +578,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 +599,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..1d51bc9 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.
@@ -143,8 +143,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.
@@ -161,17 +160,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 +190,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..3c178ef 100644
--- a/include/gui/CpuConsumer.h
+++ b/include/gui/CpuConsumer.h
@@ -25,7 +25,6 @@
#include <utils/Vector.h>
#include <utils/threads.h>
-#define ANDROID_GRAPHICS_CPUCONSUMER_JNI_ID "mCpuConsumer"
namespace android {
@@ -73,6 +72,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/GLConsumer.h b/include/gui/GLConsumer.h
index f0a75dc..031684e 100644
--- a/include/gui/GLConsumer.h
+++ b/include/gui/GLConsumer.h
@@ -233,8 +233,7 @@
// 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.
@@ -242,11 +241,13 @@
// 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);
diff --git a/include/input/Input.h b/include/input/Input.h
new file mode 100644
index 0000000..6d49b18
--- /dev/null
+++ b/include/input/Input.h
@@ -0,0 +1,622 @@
+/*
+ * 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>
+
+#ifdef HAVE_ANDROID_OS
+class SkMatrix;
+#endif
+
+/*
+ * 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);
+
+#ifdef HAVE_ANDROID_OS
+ void transform(const SkMatrix* matrix);
+
+ 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..4672ad4
--- /dev/null
+++ b/include/input/InputDevice.h
@@ -0,0 +1,156 @@
+/*
+ * 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, const InputDeviceIdentifier& identifier,
+ const String8& alias, bool isExternal);
+
+ inline int32_t getId() const { return mId; }
+ 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 const Vector<MotionRange>& getMotionRanges() const {
+ return mMotionRanges;
+ }
+
+private:
+ int32_t mId;
+ int32_t mGeneration;
+ InputDeviceIdentifier mIdentifier;
+ String8 mAlias;
+ bool mIsExternal;
+ uint32_t mSources;
+ int32_t mKeyboardType;
+ sp<KeyCharacterMap> mKeyCharacterMap;
+ bool mHasVibrator;
+
+ 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..c76ba12 100644
--- a/include/input/KeycodeLabels.h
+++ b/include/input/KeycodeLabels.h
@@ -1,6 +1,321 @@
+/*
+ * 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 },
+
+ // 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..a6a849d 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>
@@ -73,6 +74,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/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/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/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..4c8820e 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)
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/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 2ffa927..28b74ba 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>
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/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/gui/BufferItemConsumer.cpp b/libs/gui/BufferItemConsumer.cpp
index 7db1b84..ba04bdf 100644
--- a/libs/gui/BufferItemConsumer.cpp
+++ b/libs/gui/BufferItemConsumer.cpp
@@ -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..0dab864 100644
--- a/libs/gui/BufferQueue.cpp
+++ b/libs/gui/BufferQueue.cpp
@@ -178,12 +178,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
@@ -418,6 +416,7 @@
return NO_INIT;
}
+ mSlots[*outBuf].mFrameNumber = ~0;
mSlots[*outBuf].mGraphicBuffer = graphicBuffer;
}
}
@@ -435,7 +434,8 @@
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;
@@ -491,15 +491,22 @@
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;
@@ -519,6 +526,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,9 +542,25 @@
return -EINVAL;
}
+ mSlots[buf].mFence = fence;
+ mSlots[buf].mBufferState = BufferSlot::QUEUED;
+ mFrameCounter++;
+ mSlots[buf].mFrameNumber = mFrameCounter;
+
+ BufferItem item;
+ item.mAcquireCalled = mSlots[buf].mAcquireCalled;
+ item.mGraphicBuffer = mSlots[buf].mGraphicBuffer;
+ item.mCrop = crop;
+ item.mTransform = transform;
+ item.mScalingMode = scalingMode;
+ item.mTimestamp = timestamp;
+ item.mFrameNumber = mFrameCounter;
+ item.mBuf = buf;
+ item.mFence = fence;
+
if (mSynchronousMode) {
// In synchronous mode we queue all buffers in a FIFO.
- mQueue.push_back(buf);
+ mQueue.push_back(item);
// Synchronous mode always signals that an additional frame should
// be consumed.
@@ -539,7 +568,7 @@
} else {
// In asynchronous mode we only keep the most recent buffer.
if (mQueue.empty()) {
- mQueue.push_back(buf);
+ mQueue.push_back(item);
// Asynchronous mode only signals that a frame should be
// consumed if no previous frame was pending. If a frame were
@@ -547,34 +576,15 @@
listener = mConsumerListener;
} else {
Fifo::iterator front(mQueue.begin());
- // buffer currently queued is freed
- mSlots[*front].mBufferState = BufferSlot::FREE;
+ // buffer slot currently queued is marked free if still tracked
+ if (stillTracking(front)) {
+ mSlots[front->mBuf].mBufferState = BufferSlot::FREE;
+ }
// and we record the new buffer index in the queued list
- *front = buf;
+ *front = item;
}
}
- mSlots[buf].mTimestamp = timestamp;
- mSlots[buf].mCrop = crop;
- mSlots[buf].mTransform = transform;
- mSlots[buf].mFence = fence;
-
- switch (scalingMode) {
- case NATIVE_WINDOW_SCALING_MODE_FREEZE:
- case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW:
- case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP:
- break;
- default:
- ST_LOGE("unknown scaling mode: %d (ignoring)", scalingMode);
- scalingMode = mSlots[buf].mScalingMode;
- break;
- }
-
- mSlots[buf].mBufferState = BufferSlot::QUEUED;
- mSlots[buf].mScalingMode = scalingMode;
- mFrameCounter++;
- mSlots[buf].mFrameNumber = mFrameCounter;
-
mBufferHasBeenQueued = true;
mDequeueCondition.broadcast();
@@ -707,36 +717,36 @@
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,
+ result.appendFormat(
"%s-BufferQueue maxBufferCount=%d, mSynchronousMode=%d, default-size=[%dx%d], "
"default-format=%d, transform-hint=%02x, FIFO(%d)={%s}\n",
prefix, maxBufferCount, mSynchronousMode, mDefaultWidth,
mDefaultHeight, mDefaultBufferFormat, mTransformHint,
fifoSize, fifo.string());
- result.append(buffer);
-
struct {
const char * operator()(int state) const {
@@ -752,25 +762,19 @@
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",
+ result.appendFormat(
+ "%s%s[%02d:%p] state=%-8s",
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)
+ slot.mGraphicBuffer.get(),
+ stateName(slot.mBufferState)
);
- result.append(buffer);
const sp<GraphicBuffer>& buf(slot.mGraphicBuffer);
if (buf != NULL) {
- snprintf(buffer, SIZE,
+ result.appendFormat(
", %p [%4ux%4u:%4u,%3X]",
buf->handle, buf->width, buf->height, buf->stride,
buf->format);
- result.append(buffer);
}
result.append("\n");
}
@@ -795,9 +799,8 @@
}
void BufferQueue::freeAllBuffersLocked() {
- ALOGW_IF(!mQueue.isEmpty(),
- "freeAllBuffersLocked called but mQueue is not empty");
- mQueue.clear();
+ ALOGD_IF(!mQueue.isEmpty(),
+ "freeAllBuffersLocked called with non-empty mQueue");
mBufferHasBeenQueued = false;
for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
freeBufferLocked(i);
@@ -829,27 +832,27 @@
// deep, while in synchronous mode we use the oldest buffer.
if (!mQueue.empty()) {
Fifo::iterator front(mQueue.begin());
- int buf = *front;
-
+ int buf = front->mBuf;
+ *buffer = *front;
ATRACE_BUFFER_INDEX(buf);
- if (mSlots[buf].mAcquireCalled) {
- buffer->mGraphicBuffer = NULL;
- } else {
- buffer->mGraphicBuffer = mSlots[buf].mGraphicBuffer;
+ 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;
}
- 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;
- mSlots[buf].mAcquireCalled = true;
- mSlots[buf].mNeedsCleanupOnRelease = false;
- mSlots[buf].mBufferState = BufferSlot::ACQUIRED;
- mSlots[buf].mFence = Fence::NO_FENCE;
+ // 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();
@@ -862,7 +865,8 @@
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);
@@ -873,12 +877,35 @@
return BAD_VALUE;
}
- mSlots[buf].mEglDisplay = display;
- mSlots[buf].mEglFence = eglFence;
- mSlots[buf].mFence = fence;
+ // Check if this buffer slot is on the queue
+ bool slotQueued = false;
+ Fifo::iterator front(mQueue.begin());
+ while (front != mQueue.end() && !slotQueued) {
+ if (front->mBuf == buf)
+ slotQueued = true;
+ front++;
+ }
+
+ // 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) {
+ // This should only occur if new buffer is still in the queue
+ ALOGE_IF(!slotQueued,
+ "received old buffer(#%lld) after new buffer(#%lld) on same "
+ "slot #%d already acquired", frameNumber,
+ mSlots[buf].mFrameNumber, buf);
+ return STALE_BUFFER_SLOT;
+ }
+ // this should never happen
+ ALOGE_IF(slotQueued,
+ "received new buffer(#%lld) on slot #%d that has not yet been "
+ "acquired", frameNumber, buf);
// 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);
@@ -943,6 +970,17 @@
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);
@@ -985,20 +1023,6 @@
return NO_ERROR;
}
-void BufferQueue::freeAllBuffersExceptHeadLocked() {
- int head = -1;
- if (!mQueue.empty()) {
- Fifo::iterator front(mQueue.begin());
- head = *front;
- }
- mBufferHasBeenQueued = false;
- for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
- if (i != head) {
- freeBufferLocked(i);
- }
- }
-}
-
status_t BufferQueue::drainQueueLocked() {
while (mSynchronousMode && mQueue.size() > 1) {
mDequeueCondition.wait(mMutex);
@@ -1017,11 +1041,7 @@
status_t BufferQueue::drainQueueAndFreeBuffersLocked() {
status_t err = drainQueueLocked();
if (err == NO_ERROR) {
- if (mQueue.empty()) {
- freeAllBuffersLocked();
- } else {
- freeAllBuffersExceptHeadLocked();
- }
+ freeAllBuffersLocked();
}
return err;
}
@@ -1061,6 +1081,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..fd9d153 100644
--- a/libs/gui/ConsumerBase.cpp
+++ b/libs/gui/ConsumerBase.cpp
@@ -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,23 +166,19 @@
}
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);
}
}
@@ -195,21 +192,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 +236,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 +259,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..123b470 100644
--- a/libs/gui/CpuConsumer.cpp
+++ b/libs/gui/CpuConsumer.cpp
@@ -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;
@@ -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/GLConsumer.cpp b/libs/gui/GLConsumer.cpp
index bec0f90..6d29edc 100644
--- a/libs/gui/GLConsumer.cpp
+++ b/libs/gui/GLConsumer.cpp
@@ -188,12 +188,16 @@
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;
}
@@ -237,7 +241,10 @@
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;
}
@@ -248,7 +255,8 @@
// 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)",
@@ -334,7 +342,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);
@@ -503,7 +512,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);
@@ -887,18 +897,16 @@
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/input/Android.mk b/libs/input/Android.mk
new file mode 100644
index 0000000..2a1e762
--- /dev/null
+++ b/libs/input/Android.mk
@@ -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.
+
+ifneq ($(TARGET_BUILD_PDK),true)
+
+LOCAL_PATH:= $(call my-dir)
+
+# libinput is partially built for the host (used by build time keymap validation tool)
+# These files are common to host and target builds.
+
+commonSources := \
+ Input.cpp \
+ InputDevice.cpp \
+ Keyboard.cpp \
+ KeyCharacterMap.cpp \
+ KeyLayoutMap.cpp \
+ VirtualKeyMap.cpp
+
+deviceSources := \
+ $(commonSources) \
+ InputTransport.cpp \
+ VelocityControl.cpp \
+ VelocityTracker.cpp
+
+hostSources := \
+ $(commonSources)
+
+# For the host
+# =====================================================
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= $(hostSources)
+
+LOCAL_MODULE:= libinput
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+
+# For the device
+# =====================================================
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= $(deviceSources)
+
+LOCAL_SHARED_LIBRARIES := \
+ liblog \
+ libcutils \
+ libutils \
+ libbinder \
+ libskia \
+ libz
+
+LOCAL_C_INCLUDES := \
+ external/skia/include/core \
+ external/icu4c/common \
+ external/zlib
+
+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
+
+endif #!PDK
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
new file mode 100644
index 0000000..7a217c3
--- /dev/null
+++ b/libs/input/Input.cpp
@@ -0,0 +1,634 @@
+/*
+ * 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>
+
+#include "SkPoint.h"
+#include "SkMatrix.h"
+#include "SkScalar.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:
+ 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:
+ 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);
+ }
+}
+
+#ifdef HAVE_ANDROID_OS
+static inline float transformAngle(const SkMatrix* matrix, float angleRadians) {
+ // Construct and transform a vector oriented at the specified clockwise angle from vertical.
+ // Coordinate system: down is increasing Y, right is increasing X.
+ SkPoint vector;
+ vector.fX = SkFloatToScalar(sinf(angleRadians));
+ vector.fY = SkFloatToScalar(-cosf(angleRadians));
+ matrix->mapVectors(& vector, 1);
+
+ // Derive the transformed vector's clockwise angle from vertical.
+ float result = atan2f(SkScalarToFloat(vector.fX), SkScalarToFloat(-vector.fY));
+ if (result < - M_PI_2) {
+ result += M_PI;
+ } else if (result > M_PI_2) {
+ result -= M_PI;
+ }
+ return result;
+}
+
+void MotionEvent::transform(const SkMatrix* matrix) {
+ float oldXOffset = mXOffset;
+ float oldYOffset = mYOffset;
+
+ // 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.
+ SkPoint point;
+ float rawX = getRawX(0);
+ float rawY = getRawY(0);
+ matrix->mapXY(SkFloatToScalar(rawX + oldXOffset), SkFloatToScalar(rawY + oldYOffset),
+ & point);
+ float newX = SkScalarToFloat(point.fX);
+ float newY = SkScalarToFloat(point.fY);
+ float newXOffset = newX - rawX;
+ float newYOffset = newY - rawY;
+
+ mXOffset = newXOffset;
+ mYOffset = newYOffset;
+
+ // 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;
+ matrix->mapXY(SkFloatToScalar(x), SkFloatToScalar(y), &point);
+ c.setAxisValue(AMOTION_EVENT_AXIS_X, SkScalarToFloat(point.fX) - newXOffset);
+ c.setAxisValue(AMOTION_EVENT_AXIS_Y, SkScalarToFloat(point.fY) - newYOffset);
+
+ float orientation = c.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION);
+ c.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, transformAngle(matrix, orientation));
+ }
+}
+
+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..77fa49d
--- /dev/null
+++ b/libs/input/InputDevice.cpp
@@ -0,0 +1,184 @@
+/*
+ * 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, -1, InputDeviceIdentifier(), String8(), false);
+}
+
+InputDeviceInfo::InputDeviceInfo(const InputDeviceInfo& other) :
+ mId(other.mId), mGeneration(other.mGeneration), mIdentifier(other.mIdentifier),
+ mAlias(other.mAlias), mIsExternal(other.mIsExternal), mSources(other.mSources),
+ mKeyboardType(other.mKeyboardType),
+ mKeyCharacterMap(other.mKeyCharacterMap),
+ mHasVibrator(other.mHasVibrator),
+ mMotionRanges(other.mMotionRanges) {
+}
+
+InputDeviceInfo::~InputDeviceInfo() {
+}
+
+void InputDeviceInfo::initialize(int32_t id, int32_t generation,
+ const InputDeviceIdentifier& identifier, const String8& alias, bool isExternal) {
+ mId = id;
+ mGeneration = generation;
+ mIdentifier = identifier;
+ mAlias = alias;
+ mIsExternal = isExternal;
+ mSources = 0;
+ mKeyboardType = AINPUT_KEYBOARD_TYPE_NONE;
+ mHasVibrator = 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..15a877447
--- /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..4292741
--- /dev/null
+++ b/libs/input/tests/Android.mk
@@ -0,0 +1,34 @@
+# 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 \
+ libskia
+
+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..ab1feb3
--- /dev/null
+++ b/libs/input/tests/InputEvent_test.cpp
@@ -0,0 +1,581 @@
+/*
+ * 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 <core/SkMatrix.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));
+}
+
+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.
+ SkMatrix matrix;
+ matrix.setRotate(ROTATION);
+ 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/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..42e1c7e 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,6 @@
LOCAL_SRC_FILES += Looper.cpp
endif
LOCAL_MODULE:= libutils
-LOCAL_STATIC_LIBRARIES := libz
-LOCAL_C_INCLUDES := \
- external/zlib
LOCAL_CFLAGS += $(host_commonCflags)
LOCAL_LDLIBS += $(host_commonLdlibs)
include $(BUILD_HOST_STATIC_LIBRARY)
@@ -90,15 +79,12 @@
LOCAL_SRC_FILES += Looper.cpp
endif
LOCAL_MODULE:= lib64utils
-LOCAL_STATIC_LIBRARIES := libz
-LOCAL_C_INCLUDES := \
- external/zlib
LOCAL_CFLAGS += $(host_commonCflags) -m64
LOCAL_LDLIBS += $(host_commonLdlibs)
include $(BUILD_HOST_STATIC_LIBRARY)
-# For the device
+# For the device, static
# =====================================================
include $(CLEAR_VARS)
@@ -123,14 +109,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/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/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index 00bfa5a..c02a85f 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -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,9 +39,25 @@
/*
- * EGL drivers are called
+ * 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:
*
- * /system/lib/egl/lib{[EGL|GLESv1_CM|GLESv2] | GLES}_$TAG.so
+ * /{vendor|system}/lib/egl/lib{GLES | [EGL|GLESv1_CM|GLESv2]}_*.so
*
*/
@@ -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();
}
@@ -180,26 +166,20 @@
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");
return (void*)hnd;
}
@@ -267,21 +247,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) {
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 93eedff..0358fcc 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -849,9 +849,7 @@
}
if (found) {
-#if USE_FAST_TLS_KEY
addr = gExtensionForwarders[slot];
-#endif
sGLExtentionMap.add(name, addr);
sGLExtentionSlot++;
}
@@ -1182,6 +1180,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()));
@@ -1189,12 +1192,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/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/powermanager/IPowerManager.cpp b/services/powermanager/IPowerManager.cpp
index 0265df3..3f5b81e7 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..14a4e55 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,6 @@
libui \
libgui
-
-
LOCAL_MODULE:= libsensorservice
include $(BUILD_SHARED_LIBRARY)
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/SensorFusion.cpp b/services/sensorservice/SensorFusion.cpp
index d23906d..a0a17da 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);
@@ -132,7 +143,7 @@
"b=< %g, %g, %g >\n",
mEnabled ? "enabled" : "disabled",
mClients.size(),
- mGyroRate,
+ mEstimatedGyroRate,
fusion.getAttitude().x,
fusion.getAttitude().y,
fusion.getAttitude().z,
diff --git a/services/sensorservice/SensorFusion.h b/services/sensorservice/SensorFusion.h
index 4c99bcb..3c2244e 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);
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index ebf5cf0..4718447 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()
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index 25e5f76..67489cc 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();
@@ -113,12 +114,12 @@
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);
+ 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;
@@ -138,8 +139,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/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index ec296d3..f2051dd 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -18,6 +18,8 @@
DisplayHardware/HWComposer.cpp \
DisplayHardware/PowerHAL.cpp \
DisplayHardware/VirtualDisplaySurface.cpp \
+ EventLog/EventLogTags.logtags \
+ EventLog/EventLog.cpp
LOCAL_CFLAGS:= -DLOG_TAG=\"SurfaceFlinger\"
LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
@@ -41,6 +43,8 @@
LOCAL_CFLAGS += -DNUM_FRAMEBUFFER_SURFACE_BUFFERS=$(NUM_FRAMEBUFFER_SURFACE_BUFFERS)
endif
+LOCAL_CFLAGS += -fvisibility=hidden
+
LOCAL_SHARED_LIBRARIES := \
libcutils \
liblog \
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..b001bdb 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -416,7 +416,7 @@
mScissor = mGlobalTransform.transform(viewport);
if (mScissor.isEmpty()) {
- mScissor.set(getBounds());
+ mScissor = getBounds();
}
mOrientation = orientation;
@@ -424,9 +424,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 +443,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..047eecd 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -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/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
index 54a3ce8..10bca38 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
@@ -103,8 +103,8 @@
if (mCurrentBufferSlot != BufferQueue::INVALID_BUFFER_SLOT &&
item.mBuf != mCurrentBufferSlot) {
// Release the previous buffer.
- err = releaseBufferLocked(mCurrentBufferSlot, EGL_NO_DISPLAY,
- EGL_NO_SYNC_KHR);
+ err = releaseBufferLocked(mCurrentBufferSlot, mCurrentBuffer,
+ EGL_NO_DISPLAY, EGL_NO_SYNC_KHR);
if (err != NO_ERROR && err != BufferQueue::STALE_BUFFER_SLOT) {
ALOGE("error releasing buffer: %s (%d)", strerror(-err), err);
return err;
@@ -144,7 +144,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 +176,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..c86e9ae 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
@@ -55,8 +55,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..5082192 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -550,9 +550,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;
}
@@ -962,7 +959,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 +1027,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..a20da08 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -275,7 +275,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/EventLog/EventLog.cpp b/services/surfaceflinger/EventLog/EventLog.cpp
new file mode 100644
index 0000000..815242b
--- /dev/null
+++ b/services/surfaceflinger/EventLog/EventLog.cpp
@@ -0,0 +1,128 @@
+/*
+ * 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::doLogJank(const String8& window, int32_t value) {
+ EventLog::TagBuffer buffer(LOGTAG_SF_JANK);
+ buffer.startList(2);
+ buffer.writeString8(window);
+ buffer.writeInt32(value);
+ buffer.endList();
+ buffer.log();
+}
+
+void EventLog::logJank(const String8& window, int32_t value) {
+ EventLog::getInstance().doLogJank(window, value);
+}
+
+// ---------------------------------------------------------------------------
+
+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..2f1cd9b
--- /dev/null
+++ b/services/surfaceflinger/EventLog/EventLog.h
@@ -0,0 +1,83 @@
+/*
+ * 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 logJank(const String8& window, int32_t value);
+
+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_JANK = 60100 };
+ void doLogJank(const String8& window, int32_t value);
+};
+
+// ---------------------------------------------------------------------------
+}// 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..c83692f
--- /dev/null
+++ b/services/surfaceflinger/EventLog/EventLogTags.logtags
@@ -0,0 +1,38 @@
+# 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).
+
+# surfaceflinger
+60100 sf_jank (window|3),(value|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..f6bd676 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();
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 4779804..b08b8d1 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -36,6 +36,7 @@
#include <gui/Surface.h>
#include "clz.h"
+#include "Colorizer.h"
#include "DisplayDevice.h"
#include "GLExtensions.h"
#include "Layer.h"
@@ -164,7 +165,7 @@
// set-up
// ---------------------------------------------------------------------------
-String8 Layer::getName() const {
+const String8& Layer::getName() const {
return mName;
}
@@ -269,13 +270,24 @@
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 {
@@ -293,7 +305,7 @@
// 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.
@@ -309,6 +321,9 @@
// 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
@@ -357,7 +372,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 :
@@ -544,31 +559,88 @@
clearWithOpenGL(hw, clip, 0,0,0,0);
}
+static void setupOpenGL10(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);
+ }
+}
+
+static void setupOpenGL11(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);
+ }
+}
+
void Layer::drawWithOpenGL(
const sp<const DisplayDevice>& hw, const Region& clip) const {
const uint32_t fbHeight = hw->getHeight();
- const State& s(drawingState());
+ const State& s(getDrawingState());
- 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);
+ if (mFlinger->getGlesVersion() == GLES_VERSION_1_0) {
+ setupOpenGL10(mPremultipliedAlpha, isOpaque(), s.alpha);
} 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);
- }
+ setupOpenGL11(mPremultipliedAlpha, isOpaque(), s.alpha);
+ }
+
+ if (s.alpha < 0xFF || !isOpaque()) {
+ glEnable(GL_BLEND);
+ glBlendFunc(mPremultipliedAlpha ? GL_ONE : GL_SRC_ALPHA,
+ GL_ONE_MINUS_SRC_ALPHA);
+ } else {
+ glDisable(GL_BLEND);
}
LayerMesh mesh;
@@ -658,13 +730,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 +805,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 +819,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 +878,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));
}
@@ -1085,7 +1159,7 @@
};
- Reject r(mDrawingState, currentState(), recomputeVisibleRegions);
+ Reject r(mDrawingState, getCurrentState(), recomputeVisibleRegions);
if (mSurfaceFlingerConsumer->updateTexImage(&r) != NO_ERROR) {
// something happened!
@@ -1139,11 +1213,11 @@
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 +1252,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 +1279,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 +1288,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);
}
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 2765db1..f79bf2d 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -51,6 +51,7 @@
// ---------------------------------------------------------------------------
class Client;
+class Colorizer;
class DisplayDevice;
class GraphicBuffer;
class SurfaceFlinger;
@@ -130,6 +131,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 +148,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 +156,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 +190,102 @@
*/
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;
+ Rect getContentCrop() const;
/*
* returns the transform bits (90 rotation / h-flip / v-flip) of the
* layer's content
*/
- virtual uint32_t getContentTransform() const;
+ uint32_t getContentTransform() const;
// -----------------------------------------------------------------------
@@ -298,16 +296,15 @@
// 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();
protected:
// constant
@@ -333,6 +330,10 @@
// 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;
diff --git a/services/surfaceflinger/LayerDim.cpp b/services/surfaceflinger/LayerDim.cpp
index 36bafdb..f4adeeb 100644
--- a/services/surfaceflinger/LayerDim.cpp
+++ b/services/surfaceflinger/LayerDim.cpp
@@ -43,7 +43,7 @@
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();
@@ -71,7 +71,7 @@
}
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..2a96149 100644
--- a/services/surfaceflinger/LayerDim.h
+++ b/services/surfaceflinger/LayerDim.h
@@ -36,13 +36,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/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index ef0d521..fc193e5 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -55,10 +55,11 @@
#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"
@@ -413,6 +414,22 @@
return ctxt;
}
+static GlesVersion parseGlesVersion(const char* str) {
+ int major, minor;
+ if (sscanf(str, "OpenGL ES-CM %d.%d", &major, &minor) != 2) {
+ ALOGW("Unable to parse GL_VERSION string: \"%s\"", str);
+ return GLES_VERSION_1_0;
+ }
+
+ if (major == 1 && minor == 0) return GLES_VERSION_1_0;
+ if (major == 1 && minor >= 1) return GLES_VERSION_1_1;
+ if (major == 2 && minor >= 0) return GLES_VERSION_2_0;
+ if (major == 3 && minor >= 0) return GLES_VERSION_3_0;
+
+ ALOGW("Unrecognized OpenGL ES version: %d.%d", major, minor);
+ return GLES_VERSION_1_0;
+}
+
void SurfaceFlinger::initializeGL(EGLDisplay display) {
GLExtensions& extensions(GLExtensions::getInstance());
extensions.initWithGLStrings(
@@ -424,6 +441,8 @@
eglQueryString(display, EGL_VERSION),
eglQueryString(display, EGL_EXTENSIONS));
+ mGlesVersion = parseGlesVersion(extensions.getVersion());
+
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
glGetIntegerv(GL_MAX_VIEWPORT_DIMS, mMaxViewportDims);
@@ -837,10 +856,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 +870,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 +900,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 +909,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));
@@ -1037,6 +1056,12 @@
{
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;
@@ -1232,7 +1257,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 +1294,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 +1305,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 +1361,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 +1498,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);
}
@@ -2145,19 +2170,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 +2189,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 +2201,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 +2233,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 +2264,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()) {
@@ -2292,9 +2311,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 +2333,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 +2353,57 @@
*/
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",
+ colorizer.bold(result);
+ result.appendFormat("GLES: %s, %s, %s\n",
extensions.getVendor(),
extensions.getRenderer(),
extensions.getVersion());
- result.append(buffer);
- snprintf(buffer, SIZE, "%s\n", extensions.getExtension());
- result.append(buffer);
+ colorizer.reset(result);
+ result.appendFormat("%s\n", extensions.getExtension());
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 +2421,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
@@ -2571,86 +2602,6 @@
// Capture screen into an IGraphiBufferProducer
// ---------------------------------------------------------------------------
-/* The code below is here to handle b/8734824
- *
- * We create a IGraphicBufferProducer wrapper that forwards all calls
- * to the calling binder thread, where they are executed. This allows
- * the calling thread to be reused (on the other side) and not
- * depend on having "enough" binder threads to handle the requests.
- *
- */
-
-class GraphicProducerWrapper : public BBinder, public MessageHandler {
- sp<IGraphicBufferProducer> impl;
- sp<Looper> looper;
- status_t result;
- bool exitPending;
- bool exitRequested;
- mutable Barrier barrier;
- volatile int32_t memoryBarrier;
- uint32_t code;
- Parcel const* data;
- Parcel* reply;
-
- enum {
- MSG_API_CALL,
- MSG_EXIT
- };
-
- /*
- * this is called by our "fake" BpGraphicBufferProducer. We package the
- * data and reply Parcel and forward them to the calling thread.
- */
- virtual status_t transact(uint32_t code,
- const Parcel& data, Parcel* reply, uint32_t flags) {
- this->code = code;
- this->data = &data;
- this->reply = reply;
- android_atomic_acquire_store(0, &memoryBarrier);
- if (exitPending) {
- // if we've exited, we run the message synchronously right here
- handleMessage(Message(MSG_API_CALL));
- } else {
- barrier.close();
- looper->sendMessage(this, Message(MSG_API_CALL));
- barrier.wait();
- }
- return NO_ERROR;
- }
-
- /*
- * here we run on the binder calling thread. All we've got to do is
- * call the real BpGraphicBufferProducer.
- */
- virtual void handleMessage(const Message& message) {
- android_atomic_release_load(&memoryBarrier);
- if (message.what == MSG_API_CALL) {
- impl->asBinder()->transact(code, data[0], reply);
- barrier.open();
- } else if (message.what == MSG_EXIT) {
- exitRequested = true;
- }
- }
-
-public:
- GraphicProducerWrapper(const sp<IGraphicBufferProducer>& impl) :
- impl(impl), looper(new Looper(true)), result(NO_ERROR),
- exitPending(false), exitRequested(false) {
- }
-
- status_t waitForResponse() {
- do {
- looper->pollOnce(-1);
- } while (!exitRequested);
- return result;
- }
-
- void exit(status_t 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,
@@ -2663,25 +2614,24 @@
if (CC_UNLIKELY(producer == 0))
return BAD_VALUE;
-
class MessageCaptureScreen : public MessageBase {
SurfaceFlinger* flinger;
sp<IBinder> display;
sp<IGraphicBufferProducer> producer;
uint32_t reqWidth, reqHeight;
uint32_t minLayerZ,maxLayerZ;
- bool useReadPixels;
+ bool isCpuConsumer;
status_t result;
public:
MessageCaptureScreen(SurfaceFlinger* flinger,
const sp<IBinder>& display,
const sp<IGraphicBufferProducer>& producer,
uint32_t reqWidth, uint32_t reqHeight,
- uint32_t minLayerZ, uint32_t maxLayerZ, bool useReadPixels)
+ uint32_t minLayerZ, uint32_t maxLayerZ, bool isCpuConsumer)
: flinger(flinger), display(display), producer(producer),
reqWidth(reqWidth), reqHeight(reqHeight),
minLayerZ(minLayerZ), maxLayerZ(maxLayerZ),
- useReadPixels(useReadPixels),
+ isCpuConsumer(isCpuConsumer),
result(PERMISSION_DENIED)
{
}
@@ -2691,14 +2641,11 @@
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);
- }
- static_cast<GraphicProducerWrapper*>(producer->asBinder().get())->exit(result);
+ bool useReadPixels = isCpuConsumer && !flinger->mGpuToCpuSupported;
+ result = flinger->captureScreenImplLocked(hw,
+ producer, reqWidth, reqHeight, minLayerZ, maxLayerZ,
+ useReadPixels);
+
return true;
}
};
@@ -2710,40 +2657,12 @@
// 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)
- sp<GraphicProducerWrapper> wrapper = new GraphicProducerWrapper(producer);
-
- // the asInterface() call below creates our "fake" BpGraphicBufferProducer
- // which does the marshaling work forwards to our "fake remote" above.
sp<MessageBase> msg = new MessageCaptureScreen(this,
- display, IGraphicBufferProducer::asInterface( wrapper ),
- reqWidth, reqHeight, minLayerZ, maxLayerZ,
- useReadPixels);
-
- status_t res = postMessageAsync(msg);
+ display, producer, reqWidth, reqHeight, minLayerZ, maxLayerZ,
+ isCpuConsumer);
+ status_t res = postMessageSync(msg);
if (res == NO_ERROR) {
- res = wrapper->waitForResponse();
+ res = static_cast<MessageCaptureScreen*>( msg.get() )->getResult();
}
return res;
}
@@ -2786,7 +2705,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,73 +2726,8 @@
const sp<const DisplayDevice>& hw,
const sp<IGraphicBufferProducer>& producer,
uint32_t reqWidth, uint32_t reqHeight,
- uint32_t minLayerZ, uint32_t maxLayerZ)
-{
- ATRACE_CALL();
-
- // 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;
-
- // 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)
+ uint32_t minLayerZ, uint32_t maxLayerZ,
+ bool useReadPixels)
{
ATRACE_CALL();
@@ -2900,66 +2754,102 @@
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);
- // back to main framebuffer
- glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);
- glDeleteRenderbuffersOES(1, &tname);
- glDeleteFramebuffersOES(1, &name);
+ 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, 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);
+ 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);
+ }
DisplayDevice::setViewportAndProjection(hw);
@@ -2982,13 +2872,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..089c265 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -72,6 +72,13 @@
eTransactionMask = 0x07
};
+enum GlesVersion {
+ GLES_VERSION_1_0 = 0x10000,
+ GLES_VERSION_1_1 = 0x10001,
+ GLES_VERSION_2_0 = 0x20000,
+ GLES_VERSION_3_0 = 0x30000,
+};
+
class SurfaceFlinger : public BinderService<SurfaceFlinger>,
public BnSurfaceComposer,
private IBinder::DeathRecipient,
@@ -79,11 +86,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,6 +128,11 @@
// TODO: this should be made accessible only to HWComposer
const Vector< sp<Layer> >& getLayerSortedByZForHwcDisplay(int id);
+ // return the version of the OpenGL ES composition context
+ GlesVersion getGlesVersion() const {
+ return mGlesVersion;
+ }
+
private:
friend class Client;
friend class DisplayEventConnection;
@@ -298,14 +310,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
@@ -385,12 +391,13 @@
* 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);
@@ -426,6 +433,7 @@
EGLConfig mEGLConfig;
EGLDisplay mEGLDisplay;
EGLint mEGLNativeVisualId;
+ GlesVersion mGlesVersion;
sp<IBinder> mBuiltinDisplays[DisplayDevice::NUM_DISPLAY_TYPES];
// Can only accessed from the main thread, these members
diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.cpp b/services/surfaceflinger/SurfaceFlingerConsumer.cpp
index 2869250..7ac5c60 100644
--- a/services/surfaceflinger/SurfaceFlingerConsumer.cpp
+++ b/services/surfaceflinger/SurfaceFlingerConsumer.cpp
@@ -69,7 +69,7 @@
// 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;
}