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);