Merge "Add sideband streams to BufferQueue and related classes"
diff --git a/cmds/installd/commands.c b/cmds/installd/commands.c
index a167be0..10244ac 100644
--- a/cmds/installd/commands.c
+++ b/cmds/installd/commands.c
@@ -1,16 +1,16 @@
/*
** Copyright 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
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
**
-** http://www.apache.org/licenses/LICENSE-2.0
+** http://www.apache.org/licenses/LICENSE-2.0
**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
** limitations under the License.
*/
@@ -115,6 +115,8 @@
if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, userid))
return -1;
+ remove_profile_file(pkgname);
+
/* delete contents AND directory, no exceptions */
return delete_dir_contents(pkgdir, 1, NULL);
}
@@ -557,9 +559,9 @@
return -1;
}
- dstlen = srclen + strlen(DALVIK_CACHE_PREFIX) +
+ dstlen = srclen + strlen(DALVIK_CACHE_PREFIX) +
strlen(DALVIK_CACHE_POSTFIX) + 1;
-
+
if (dstlen > PKG_PATH_MAX) {
return -1;
}
@@ -568,7 +570,7 @@
DALVIK_CACHE_PREFIX,
src + 1, /* skip the leading / */
DALVIK_CACHE_POSTFIX);
-
+
for(tmp = path + strlen(DALVIK_CACHE_PREFIX); *tmp; tmp++) {
if (*tmp == '/') {
*tmp = '@';
@@ -601,7 +603,7 @@
}
static void run_dex2oat(int zip_fd, int oat_fd, const char* input_file_name,
- const char* output_file_name)
+ const char* output_file_name, const char *pkgname)
{
char dex2oat_flags[PROPERTY_VALUE_MAX];
property_get("dalvik.vm.dex2oat-flags", dex2oat_flags, "");
@@ -613,16 +615,24 @@
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];
+ char profile_file[strlen("--profile-file=") + 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);
+ if (strcmp(pkgname, "*") != 0) {
+ snprintf(profile_file, sizeof(profile_file), "--profile-file=%s/%s",
+ DALVIK_CACHE_PREFIX "profiles", pkgname);
+ } else {
+ strcpy(profile_file, "--no-profile-file");
+ }
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,
+ profile_file,
strlen(dex2oat_flags) > 0 ? dex2oat_flags : NULL,
(char*) NULL);
ALOGE("execl(%s) failed: %s\n", DEX2OAT_BIN, strerror(errno));
@@ -654,7 +664,8 @@
}
}
-int dexopt(const char *apk_path, uid_t uid, int is_public)
+int dexopt(const char *apk_path, uid_t uid, int is_public,
+ const char *pkgname)
{
struct utimbuf ut;
struct stat apk_stat, dex_stat;
@@ -708,6 +719,12 @@
goto fail;
}
+ // Create profile file if there is a package name present.
+ if (strcmp(pkgname, "*") != 0) {
+ create_profile_file(pkgname, uid);
+ }
+
+
ALOGV("DexInv: --- BEGIN '%s' ---\n", apk_path);
pid_t pid;
@@ -740,7 +757,7 @@
if (strncmp(persist_sys_dalvik_vm_lib, "libdvm", 6) == 0) {
run_dexopt(zip_fd, out_fd, apk_path, out_path);
} else if (strncmp(persist_sys_dalvik_vm_lib, "libart", 6) == 0) {
- run_dex2oat(zip_fd, out_fd, apk_path, out_path);
+ run_dex2oat(zip_fd, out_fd, apk_path, out_path, pkgname);
} else {
exit(69); /* Unexpected persist.sys.dalvik.vm.lib value */
}
@@ -804,12 +821,12 @@
int srcend = strlen(srcpath);
int dstend = strlen(dstpath);
-
+
if (lstat(srcpath, statbuf) < 0) {
ALOGW("Unable to stat %s: %s\n", srcpath, strerror(errno));
return 1;
}
-
+
if ((statbuf->st_mode&S_IFDIR) == 0) {
mkinnerdirs(dstpath, dstbasepos, S_IRWXU|S_IRWXG|S_IXOTH,
dstuid, dstgid, statbuf);
@@ -835,7 +852,7 @@
}
res = 0;
-
+
while ((de = readdir(d))) {
const char *name = de->d_name;
/* always skip "." and ".." */
@@ -843,32 +860,32 @@
if (name[1] == 0) continue;
if ((name[1] == '.') && (name[2] == 0)) continue;
}
-
+
if ((srcend+strlen(name)) >= (PKG_PATH_MAX-2)) {
ALOGW("Source path too long; skipping: %s/%s\n", srcpath, name);
continue;
}
-
+
if ((dstend+strlen(name)) >= (PKG_PATH_MAX-2)) {
ALOGW("Destination path too long; skipping: %s/%s\n", dstpath, name);
continue;
}
-
+
srcpath[srcend] = dstpath[dstend] = '/';
strcpy(srcpath+srcend+1, name);
strcpy(dstpath+dstend+1, name);
-
+
if (movefileordir(srcpath, dstpath, dstbasepos, dstuid, dstgid, statbuf) != 0) {
res = 1;
}
-
+
// Note: we will be leaving empty directories behind in srcpath,
// but that is okay, the package manager will be erasing all of the
// data associated with .apks that disappear.
-
+
srcpath[srcend] = dstpath[dstend] = 0;
}
-
+
closedir(d);
return res;
}
@@ -910,7 +927,7 @@
UPDATE_COMMANDS_DIR_PREFIX, name);
continue;
}
-
+
bufp = 0;
bufe = 0;
buf[PKG_PATH_MAX] = 0;
diff --git a/cmds/installd/installd.c b/cmds/installd/installd.c
index 549aaab..b4df3a3 100644
--- a/cmds/installd/installd.c
+++ b/cmds/installd/installd.c
@@ -38,8 +38,8 @@
static int do_dexopt(char **arg, char reply[REPLY_MAX])
{
- /* apk_path, uid, is_public */
- return dexopt(arg[0], atoi(arg[1]), atoi(arg[2]));
+ /* apk_path, uid, is_public, pkgname */
+ return dexopt(arg[0], atoi(arg[1]), atoi(arg[2]), arg[3]);
}
static int do_move_dex(char **arg, char reply[REPLY_MAX])
@@ -138,7 +138,7 @@
struct cmdinfo cmds[] = {
{ "ping", 0, do_ping },
{ "install", 4, do_install },
- { "dexopt", 3, do_dexopt },
+ { "dexopt", 4, do_dexopt },
{ "movedex", 2, do_move_dex },
{ "rmdex", 1, do_rm_dex },
{ "remove", 2, do_remove },
diff --git a/cmds/installd/installd.h b/cmds/installd/installd.h
index cab5cb7..b458031 100644
--- a/cmds/installd/installd.h
+++ b/cmds/installd/installd.h
@@ -189,6 +189,8 @@
int ensure_dir(const char* path, mode_t mode, uid_t uid, gid_t gid);
int ensure_media_user_dirs(userid_t userid);
+int create_profile_file(const char *pkgname, gid_t gid);
+void remove_profile_file(const char *pkgname);
/* commands.c */
@@ -207,7 +209,7 @@
const char *fwdlock_apkpath, const char *asecpath, int64_t *codesize,
int64_t *datasize, int64_t *cachesize, int64_t *asecsize);
int free_cache(int64_t free_size);
-int dexopt(const char *apk_path, uid_t uid, int is_public);
+int dexopt(const char *apk_path, uid_t uid, int is_public, const char *pkgName);
int movefiles();
int linklib(const char* target, const char* source, int userId);
int idmap(const char *target_path, const char *overlay_path, uid_t uid);
diff --git a/cmds/installd/utils.c b/cmds/installd/utils.c
index ef634c6..0642330 100644
--- a/cmds/installd/utils.c
+++ b/cmds/installd/utils.c
@@ -1,16 +1,16 @@
/*
** Copyright 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
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
**
-** http://www.apache.org/licenses/LICENSE-2.0
+** http://www.apache.org/licenses/LICENSE-2.0
**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
** limitations under the License.
*/
@@ -1005,3 +1005,59 @@
return 0;
}
+
+int create_profile_file(const char *pkgname, gid_t gid) {
+ const char *profile_dir = DALVIK_CACHE_PREFIX "profiles";
+ struct stat profileStat;
+ char profile_file[PKG_PATH_MAX];
+
+ // If we don't have a profile directory under dalvik-cache we need to create one.
+ if (stat(profile_dir, &profileStat) < 0) {
+ // Create the profile directory under dalvik-cache.
+ if (mkdir(profile_dir, 0711) < 0) {
+ ALOGE("cannot make profile dir '%s': %s\n", profile_dir, strerror(errno));
+ return -1;
+ }
+
+ // Make the profile directory write-only for group and other. Owner can rwx it.
+ if (chmod(profile_dir, 0711) < 0) {
+ ALOGE("cannot chown profile dir '%s': %s\n", profile_dir, strerror(errno));
+ unlink(profile_dir);
+ return -1;
+ }
+ }
+
+ snprintf(profile_file, sizeof(profile_file), "%s/%s", profile_dir, pkgname);
+
+ // The 'system' user needs to be able to read the profile to determine if dex2oat
+ // needs to be run. This is done in dalvik.system.DexFile.isDexOptNeededInternal(). So
+ // we make it world readable. Not a problem since the dalvik cache is world
+ // readable anyway.
+
+ int fd = open(profile_file, O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW, 0664);
+
+ // Open will fail if the file already exists. We want to ignore that.
+ if (fd >= 0) {
+ if (fchown(fd, -1, gid) < 0) {
+ ALOGE("cannot chown profile file '%s': %s\n", profile_file, strerror(errno));
+ close(fd);
+ unlink(profile_file);
+ return -1;
+ }
+
+ if (fchmod(fd, 0664) < 0) {
+ ALOGE("cannot chmod profile file '%s': %s\n", profile_file, strerror(errno));
+ close(fd);
+ unlink(profile_file);
+ return -1;
+ }
+ close(fd);
+ }
+ return 0;
+}
+
+void remove_profile_file(const char *pkgname) {
+ char profile_file[PKG_PATH_MAX];
+ snprintf(profile_file, sizeof(profile_file), "%s/%s", DALVIK_CACHE_PREFIX "profiles", pkgname);
+ unlink(profile_file);
+}
diff --git a/cmds/screenshot/Android.mk b/cmds/screenshot/Android.mk
deleted file mode 100644
index 1ee7807..0000000
--- a/cmds/screenshot/Android.mk
+++ /dev/null
@@ -1,12 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := screenshot.c
-
-LOCAL_MODULE := screenshot
-
-LOCAL_SHARED_LIBRARIES := libcutils libz liblog
-LOCAL_STATIC_LIBRARIES := libpng
-LOCAL_C_INCLUDES += external/zlib external/libpng
-
-include $(BUILD_EXECUTABLE)
diff --git a/cmds/screenshot/screenshot.c b/cmds/screenshot/screenshot.c
deleted file mode 100644
index be1ecd4..0000000
--- a/cmds/screenshot/screenshot.c
+++ /dev/null
@@ -1,171 +0,0 @@
-#include <stdlib.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-#include <fcntl.h>
-#include <errno.h>
-
-#include <linux/fb.h>
-
-#include <zlib.h>
-#include <png.h>
-
-#include "private/android_filesystem_config.h"
-
-#define LOG_TAG "screenshot"
-#include <utils/Log.h>
-
-void take_screenshot(FILE *fb_in, FILE *fb_out) {
- int fb;
- char imgbuf[0x10000];
- struct fb_var_screeninfo vinfo;
- png_structp png;
- png_infop info;
- unsigned int r,c,rowlen;
- unsigned int bytespp,offset;
-
- fb = fileno(fb_in);
- if(fb < 0) {
- ALOGE("failed to open framebuffer\n");
- return;
- }
- fb_in = fdopen(fb, "r");
-
- if(ioctl(fb, FBIOGET_VSCREENINFO, &vinfo) < 0) {
- ALOGE("failed to get framebuffer info\n");
- return;
- }
- fcntl(fb, F_SETFD, FD_CLOEXEC);
-
- png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
- if (png == NULL) {
- ALOGE("failed png_create_write_struct\n");
- fclose(fb_in);
- return;
- }
-
- png_init_io(png, fb_out);
- info = png_create_info_struct(png);
- if (info == NULL) {
- ALOGE("failed png_create_info_struct\n");
- png_destroy_write_struct(&png, NULL);
- fclose(fb_in);
- return;
- }
- if (setjmp(png_jmpbuf(png))) {
- ALOGE("failed png setjmp\n");
- png_destroy_write_struct(&png, NULL);
- fclose(fb_in);
- return;
- }
-
- bytespp = vinfo.bits_per_pixel / 8;
- png_set_IHDR(png, info,
- vinfo.xres, vinfo.yres, vinfo.bits_per_pixel / 4,
- PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
- PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
- png_write_info(png, info);
-
- rowlen=vinfo.xres * bytespp;
- if (rowlen > sizeof(imgbuf)) {
- ALOGE("crazy rowlen: %d\n", rowlen);
- png_destroy_write_struct(&png, NULL);
- fclose(fb_in);
- return;
- }
-
- offset = vinfo.xoffset * bytespp + vinfo.xres * vinfo.yoffset * bytespp;
- fseek(fb_in, offset, SEEK_SET);
-
- for(r=0; r<vinfo.yres; r++) {
- int len = fread(imgbuf, 1, rowlen, fb_in);
- if (len <= 0) break;
- png_write_row(png, (png_bytep)imgbuf);
- }
-
- png_write_end(png, info);
- fclose(fb_in);
- png_destroy_write_struct(&png, NULL);
-}
-
-void fork_sound(const char* path) {
- pid_t pid = fork();
- if (pid == 0) {
- execl("/system/bin/stagefright", "stagefright", "-o", "-a", path, NULL);
- }
-}
-
-void usage() {
- fprintf(stderr,
- "usage: screenshot [-s soundfile] filename.png\n"
- " -s: play a sound effect to signal success\n"
- " -i: autoincrement to avoid overwriting filename.png\n"
- );
-}
-
-int main(int argc, char**argv) {
- FILE *png = NULL;
- FILE *fb_in = NULL;
- char outfile[PATH_MAX] = "";
-
- char * soundfile = NULL;
- int do_increment = 0;
-
- int c;
- while ((c = getopt(argc, argv, "s:i")) != -1) {
- switch (c) {
- case 's': soundfile = optarg; break;
- case 'i': do_increment = 1; break;
- case '?':
- case 'h':
- usage(); exit(1);
- }
- }
- argc -= optind;
- argv += optind;
-
- if (argc < 1) {
- usage(); exit(1);
- }
-
- strlcpy(outfile, argv[0], PATH_MAX);
- if (do_increment) {
- struct stat st;
- char base[PATH_MAX] = "";
- int i = 0;
- while (stat(outfile, &st) == 0) {
- if (!base[0]) {
- char *p = strrchr(outfile, '.');
- if (p) *p = '\0';
- strcpy(base, outfile);
- }
- snprintf(outfile, PATH_MAX, "%s-%d.png", base, ++i);
- }
- }
-
- fb_in = fopen("/dev/graphics/fb0", "r");
- if (!fb_in) {
- fprintf(stderr, "error: could not read framebuffer\n");
- exit(1);
- }
-
- /* switch to non-root user and group */
- gid_t groups[] = { AID_LOG, AID_SDCARD_RW };
- setgroups(sizeof(groups)/sizeof(groups[0]), groups);
- setuid(AID_SHELL);
-
- png = fopen(outfile, "w");
- if (!png) {
- fprintf(stderr, "error: writing file %s: %s\n",
- outfile, strerror(errno));
- exit(1);
- }
-
- take_screenshot(fb_in, png);
-
- if (soundfile) {
- fork_sound(soundfile);
- }
-
- exit(0);
-}
diff --git a/include/android/keycodes.h b/include/android/keycodes.h
index 1ca1332..b6a5f4c 100644
--- a/include/android/keycodes.h
+++ b/include/android/keycodes.h
@@ -266,6 +266,8 @@
AKEYCODE_BRIGHTNESS_DOWN = 220,
AKEYCODE_BRIGHTNESS_UP = 221,
AKEYCODE_MEDIA_AUDIO_TRACK = 222,
+ AKEYCODE_SLEEP = 223,
+ AKEYCODE_WAKEUP = 224,
// NOTE: If you add a new keycode here you must also add it to several other files.
// Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
diff --git a/include/gui/BufferQueue.h b/include/gui/BufferQueue.h
index 867486b..f74dc26 100644
--- a/include/gui/BufferQueue.h
+++ b/include/gui/BufferQueue.h
@@ -28,10 +28,43 @@
#include <binder/IBinder.h>
namespace android {
-// ----------------------------------------------------------------------------
-class BufferQueue : public BnGraphicBufferProducer,
- public BnGraphicBufferConsumer,
+// BQProducer and BQConsumer are thin shim classes to allow methods with the
+// same signature in both IGraphicBufferProducer and IGraphicBufferConsumer.
+// This will stop being an issue when we deprecate creating BufferQueues
+// directly (as opposed to using the *Producer and *Consumer interfaces).
+class BQProducer : public BnGraphicBufferProducer {
+public:
+ virtual status_t detachProducerBuffer(int slot) = 0;
+ virtual status_t attachProducerBuffer(int* slot,
+ const sp<GraphicBuffer>& buffer) = 0;
+
+ virtual status_t detachBuffer(int slot) {
+ return detachProducerBuffer(slot);
+ }
+
+ virtual status_t attachBuffer(int* slot, const sp<GraphicBuffer>& buffer) {
+ return attachProducerBuffer(slot, buffer);
+ }
+};
+
+class BQConsumer : public BnGraphicBufferConsumer {
+public:
+ virtual status_t detachConsumerBuffer(int slot) = 0;
+ virtual status_t attachConsumerBuffer(int* slot,
+ const sp<GraphicBuffer>& buffer) = 0;
+
+ virtual status_t detachBuffer(int slot) {
+ return detachConsumerBuffer(slot);
+ }
+
+ virtual status_t attachBuffer(int* slot, const sp<GraphicBuffer>& buffer) {
+ return attachConsumerBuffer(slot, buffer);
+ }
+};
+
+class BufferQueue : public BQProducer,
+ public BQConsumer,
private IBinder::DeathRecipient {
public:
// BufferQueue will keep track of at most this value of buffers.
@@ -78,6 +111,15 @@
// producers and consumers. allocator is used to allocate all the
// needed gralloc buffers.
BufferQueue(const sp<IGraphicBufferAlloc>& allocator = NULL);
+
+ static void createBufferQueue(sp<BnGraphicBufferProducer>* outProducer,
+ sp<BnGraphicBufferConsumer>* outConsumer,
+ const sp<IGraphicBufferAlloc>& allocator = NULL);
+
+ static void createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
+ sp<IGraphicBufferConsumer>* outConsumer,
+ const sp<IGraphicBufferAlloc>& allocator = NULL);
+
virtual ~BufferQueue();
/*
@@ -158,6 +200,13 @@
virtual status_t dequeueBuffer(int *buf, sp<Fence>* fence, bool async,
uint32_t width, uint32_t height, uint32_t format, uint32_t usage);
+ // See IGraphicBufferProducer::detachBuffer
+ virtual status_t detachProducerBuffer(int slot);
+
+ // See IGraphicBufferProducer::attachBuffer
+ virtual status_t attachProducerBuffer(int* slot,
+ const sp<GraphicBuffer>& buffer);
+
// queueBuffer returns a filled buffer to the BufferQueue.
//
// Additional data is provided in the QueueBufferInput struct. Notably,
@@ -236,6 +285,13 @@
// is CLOCK_MONOTONIC.
virtual status_t acquireBuffer(BufferItem* buffer, nsecs_t presentWhen);
+ // See IGraphicBufferConsumer::detachBuffer
+ virtual status_t detachConsumerBuffer(int slot);
+
+ // See IGraphicBufferConsumer::attachBuffer
+ virtual status_t attachConsumerBuffer(int* slot,
+ const sp<GraphicBuffer>& buffer);
+
// 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
diff --git a/include/gui/BufferQueueConsumer.h b/include/gui/BufferQueueConsumer.h
index 2bdcf28..7f24c83 100644
--- a/include/gui/BufferQueueConsumer.h
+++ b/include/gui/BufferQueueConsumer.h
@@ -49,6 +49,12 @@
virtual status_t acquireBuffer(BufferItem* outBuffer,
nsecs_t expectedPresent);
+ // See IGraphicBufferConsumer::detachBuffer
+ virtual status_t detachBuffer(int slot);
+
+ // See IGraphicBufferConsumer::attachBuffer
+ virtual status_t attachBuffer(int* slot, const sp<GraphicBuffer>& buffer);
+
// 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
diff --git a/include/gui/BufferQueueProducer.h b/include/gui/BufferQueueProducer.h
index 250d33c..0013b0a 100644
--- a/include/gui/BufferQueueProducer.h
+++ b/include/gui/BufferQueueProducer.h
@@ -96,6 +96,12 @@
virtual status_t dequeueBuffer(int *outSlot, sp<Fence>* outFence, bool async,
uint32_t width, uint32_t height, uint32_t format, uint32_t usage);
+ // See IGraphicBufferProducer::detachBuffer
+ virtual status_t detachBuffer(int slot);
+
+ // See IGraphicBufferProducer::attachBuffer
+ virtual status_t attachBuffer(int* outSlot, const sp<GraphicBuffer>& buffer);
+
// queueBuffer returns a filled buffer to the BufferQueue.
//
// Additional data is provided in the QueueBufferInput struct. Notably,
@@ -163,6 +169,14 @@
// This is required by the IBinder::DeathRecipient interface
virtual void binderDied(const wp<IBinder>& who);
+ // waitForFreeSlotThenRelock finds the oldest slot in the FREE state. It may
+ // block if there are no available slots and we are not in non-blocking
+ // mode (producer and consumer controlled by the application). If it blocks,
+ // it will release mCore->mMutex while blocked so that other operations on
+ // the BufferQueue may succeed.
+ status_t waitForFreeSlotThenRelock(const char* caller, bool async,
+ int* found, status_t* returnFlags) const;
+
sp<BufferQueueCore> mCore;
// This references mCore->mSlots. Lock mCore->mMutex while accessing.
diff --git a/include/gui/BufferSlot.h b/include/gui/BufferSlot.h
index 2c4b43f..6085e11 100644
--- a/include/gui/BufferSlot.h
+++ b/include/gui/BufferSlot.h
@@ -38,7 +38,8 @@
mFrameNumber(0),
mEglFence(EGL_NO_SYNC_KHR),
mAcquireCalled(false),
- mNeedsCleanupOnRelease(false) {
+ mNeedsCleanupOnRelease(false),
+ mAttachedByConsumer(false) {
}
// mGraphicBuffer points to the buffer allocated for this slot or is NULL
@@ -129,6 +130,11 @@
// consumer. This is set when a buffer in ACQUIRED state is freed.
// It causes releaseBuffer to return STALE_BUFFER_SLOT.
bool mNeedsCleanupOnRelease;
+
+ // Indicates whether the buffer was attached on the consumer side.
+ // If so, it needs to set the BUFFER_NEEDS_REALLOCATION flag when dequeued
+ // to prevent the producer from using a stale cached buffer.
+ bool mAttachedByConsumer;
};
} // namespace android
diff --git a/include/gui/IGraphicBufferConsumer.h b/include/gui/IGraphicBufferConsumer.h
index 67b2b04..b0d4c76 100644
--- a/include/gui/IGraphicBufferConsumer.h
+++ b/include/gui/IGraphicBufferConsumer.h
@@ -140,6 +140,36 @@
// * INVALID_OPERATION - too many buffers have been acquired
virtual status_t acquireBuffer(BufferItem* buffer, nsecs_t presentWhen) = 0;
+ // detachBuffer attempts to remove all ownership of the buffer in the given
+ // slot from the buffer queue. If this call succeeds, the slot will be
+ // freed, and there will be no way to obtain the buffer from this interface.
+ // The freed slot will remain unallocated until either it is selected to
+ // hold a freshly allocated buffer in dequeueBuffer or a buffer is attached
+ // to the slot. The buffer must have already been acquired.
+ //
+ // Return of a value other than NO_ERROR means an error has occurred:
+ // * BAD_VALUE - the given slot number is invalid, either because it is
+ // out of the range [0, NUM_BUFFER_SLOTS) or because the slot
+ // it refers to is not currently acquired.
+ virtual status_t detachBuffer(int slot) = 0;
+
+ // attachBuffer attempts to transfer ownership of a buffer to the buffer
+ // queue. If this call succeeds, it will be as if this buffer was acquired
+ // from the returned slot number. As such, this call will fail if attaching
+ // this buffer would cause too many buffers to be simultaneously acquired.
+ //
+ // If the buffer is successfully attached, its frameNumber is initialized
+ // to 0. This must be passed into the releaseBuffer call or else the buffer
+ // will be deallocated as stale.
+ //
+ // Return of a value other than NO_ERROR means an error has occurred:
+ // * BAD_VALUE - outSlot or buffer were NULL
+ // * INVALID_OPERATION - cannot attach the buffer because it would cause too
+ // many buffers to be acquired.
+ // * NO_MEMORY - no free slots available
+ virtual status_t attachBuffer(int *outSlot,
+ const sp<GraphicBuffer>& buffer) = 0;
+
// releaseBuffer releases a buffer slot from the consumer back to the
// BufferQueue. This may be done while the buffer's contents are still
// being accessed. The fence will signal when the buffer is no longer
diff --git a/include/gui/IGraphicBufferProducer.h b/include/gui/IGraphicBufferProducer.h
index 3fe564d..0874f03 100644
--- a/include/gui/IGraphicBufferProducer.h
+++ b/include/gui/IGraphicBufferProducer.h
@@ -151,12 +151,14 @@
//
// Return of a negative means an error has occurred:
// * NO_INIT - the buffer queue has been abandoned.
- // * BAD_VALUE - one of the below conditions occurred:
- // * both in async mode and buffer count was less than the
- // max numbers of buffers that can be allocated at once
- // * attempting dequeue more than one buffer at a time
- // without setting the buffer count with setBufferCount()
- // * -EBUSY - attempting to dequeue too many buffers at a time
+ // * BAD_VALUE - both in async mode and buffer count was less than the
+ // max numbers of buffers that can be allocated at once.
+ // * INVALID_OPERATION - cannot attach the buffer because it would cause
+ // too many buffers to be dequeued, either because
+ // the producer already has a single buffer dequeued
+ // and did not set a buffer count, or because a
+ // buffer count was set and this call would cause
+ // it to be exceeded.
// * WOULD_BLOCK - no buffer is currently available, and blocking is disabled
// since both the producer/consumer are controlled by app
// * NO_MEMORY - out of memory, cannot allocate the graphics buffer.
@@ -166,6 +168,49 @@
virtual status_t dequeueBuffer(int* slot, sp<Fence>* fence, bool async,
uint32_t w, uint32_t h, uint32_t format, uint32_t usage) = 0;
+ // detachBuffer attempts to remove all ownership of the buffer in the given
+ // slot from the buffer queue. If this call succeeds, the slot will be
+ // freed, and there will be no way to obtain the buffer from this interface.
+ // The freed slot will remain unallocated until either it is selected to
+ // hold a freshly allocated buffer in dequeueBuffer or a buffer is attached
+ // to the slot. The buffer must have already been dequeued, and the caller
+ // must already possesses the sp<GraphicBuffer> (i.e., must have called
+ // requestBuffer).
+ //
+ // Return of a value other than NO_ERROR means an error has occurred:
+ // * NO_INIT - the buffer queue has been abandoned.
+ // * BAD_VALUE - the given slot number is invalid, either because it is
+ // out of the range [0, NUM_BUFFER_SLOTS), or because the slot
+ // it refers to is not currently dequeued and requested.
+ virtual status_t detachBuffer(int slot) = 0;
+
+ // attachBuffer attempts to transfer ownership of a buffer to the buffer
+ // queue. If this call succeeds, it will be as if this buffer was dequeued
+ // from the returned slot number. As such, this call will fail if attaching
+ // this buffer would cause too many buffers to be simultaneously dequeued.
+ //
+ // If attachBuffer returns the RELEASE_ALL_BUFFERS flag, the caller is
+ // expected to release all of the mirrored slot->buffer mappings.
+ //
+ // A non-negative value with flags set (see above) will be returned upon
+ // success.
+ //
+ // Return of a negative value means an error has occurred:
+ // * NO_INIT - the buffer queue has been abandoned.
+ // * BAD_VALUE - outSlot or buffer were NULL or invalid combination of
+ // async mode and buffer count override.
+ // * INVALID_OPERATION - cannot attach the buffer because it would cause
+ // too many buffers to be dequeued, either because
+ // the producer already has a single buffer dequeued
+ // and did not set a buffer count, or because a
+ // buffer count was set and this call would cause
+ // it to be exceeded.
+ // * WOULD_BLOCK - no buffer slot is currently available, and blocking is
+ // disabled since both the producer/consumer are
+ // controlled by the app.
+ virtual status_t attachBuffer(int* outSlot,
+ const sp<GraphicBuffer>& buffer) = 0;
+
// queueBuffer indicates that the client has finished filling in the
// contents of the buffer associated with slot and transfers ownership of
// that slot back to the server.
diff --git a/include/input/KeycodeLabels.h b/include/input/KeycodeLabels.h
index 19582e9..a8d63da 100644
--- a/include/input/KeycodeLabels.h
+++ b/include/input/KeycodeLabels.h
@@ -247,6 +247,8 @@
{ "BRIGHTNESS_DOWN", 220 },
{ "BRIGHTNESS_UP", 221 },
{ "MEDIA_AUDIO_TRACK", 222 },
+ { "SLEEP", 223 },
+ { "WAKEUP", 224 },
// NOTE: If you add a new keycode here you must also add it to several other files.
// Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp
index 8584dc9..c306f9d 100644
--- a/libs/gui/BufferQueue.cpp
+++ b/libs/gui/BufferQueue.cpp
@@ -43,6 +43,32 @@
}
}
+void BufferQueue::createBufferQueue(sp<BnGraphicBufferProducer>* outProducer,
+ sp<BnGraphicBufferConsumer>* outConsumer,
+ const sp<IGraphicBufferAlloc>& allocator) {
+ LOG_ALWAYS_FATAL_IF(outProducer == NULL,
+ "BufferQueue: outProducer must not be NULL");
+ LOG_ALWAYS_FATAL_IF(outConsumer == NULL,
+ "BufferQueue: outConsumer must not be NULL");
+
+ sp<BufferQueueCore> core(new BufferQueueCore(allocator));
+ *outProducer = new BufferQueueProducer(core);
+ *outConsumer = new BufferQueueConsumer(core);
+}
+
+void BufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
+ sp<IGraphicBufferConsumer>* outConsumer,
+ const sp<IGraphicBufferAlloc>& allocator) {
+ LOG_ALWAYS_FATAL_IF(outProducer == NULL,
+ "BufferQueue: outProducer must not be NULL");
+ LOG_ALWAYS_FATAL_IF(outConsumer == NULL,
+ "BufferQueue: outConsumer must not be NULL");
+
+ sp<BufferQueueCore> core(new BufferQueueCore(allocator));
+ *outProducer = new BufferQueueProducer(core);
+ *outConsumer = new BufferQueueConsumer(core);
+}
+
BufferQueue::BufferQueue(const sp<IGraphicBufferAlloc>& allocator) :
mProducer(),
mConsumer()
@@ -75,6 +101,15 @@
return mProducer->dequeueBuffer(outBuf, outFence, async, w, h, format, usage);
}
+status_t BufferQueue::detachProducerBuffer(int slot) {
+ return mProducer->detachBuffer(slot);
+}
+
+status_t BufferQueue::attachProducerBuffer(int* slot,
+ const sp<GraphicBuffer>& buffer) {
+ return mProducer->attachBuffer(slot, buffer);
+}
+
status_t BufferQueue::queueBuffer(int buf,
const QueueBufferInput& input, QueueBufferOutput* output) {
return mProducer->queueBuffer(buf, input, output);
@@ -101,6 +136,15 @@
return mConsumer->acquireBuffer(buffer, presentWhen);
}
+status_t BufferQueue::detachConsumerBuffer(int slot) {
+ return mConsumer->detachBuffer(slot);
+}
+
+status_t BufferQueue::attachConsumerBuffer(int* slot,
+ const sp<GraphicBuffer>& buffer) {
+ return mConsumer->attachBuffer(slot, buffer);
+}
+
status_t BufferQueue::releaseBuffer(
int buf, uint64_t frameNumber, EGLDisplay display,
EGLSyncKHR eglFence, const sp<Fence>& fence) {
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index e34f716..756cd61 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -168,13 +168,96 @@
return NO_ERROR;
}
+status_t BufferQueueConsumer::detachBuffer(int slot) {
+ ATRACE_CALL();
+ ATRACE_BUFFER_INDEX(slot);
+ BQ_LOGV("detachBuffer(C): slot %d", slot);
+ Mutex::Autolock lock(mCore->mMutex);
+
+ if (mCore->mIsAbandoned) {
+ BQ_LOGE("detachBuffer(C): BufferQueue has been abandoned");
+ return NO_INIT;
+ }
+
+ if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
+ BQ_LOGE("detachBuffer(C): slot index %d out of range [0, %d)",
+ slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
+ return BAD_VALUE;
+ } else if (mSlots[slot].mBufferState != BufferSlot::ACQUIRED) {
+ BQ_LOGE("detachBuffer(C): slot %d is not owned by the consumer "
+ "(state = %d)", slot, mSlots[slot].mBufferState);
+ return BAD_VALUE;
+ }
+
+ mCore->freeBufferLocked(slot);
+ mCore->mDequeueCondition.broadcast();
+
+ return NO_ERROR;
+}
+
+status_t BufferQueueConsumer::attachBuffer(int* outSlot,
+ const sp<android::GraphicBuffer>& buffer) {
+ ATRACE_CALL();
+
+ if (outSlot == NULL) {
+ BQ_LOGE("attachBuffer(P): outSlot must not be NULL");
+ return BAD_VALUE;
+ } else if (buffer == NULL) {
+ BQ_LOGE("attachBuffer(P): cannot attach NULL buffer");
+ return BAD_VALUE;
+ }
+
+ Mutex::Autolock lock(mCore->mMutex);
+
+ // Make sure we don't have too many acquired buffers and find a free slot
+ // to put the buffer into (the oldest if there are multiple).
+ int numAcquiredBuffers = 0;
+ int found = BufferQueueCore::INVALID_BUFFER_SLOT;
+ for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
+ if (mSlots[s].mBufferState == BufferSlot::ACQUIRED) {
+ ++numAcquiredBuffers;
+ } else if (mSlots[s].mBufferState == BufferSlot::FREE) {
+ if (found == BufferQueueCore::INVALID_BUFFER_SLOT ||
+ mSlots[s].mFrameNumber < mSlots[found].mFrameNumber) {
+ found = s;
+ }
+ }
+ }
+
+ if (numAcquiredBuffers >= mCore->mMaxAcquiredBufferCount + 1) {
+ BQ_LOGE("attachBuffer(P): max acquired buffer count reached: %d "
+ "(max %d)", numAcquiredBuffers,
+ mCore->mMaxAcquiredBufferCount);
+ return INVALID_OPERATION;
+ }
+ if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
+ BQ_LOGE("attachBuffer(P): could not find free buffer slot");
+ return NO_MEMORY;
+ }
+
+ *outSlot = found;
+ ATRACE_BUFFER_INDEX(*outSlot);
+ BQ_LOGV("attachBuffer(C): returning slot %d", *outSlot);
+
+ mSlots[*outSlot].mGraphicBuffer = buffer;
+ mSlots[*outSlot].mBufferState = BufferSlot::ACQUIRED;
+ mSlots[*outSlot].mAttachedByConsumer = true;
+ mSlots[*outSlot].mAcquireCalled = true;
+ mSlots[*outSlot].mNeedsCleanupOnRelease = false;
+ mSlots[*outSlot].mFence = Fence::NO_FENCE;
+ mSlots[*outSlot].mFrameNumber = 0;
+
+ return NO_ERROR;
+}
+
status_t BufferQueueConsumer::releaseBuffer(int slot, uint64_t frameNumber,
const sp<Fence>& releaseFence, EGLDisplay eglDisplay,
EGLSyncKHR eglFence) {
ATRACE_CALL();
ATRACE_BUFFER_INDEX(slot);
- if (slot == BufferQueueCore::INVALID_BUFFER_SLOT || releaseFence == NULL) {
+ if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS ||
+ releaseFence == NULL) {
return BAD_VALUE;
}
@@ -192,7 +275,7 @@
if (current->mSlot == slot) {
BQ_LOGE("releaseBuffer: buffer slot %d pending release is "
"currently queued", slot);
- return -EINVAL;
+ return BAD_VALUE;
}
++current;
}
@@ -210,7 +293,7 @@
} else {
BQ_LOGV("releaseBuffer: attempted to release buffer slot %d "
"but its state was %d", slot, mSlots[slot].mBufferState);
- return -EINVAL;
+ return BAD_VALUE;
}
mCore->mDequeueCondition.broadcast();
@@ -252,7 +335,7 @@
if (mCore->mConsumerListener == NULL) {
BQ_LOGE("disconnect(C): no consumer is connected");
- return -EINVAL;
+ return BAD_VALUE;
}
mCore->mIsAbandoned = true;
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 0ff7b80..7db344a 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -86,7 +86,7 @@
for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
if (mSlots[s].mBufferState == BufferSlot::DEQUEUED) {
BQ_LOGE("setBufferCount: buffer owned by producer");
- return -EINVAL;
+ return BAD_VALUE;
}
}
@@ -121,6 +121,110 @@
return NO_ERROR;
}
+status_t BufferQueueProducer::waitForFreeSlotThenRelock(const char* caller,
+ bool async, int* found, status_t* returnFlags) const {
+ bool tryAgain = true;
+ while (tryAgain) {
+ if (mCore->mIsAbandoned) {
+ BQ_LOGE("%s: BufferQueue has been abandoned", caller);
+ return NO_INIT;
+ }
+
+ const int maxBufferCount = mCore->getMaxBufferCountLocked(async);
+ if (async && mCore->mOverrideMaxBufferCount) {
+ // FIXME: Some drivers are manually setting the buffer count
+ // (which they shouldn't), so we do this extra test here to
+ // handle that case. This is TEMPORARY until we get this fixed.
+ if (mCore->mOverrideMaxBufferCount < maxBufferCount) {
+ BQ_LOGE("%s: async mode is invalid with buffer count override",
+ caller);
+ return BAD_VALUE;
+ }
+ }
+
+ // Free up any buffers that are in slots beyond the max buffer count
+ for (int s = maxBufferCount; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
+ assert(mSlots[s].mBufferState == BufferSlot::FREE);
+ if (mSlots[s].mGraphicBuffer != NULL) {
+ mCore->freeBufferLocked(s);
+ *returnFlags |= RELEASE_ALL_BUFFERS;
+ }
+ }
+
+ // Look for a free buffer to give to the client
+ *found = BufferQueueCore::INVALID_BUFFER_SLOT;
+ int dequeuedCount = 0;
+ int acquiredCount = 0;
+ for (int s = 0; s < maxBufferCount; ++s) {
+ switch (mSlots[s].mBufferState) {
+ case BufferSlot::DEQUEUED:
+ ++dequeuedCount;
+ break;
+ case BufferSlot::ACQUIRED:
+ ++acquiredCount;
+ break;
+ case BufferSlot::FREE:
+ // We return the oldest of the free buffers to avoid
+ // stalling the producer if possible, since the consumer
+ // may still have pending reads of in-flight buffers
+ if (*found == BufferQueueCore::INVALID_BUFFER_SLOT ||
+ mSlots[s].mFrameNumber < mSlots[*found].mFrameNumber) {
+ *found = s;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ // Producers are not allowed to dequeue more than one buffer if they
+ // did not set a buffer count
+ if (!mCore->mOverrideMaxBufferCount && dequeuedCount) {
+ BQ_LOGE("%s: can't dequeue multiple buffers without setting the "
+ "buffer count", caller);
+ return INVALID_OPERATION;
+ }
+
+ // See whether a buffer has been queued since the last
+ // setBufferCount so we know whether to perform the min undequeued
+ // buffers check below
+ if (mCore->mBufferHasBeenQueued) {
+ // Make sure the producer is not trying to dequeue more buffers
+ // than allowed
+ const int newUndequeuedCount =
+ maxBufferCount - (dequeuedCount + 1);
+ const int minUndequeuedCount =
+ mCore->getMinUndequeuedBufferCountLocked(async);
+ if (newUndequeuedCount < minUndequeuedCount) {
+ BQ_LOGE("%s: min undequeued buffer count (%d) exceeded "
+ "(dequeued=%d undequeued=%d)",
+ caller, minUndequeuedCount,
+ dequeuedCount, newUndequeuedCount);
+ return INVALID_OPERATION;
+ }
+ }
+
+ // If no buffer is found, wait for a buffer to be released or for
+ // the max buffer count to change
+ tryAgain = (*found == BufferQueueCore::INVALID_BUFFER_SLOT);
+ if (tryAgain) {
+ // Return an error if we're in non-blocking mode (producer and
+ // consumer are controlled by the application).
+ // However, the consumer is allowed to briefly acquire an extra
+ // buffer (which could cause us to have to wait here), which is
+ // okay, since it is only used to implement an atomic acquire +
+ // release (e.g., in GLConsumer::updateTexImage())
+ if (mCore->mDequeueBufferCannotBlock &&
+ (acquiredCount <= mCore->mMaxAcquiredBufferCount)) {
+ return WOULD_BLOCK;
+ }
+ mCore->mDequeueCondition.wait(mCore->mMutex);
+ }
+ } // while (tryAgain)
+
+ return NO_ERROR;
+}
+
status_t BufferQueueProducer::dequeueBuffer(int *outSlot,
sp<android::Fence> *outFence, bool async,
uint32_t width, uint32_t height, uint32_t format, uint32_t usage) {
@@ -141,6 +245,7 @@
status_t returnFlags = NO_ERROR;
EGLDisplay eglDisplay = EGL_NO_DISPLAY;
EGLSyncKHR eglFence = EGL_NO_SYNC_KHR;
+ bool attachedByConsumer = false;
{ // Autolock scope
Mutex::Autolock lock(mCore->mMutex);
@@ -152,105 +257,12 @@
// Enable the usage bits the consumer requested
usage |= mCore->mConsumerUsageBits;
- int found = -1;
- bool tryAgain = true;
- while (tryAgain) {
- if (mCore->mIsAbandoned) {
- BQ_LOGE("dequeueBuffer: BufferQueue has been abandoned");
- return NO_INIT;
- }
-
- const int maxBufferCount = mCore->getMaxBufferCountLocked(async);
- if (async && mCore->mOverrideMaxBufferCount) {
- // FIXME: Some drivers are manually setting the buffer count
- // (which they shouldn't), so we do this extra test here to
- // handle that case. This is TEMPORARY until we get this fixed.
- if (mCore->mOverrideMaxBufferCount < maxBufferCount) {
- BQ_LOGE("dequeueBuffer: async mode is invalid with "
- "buffer count override");
- return BAD_VALUE;
- }
- }
-
- // Free up any buffers that are in slots beyond the max buffer count
- for (int s = maxBufferCount; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
- assert(mSlots[s].mBufferState == BufferSlot::FREE);
- if (mSlots[s].mGraphicBuffer != NULL) {
- mCore->freeBufferLocked(s);
- returnFlags |= RELEASE_ALL_BUFFERS;
- }
- }
-
- // Look for a free buffer to give to the client
- found = BufferQueueCore::INVALID_BUFFER_SLOT;
- int dequeuedCount = 0;
- int acquiredCount = 0;
- for (int s = 0; s < maxBufferCount; ++s) {
- switch (mSlots[s].mBufferState) {
- case BufferSlot::DEQUEUED:
- ++dequeuedCount;
- break;
- case BufferSlot::ACQUIRED:
- ++acquiredCount;
- break;
- case BufferSlot::FREE:
- // We return the oldest of the free buffers to avoid
- // stalling the producer if possible, since the consumer
- // may still have pending reads of in-flight buffers
- if (found == BufferQueueCore::INVALID_BUFFER_SLOT ||
- mSlots[s].mFrameNumber < mSlots[found].mFrameNumber) {
- found = s;
- }
- break;
- default:
- break;
- }
- }
-
- // Producers are not allowed to dequeue more than one buffer if they
- // did not set a buffer count
- if (!mCore->mOverrideMaxBufferCount && dequeuedCount) {
- BQ_LOGE("dequeueBuffer: can't dequeue multiple buffers "
- "without setting the buffer count");
- return -EINVAL;
- }
-
- // See whether a buffer has been queued since the last
- // setBufferCount so we know whether to perform the min undequeued
- // buffers check below
- if (mCore->mBufferHasBeenQueued) {
- // Make sure the producer is not trying to dequeue more buffers
- // than allowed
- const int newUndequeuedCount =
- maxBufferCount - (dequeuedCount + 1);
- const int minUndequeuedCount =
- mCore->getMinUndequeuedBufferCountLocked(async);
- if (newUndequeuedCount < minUndequeuedCount) {
- BQ_LOGE("dequeueBuffer: min undequeued buffer count (%d) "
- "exceeded (dequeued=%d undequeued=%d)",
- minUndequeuedCount, dequeuedCount,
- newUndequeuedCount);
- return -EBUSY;
- }
- }
-
- // If no buffer is found, wait for a buffer to be released or for
- // the max buffer count to change
- tryAgain = (found == BufferQueueCore::INVALID_BUFFER_SLOT);
- if (tryAgain) {
- // Return an error if we're in non-blocking mode (producer and
- // consumer are controlled by the application).
- // However, the consumer is allowed to briefly acquire an extra
- // buffer (which could cause us to have to wait here), which is
- // okay, since it is only used to implement an atomic acquire +
- // release (e.g., in GLConsumer::updateTexImage())
- if (mCore->mDequeueBufferCannotBlock &&
- (acquiredCount <= mCore->mMaxAcquiredBufferCount)) {
- return WOULD_BLOCK;
- }
- mCore->mDequeueCondition.wait(mCore->mMutex);
- }
- } // while (tryAgain)
+ int found;
+ status_t status = waitForFreeSlotThenRelock("dequeueBuffer", async,
+ &found, &returnFlags);
+ if (status != NO_ERROR) {
+ return status;
+ }
// This should not happen
if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
@@ -261,6 +273,8 @@
*outSlot = found;
ATRACE_BUFFER_INDEX(found);
+ attachedByConsumer = mSlots[found].mAttachedByConsumer;
+
const bool useDefaultSize = !width && !height;
if (useDefaultSize) {
width = mCore->mDefaultWidth;
@@ -316,11 +330,15 @@
return NO_INIT;
}
- mSlots[*outSlot].mFrameNumber = ~0;
+ mSlots[*outSlot].mFrameNumber = UINT32_MAX;
mSlots[*outSlot].mGraphicBuffer = graphicBuffer;
} // Autolock scope
}
+ if (attachedByConsumer) {
+ returnFlags |= BUFFER_NEEDS_REALLOCATION;
+ }
+
if (eglFence != EGL_NO_SYNC_KHR) {
EGLint result = eglClientWaitSyncKHR(eglDisplay, eglFence, 0,
1000000000);
@@ -343,6 +361,81 @@
return returnFlags;
}
+status_t BufferQueueProducer::detachBuffer(int slot) {
+ ATRACE_CALL();
+ ATRACE_BUFFER_INDEX(slot);
+ BQ_LOGV("detachBuffer(P): slot %d", slot);
+ Mutex::Autolock lock(mCore->mMutex);
+
+ if (mCore->mIsAbandoned) {
+ BQ_LOGE("detachBuffer(P): BufferQueue has been abandoned");
+ return NO_INIT;
+ }
+
+ if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
+ BQ_LOGE("detachBuffer(P): slot index %d out of range [0, %d)",
+ slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
+ return BAD_VALUE;
+ } else if (mSlots[slot].mBufferState != BufferSlot::DEQUEUED) {
+ BQ_LOGE("detachBuffer(P): slot %d is not owned by the producer "
+ "(state = %d)", slot, mSlots[slot].mBufferState);
+ return BAD_VALUE;
+ } else if (!mSlots[slot].mRequestBufferCalled) {
+ BQ_LOGE("detachBuffer(P): buffer in slot %d has not been requested",
+ slot);
+ return BAD_VALUE;
+ }
+
+ mCore->freeBufferLocked(slot);
+ mCore->mDequeueCondition.broadcast();
+
+ return NO_ERROR;
+}
+
+status_t BufferQueueProducer::attachBuffer(int* outSlot,
+ const sp<android::GraphicBuffer>& buffer) {
+ ATRACE_CALL();
+
+ if (outSlot == NULL) {
+ BQ_LOGE("attachBuffer(P): outSlot must not be NULL");
+ return BAD_VALUE;
+ } else if (buffer == NULL) {
+ BQ_LOGE("attachBuffer(P): cannot attach NULL buffer");
+ return BAD_VALUE;
+ }
+
+ Mutex::Autolock lock(mCore->mMutex);
+
+ status_t returnFlags = NO_ERROR;
+ int found;
+ // TODO: Should we provide an async flag to attachBuffer? It seems
+ // unlikely that buffers which we are attaching to a BufferQueue will
+ // be asynchronous (droppable), but it may not be impossible.
+ status_t status = waitForFreeSlotThenRelock("attachBuffer(P)", false,
+ &found, &returnFlags);
+ if (status != NO_ERROR) {
+ return status;
+ }
+
+ // This should not happen
+ if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
+ BQ_LOGE("attachBuffer(P): no available buffer slots");
+ return -EBUSY;
+ }
+
+ *outSlot = found;
+ ATRACE_BUFFER_INDEX(*outSlot);
+ BQ_LOGV("attachBuffer(P): returning slot %d flags=%#x",
+ *outSlot, returnFlags);
+
+ mSlots[*outSlot].mGraphicBuffer = buffer;
+ mSlots[*outSlot].mBufferState = BufferSlot::DEQUEUED;
+ mSlots[*outSlot].mEglFence = EGL_NO_SYNC_KHR;
+ mSlots[*outSlot].mFence = Fence::NO_FENCE;
+
+ return returnFlags;
+}
+
status_t BufferQueueProducer::queueBuffer(int slot,
const QueueBufferInput &input, QueueBufferOutput *output) {
ATRACE_CALL();
@@ -371,7 +464,7 @@
break;
default:
BQ_LOGE("queueBuffer: unknown scaling mode %d", scalingMode);
- return -EINVAL;
+ return BAD_VALUE;
}
sp<IConsumerListener> listener;
@@ -398,15 +491,15 @@
if (slot < 0 || slot >= maxBufferCount) {
BQ_LOGE("queueBuffer: slot index %d out of range [0, %d)",
slot, maxBufferCount);
- return -EINVAL;
+ return BAD_VALUE;
} else if (mSlots[slot].mBufferState != BufferSlot::DEQUEUED) {
BQ_LOGE("queueBuffer: slot %d is not owned by the producer "
"(state = %d)", slot, mSlots[slot].mBufferState);
- return -EINVAL;
+ return BAD_VALUE;
} else if (!mSlots[slot].mRequestBufferCalled) {
BQ_LOGE("queueBuffer: slot %d was queued without requesting "
"a buffer", slot);
- return -EINVAL;
+ return BAD_VALUE;
}
BQ_LOGV("queueBuffer: slot=%d/%llu time=%llu crop=[%d,%d,%d,%d] "
@@ -422,7 +515,7 @@
if (croppedRect != crop) {
BQ_LOGE("queueBuffer: crop rect is not contained within the "
"buffer in slot %d", slot);
- return -EINVAL;
+ return BAD_VALUE;
}
mSlots[slot].mFence = fence;
@@ -680,12 +773,12 @@
} else {
BQ_LOGE("disconnect(P): connected to another API "
"(cur=%d req=%d)", mCore->mConnectedApi, api);
- status = -EINVAL;
+ status = BAD_VALUE;
}
break;
default:
BQ_LOGE("disconnect(P): unknown API %d", api);
- status = -EINVAL;
+ status = BAD_VALUE;
break;
}
} // Autolock scope
diff --git a/libs/gui/IGraphicBufferConsumer.cpp b/libs/gui/IGraphicBufferConsumer.cpp
index c97d15b..3598a86 100644
--- a/libs/gui/IGraphicBufferConsumer.cpp
+++ b/libs/gui/IGraphicBufferConsumer.cpp
@@ -184,6 +184,8 @@
enum {
ACQUIRE_BUFFER = IBinder::FIRST_CALL_TRANSACTION,
+ DETACH_BUFFER,
+ ATTACH_BUFFER,
RELEASE_BUFFER,
CONSUMER_CONNECT,
CONSUMER_DISCONNECT,
@@ -224,6 +226,31 @@
return reply.readInt32();
}
+ virtual status_t detachBuffer(int slot) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
+ data.writeInt32(slot);
+ status_t result = remote()->transact(DETACH_BUFFER, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply.readInt32();
+ return result;
+ }
+
+ virtual status_t attachBuffer(int* slot, const sp<GraphicBuffer>& buffer) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
+ data.write(*buffer.get());
+ status_t result = remote()->transact(ATTACH_BUFFER, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ *slot = reply.readInt32();
+ result = reply.readInt32();
+ return result;
+ }
+
virtual status_t releaseBuffer(int buf, uint64_t frameNumber,
EGLDisplay display __attribute__((unused)), EGLSyncKHR fence __attribute__((unused)),
const sp<Fence>& releaseFence) {
@@ -398,6 +425,23 @@
reply->writeInt32(result);
return NO_ERROR;
} break;
+ case DETACH_BUFFER: {
+ CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
+ int slot = data.readInt32();
+ int result = detachBuffer(slot);
+ reply->writeInt32(result);
+ return NO_ERROR;
+ } break;
+ case ATTACH_BUFFER: {
+ CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
+ sp<GraphicBuffer> buffer = new GraphicBuffer();
+ data.read(*buffer.get());
+ int slot;
+ int result = attachBuffer(&slot, buffer);
+ reply->writeInt32(slot);
+ reply->writeInt32(result);
+ return NO_ERROR;
+ } break;
case RELEASE_BUFFER: {
CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
int buf = data.readInt32();
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index efbe878..1d4ec1c 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -35,6 +35,8 @@
REQUEST_BUFFER = IBinder::FIRST_CALL_TRANSACTION,
SET_BUFFER_COUNT,
DEQUEUE_BUFFER,
+ DETACH_BUFFER,
+ ATTACH_BUFFER,
QUEUE_BUFFER,
CANCEL_BUFFER,
QUERY,
@@ -108,6 +110,31 @@
return result;
}
+ virtual status_t detachBuffer(int slot) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
+ data.writeInt32(slot);
+ status_t result = remote()->transact(DETACH_BUFFER, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply.readInt32();
+ return result;
+ }
+
+ virtual status_t attachBuffer(int* slot, const sp<GraphicBuffer>& buffer) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
+ data.write(*buffer.get());
+ status_t result = remote()->transact(ATTACH_BUFFER, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ *slot = reply.readInt32();
+ result = reply.readInt32();
+ return result;
+ }
+
virtual status_t queueBuffer(int buf,
const QueueBufferInput& input, QueueBufferOutput* output) {
Parcel data, reply;
@@ -234,6 +261,23 @@
reply->writeInt32(result);
return NO_ERROR;
} break;
+ case DETACH_BUFFER: {
+ CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+ int slot = data.readInt32();
+ int result = detachBuffer(slot);
+ reply->writeInt32(result);
+ return NO_ERROR;
+ } break;
+ case ATTACH_BUFFER: {
+ CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+ sp<GraphicBuffer> buffer = new GraphicBuffer();
+ data.read(*buffer.get());
+ int slot;
+ int result = attachBuffer(&slot, buffer);
+ reply->writeInt32(slot);
+ reply->writeInt32(result);
+ return NO_ERROR;
+ } break;
case QUEUE_BUFFER: {
CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
int buf = data.readInt32();
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index aafa6ea..b859fcb 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -24,27 +24,31 @@
#include <ui/GraphicBuffer.h>
+#include <binder/IServiceManager.h>
#include <gui/BufferQueue.h>
namespace android {
class BufferQueueTest : public ::testing::Test {
+
+public:
+ static const String16 PRODUCER_NAME;
+ static const String16 CONSUMER_NAME;
+
protected:
-
- BufferQueueTest() {}
-
- virtual void SetUp() {
+ BufferQueueTest() {
const ::testing::TestInfo* const testInfo =
::testing::UnitTest::GetInstance()->current_test_info();
ALOGV("Begin test: %s.%s", testInfo->test_case_name(),
testInfo->name());
- mBQ = new BufferQueue();
+ BufferQueue::createBufferQueue(&mProducer, &mConsumer);
+ sp<IServiceManager> serviceManager = defaultServiceManager();
+ serviceManager->addService(PRODUCER_NAME, mProducer.get());
+ serviceManager->addService(CONSUMER_NAME, mConsumer.get());
}
- virtual void TearDown() {
- mBQ.clear();
-
+ ~BufferQueueTest() {
const ::testing::TestInfo* const testInfo =
::testing::UnitTest::GetInstance()->current_test_info();
ALOGV("End test: %s.%s", testInfo->test_case_name(),
@@ -52,14 +56,19 @@
}
void GetMinUndequeuedBufferCount(int* bufferCount) {
- ASSERT_NE((void*)NULL, bufferCount);
- ASSERT_EQ(OK, mBQ->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, bufferCount));
- ASSERT_LE(0, *bufferCount); // non-negative
+ ASSERT_TRUE(bufferCount != NULL);
+ ASSERT_EQ(OK, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
+ bufferCount));
+ ASSERT_GE(*bufferCount, 0);
}
- sp<BufferQueue> mBQ;
+ sp<BnGraphicBufferProducer> mProducer;
+ sp<BnGraphicBufferConsumer> mConsumer;
};
+const String16 BufferQueueTest::PRODUCER_NAME = String16("BQTestProducer");
+const String16 BufferQueueTest::CONSUMER_NAME = String16("BQTestConsumer");
+
struct DummyConsumer : public BnConsumerListener {
virtual void onFrameAvailable() {}
virtual void onBuffersReleased() {}
@@ -67,10 +76,10 @@
TEST_F(BufferQueueTest, AcquireBuffer_ExceedsMaxAcquireCount_Fails) {
sp<DummyConsumer> dc(new DummyConsumer);
- mBQ->consumerConnect(dc, false);
+ mConsumer->consumerConnect(dc, false);
IGraphicBufferProducer::QueueBufferOutput qbo;
- mBQ->connect(NULL, NATIVE_WINDOW_API_CPU, false, &qbo);
- mBQ->setBufferCount(4);
+ mProducer->connect(NULL, NATIVE_WINDOW_API_CPU, false, &qbo);
+ mProducer->setBufferCount(4);
int slot;
sp<Fence> fence;
@@ -81,50 +90,198 @@
for (int i = 0; i < 2; i++) {
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
- mBQ->dequeueBuffer(&slot, &fence, false, 1, 1, 0,
+ mProducer->dequeueBuffer(&slot, &fence, false, 1, 1, 0,
GRALLOC_USAGE_SW_READ_OFTEN));
- ASSERT_EQ(OK, mBQ->requestBuffer(slot, &buf));
- ASSERT_EQ(OK, mBQ->queueBuffer(slot, qbi, &qbo));
- ASSERT_EQ(OK, mBQ->acquireBuffer(&item, 0));
+ ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buf));
+ ASSERT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo));
+ ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
}
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
- mBQ->dequeueBuffer(&slot, &fence, false, 1, 1, 0,
+ mProducer->dequeueBuffer(&slot, &fence, false, 1, 1, 0,
GRALLOC_USAGE_SW_READ_OFTEN));
- ASSERT_EQ(OK, mBQ->requestBuffer(slot, &buf));
- ASSERT_EQ(OK, mBQ->queueBuffer(slot, qbi, &qbo));
+ ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buf));
+ ASSERT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo));
// Acquire the third buffer, which should fail.
- ASSERT_EQ(INVALID_OPERATION, mBQ->acquireBuffer(&item, 0));
+ ASSERT_EQ(INVALID_OPERATION, mConsumer->acquireBuffer(&item, 0));
}
TEST_F(BufferQueueTest, SetMaxAcquiredBufferCountWithIllegalValues_ReturnsError) {
sp<DummyConsumer> dc(new DummyConsumer);
- mBQ->consumerConnect(dc, false);
+ mConsumer->consumerConnect(dc, false);
int minBufferCount;
ASSERT_NO_FATAL_FAILURE(GetMinUndequeuedBufferCount(&minBufferCount));
- EXPECT_EQ(BAD_VALUE, mBQ->setMaxAcquiredBufferCount(minBufferCount - 1));
+ EXPECT_EQ(BAD_VALUE, mConsumer->setMaxAcquiredBufferCount(
+ minBufferCount - 1));
- EXPECT_EQ(BAD_VALUE, mBQ->setMaxAcquiredBufferCount(0));
- EXPECT_EQ(BAD_VALUE, mBQ->setMaxAcquiredBufferCount(-3));
- EXPECT_EQ(BAD_VALUE, mBQ->setMaxAcquiredBufferCount(
+ EXPECT_EQ(BAD_VALUE, mConsumer->setMaxAcquiredBufferCount(0));
+ EXPECT_EQ(BAD_VALUE, mConsumer->setMaxAcquiredBufferCount(-3));
+ EXPECT_EQ(BAD_VALUE, mConsumer->setMaxAcquiredBufferCount(
BufferQueue::MAX_MAX_ACQUIRED_BUFFERS+1));
- EXPECT_EQ(BAD_VALUE, mBQ->setMaxAcquiredBufferCount(100));
+ EXPECT_EQ(BAD_VALUE, mConsumer->setMaxAcquiredBufferCount(100));
}
TEST_F(BufferQueueTest, SetMaxAcquiredBufferCountWithLegalValues_Succeeds) {
sp<DummyConsumer> dc(new DummyConsumer);
- mBQ->consumerConnect(dc, false);
+ mConsumer->consumerConnect(dc, false);
int minBufferCount;
ASSERT_NO_FATAL_FAILURE(GetMinUndequeuedBufferCount(&minBufferCount));
- EXPECT_EQ(OK, mBQ->setMaxAcquiredBufferCount(1));
- EXPECT_EQ(OK, mBQ->setMaxAcquiredBufferCount(2));
- EXPECT_EQ(OK, mBQ->setMaxAcquiredBufferCount(minBufferCount));
- EXPECT_EQ(OK, mBQ->setMaxAcquiredBufferCount(
+ EXPECT_EQ(OK, mConsumer->setMaxAcquiredBufferCount(1));
+ EXPECT_EQ(OK, mConsumer->setMaxAcquiredBufferCount(2));
+ EXPECT_EQ(OK, mConsumer->setMaxAcquiredBufferCount(minBufferCount));
+ EXPECT_EQ(OK, mConsumer->setMaxAcquiredBufferCount(
BufferQueue::MAX_MAX_ACQUIRED_BUFFERS));
}
+TEST_F(BufferQueueTest, DetachAndReattachOnProducerSide) {
+ sp<DummyConsumer> dc(new DummyConsumer);
+ ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false));
+ IGraphicBufferProducer::QueueBufferOutput output;
+ ASSERT_EQ(OK,
+ mProducer->connect(NULL, NATIVE_WINDOW_API_CPU, false, &output));
+
+ ASSERT_EQ(BAD_VALUE, mProducer->detachBuffer(-1)); // Index too low
+ ASSERT_EQ(BAD_VALUE, mProducer->detachBuffer(
+ BufferQueueDefs::NUM_BUFFER_SLOTS)); // Index too high
+ ASSERT_EQ(BAD_VALUE, mProducer->detachBuffer(0)); // Not dequeued
+
+ int slot;
+ sp<Fence> fence;
+ sp<GraphicBuffer> buffer;
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
+ mProducer->dequeueBuffer(&slot, &fence, false, 0, 0, 0,
+ GRALLOC_USAGE_SW_WRITE_OFTEN));
+ ASSERT_EQ(BAD_VALUE, mProducer->detachBuffer(slot)); // Not requested
+ ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
+ ASSERT_EQ(OK, mProducer->detachBuffer(slot));
+ ASSERT_EQ(BAD_VALUE, mProducer->detachBuffer(slot)); // Not dequeued
+
+ sp<GraphicBuffer> safeToClobberBuffer;
+ // Can no longer request buffer from this slot
+ ASSERT_EQ(BAD_VALUE, mProducer->requestBuffer(slot, &safeToClobberBuffer));
+
+ uint32_t* dataIn;
+ ASSERT_EQ(OK, buffer->lock(GraphicBuffer::USAGE_SW_WRITE_OFTEN,
+ reinterpret_cast<void**>(&dataIn)));
+ *dataIn = 0x12345678;
+ ASSERT_EQ(OK, buffer->unlock());
+
+ int newSlot;
+ ASSERT_EQ(BAD_VALUE, mProducer->attachBuffer(NULL, safeToClobberBuffer));
+ ASSERT_EQ(BAD_VALUE, mProducer->attachBuffer(&newSlot, NULL));
+
+ ASSERT_EQ(OK, mProducer->attachBuffer(&newSlot, buffer));
+ IGraphicBufferProducer::QueueBufferInput input(0, false, Rect(0, 0, 1, 1),
+ NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, false, Fence::NO_FENCE);
+ ASSERT_EQ(OK, mProducer->queueBuffer(newSlot, input, &output));
+
+ IGraphicBufferConsumer::BufferItem item;
+ ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, static_cast<nsecs_t>(0)));
+
+ uint32_t* dataOut;
+ ASSERT_EQ(OK, item.mGraphicBuffer->lock(GraphicBuffer::USAGE_SW_READ_OFTEN,
+ reinterpret_cast<void**>(&dataOut)));
+ ASSERT_EQ(*dataOut, 0x12345678);
+}
+
+TEST_F(BufferQueueTest, DetachAndReattachOnConsumerSide) {
+ sp<DummyConsumer> dc(new DummyConsumer);
+ ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false));
+ IGraphicBufferProducer::QueueBufferOutput output;
+ ASSERT_EQ(OK,
+ mProducer->connect(NULL, NATIVE_WINDOW_API_CPU, false, &output));
+
+ int slot;
+ sp<Fence> fence;
+ sp<GraphicBuffer> buffer;
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
+ mProducer->dequeueBuffer(&slot, &fence, false, 0, 0, 0,
+ GRALLOC_USAGE_SW_WRITE_OFTEN));
+ ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
+ IGraphicBufferProducer::QueueBufferInput input(0, false, Rect(0, 0, 1, 1),
+ NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, false, Fence::NO_FENCE);
+ ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+
+ ASSERT_EQ(BAD_VALUE, mConsumer->detachBuffer(-1)); // Index too low
+ ASSERT_EQ(BAD_VALUE, mConsumer->detachBuffer(
+ BufferQueueDefs::NUM_BUFFER_SLOTS)); // Index too high
+ ASSERT_EQ(BAD_VALUE, mConsumer->detachBuffer(0)); // Not acquired
+
+ IGraphicBufferConsumer::BufferItem item;
+ ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, static_cast<nsecs_t>(0)));
+
+ ASSERT_EQ(OK, mConsumer->detachBuffer(item.mBuf));
+ ASSERT_EQ(BAD_VALUE, mConsumer->detachBuffer(item.mBuf)); // Not acquired
+
+ uint32_t* dataIn;
+ ASSERT_EQ(OK, item.mGraphicBuffer->lock(
+ GraphicBuffer::USAGE_SW_WRITE_OFTEN,
+ reinterpret_cast<void**>(&dataIn)));
+ *dataIn = 0x12345678;
+ ASSERT_EQ(OK, item.mGraphicBuffer->unlock());
+
+ int newSlot;
+ sp<GraphicBuffer> safeToClobberBuffer;
+ ASSERT_EQ(BAD_VALUE, mConsumer->attachBuffer(NULL, safeToClobberBuffer));
+ ASSERT_EQ(BAD_VALUE, mConsumer->attachBuffer(&newSlot, NULL));
+ ASSERT_EQ(OK, mConsumer->attachBuffer(&newSlot, item.mGraphicBuffer));
+
+ ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mBuf, 0, EGL_NO_DISPLAY,
+ EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
+ mProducer->dequeueBuffer(&slot, &fence, false, 0, 0, 0,
+ GRALLOC_USAGE_SW_WRITE_OFTEN));
+ ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
+
+ uint32_t* dataOut;
+ ASSERT_EQ(OK, buffer->lock(GraphicBuffer::USAGE_SW_READ_OFTEN,
+ reinterpret_cast<void**>(&dataOut)));
+ ASSERT_EQ(*dataOut, 0x12345678);
+}
+
+TEST_F(BufferQueueTest, MoveFromConsumerToProducer) {
+ sp<DummyConsumer> dc(new DummyConsumer);
+ ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false));
+ IGraphicBufferProducer::QueueBufferOutput output;
+ ASSERT_EQ(OK,
+ mProducer->connect(NULL, NATIVE_WINDOW_API_CPU, false, &output));
+
+ int slot;
+ sp<Fence> fence;
+ sp<GraphicBuffer> buffer;
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
+ mProducer->dequeueBuffer(&slot, &fence, false, 0, 0, 0,
+ GRALLOC_USAGE_SW_WRITE_OFTEN));
+ ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
+
+ uint32_t* dataIn;
+ ASSERT_EQ(OK, buffer->lock(GraphicBuffer::USAGE_SW_WRITE_OFTEN,
+ reinterpret_cast<void**>(&dataIn)));
+ *dataIn = 0x12345678;
+ ASSERT_EQ(OK, buffer->unlock());
+
+ IGraphicBufferProducer::QueueBufferInput input(0, false, Rect(0, 0, 1, 1),
+ NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, false, Fence::NO_FENCE);
+ ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+
+ IGraphicBufferConsumer::BufferItem item;
+ ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, static_cast<nsecs_t>(0)));
+ ASSERT_EQ(OK, mConsumer->detachBuffer(item.mBuf));
+
+ int newSlot;
+ ASSERT_EQ(OK, mProducer->attachBuffer(&newSlot, item.mGraphicBuffer));
+ ASSERT_EQ(OK, mProducer->queueBuffer(newSlot, input, &output));
+ ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, static_cast<nsecs_t>(0)));
+
+ uint32_t* dataOut;
+ ASSERT_EQ(OK, item.mGraphicBuffer->lock(GraphicBuffer::USAGE_SW_READ_OFTEN,
+ reinterpret_cast<void**>(&dataOut)));
+ ASSERT_EQ(*dataOut, 0x12345678);
+}
+
} // namespace android
diff --git a/opengl/tests/lib/WindowSurface.cpp b/opengl/tests/lib/WindowSurface.cpp
index 7b57dfd..ff91260 100644
--- a/opengl/tests/lib/WindowSurface.cpp
+++ b/opengl/tests/lib/WindowSurface.cpp
@@ -43,8 +43,19 @@
return;
}
+ uint32_t width, height;
+ if (mainDpyInfo.orientation != DISPLAY_ORIENTATION_0 &&
+ mainDpyInfo.orientation != DISPLAY_ORIENTATION_180) {
+ // rotated
+ width = mainDpyInfo.h;
+ height = mainDpyInfo.w;
+ } else {
+ width = mainDpyInfo.w;
+ height = mainDpyInfo.h;
+ }
+
sp<SurfaceControl> sc = surfaceComposerClient->createSurface(
- String8("Benchmark"), mainDpyInfo.w, mainDpyInfo.h,
+ String8("Benchmark"), width, height,
PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eOpaque);
if (sc == NULL || !sc->isValid()) {
fprintf(stderr, "Failed to create SurfaceControl\n");
diff --git a/services/inputflinger/EventHub.cpp b/services/inputflinger/EventHub.cpp
index e2efd17..94fc0af 100644
--- a/services/inputflinger/EventHub.cpp
+++ b/services/inputflinger/EventHub.cpp
@@ -1291,7 +1291,8 @@
device->classes |= INPUT_DEVICE_CLASS_EXTERNAL;
}
- if (device->classes & (INPUT_DEVICE_CLASS_JOYSTICK | INPUT_DEVICE_CLASS_GAMEPAD)) {
+ if (device->classes & (INPUT_DEVICE_CLASS_JOYSTICK | INPUT_DEVICE_CLASS_DPAD)
+ && device->classes & INPUT_DEVICE_CLASS_GAMEPAD) {
device->controllerNumber = getNextControllerNumberLocked(device);
setLedForController(device);
}
diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp
index 5eaf00b..05b1a9c 100644
--- a/services/inputflinger/InputReader.cpp
+++ b/services/inputflinger/InputReader.cpp
@@ -218,6 +218,17 @@
}
+// -- TouchAffineTransformation --
+void TouchAffineTransformation::applyTo(float& x, float& y) const {
+ float newX, newY;
+ newX = x * x_scale + y * x_ymix + x_offset;
+ newY = x * y_xmix + y * y_scale + y_offset;
+
+ x = newX;
+ y = newY;
+}
+
+
// --- InputReader ---
InputReader::InputReader(const sp<EventHubInterface>& eventHub,
@@ -2642,6 +2653,7 @@
dumpVirtualKeys(dump);
dumpRawPointerAxes(dump);
dumpCalibration(dump);
+ dumpAffineTransformation(dump);
dumpSurface(dump);
dump.appendFormat(INDENT3 "Translation and Scaling Factors:\n");
@@ -2740,6 +2752,11 @@
resolveCalibration();
}
+ if (!changes || (changes & InputReaderConfiguration::TOUCH_AFFINE_TRANSFORMATION)) {
+ // Update location calibration to reflect current settings
+ updateAffineTransformation();
+ }
+
if (!changes || (changes & InputReaderConfiguration::CHANGE_POINTER_SPEED)) {
// Update pointer speed.
mPointerVelocityControl.setParameters(mConfig.pointerVelocityControlParameters);
@@ -3265,6 +3282,9 @@
break;
}
+ // Location
+ updateAffineTransformation();
+
if (mDeviceMode == DEVICE_MODE_POINTER) {
// Compute pointer gesture detection parameters.
float rawDiagonal = hypotf(rawWidth, rawHeight);
@@ -3631,6 +3651,22 @@
}
}
+void TouchInputMapper::dumpAffineTransformation(String8& dump) {
+ dump.append(INDENT3 "Affine Transformation:\n");
+
+ dump.appendFormat(INDENT4 "X scale: %0.3f\n", mAffineTransform.x_scale);
+ dump.appendFormat(INDENT4 "X ymix: %0.3f\n", mAffineTransform.x_ymix);
+ dump.appendFormat(INDENT4 "X offset: %0.3f\n", mAffineTransform.x_offset);
+ dump.appendFormat(INDENT4 "Y xmix: %0.3f\n", mAffineTransform.y_xmix);
+ dump.appendFormat(INDENT4 "Y scale: %0.3f\n", mAffineTransform.y_scale);
+ dump.appendFormat(INDENT4 "Y offset: %0.3f\n", mAffineTransform.y_offset);
+}
+
+void TouchInputMapper::updateAffineTransformation() {
+ mAffineTransform = getPolicy()->getTouchAffineTransformation(mDevice->getDescriptor(),
+ mSurfaceOrientation);
+}
+
void TouchInputMapper::reset(nsecs_t when) {
mCursorButtonAccumulator.reset(getDevice());
mCursorScrollAccumulator.reset(getDevice());
@@ -4246,13 +4282,19 @@
break;
}
- // X, Y, and the bounding box for coverage information
- // Adjust coords for surface orientation.
- float x, y, left, top, right, bottom;
+ // Adjust X,Y coords for device calibration
+ // TODO: Adjust coverage coords?
+ float xTransformed = in.x, yTransformed = in.y;
+ mAffineTransform.applyTo(xTransformed, yTransformed);
+
+ // Adjust X, Y, and coverage coords for surface orientation.
+ float x, y;
+ float left, top, right, bottom;
+
switch (mSurfaceOrientation) {
case DISPLAY_ORIENTATION_90:
- x = float(in.y - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
- y = float(mRawPointerAxes.x.maxValue - in.x) * mXScale + mXTranslate;
+ x = float(yTransformed - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
+ y = float(mRawPointerAxes.x.maxValue - xTransformed) * mXScale + mXTranslate;
left = float(rawTop - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
right = float(rawBottom- mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
bottom = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale + mXTranslate;
@@ -4263,8 +4305,8 @@
}
break;
case DISPLAY_ORIENTATION_180:
- x = float(mRawPointerAxes.x.maxValue - in.x) * mXScale + mXTranslate;
- y = float(mRawPointerAxes.y.maxValue - in.y) * mYScale + mYTranslate;
+ x = float(mRawPointerAxes.x.maxValue - xTransformed) * mXScale + mXTranslate;
+ y = float(mRawPointerAxes.y.maxValue - yTransformed) * mYScale + mYTranslate;
left = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale + mXTranslate;
right = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale + mXTranslate;
bottom = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale + mYTranslate;
@@ -4275,8 +4317,8 @@
}
break;
case DISPLAY_ORIENTATION_270:
- x = float(mRawPointerAxes.y.maxValue - in.y) * mYScale + mYTranslate;
- y = float(in.x - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
+ x = float(mRawPointerAxes.y.maxValue - yTransformed) * mYScale + mYTranslate;
+ y = float(xTransformed - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
left = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale + mYTranslate;
right = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale + mYTranslate;
bottom = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
@@ -4287,8 +4329,8 @@
}
break;
default:
- x = float(in.x - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
- y = float(in.y - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
+ x = float(xTransformed - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
+ y = float(yTransformed - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
left = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
right = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
bottom = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
diff --git a/services/inputflinger/InputReader.h b/services/inputflinger/InputReader.h
index 36b206a..c1ce5f7 100644
--- a/services/inputflinger/InputReader.h
+++ b/services/inputflinger/InputReader.h
@@ -138,6 +138,9 @@
// The device name alias supplied by the may have changed for some devices.
CHANGE_DEVICE_ALIAS = 1 << 5,
+ // The location calibration matrix changed.
+ TOUCH_AFFINE_TRANSFORMATION = 1 << 6,
+
// All devices must be reopened.
CHANGE_MUST_REOPEN = 1 << 31,
};
@@ -252,6 +255,29 @@
};
+struct TouchAffineTransformation {
+ float x_scale;
+ float x_ymix;
+ float x_offset;
+ float y_xmix;
+ float y_scale;
+ float y_offset;
+
+ TouchAffineTransformation() :
+ x_scale(1.0f), x_ymix(0.0f), x_offset(0.0f),
+ y_xmix(0.0f), y_scale(1.0f), y_offset(0.0f) {
+ }
+
+ TouchAffineTransformation(float xscale, float xymix, float xoffset,
+ float yxmix, float yscale, float yoffset) :
+ x_scale(xscale), x_ymix(xymix), x_offset(xoffset),
+ y_xmix(yxmix), y_scale(yscale), y_offset(yoffset) {
+ }
+
+ void applyTo(float& x, float& y) const;
+};
+
+
/*
* Input reader policy interface.
*
@@ -287,6 +313,10 @@
/* Gets a user-supplied alias for a particular input device, or an empty string if none. */
virtual String8 getDeviceAlias(const InputDeviceIdentifier& identifier) = 0;
+
+ /* Gets the affine calibration associated with the specified device. */
+ virtual TouchAffineTransformation getTouchAffineTransformation(
+ const String8& inputDeviceDescriptor, int32_t surfaceRotation) = 0;
};
@@ -518,6 +548,7 @@
inline int32_t getControllerNumber() const { return mControllerNumber; }
inline int32_t getGeneration() const { return mGeneration; }
inline const String8& getName() const { return mIdentifier.name; }
+ inline const String8& getDescriptor() { return mIdentifier.descriptor; }
inline uint32_t getClasses() const { return mClasses; }
inline uint32_t getSources() const { return mSources; }
@@ -1295,6 +1326,9 @@
}
} mCalibration;
+ // Affine location transformation/calibration
+ struct TouchAffineTransformation mAffineTransform;
+
// Raw pointer axis information from the driver.
RawPointerAxes mRawPointerAxes;
@@ -1344,7 +1378,9 @@
virtual void parseCalibration();
virtual void resolveCalibration();
virtual void dumpCalibration(String8& dump);
+ virtual void dumpAffineTransformation(String8& dump);
virtual bool hasStylus() const = 0;
+ virtual void updateAffineTransformation();
virtual void syncTouch(nsecs_t when, bool* outHavePointerIds) = 0;
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index aaa973d..c6eb1fd 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -128,6 +128,7 @@
InputReaderConfiguration mConfig;
KeyedVector<int32_t, sp<FakePointerController> > mPointerControllers;
Vector<InputDeviceInfo> mInputDevices;
+ TouchAffineTransformation transform;
protected:
virtual ~FakeInputReaderPolicy() { }
@@ -173,6 +174,15 @@
return mInputDevices;
}
+ TouchAffineTransformation getTouchAffineTransformation(const String8& inputDeviceDescriptor,
+ int32_t surfaceRotation) {
+ return transform;
+ }
+
+ void setTouchAffineTransformation(const TouchAffineTransformation t) {
+ transform = t;
+ }
+
private:
virtual void getReaderConfiguration(InputReaderConfiguration* outConfig) {
*outConfig = mConfig;
@@ -2463,6 +2473,7 @@
static const float Y_PRECISION;
static const float GEOMETRIC_SCALE;
+ static const TouchAffineTransformation AFFINE_TRANSFORM;
static const VirtualKeyDefinition VIRTUAL_KEYS[2];
@@ -2482,8 +2493,11 @@
void prepareDisplay(int32_t orientation);
void prepareVirtualKeys();
+ void prepareLocationCalibration();
int32_t toRawX(float displayX);
int32_t toRawY(float displayY);
+ float toCookedX(float rawX, float rawY);
+ float toCookedY(float rawX, float rawY);
float toDisplayX(int32_t rawX);
float toDisplayY(int32_t rawY);
};
@@ -2510,6 +2524,8 @@
const int32_t TouchInputMapperTest::RAW_SLOT_MAX = 9;
const float TouchInputMapperTest::X_PRECISION = float(RAW_X_MAX - RAW_X_MIN + 1) / DISPLAY_WIDTH;
const float TouchInputMapperTest::Y_PRECISION = float(RAW_Y_MAX - RAW_Y_MIN + 1) / DISPLAY_HEIGHT;
+const TouchAffineTransformation TouchInputMapperTest::AFFINE_TRANSFORM =
+ TouchAffineTransformation(1, -2, 3, -4, 5, -6);
const float TouchInputMapperTest::GEOMETRIC_SCALE =
avg(float(DISPLAY_WIDTH) / (RAW_X_MAX - RAW_X_MIN + 1),
@@ -2531,6 +2547,10 @@
mFakeEventHub->addKey(DEVICE_ID, KEY_MENU, 0, AKEYCODE_MENU, POLICY_FLAG_WAKE);
}
+void TouchInputMapperTest::prepareLocationCalibration() {
+ mFakePolicy->setTouchAffineTransformation(AFFINE_TRANSFORM);
+}
+
int32_t TouchInputMapperTest::toRawX(float displayX) {
return int32_t(displayX * (RAW_X_MAX - RAW_X_MIN + 1) / DISPLAY_WIDTH + RAW_X_MIN);
}
@@ -2539,6 +2559,16 @@
return int32_t(displayY * (RAW_Y_MAX - RAW_Y_MIN + 1) / DISPLAY_HEIGHT + RAW_Y_MIN);
}
+float TouchInputMapperTest::toCookedX(float rawX, float rawY) {
+ AFFINE_TRANSFORM.applyTo(rawX, rawY);
+ return rawX;
+}
+
+float TouchInputMapperTest::toCookedY(float rawX, float rawY) {
+ AFFINE_TRANSFORM.applyTo(rawX, rawY);
+ return rawY;
+}
+
float TouchInputMapperTest::toDisplayX(int32_t rawX) {
return float(rawX - RAW_X_MIN) * DISPLAY_WIDTH / (RAW_X_MAX - RAW_X_MIN + 1);
}
@@ -3226,6 +3256,30 @@
ASSERT_EQ(tilt, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_TILT));
}
+TEST_F(SingleTouchInputMapperTest, Process_XYAxes_AffineCalibration) {
+ SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareLocationCalibration();
+ prepareButtons();
+ prepareAxes(POSITION);
+ addMapperAndConfigure(mapper);
+
+ int32_t rawX = 100;
+ int32_t rawY = 200;
+
+ float x = toDisplayX(toCookedX(rawX, rawY));
+ float y = toDisplayY(toCookedY(rawX, rawY));
+
+ processDown(mapper, rawX, rawY);
+ processSync(mapper);
+
+ NotifyMotionArgs args;
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
+ x, y, 1, 0, 0, 0, 0, 0, 0, 0));
+}
+
TEST_F(SingleTouchInputMapperTest, Process_ShouldHandleAllButtons) {
SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
addConfigurationProperty("touch.deviceType", "touchScreen");
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 09e445d..42993b9 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -464,7 +464,7 @@
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"
+ "flips=%u, isSecure=%d, secureVis=%d, acquired=%d, numLayers=%zu\n"
" v:[%d,%d,%d,%d], f:[%d,%d,%d,%d], s:[%d,%d,%d,%d],"
"transform:[[%0.3f,%0.3f,%0.3f][%0.3f,%0.3f,%0.3f][%0.3f,%0.3f,%0.3f]]\n",
mDisplayName.string(), mType, mHwcDisplayId,
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index 0f87066..11c42e0 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -374,6 +374,17 @@
return result;
}
+status_t VirtualDisplaySurface::detachBuffer(int /* slot */) {
+ VDS_LOGE("detachBuffer is not available for VirtualDisplaySurface");
+ return INVALID_OPERATION;
+}
+
+status_t VirtualDisplaySurface::attachBuffer(int* /* outSlot */,
+ const sp<GraphicBuffer>& /* buffer */) {
+ VDS_LOGE("attachBuffer is not available for VirtualDisplaySurface");
+ return INVALID_OPERATION;
+}
+
status_t VirtualDisplaySurface::queueBuffer(int pslot,
const QueueBufferInput& input, QueueBufferOutput* output) {
VDS_LOGW_IF(mDbgState != DBG_STATE_GLES,
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
index c2cd779..0d30a1b 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
@@ -98,6 +98,8 @@
virtual status_t setBufferCount(int bufferCount);
virtual status_t dequeueBuffer(int* pslot, sp<Fence>* fence, bool async,
uint32_t w, uint32_t h, uint32_t format, uint32_t usage);
+ virtual status_t detachBuffer(int slot);
+ virtual status_t attachBuffer(int* slot, const sp<GraphicBuffer>& buffer);
virtual status_t queueBuffer(int pslot,
const QueueBufferInput& input, QueueBufferOutput* output);
virtual void cancelBuffer(int pslot, const sp<Fence>& fence);
diff --git a/services/surfaceflinger/Effects/Daltonizer.cpp b/services/surfaceflinger/Effects/Daltonizer.cpp
index f384ba4..feb8936 100644
--- a/services/surfaceflinger/Effects/Daltonizer.cpp
+++ b/services/surfaceflinger/Effects/Daltonizer.cpp
@@ -148,9 +148,6 @@
// set to identity, errp, errd, errt ([0] for simulation only)
mat4 correction(0);
- // control: simulation post-correction (used for debugging):
- // set to identity or lms2lmsp, lms2lmsd, lms2lmst
- mat4 control;
switch (mType) {
case protanopia:
case protanomaly:
@@ -172,12 +169,8 @@
break;
}
- if (true) {
- control = simulation;
- }
-
- mColorTransform = lms2rgb * control *
- (simulation * rgb2lms + correction * (rgb2lms - simulation * rgb2lms));
+ mColorTransform = lms2rgb *
+ (simulation * rgb2lms + correction * (rgb2lms - simulation * rgb2lms));
}
} /* namespace android */
diff --git a/services/surfaceflinger/EventThread.cpp b/services/surfaceflinger/EventThread.cpp
index 3528b62..d868f32 100644
--- a/services/surfaceflinger/EventThread.cpp
+++ b/services/surfaceflinger/EventThread.cpp
@@ -328,7 +328,7 @@
mDebugVsyncEnabled?"enabled":"disabled");
result.appendFormat(" soft-vsync: %s\n",
mUseSoftwareVSync?"enabled":"disabled");
- result.appendFormat(" numListeners=%u,\n events-delivered: %u\n",
+ result.appendFormat(" numListeners=%zu,\n events-delivered: %u\n",
mDisplayEventConnections.size(),
mVSyncEvent[DisplayDevice::DISPLAY_PRIMARY].vsync.count);
for (size_t i=0 ; i<mDisplayEventConnections.size() ; i++) {
diff --git a/services/surfaceflinger/FrameTracker.cpp b/services/surfaceflinger/FrameTracker.cpp
index d406672..2fb665e 100644
--- a/services/surfaceflinger/FrameTracker.cpp
+++ b/services/surfaceflinger/FrameTracker.cpp
@@ -17,6 +17,8 @@
// This is needed for stdint.h to define INT64_MAX in C++
#define __STDC_LIMIT_MACROS
+#include <inttypes.h>
+
#include <cutils/log.h>
#include <ui/Fence.h>
@@ -211,7 +213,7 @@
const size_t o = mOffset;
for (size_t i = 1; i < NUM_FRAME_RECORDS; i++) {
const size_t index = (o+i) % NUM_FRAME_RECORDS;
- result.appendFormat("%lld\t%lld\t%lld\n",
+ result.appendFormat("%" PRId64 "\t%" PRId64 "\t%" PRId64 "\n",
mFrameRecords[index].desiredPresentTime,
mFrameRecords[index].actualPresentTime,
mFrameRecords[index].frameReadyTime);
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 0d835dc..31d3f6f 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -21,6 +21,7 @@
#include <errno.h>
#include <math.h>
#include <dlfcn.h>
+#include <inttypes.h>
#include <EGL/egl.h>
@@ -431,7 +432,7 @@
// FIXME: currently we don't get blank/unblank requests
// for displays other than the main display, so we always
// assume a connected display is unblanked.
- ALOGD("marking display %d as acquired/unblanked", i);
+ ALOGD("marking display %zu as acquired/unblanked", i);
hw->acquireScreen();
}
mDisplays.add(token, hw);
@@ -1656,7 +1657,7 @@
case HWC_FRAMEBUFFER_TARGET: {
// this should not happen as the iterator shouldn't
// let us get there.
- ALOGW("HWC_FRAMEBUFFER_TARGET found in hwc list (index=%d)", i);
+ ALOGW("HWC_FRAMEBUFFER_TARGET found in hwc list (index=%zu)", i);
break;
}
}
@@ -2258,7 +2259,7 @@
const nsecs_t period =
getHwComposer().getRefreshPeriod(HWC_DISPLAY_PRIMARY);
- result.appendFormat("%lld\n", period);
+ result.appendFormat("%" PRId64 "\n", period);
if (name.isEmpty()) {
mAnimFrameTracker.dump(result);
@@ -2371,7 +2372,7 @@
const LayerVector& currentLayers = mCurrentState.layersSortedByZ;
const size_t count = currentLayers.size();
colorizer.bold(result);
- result.appendFormat("Visible layers (count = %d)\n", count);
+ result.appendFormat("Visible layers (count = %zu)\n", count);
colorizer.reset(result);
for (size_t i=0 ; i<count ; i++) {
const sp<Layer>& layer(currentLayers[i]);
@@ -2383,7 +2384,7 @@
*/
colorizer.bold(result);
- result.appendFormat("Displays (%d entries)\n", mDisplays.size());
+ result.appendFormat("Displays (%zu entries)\n", mDisplays.size());
colorizer.reset(result);
for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
const sp<const DisplayDevice>& hw(mDisplays[dpy]);
@@ -2993,7 +2994,7 @@
const bool visible = (state.layerStack == hw->getLayerStack())
&& (state.z >= minLayerZ && state.z <= maxLayerZ)
&& (layer->isVisible());
- ALOGE("%c index=%d, name=%s, layerStack=%d, z=%d, visible=%d, flags=%x, alpha=%x",
+ ALOGE("%c index=%zu, name=%s, layerStack=%d, z=%d, visible=%d, flags=%x, alpha=%x",
visible ? '+' : '-',
i, layer->getName().string(), state.layerStack, state.z,
layer->isVisible(), state.flags, state.alpha);