Merge "DO NOT MERGE: Disconnect PBAP while disconnecting HFP profile." into gingerbread
diff --git a/cmds/keystore/keystore.c b/cmds/keystore/keystore.c
index 971a177..afa64f8 100644
--- a/cmds/keystore/keystore.c
+++ b/cmds/keystore/keystore.c
@@ -143,15 +143,20 @@
     send(the_socket, message, length, 0);
 }
 
-/* Here is the file format. Values are encrypted by AES CBC, and MD5 is used to
- * compute their checksums. To make the files portable, the length is stored in
- * network order. Note that the first four bytes are reserved for future use and
- * are always set to zero in this implementation. */
+/* Here is the file format. There are two parts in blob.value, the secret and
+ * the description. The secret is stored in ciphertext, and its original size
+ * can be found in blob.length. The description is stored after the secret in
+ * plaintext, and its size is specified in blob.info. The total size of the two
+ * parts must be no more than VALUE_SIZE bytes. The first three bytes of the
+ * file are reserved for future use and are always set to zero. Fields other
+ * than blob.info, blob.length, and blob.value are modified by encrypt_blob()
+ * and decrypt_blob(). Thus they should not be accessed from outside. */
 
 static int the_entropy = -1;
 
 static struct __attribute__((packed)) {
-    uint32_t reserved;
+    uint8_t reserved[3];
+    uint8_t info;
     uint8_t vector[AES_BLOCK_SIZE];
     uint8_t encrypted[0];
     uint8_t digest[MD5_DIGEST_LENGTH];
@@ -170,9 +175,13 @@
         return SYSTEM_ERROR;
     }
 
-    length = blob.length + blob.value - blob.encrypted;
+    length = blob.length + (blob.value - blob.encrypted);
     length = (length + AES_BLOCK_SIZE - 1) / AES_BLOCK_SIZE * AES_BLOCK_SIZE;
 
+    if (blob.info != 0) {
+        memmove(&blob.encrypted[length], &blob.value[blob.length], blob.info);
+    }
+
     blob.length = htonl(blob.length);
     MD5(blob.digested, length - (blob.digested - blob.encrypted), blob.digest);
 
@@ -180,8 +189,8 @@
     AES_cbc_encrypt(blob.encrypted, blob.encrypted, length, aes_key, vector,
                     AES_ENCRYPT);
 
-    blob.reserved = 0;
-    length += blob.encrypted - (uint8_t *)&blob;
+    memset(blob.reserved, 0, sizeof(blob.reserved));
+    length += (blob.encrypted - (uint8_t *)&blob) + blob.info;
 
     fd = open(".tmp", O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR);
     length -= write(fd, &blob, length);
@@ -200,7 +209,7 @@
     length = read(fd, &blob, sizeof(blob));
     close(fd);
 
-    length -= blob.encrypted - (uint8_t *)&blob;
+    length -= (blob.encrypted - (uint8_t *)&blob) + blob.info;
     if (length < blob.value - blob.encrypted || length % AES_BLOCK_SIZE != 0) {
         return VALUE_CORRUPTED;
     }
@@ -215,8 +224,13 @@
 
     length -= blob.value - blob.digested;
     blob.length = ntohl(blob.length);
-    return (blob.length < 0 || blob.length > length) ? VALUE_CORRUPTED :
-           NO_ERROR;
+    if (blob.length < 0 || blob.length > length) {
+        return VALUE_CORRUPTED;
+    }
+    if (blob.info != 0) {
+        memmove(&blob.value[blob.length], &blob.value[length], blob.info);
+    }
+    return NO_ERROR;
 }
 
 /* Here are the actions. Each of them is a function without arguments. All
@@ -266,6 +280,7 @@
     char name[NAME_MAX];
     int n = sprintf(name, "%u_", uid);
     encode_key(&name[n], params[0].value, params[0].length);
+    blob.info = 0;
     blob.length = params[1].length;
     memcpy(blob.value, params[1].value, params[1].length);
     return encrypt_blob(name, &encryption_key);
@@ -336,56 +351,88 @@
 
 #define MASTER_KEY_FILE ".masterkey"
 #define MASTER_KEY_SIZE 16
+#define SALT_SIZE       16
 
-static void generate_key(uint8_t *key, uint8_t *password, int length)
+static void set_key(uint8_t *key, uint8_t *password, int length, uint8_t *salt)
 {
-    PKCS5_PBKDF2_HMAC_SHA1((char *)password, length, (uint8_t *)"keystore",
-                           sizeof("keystore"), 1024, MASTER_KEY_SIZE, key);
+    if (salt) {
+        PKCS5_PBKDF2_HMAC_SHA1((char *)password, length, salt, SALT_SIZE,
+                               8192, MASTER_KEY_SIZE, key);
+    } else {
+        PKCS5_PBKDF2_HMAC_SHA1((char *)password, length, (uint8_t *)"keystore",
+                               sizeof("keystore"), 1024, MASTER_KEY_SIZE, key);
+    }
 }
 
+/* Here is the history. To improve the security, the parameters to generate the
+ * master key has been changed. To make a seamless transition, we update the
+ * file using the same password when the user unlock it for the first time. If
+ * any thing goes wrong during the transition, the new file will not overwrite
+ * the old one. This avoids permanent damages of the existing data. */
+
 static int8_t password()
 {
     uint8_t key[MASTER_KEY_SIZE];
     AES_KEY aes_key;
-    int n;
+    int8_t response = SYSTEM_ERROR;
 
     if (state == UNINITIALIZED) {
-        blob.length = MASTER_KEY_SIZE;
         if (read(the_entropy, blob.value, MASTER_KEY_SIZE) != MASTER_KEY_SIZE) {
            return SYSTEM_ERROR;
         }
     } else {
-        generate_key(key, params[0].value, params[0].length);
+        int fd = open(MASTER_KEY_FILE, O_RDONLY);
+        uint8_t *salt = NULL;
+        if (fd != -1) {
+            int length = read(fd, &blob, sizeof(blob));
+            close(fd);
+            if (length > SALT_SIZE && blob.info == SALT_SIZE) {
+                salt = (uint8_t *)&blob + length - SALT_SIZE;
+            }
+        }
+
+        set_key(key, params[0].value, params[0].length, salt);
         AES_set_decrypt_key(key, MASTER_KEY_SIZE * 8, &aes_key);
-        n = decrypt_blob(MASTER_KEY_FILE, &aes_key);
-        if (n == SYSTEM_ERROR) {
+        response = decrypt_blob(MASTER_KEY_FILE, &aes_key);
+        if (response == SYSTEM_ERROR) {
             return SYSTEM_ERROR;
         }
-        if (n != NO_ERROR || blob.length != MASTER_KEY_SIZE) {
+        if (response != NO_ERROR || blob.length != MASTER_KEY_SIZE) {
             if (retry <= 0) {
                 reset();
                 return UNINITIALIZED;
             }
             return WRONG_PASSWORD + --retry;
         }
+
+        if (!salt && params[1].length == -1) {
+            params[1] = params[0];
+        }
     }
 
     if (params[1].length == -1) {
         memcpy(key, blob.value, MASTER_KEY_SIZE);
     } else {
-        generate_key(key, params[1].value, params[1].length);
+        uint8_t *salt = &blob.value[MASTER_KEY_SIZE];
+        if (read(the_entropy, salt, SALT_SIZE) != SALT_SIZE) {
+            return SYSTEM_ERROR;
+        }
+
+        set_key(key, params[1].value, params[1].length, salt);
         AES_set_encrypt_key(key, MASTER_KEY_SIZE * 8, &aes_key);
         memcpy(key, blob.value, MASTER_KEY_SIZE);
-        n = encrypt_blob(MASTER_KEY_FILE, &aes_key);
+        blob.info = SALT_SIZE;
+        blob.length = MASTER_KEY_SIZE;
+        response = encrypt_blob(MASTER_KEY_FILE, &aes_key);
     }
 
-    if (n == NO_ERROR) {
+    if (response == NO_ERROR) {
         AES_set_encrypt_key(key, MASTER_KEY_SIZE * 8, &encryption_key);
         AES_set_decrypt_key(key, MASTER_KEY_SIZE * 8, &decryption_key);
         state = NO_ERROR;
         retry = MAX_RETRY;
     }
-    return n;
+    return response;
 }
 
 static int8_t lock()
diff --git a/include/private/surfaceflinger/SharedBufferStack.h b/include/private/surfaceflinger/SharedBufferStack.h
index d689667..d6ae5e9 100644
--- a/include/private/surfaceflinger/SharedBufferStack.h
+++ b/include/private/surfaceflinger/SharedBufferStack.h
@@ -114,8 +114,9 @@
 
     int32_t     identity;       // surface's identity (const)
     int32_t     token;          // surface's token (for debugging)
-    int32_t     reserved32[1];
     Statistics  stats;
+    int8_t      headBuf;        // last retired buffer
+    uint8_t     reservedBytes[3];
     int32_t     reserved;
     BufferData  buffers[NUM_BUFFER_MAX];     // 1024 bytes
 };
@@ -201,6 +202,7 @@
     status_t undoDequeue(int buf);
     
     status_t lock(int buf);
+    status_t cancel(int buf);
     status_t queue(int buf);
     bool needNewBuffer(int buffer) const;
     status_t setDirtyRegion(int buffer, const Region& reg);
@@ -230,8 +232,9 @@
         inline ssize_t operator()();
     };
 
-    struct UndoDequeueUpdate : public UpdateBase {
-        inline UndoDequeueUpdate(SharedBufferBase* sbb);
+    struct CancelUpdate : public UpdateBase {
+        int tail, buf;
+        inline CancelUpdate(SharedBufferBase* sbb, int tail, int buf);
         inline ssize_t operator()();
     };
 
@@ -256,7 +259,6 @@
     int mNumBuffers;
 
     int32_t tail;
-    int32_t undoDequeueTail;
     int32_t queued_head;
     // statistics...
     nsecs_t mDequeueTime[SharedBufferStack::NUM_BUFFER_MAX];
diff --git a/include/surfaceflinger/ISurfaceComposer.h b/include/surfaceflinger/ISurfaceComposer.h
index 76307b2..6533600 100644
--- a/include/surfaceflinger/ISurfaceComposer.h
+++ b/include/surfaceflinger/ISurfaceComposer.h
@@ -115,7 +115,8 @@
      */
     virtual status_t captureScreen(DisplayID dpy,
             sp<IMemoryHeap>* heap,
-            uint32_t* width, uint32_t* height, PixelFormat* format) = 0;
+            uint32_t* width, uint32_t* height, PixelFormat* format,
+            uint32_t reqWidth, uint32_t reqHeight) = 0;
 
     /* Signal surfaceflinger that there might be some work to do
      * This is an ASYNCHRONOUS call.
diff --git a/include/surfaceflinger/Surface.h b/include/surfaceflinger/Surface.h
index 7c5a39b..22684db 100644
--- a/include/surfaceflinger/Surface.h
+++ b/include/surfaceflinger/Surface.h
@@ -199,6 +199,7 @@
      */
     static int setSwapInterval(ANativeWindow* window, int interval);
     static int dequeueBuffer(ANativeWindow* window, android_native_buffer_t** buffer);
+    static int cancelBuffer(ANativeWindow* window, android_native_buffer_t* buffer);
     static int lockBuffer(ANativeWindow* window, android_native_buffer_t* buffer);
     static int queueBuffer(ANativeWindow* window, android_native_buffer_t* buffer);
     static int query(ANativeWindow* window, int what, int* value);
@@ -207,6 +208,7 @@
     int dequeueBuffer(android_native_buffer_t** buffer);
     int lockBuffer(android_native_buffer_t* buffer);
     int queueBuffer(android_native_buffer_t* buffer);
+    int cancelBuffer(android_native_buffer_t* buffer);
     int query(int what, int* value);
     int perform(int operation, va_list args);
 
diff --git a/include/surfaceflinger/SurfaceComposerClient.h b/include/surfaceflinger/SurfaceComposerClient.h
index 8773d71..a80832d 100644
--- a/include/surfaceflinger/SurfaceComposerClient.h
+++ b/include/surfaceflinger/SurfaceComposerClient.h
@@ -170,6 +170,36 @@
 };
 
 // ---------------------------------------------------------------------------
+
+class ScreenshotClient
+{
+    sp<IMemoryHeap> mHeap;
+    uint32_t mWidth;
+    uint32_t mHeight;
+    PixelFormat mFormat;
+public:
+    ScreenshotClient();
+
+    // frees the previous screenshot and capture a new one
+    status_t update();
+    status_t update(uint32_t reqWidth, uint32_t reqHeight);
+
+    // release memory occupied by the screenshot
+    void release();
+
+    // pixels are valid until this object is freed or
+    // release() or update() is called
+    void const* getPixels() const;
+
+    uint32_t getWidth() const;
+    uint32_t getHeight() const;
+    PixelFormat getFormat() const;
+    uint32_t getStride() const;
+    // size of allocated memory in bytes
+    size_t getSize() const;
+};
+
+// ---------------------------------------------------------------------------
 }; // namespace android
 
 #endif // ANDROID_SF_SURFACE_COMPOSER_CLIENT_H
diff --git a/include/ui/EventHub.h b/include/ui/EventHub.h
index d6b09dc..d78e35f 100644
--- a/include/ui/EventHub.h
+++ b/include/ui/EventHub.h
@@ -142,8 +142,13 @@
 public:
     // Synthetic raw event type codes produced when devices are added or removed.
     enum {
+        // Sent when a device is added.
         DEVICE_ADDED = 0x10000000,
-        DEVICE_REMOVED = 0x20000000
+        // Sent when a device is removed.
+        DEVICE_REMOVED = 0x20000000,
+        // Sent when all added/removed devices from the most recent scan have been reported.
+        // This event is always sent at least once.
+        FINISHED_DEVICE_SCAN = 0x30000000,
     };
 
     virtual uint32_t getDeviceClasses(int32_t deviceId) const = 0;
@@ -181,6 +186,8 @@
      */
     virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes,
             uint8_t* outFlags) const = 0;
+
+    virtual void dump(String8& dump) = 0;
 };
 
 class EventHub : public EventHubInterface
@@ -211,16 +218,18 @@
 
     virtual bool getEvent(RawEvent* outEvent);
 
+    virtual void dump(String8& dump);
+
 protected:
     virtual ~EventHub();
     
 private:
     bool openPlatformInput(void);
 
-    int open_device(const char *device);
-    int close_device(const char *device);
-    int scan_dir(const char *dirname);
-    int read_notify(int nfd);
+    int openDevice(const char *device);
+    int closeDevice(const char *device);
+    int scanDir(const char *dirname);
+    int readNotify(int nfd);
 
     status_t mError;
 
@@ -239,8 +248,8 @@
         ~device_t();
     };
 
-    device_t* getDevice(int32_t deviceId) const;
-    bool hasKeycode(device_t* device, int keycode) const;
+    device_t* getDeviceLocked(int32_t deviceId) const;
+    bool hasKeycodeLocked(device_t* device, int keycode) const;
     
     int32_t getScanCodeStateLocked(device_t* device, int32_t scanCode) const;
     int32_t getKeyCodeStateLocked(device_t* device, int32_t keyCode) const;
@@ -269,6 +278,7 @@
     int             mFDCount;
 
     bool            mOpened;
+    bool            mNeedToSendFinishedDeviceScan;
     List<String8>   mExcludedDevices;
 
     // device ids that report particular switches.
diff --git a/include/ui/Input.h b/include/ui/Input.h
index 21baf32..ee40b85 100644
--- a/include/ui/Input.h
+++ b/include/ui/Input.h
@@ -73,7 +73,8 @@
  * policy decisions such as waking from device sleep.
  */
 enum {
-    /* These flags originate in RawEvents and are generally set in the key map. */
+    /* These flags originate in RawEvents and are generally set in the key map.
+     * See also labels for policy flags in KeycodeLabels.h. */
 
     POLICY_FLAG_WAKE = 0x00000001,
     POLICY_FLAG_WAKE_DROPPED = 0x00000002,
@@ -83,6 +84,7 @@
     POLICY_FLAG_ALT_GR = 0x00000020,
     POLICY_FLAG_MENU = 0x00000040,
     POLICY_FLAG_LAUNCHER = 0x00000080,
+    POLICY_FLAG_VIRTUAL = 0x00000100,
 
     POLICY_FLAG_RAW_MASK = 0x0000ffff,
 
diff --git a/include/ui/InputDispatcher.h b/include/ui/InputDispatcher.h
index cc16012..47c5326 100644
--- a/include/ui/InputDispatcher.h
+++ b/include/ui/InputDispatcher.h
@@ -212,8 +212,15 @@
     int32_t ownerPid;
     int32_t ownerUid;
 
-    bool visibleFrameIntersects(const InputWindow* other) const;
     bool touchableAreaContainsPoint(int32_t x, int32_t y) const;
+    bool frameContainsPoint(int32_t x, int32_t y) const;
+
+    /* Returns true if the window is of a trusted type that is allowed to silently
+     * overlay other windows for the purpose of implementing the secure views feature.
+     * Trusted overlays, such as IME windows, can partly obscure other windows without causing
+     * motion events to be delivered to them with AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED.
+     */
+    bool isTrustedOverlay() const;
 };
 
 
@@ -962,7 +969,7 @@
     bool shouldPokeUserActivityForCurrentInputTargetsLocked();
     void pokeUserActivityLocked(nsecs_t eventTime, int32_t eventType);
     bool checkInjectionPermission(const InputWindow* window, const InjectionState* injectionState);
-    bool isWindowObscuredLocked(const InputWindow* window);
+    bool isWindowObscuredAtPointLocked(const InputWindow* window, int32_t x, int32_t y) const;
     bool isWindowFinishedWithPreviousInputLocked(const InputWindow* window);
     String8 getApplicationWindowLabelLocked(const InputApplication* application,
             const InputWindow* window);
diff --git a/include/ui/InputReader.h b/include/ui/InputReader.h
index d3fbaf7..3619189 100644
--- a/include/ui/InputReader.h
+++ b/include/ui/InputReader.h
@@ -103,10 +103,6 @@
     virtual bool getDisplayInfo(int32_t displayId,
             int32_t* width, int32_t* height, int32_t* orientation) = 0;
 
-    /* Provides feedback for a virtual key down.
-     */
-    virtual void virtualKeyDownFeedback() = 0;
-
     /* Intercepts a key event.
      * The policy can use this method as an opportunity to perform power management functions
      * and early event preprocessing such as updating policy flags.
@@ -283,14 +279,14 @@
     // low-level input event decoding and device management
     void process(const RawEvent* rawEvent);
 
-    void addDevice(nsecs_t when, int32_t deviceId);
-    void removeDevice(nsecs_t when, int32_t deviceId);
+    void addDevice(int32_t deviceId);
+    void removeDevice(int32_t deviceId);
     InputDevice* createDevice(int32_t deviceId, const String8& name, uint32_t classes);
     void configureExcludedDevices();
 
     void consumeEvent(const RawEvent* rawEvent);
 
-    void handleConfigurationChanged(nsecs_t when);
+    void handleConfigurationChanged();
 
     // state management for all devices
     Mutex mStateLock;
diff --git a/include/ui/KeycodeLabels.h b/include/ui/KeycodeLabels.h
index c8d6ffc..f71d9cd 100755
--- a/include/ui/KeycodeLabels.h
+++ b/include/ui/KeycodeLabels.h
@@ -142,6 +142,7 @@
     { NULL, 0 }
 };
 
+// See also policy flags in Input.h.
 static const KeycodeLabel FLAGS[] = {
     { "WAKE", 0x00000001 },
     { "WAKE_DROPPED", 0x00000002 },
@@ -151,6 +152,7 @@
     { "ALT_GR", 0x00000020 },
     { "MENU", 0x00000040 },
     { "LAUNCHER", 0x00000080 },
+    { "VIRTUAL", 0x00000100 },
     { NULL, 0 }
 };
 
diff --git a/include/ui/egl/android_natives.h b/include/ui/egl/android_natives.h
index d59d72b..654d0f3 100644
--- a/include/ui/egl/android_natives.h
+++ b/include/ui/egl/android_natives.h
@@ -218,7 +218,17 @@
     int     (*perform)(struct ANativeWindow* window,
                 int operation, ... );
     
-    void* reserved_proc[3];
+    /*
+     * hook used to cancel a buffer that has been dequeued.
+     * No synchronization is performed between dequeue() and cancel(), so
+     * either external synchronization is needed, or these functions must be
+     * called from the same thread.
+     */
+    int     (*cancelBuffer)(struct ANativeWindow* window,
+                struct android_native_buffer_t* buffer);
+
+
+    void* reserved_proc[2];
 };
 
 // Backwards compatibility...  please switch to ANativeWindow.
diff --git a/include/utils/Looper.h b/include/utils/Looper.h
index 3f00b78..cc51490 100644
--- a/include/utils/Looper.h
+++ b/include/utils/Looper.h
@@ -20,9 +20,22 @@
 #include <utils/threads.h>
 #include <utils/RefBase.h>
 #include <utils/KeyedVector.h>
+#include <utils/Timers.h>
 
 #include <android/looper.h>
 
+// Currently using poll() instead of epoll_wait() since it does a better job of meeting a
+// timeout deadline.  epoll_wait() typically causes additional delays of up to 10ms
+// beyond the requested timeout.
+//#define LOOPER_USES_EPOLL
+//#define LOOPER_STATISTICS
+
+#ifdef LOOPER_USES_EPOLL
+#include <sys/epoll.h>
+#else
+#include <sys/poll.h>
+#endif
+
 /*
  * Declare a concrete type for the NDK's looper forward declaration.
  */
@@ -190,13 +203,54 @@
 
     const bool mAllowNonCallbacks; // immutable
 
-    int mEpollFd; // immutable
     int mWakeReadPipeFd;  // immutable
     int mWakeWritePipeFd; // immutable
+    Mutex mLock;
+
+#ifdef LOOPER_USES_EPOLL
+    int mEpollFd; // immutable
 
     // Locked list of file descriptor monitoring requests.
-    Mutex mLock;
-    KeyedVector<int, Request> mRequests;
+    KeyedVector<int, Request> mRequests;  // guarded by mLock
+#else
+    // The lock guards state used to track whether there is a poll() in progress and whether
+    // there are any other threads waiting in wakeAndLock().  The condition variables
+    // are used to transfer control among these threads such that all waiters are
+    // serviced before a new poll can begin.
+    // The wakeAndLock() method increments mWaiters, wakes the poll, blocks on mAwake
+    // until mPolling becomes false, then decrements mWaiters again.
+    // The poll() method blocks on mResume until mWaiters becomes 0, then sets
+    // mPolling to true, blocks until the poll completes, then resets mPolling to false
+    // and signals mResume if there are waiters.
+    bool mPolling;      // guarded by mLock
+    uint32_t mWaiters;  // guarded by mLock
+    Condition mAwake;   // guarded by mLock
+    Condition mResume;  // guarded by mLock
+
+    Vector<struct pollfd> mRequestedFds;  // must hold mLock and mPolling must be false to modify
+    Vector<Request> mRequests;            // must hold mLock and mPolling must be false to modify
+
+    ssize_t getRequestIndexLocked(int fd);
+    void wakeAndLock();
+#endif
+
+#ifdef LOOPER_STATISTICS
+    static const int SAMPLED_WAKE_CYCLES_TO_AGGREGATE = 100;
+    static const int SAMPLED_POLLS_TO_AGGREGATE = 1000;
+
+    nsecs_t mPendingWakeTime;
+    int mPendingWakeCount;
+
+    int mSampledWakeCycles;
+    int mSampledWakeCountSum;
+    nsecs_t mSampledWakeLatencySum;
+
+    int mSampledPolls;
+    int mSampledZeroPollCount;
+    int mSampledZeroPollLatencySum;
+    int mSampledTimeoutPollCount;
+    int mSampledTimeoutPollLatencySum;
+#endif
 
     // This state is only used privately by pollOnce and does not require a lock since
     // it runs on a single thread.
@@ -204,6 +258,8 @@
     size_t mResponseIndex;
 
     int pollInner(int timeoutMillis);
+    void awoken();
+    void pushResponse(int events, const Request& request);
 
     static void initTLSKey();
     static void threadDestructor(void *st);
diff --git a/include/utils/ZipFileRO.h b/include/utils/ZipFileRO.h
index 9668bde..3c1f3ca 100644
--- a/include/utils/ZipFileRO.h
+++ b/include/utils/ZipFileRO.h
@@ -14,13 +14,19 @@
  * limitations under the License.
  */
 
-//
-// Read-only access to Zip archives, with minimal heap allocation.
-//
-// This is similar to the more-complete ZipFile class, but no attempt
-// has been made to make them interchangeable.  This class operates under
-// a very different set of assumptions and constraints.
-//
+/*
+ * Read-only access to Zip archives, with minimal heap allocation.
+ *
+ * This is similar to the more-complete ZipFile class, but no attempt
+ * has been made to make them interchangeable.  This class operates under
+ * a very different set of assumptions and constraints.
+ *
+ * One such assumption is that if you're getting file descriptors for
+ * use with this class as a child of a fork() operation, you must be on
+ * a pread() to guarantee correct operation. This is because pread() can
+ * atomically read at a file offset without worrying about a lock around an
+ * lseek() + read() pair.
+ */
 #ifndef __LIBS_ZIPFILERO_H
 #define __LIBS_ZIPFILERO_H
 
@@ -55,6 +61,10 @@
  * the record structure.  However, this requires a private mapping of
  * every page that the Central Directory touches.  Easier to tuck a copy
  * of the string length into the hash table entry.
+ *
+ * NOTE: If this is used on file descriptors inherited from a fork() operation,
+ * you must be on a platform that implements pread() to guarantee correctness
+ * on the shared file descriptors.
  */
 class ZipFileRO {
 public:
@@ -64,15 +74,8 @@
           mNumEntries(-1), mDirectoryOffset(-1),
           mHashTableSize(-1), mHashTable(NULL)
         {}
-    ~ZipFileRO() {
-        free(mHashTable);
-        if (mDirectoryMap)
-            mDirectoryMap->release();
-        if (mFd >= 0)
-            close(mFd);
-        if (mFileName)
-            free(mFileName);
-    }
+
+    ~ZipFileRO();
 
     /*
      * Open an archive.
diff --git a/libs/surfaceflinger_client/ISurfaceComposer.cpp b/libs/surfaceflinger_client/ISurfaceComposer.cpp
index 040060e..d676f5e 100644
--- a/libs/surfaceflinger_client/ISurfaceComposer.cpp
+++ b/libs/surfaceflinger_client/ISurfaceComposer.cpp
@@ -126,11 +126,14 @@
 
     virtual status_t captureScreen(DisplayID dpy,
             sp<IMemoryHeap>* heap,
-            uint32_t* width, uint32_t* height, PixelFormat* format)
+            uint32_t* width, uint32_t* height, PixelFormat* format,
+            uint32_t reqWidth, uint32_t reqHeight)
     {
         Parcel data, reply;
         data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
         data.writeInt32(dpy);
+        data.writeInt32(reqWidth);
+        data.writeInt32(reqHeight);
         remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN, data, &reply);
         *heap = interface_cast<IMemoryHeap>(reply.readStrongBinder());
         *width = reply.readInt32();
@@ -208,10 +211,13 @@
         case CAPTURE_SCREEN: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
             DisplayID dpy = data.readInt32();
+            uint32_t reqWidth = data.readInt32();
+            uint32_t reqHeight = data.readInt32();
             sp<IMemoryHeap> heap;
             uint32_t w, h;
             PixelFormat f;
-            status_t res = captureScreen(dpy, &heap, &w, &h, &f);
+            status_t res = captureScreen(dpy, &heap, &w, &h, &f,
+                    reqWidth, reqHeight);
             reply->writeStrongBinder(heap->asBinder());
             reply->writeInt32(w);
             reply->writeInt32(h);
diff --git a/libs/surfaceflinger_client/SharedBufferStack.cpp b/libs/surfaceflinger_client/SharedBufferStack.cpp
index 38b2fae..4bc5d9e 100644
--- a/libs/surfaceflinger_client/SharedBufferStack.cpp
+++ b/libs/surfaceflinger_client/SharedBufferStack.cpp
@@ -285,10 +285,12 @@
     return NO_ERROR;
 }
 
-SharedBufferClient::UndoDequeueUpdate::UndoDequeueUpdate(SharedBufferBase* sbb)
-    : UpdateBase(sbb) {    
+SharedBufferClient::CancelUpdate::CancelUpdate(SharedBufferBase* sbb,
+        int tail, int buf)
+    : UpdateBase(sbb), tail(tail), buf(buf) {
 }
-ssize_t SharedBufferClient::UndoDequeueUpdate::operator()() {
+ssize_t SharedBufferClient::CancelUpdate::operator()() {
+    stack.index[tail] = buf;
     android_atomic_inc(&stack.available);
     return NO_ERROR;
 }
@@ -319,7 +321,7 @@
         return BAD_VALUE;
 
     // Preventively lock the current buffer before updating queued.
-    android_atomic_write(stack.index[head], &stack.inUse);
+    android_atomic_write(stack.headBuf, &stack.inUse);
 
     // Decrement the number of queued buffers 
     int32_t queued;
@@ -334,7 +336,9 @@
     // the buffer we preventively locked upon entering this function
 
     head = (head + 1) % numBuffers;
-    android_atomic_write(stack.index[head], &stack.inUse);
+    const int8_t headBuf = stack.index[head];
+    stack.headBuf = headBuf;
+    android_atomic_write(headBuf, &stack.inUse);
 
     // head is only modified here, so we don't need to use cmpxchg
     android_atomic_write(head, &stack.head);
@@ -359,7 +363,7 @@
 SharedBufferClient::SharedBufferClient(SharedClient* sharedClient,
         int surface, int num, int32_t identity)
     : SharedBufferBase(sharedClient, surface, identity),
-      mNumBuffers(num), tail(0), undoDequeueTail(0)
+      mNumBuffers(num), tail(0)
 {
     SharedBufferStack& stack( *mSharedStack );
     tail = computeTail();
@@ -395,7 +399,6 @@
     DequeueUpdate update(this);
     updateCondition( update );
 
-    undoDequeueTail = tail;
     int dequeued = stack.index[tail];
     tail = ((tail+1 >= mNumBuffers) ? 0 : tail+1);
     LOGD_IF(DEBUG_ATOMICS, "dequeued=%d, tail++=%d, %s",
@@ -408,14 +411,19 @@
 
 status_t SharedBufferClient::undoDequeue(int buf)
 {
+    return cancel(buf);
+}
+
+status_t SharedBufferClient::cancel(int buf)
+{
     RWLock::AutoRLock _rd(mLock);
 
-    // TODO: we can only undo the previous dequeue, we should
-    // enforce that in the api
-    UndoDequeueUpdate update(this);
+    // calculate the new position of the tail index (essentially tail--)
+    int localTail = (tail + mNumBuffers - 1) % mNumBuffers;
+    CancelUpdate update(this, localTail, buf);
     status_t err = updateCondition( update );
     if (err == NO_ERROR) {
-        tail = undoDequeueTail;
+        tail = localTail;
     }
     return err;
 }
diff --git a/libs/surfaceflinger_client/Surface.cpp b/libs/surfaceflinger_client/Surface.cpp
index 560ea67..d44aab9 100644
--- a/libs/surfaceflinger_client/Surface.cpp
+++ b/libs/surfaceflinger_client/Surface.cpp
@@ -409,6 +409,7 @@
 {
     ANativeWindow::setSwapInterval  = setSwapInterval;
     ANativeWindow::dequeueBuffer    = dequeueBuffer;
+    ANativeWindow::cancelBuffer     = cancelBuffer;
     ANativeWindow::lockBuffer       = lockBuffer;
     ANativeWindow::queueBuffer      = queueBuffer;
     ANativeWindow::query            = query;
@@ -517,6 +518,12 @@
     return self->dequeueBuffer(buffer);
 }
 
+int Surface::cancelBuffer(ANativeWindow* window,
+        android_native_buffer_t* buffer) {
+    Surface* self = getSelf(window);
+    return self->cancelBuffer(buffer);
+}
+
 int Surface::lockBuffer(ANativeWindow* window, 
         android_native_buffer_t* buffer) {
     Surface* self = getSelf(window);
@@ -617,6 +624,33 @@
     return err;
 }
 
+int Surface::cancelBuffer(android_native_buffer_t* buffer)
+{
+    status_t err = validate();
+    switch (err) {
+    case NO_ERROR:
+        // no error, common case
+        break;
+    case INVALID_OPERATION:
+        // legitimate errors here
+        return err;
+    default:
+        // other errors happen because the surface is now invalid,
+        // for instance because it has been destroyed. In this case,
+        // we just fail silently (canceling a buffer is not technically
+        // an error at this point)
+        return NO_ERROR;
+    }
+
+    int32_t bufIdx = getBufferIndex(GraphicBuffer::getSelf(buffer));
+
+    err = mSharedBufferClient->cancel(bufIdx);
+
+    LOGE_IF(err, "error canceling buffer %d (%s)", bufIdx, strerror(-err));
+    return err;
+}
+
+
 int Surface::lockBuffer(android_native_buffer_t* buffer)
 {
     status_t err = validate();
diff --git a/libs/surfaceflinger_client/SurfaceComposerClient.cpp b/libs/surfaceflinger_client/SurfaceComposerClient.cpp
index 4096ac6..f270461 100644
--- a/libs/surfaceflinger_client/SurfaceComposerClient.cpp
+++ b/libs/surfaceflinger_client/SurfaceComposerClient.cpp
@@ -545,5 +545,55 @@
 }
 
 // ----------------------------------------------------------------------------
+
+ScreenshotClient::ScreenshotClient()
+    : mWidth(0), mHeight(0), mFormat(PIXEL_FORMAT_NONE) {
+}
+
+status_t ScreenshotClient::update() {
+    sp<ISurfaceComposer> s(ComposerService::getComposerService());
+    if (s == NULL) return NO_INIT;
+    mHeap = 0;
+    return s->captureScreen(0, &mHeap,
+            &mWidth, &mHeight, &mFormat, 0, 0);
+}
+
+status_t ScreenshotClient::update(uint32_t reqWidth, uint32_t reqHeight) {
+    sp<ISurfaceComposer> s(ComposerService::getComposerService());
+    if (s == NULL) return NO_INIT;
+    mHeap = 0;
+    return s->captureScreen(0, &mHeap,
+            &mWidth, &mHeight, &mFormat, reqWidth, reqHeight);
+}
+
+void ScreenshotClient::release() {
+    mHeap = 0;
+}
+
+void const* ScreenshotClient::getPixels() const {
+    return mHeap->getBase();
+}
+
+uint32_t ScreenshotClient::getWidth() const {
+    return mWidth;
+}
+
+uint32_t ScreenshotClient::getHeight() const {
+    return mHeight;
+}
+
+PixelFormat ScreenshotClient::getFormat() const {
+    return mFormat;
+}
+
+uint32_t ScreenshotClient::getStride() const {
+    return mWidth;
+}
+
+size_t ScreenshotClient::getSize() const {
+    return mHeap->getSize();
+}
+
+// ----------------------------------------------------------------------------
 }; // namespace android
 
diff --git a/libs/ui/EventHub.cpp b/libs/ui/EventHub.cpp
index 1d38b4b..c0be3a0 100644
--- a/libs/ui/EventHub.cpp
+++ b/libs/ui/EventHub.cpp
@@ -73,6 +73,10 @@
 #define ABS_MT_POSITION_Y       0x36    /* Center Y ellipse position */
 #endif
 
+#define INDENT "  "
+#define INDENT2 "    "
+#define INDENT3 "      "
+
 namespace android {
 
 static const char *WAKE_LOCK_ID = "KeyEvents";
@@ -84,6 +88,10 @@
     return (v1 > v2) ? v1 : v2;
 }
 
+static inline const char* toString(bool value) {
+    return value ? "true" : "false";
+}
+
 EventHub::device_t::device_t(int32_t _id, const char* _path, const char* name)
     : id(_id), path(_path), name(name), classes(0)
     , keyBitmask(NULL), layoutMap(new KeyLayoutMap()), fd(-1), next(NULL) {
@@ -98,7 +106,7 @@
     : mError(NO_INIT), mHaveFirstKeyboard(false), mFirstKeyboardId(0)
     , mDevicesById(0), mNumDevicesById(0)
     , mOpeningDevices(0), mClosingDevices(0)
-    , mDevices(0), mFDs(0), mFDCount(0), mOpened(false)
+    , mDevices(0), mFDs(0), mFDCount(0), mOpened(false), mNeedToSendFinishedDeviceScan(false)
     , mInputBufferIndex(0), mInputBufferCount(0), mInputDeviceIndex(0)
 {
     acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
@@ -124,7 +132,7 @@
 String8 EventHub::getDeviceName(int32_t deviceId) const
 {
     AutoMutex _l(mLock);
-    device_t* device = getDevice(deviceId);
+    device_t* device = getDeviceLocked(deviceId);
     if (device == NULL) return String8();
     return device->name;
 }
@@ -132,7 +140,7 @@
 uint32_t EventHub::getDeviceClasses(int32_t deviceId) const
 {
     AutoMutex _l(mLock);
-    device_t* device = getDevice(deviceId);
+    device_t* device = getDeviceLocked(deviceId);
     if (device == NULL) return 0;
     return device->classes;
 }
@@ -142,7 +150,7 @@
     outAxisInfo->clear();
 
     AutoMutex _l(mLock);
-    device_t* device = getDevice(deviceId);
+    device_t* device = getDeviceLocked(deviceId);
     if (device == NULL) return -1;
 
     struct input_absinfo info;
@@ -167,7 +175,7 @@
     if (scanCode >= 0 && scanCode <= KEY_MAX) {
         AutoMutex _l(mLock);
 
-        device_t* device = getDevice(deviceId);
+        device_t* device = getDeviceLocked(deviceId);
         if (device != NULL) {
             return getScanCodeStateLocked(device, scanCode);
         }
@@ -188,7 +196,7 @@
 int32_t EventHub::getKeyCodeState(int32_t deviceId, int32_t keyCode) const {
     AutoMutex _l(mLock);
 
-    device_t* device = getDevice(deviceId);
+    device_t* device = getDeviceLocked(deviceId);
     if (device != NULL) {
         return getKeyCodeStateLocked(device, keyCode);
     }
@@ -225,7 +233,7 @@
     if (sw >= 0 && sw <= SW_MAX) {
         AutoMutex _l(mLock);
 
-        device_t* device = getDevice(deviceId);
+        device_t* device = getDeviceLocked(deviceId);
         if (device != NULL) {
             return getSwitchStateLocked(device, sw);
         }
@@ -248,7 +256,7 @@
         const int32_t* keyCodes, uint8_t* outFlags) const {
     AutoMutex _l(mLock);
 
-    device_t* device = getDevice(deviceId);
+    device_t* device = getDeviceLocked(deviceId);
     if (device != NULL) {
         return markSupportedKeyCodesLocked(device, numCodes, keyCodes, outFlags);
     }
@@ -284,7 +292,7 @@
         int32_t* outKeycode, uint32_t* outFlags) const
 {
     AutoMutex _l(mLock);
-    device_t* device = getDevice(deviceId);
+    device_t* device = getDeviceLocked(deviceId);
     
     if (device != NULL && device->layoutMap != NULL) {
         status_t err = device->layoutMap->map(scancode, outKeycode, outFlags);
@@ -294,7 +302,7 @@
     }
     
     if (mHaveFirstKeyboard) {
-        device = getDevice(mFirstKeyboardId);
+        device = getDeviceLocked(mFirstKeyboardId);
         
         if (device != NULL && device->layoutMap != NULL) {
             status_t err = device->layoutMap->map(scancode, outKeycode, outFlags);
@@ -311,11 +319,13 @@
 
 void EventHub::addExcludedDevice(const char* deviceName)
 {
+    AutoMutex _l(mLock);
+
     String8 name(deviceName);
     mExcludedDevices.push_back(name);
 }
 
-EventHub::device_t* EventHub::getDevice(int32_t deviceId) const
+EventHub::device_t* EventHub::getDeviceLocked(int32_t deviceId) const
 {
     if (deviceId == 0) deviceId = mFirstKeyboardId;
     int32_t id = deviceId & ID_MASK;
@@ -344,6 +354,7 @@
     if (!mOpened) {
         mError = openPlatformInput() ? NO_ERROR : UNKNOWN_ERROR;
         mOpened = true;
+        mNeedToSendFinishedDeviceScan = true;
     }
 
     for (;;) {
@@ -360,6 +371,7 @@
             }
             outEvent->type = DEVICE_REMOVED;
             delete device;
+            mNeedToSendFinishedDeviceScan = true;
             return true;
         }
 
@@ -374,6 +386,13 @@
                 outEvent->deviceId = device->id;
             }
             outEvent->type = DEVICE_ADDED;
+            mNeedToSendFinishedDeviceScan = true;
+            return true;
+        }
+
+        if (mNeedToSendFinishedDeviceScan) {
+            mNeedToSendFinishedDeviceScan = false;
+            outEvent->type = FINISHED_DEVICE_SCAN;
             return true;
         }
 
@@ -441,10 +460,10 @@
             }
         }
 
-        // read_notify() will modify mFDs and mFDCount, so this must be done after
+        // readNotify() will modify mFDs and mFDCount, so this must be done after
         // processing all other events.
         if(mFDs[0].revents & POLLIN) {
-            read_notify(mFDs[0].fd);
+            readNotify(mFDs[0].fd);
         }
 
         // Poll for events.  Mind the wake lock dance!
@@ -500,10 +519,9 @@
     mFDs[0].fd = -1;
 #endif
 
-    res = scan_dir(device_path);
+    res = scanDir(device_path);
     if(res < 0) {
         LOGE("scan dir failed for %s\n", device_path);
-        //open_device("/dev/input/event0");
     }
 
     return true;
@@ -531,8 +549,7 @@
         AKEYCODE_BUTTON_START, AKEYCODE_BUTTON_SELECT, AKEYCODE_BUTTON_MODE
 };
 
-int EventHub::open_device(const char *deviceName)
-{
+int EventHub::openDevice(const char *deviceName) {
     int version;
     int fd;
     struct pollfd *new_mFDs;
@@ -782,22 +799,22 @@
         property_set(propName, name);
 
         // 'Q' key support = cheap test of whether this is an alpha-capable kbd
-        if (hasKeycode(device, AKEYCODE_Q)) {
+        if (hasKeycodeLocked(device, AKEYCODE_Q)) {
             device->classes |= INPUT_DEVICE_CLASS_ALPHAKEY;
         }
         
         // See if this device has a DPAD.
-        if (hasKeycode(device, AKEYCODE_DPAD_UP) &&
-                hasKeycode(device, AKEYCODE_DPAD_DOWN) &&
-                hasKeycode(device, AKEYCODE_DPAD_LEFT) &&
-                hasKeycode(device, AKEYCODE_DPAD_RIGHT) &&
-                hasKeycode(device, AKEYCODE_DPAD_CENTER)) {
+        if (hasKeycodeLocked(device, AKEYCODE_DPAD_UP) &&
+                hasKeycodeLocked(device, AKEYCODE_DPAD_DOWN) &&
+                hasKeycodeLocked(device, AKEYCODE_DPAD_LEFT) &&
+                hasKeycodeLocked(device, AKEYCODE_DPAD_RIGHT) &&
+                hasKeycodeLocked(device, AKEYCODE_DPAD_CENTER)) {
             device->classes |= INPUT_DEVICE_CLASS_DPAD;
         }
         
         // See if this device has a gamepad.
         for (size_t i = 0; i < sizeof(GAMEPAD_KEYCODES); i++) {
-            if (hasKeycode(device, GAMEPAD_KEYCODES[i])) {
+            if (hasKeycodeLocked(device, GAMEPAD_KEYCODES[i])) {
                 device->classes |= INPUT_DEVICE_CLASS_GAMEPAD;
                 break;
             }
@@ -830,7 +847,7 @@
     return 0;
 }
 
-bool EventHub::hasKeycode(device_t* device, int keycode) const
+bool EventHub::hasKeycodeLocked(device_t* device, int keycode) const
 {
     if (device->keyBitmask == NULL || device->layoutMap == NULL) {
         return false;
@@ -849,10 +866,9 @@
     return false;
 }
 
-int EventHub::close_device(const char *deviceName)
-{
+int EventHub::closeDevice(const char *deviceName) {
     AutoMutex _l(mLock);
-    
+
     int i;
     for(i = 1; i < mFDCount; i++) {
         if(strcmp(mDevices[i]->path.string(), deviceName) == 0) {
@@ -902,8 +918,7 @@
     return -1;
 }
 
-int EventHub::read_notify(int nfd)
-{
+int EventHub::readNotify(int nfd) {
 #ifdef HAVE_INOTIFY
     int res;
     char devname[PATH_MAX];
@@ -913,7 +928,7 @@
     int event_pos = 0;
     struct inotify_event *event;
 
-    LOGV("EventHub::read_notify nfd: %d\n", nfd);
+    LOGV("EventHub::readNotify nfd: %d\n", nfd);
     res = read(nfd, event_buf, sizeof(event_buf));
     if(res < (int)sizeof(*event)) {
         if(errno == EINTR)
@@ -933,10 +948,10 @@
         if(event->len) {
             strcpy(filename, event->name);
             if(event->mask & IN_CREATE) {
-                open_device(devname);
+                openDevice(devname);
             }
             else {
-                close_device(devname);
+                closeDevice(devname);
             }
         }
         event_size = sizeof(*event) + event->len;
@@ -948,7 +963,7 @@
 }
 
 
-int EventHub::scan_dir(const char *dirname)
+int EventHub::scanDir(const char *dirname)
 {
     char devname[PATH_MAX];
     char *filename;
@@ -966,10 +981,38 @@
             (de->d_name[1] == '.' && de->d_name[2] == '\0')))
             continue;
         strcpy(filename, de->d_name);
-        open_device(devname);
+        openDevice(devname);
     }
     closedir(dir);
     return 0;
 }
 
+void EventHub::dump(String8& dump) {
+    dump.append("Event Hub State:\n");
+
+    { // acquire lock
+        AutoMutex _l(mLock);
+
+        dump.appendFormat(INDENT "HaveFirstKeyboard: %s\n", toString(mHaveFirstKeyboard));
+        dump.appendFormat(INDENT "FirstKeyboardId: 0x%x\n", mFirstKeyboardId);
+
+        dump.append(INDENT "Devices:\n");
+
+        for (int i = 0; i < mNumDevicesById; i++) {
+            const device_t* device = mDevicesById[i].device;
+            if (device) {
+                if (mFirstKeyboardId == device->id) {
+                    dump.appendFormat(INDENT2 "0x%x: %s (aka device 0 - first keyboard)\n",
+                            device->id, device->name.string());
+                } else {
+                    dump.appendFormat(INDENT2 "0x%x: %s\n", device->id, device->name.string());
+                }
+                dump.appendFormat(INDENT3 "Classes: 0x%08x\n", device->classes);
+                dump.appendFormat(INDENT3 "Path: %s\n", device->path.string());
+                dump.appendFormat(INDENT3 "KeyLayoutFile: %s\n", device->keylayoutFilename.string());
+            }
+        }
+    } // release lock
+}
+
 }; // namespace android
diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp
index 9544a95..fe8555d 100644
--- a/libs/ui/InputDispatcher.cpp
+++ b/libs/ui/InputDispatcher.cpp
@@ -46,6 +46,9 @@
 #include <errno.h>
 #include <limits.h>
 
+#define INDENT "  "
+#define INDENT2 "    "
+
 namespace android {
 
 // Delay between reporting long touch events to the power manager.
@@ -131,18 +134,21 @@
 
 // --- InputWindow ---
 
-bool InputWindow::visibleFrameIntersects(const InputWindow* other) const {
-    return visibleFrameRight > other->visibleFrameLeft
-        && visibleFrameLeft < other->visibleFrameRight
-        && visibleFrameBottom > other->visibleFrameTop
-        && visibleFrameTop < other->visibleFrameBottom;
-}
-
 bool InputWindow::touchableAreaContainsPoint(int32_t x, int32_t y) const {
     return x >= touchableAreaLeft && x <= touchableAreaRight
             && y >= touchableAreaTop && y <= touchableAreaBottom;
 }
 
+bool InputWindow::frameContainsPoint(int32_t x, int32_t y) const {
+    return x >= frameLeft && x <= frameRight
+            && y >= frameTop && y <= frameBottom;
+}
+
+bool InputWindow::isTrustedOverlay() const {
+    return layoutParamsType == TYPE_INPUT_METHOD
+            || layoutParamsType == TYPE_INPUT_METHOD_DIALOG;
+}
+
 
 // --- InputDispatcher ---
 
@@ -1050,8 +1056,12 @@
 
                 if (maskedAction == AMOTION_EVENT_ACTION_DOWN
                         && (flags & InputWindow::FLAG_WATCH_OUTSIDE_TOUCH)) {
-                    mTempTouchState.addOrUpdateWindow(window,
-                            InputTarget::FLAG_OUTSIDE, BitSet32(0));
+                    int32_t outsideTargetFlags = InputTarget::FLAG_OUTSIDE;
+                    if (isWindowObscuredAtPointLocked(window, x, y)) {
+                        outsideTargetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
+                    }
+
+                    mTempTouchState.addOrUpdateWindow(window, outsideTargetFlags, BitSet32(0));
                 }
             }
         }
@@ -1080,10 +1090,6 @@
             // (May be NULL which is why we put this code block before the next check.)
             newTouchedWindow = mTempTouchState.getFirstForegroundWindow();
         }
-        int32_t targetFlags = InputTarget::FLAG_FOREGROUND;
-        if (isSplit) {
-            targetFlags |= InputTarget::FLAG_SPLIT;
-        }
 
         // If we did not find a touched window then fail.
         if (! newTouchedWindow) {
@@ -1103,6 +1109,15 @@
             goto Failed;
         }
 
+        // Set target flags.
+        int32_t targetFlags = InputTarget::FLAG_FOREGROUND;
+        if (isSplit) {
+            targetFlags |= InputTarget::FLAG_SPLIT;
+        }
+        if (isWindowObscuredAtPointLocked(newTouchedWindow, x, y)) {
+            targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
+        }
+
         // Update the temporary touch state.
         BitSet32 pointerIds;
         if (isSplit) {
@@ -1183,23 +1198,13 @@
             for (size_t i = 0; i < mWindows.size(); i++) {
                 const InputWindow* window = & mWindows[i];
                 if (window->layoutParamsType == InputWindow::TYPE_WALLPAPER) {
-                    mTempTouchState.addOrUpdateWindow(window, 0, BitSet32(0));
+                    mTempTouchState.addOrUpdateWindow(window,
+                            InputTarget::FLAG_WINDOW_IS_OBSCURED, BitSet32(0));
                 }
             }
         }
     }
 
-    // If a touched window has been obscured at any point during the touch gesture, set
-    // the appropriate flag so we remember it for the entire gesture.
-    for (size_t i = 0; i < mTempTouchState.windows.size(); i++) {
-        TouchedWindow& touchedWindow = mTempTouchState.windows.editItemAt(i);
-        if ((touchedWindow.targetFlags & InputTarget::FLAG_WINDOW_IS_OBSCURED) == 0) {
-            if (isWindowObscuredLocked(touchedWindow.window)) {
-                touchedWindow.targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
-            }
-        }
-    }
-
     // Success!  Output targets.
     injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
 
@@ -1323,14 +1328,15 @@
     return true;
 }
 
-bool InputDispatcher::isWindowObscuredLocked(const InputWindow* window) {
+bool InputDispatcher::isWindowObscuredAtPointLocked(
+        const InputWindow* window, int32_t x, int32_t y) const {
     size_t numWindows = mWindows.size();
     for (size_t i = 0; i < numWindows; i++) {
         const InputWindow* other = & mWindows.itemAt(i);
         if (other == window) {
             break;
         }
-        if (other->visible && window->visibleFrameIntersects(other)) {
+        if (other->visible && ! other->isTrustedOverlay() && other->frameContainsPoint(x, y)) {
             return true;
         }
     }
@@ -2490,74 +2496,96 @@
 }
 
 void InputDispatcher::dumpDispatchStateLocked(String8& dump) {
-    dump.appendFormat("  dispatchEnabled: %d\n", mDispatchEnabled);
-    dump.appendFormat("  dispatchFrozen: %d\n", mDispatchFrozen);
+    dump.appendFormat(INDENT "DispatchEnabled: %d\n", mDispatchEnabled);
+    dump.appendFormat(INDENT "DispatchFrozen: %d\n", mDispatchFrozen);
 
     if (mFocusedApplication) {
-        dump.appendFormat("  focusedApplication: name='%s', dispatchingTimeout=%0.3fms\n",
+        dump.appendFormat(INDENT "FocusedApplication: name='%s', dispatchingTimeout=%0.3fms\n",
                 mFocusedApplication->name.string(),
                 mFocusedApplication->dispatchingTimeout / 1000000.0);
     } else {
-        dump.append("  focusedApplication: <null>\n");
+        dump.append(INDENT "FocusedApplication: <null>\n");
     }
-    dump.appendFormat("  focusedWindow: name='%s'\n",
+    dump.appendFormat(INDENT "FocusedWindow: name='%s'\n",
             mFocusedWindow != NULL ? mFocusedWindow->name.string() : "<null>");
-    dump.appendFormat("  touchState: down=%s, split=%s\n", toString(mTouchState.down),
-            toString(mTouchState.split));
-    for (size_t i = 0; i < mTouchState.windows.size(); i++) {
-        const TouchedWindow& touchedWindow = mTouchState.windows[i];
-        dump.appendFormat("  touchedWindow[%d]: name='%s', pointerIds=0x%0x, targetFlags=0x%x\n",
-                i, touchedWindow.window->name.string(), touchedWindow.pointerIds.value,
-                touchedWindow.targetFlags);
-    }
-    for (size_t i = 0; i < mWindows.size(); i++) {
-        dump.appendFormat("  windows[%d]: name='%s', paused=%s, hasFocus=%s, hasWallpaper=%s, "
-                "visible=%s, canReceiveKeys=%s, flags=0x%08x, type=0x%08x, layer=%d, "
-                "frame=[%d,%d][%d,%d], "
-                "visibleFrame=[%d,%d][%d,%d], "
-                "touchableArea=[%d,%d][%d,%d], "
-                "ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n",
-                i, mWindows[i].name.string(),
-                toString(mWindows[i].paused),
-                toString(mWindows[i].hasFocus),
-                toString(mWindows[i].hasWallpaper),
-                toString(mWindows[i].visible),
-                toString(mWindows[i].canReceiveKeys),
-                mWindows[i].layoutParamsFlags, mWindows[i].layoutParamsType,
-                mWindows[i].layer,
-                mWindows[i].frameLeft, mWindows[i].frameTop,
-                mWindows[i].frameRight, mWindows[i].frameBottom,
-                mWindows[i].visibleFrameLeft, mWindows[i].visibleFrameTop,
-                mWindows[i].visibleFrameRight, mWindows[i].visibleFrameBottom,
-                mWindows[i].touchableAreaLeft, mWindows[i].touchableAreaTop,
-                mWindows[i].touchableAreaRight, mWindows[i].touchableAreaBottom,
-                mWindows[i].ownerPid, mWindows[i].ownerUid,
-                mWindows[i].dispatchingTimeout / 1000000.0);
+
+    dump.appendFormat(INDENT "TouchDown: %s\n", toString(mTouchState.down));
+    dump.appendFormat(INDENT "TouchSplit: %s\n", toString(mTouchState.split));
+    if (!mTouchState.windows.isEmpty()) {
+        dump.append(INDENT "TouchedWindows:\n");
+        for (size_t i = 0; i < mTouchState.windows.size(); i++) {
+            const TouchedWindow& touchedWindow = mTouchState.windows[i];
+            dump.appendFormat(INDENT2 "%d: name='%s', pointerIds=0x%0x, targetFlags=0x%x\n",
+                    i, touchedWindow.window->name.string(), touchedWindow.pointerIds.value,
+                    touchedWindow.targetFlags);
+        }
+    } else {
+        dump.append(INDENT "TouchedWindows: <none>\n");
     }
 
-    for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
-        const sp<InputChannel>& channel = mMonitoringChannels[i];
-        dump.appendFormat("  monitoringChannel[%d]: '%s'\n",
-                i, channel->getName().string());
+    if (!mWindows.isEmpty()) {
+        dump.append(INDENT "Windows:\n");
+        for (size_t i = 0; i < mWindows.size(); i++) {
+            const InputWindow& window = mWindows[i];
+            dump.appendFormat(INDENT2 "%d: name='%s', paused=%s, hasFocus=%s, hasWallpaper=%s, "
+                    "visible=%s, canReceiveKeys=%s, flags=0x%08x, type=0x%08x, layer=%d, "
+                    "frame=[%d,%d][%d,%d], "
+                    "visibleFrame=[%d,%d][%d,%d], "
+                    "touchableArea=[%d,%d][%d,%d], "
+                    "ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n",
+                    i, window.name.string(),
+                    toString(window.paused),
+                    toString(window.hasFocus),
+                    toString(window.hasWallpaper),
+                    toString(window.visible),
+                    toString(window.canReceiveKeys),
+                    window.layoutParamsFlags, window.layoutParamsType,
+                    window.layer,
+                    window.frameLeft, window.frameTop,
+                    window.frameRight, window.frameBottom,
+                    window.visibleFrameLeft, window.visibleFrameTop,
+                    window.visibleFrameRight, window.visibleFrameBottom,
+                    window.touchableAreaLeft, window.touchableAreaTop,
+                    window.touchableAreaRight, window.touchableAreaBottom,
+                    window.ownerPid, window.ownerUid,
+                    window.dispatchingTimeout / 1000000.0);
+        }
+    } else {
+        dump.append(INDENT "Windows: <none>\n");
     }
 
-    dump.appendFormat("  inboundQueue: length=%u", mInboundQueue.count());
+    if (!mMonitoringChannels.isEmpty()) {
+        dump.append(INDENT "MonitoringChannels:\n");
+        for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
+            const sp<InputChannel>& channel = mMonitoringChannels[i];
+            dump.appendFormat(INDENT2 "%d: '%s'\n", i, channel->getName().string());
+        }
+    } else {
+        dump.append(INDENT "MonitoringChannels: <none>\n");
+    }
 
-    for (size_t i = 0; i < mActiveConnections.size(); i++) {
-        const Connection* connection = mActiveConnections[i];
-        dump.appendFormat("  activeConnection[%d]: '%s', status=%s, outboundQueueLength=%u"
-                "inputState.isNeutral=%s, inputState.isOutOfSync=%s\n",
-                i, connection->getInputChannelName(), connection->getStatusLabel(),
-                connection->outboundQueue.count(),
-                toString(connection->inputState.isNeutral()),
-                toString(connection->inputState.isOutOfSync()));
+    dump.appendFormat(INDENT "InboundQueue: length=%u\n", mInboundQueue.count());
+
+    if (!mActiveConnections.isEmpty()) {
+        dump.append(INDENT "ActiveConnections:\n");
+        for (size_t i = 0; i < mActiveConnections.size(); i++) {
+            const Connection* connection = mActiveConnections[i];
+            dump.appendFormat(INDENT2 "%d: '%s', status=%s, outboundQueueLength=%u"
+                    "inputState.isNeutral=%s, inputState.isOutOfSync=%s\n",
+                    i, connection->getInputChannelName(), connection->getStatusLabel(),
+                    connection->outboundQueue.count(),
+                    toString(connection->inputState.isNeutral()),
+                    toString(connection->inputState.isOutOfSync()));
+        }
+    } else {
+        dump.append(INDENT "ActiveConnections: <none>\n");
     }
 
     if (isAppSwitchPendingLocked()) {
-        dump.appendFormat("  appSwitch: pending, due in %01.1fms\n",
+        dump.appendFormat(INDENT "AppSwitch: pending, due in %01.1fms\n",
                 (mAppSwitchDueTime - now()) / 1000000.0);
     } else {
-        dump.append("  appSwitch: not pending\n");
+        dump.append(INDENT "AppSwitch: not pending\n");
     }
 }
 
@@ -2774,6 +2802,7 @@
 }
 
 void InputDispatcher::dump(String8& dump) {
+    dump.append("Input Dispatcher State:\n");
     dumpDispatchStateLocked(dump);
 }
 
diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp
index 1d35e10..7adc764 100644
--- a/libs/ui/InputReader.cpp
+++ b/libs/ui/InputReader.cpp
@@ -226,11 +226,15 @@
 void InputReader::process(const RawEvent* rawEvent) {
     switch (rawEvent->type) {
     case EventHubInterface::DEVICE_ADDED:
-        addDevice(rawEvent->when, rawEvent->deviceId);
+        addDevice(rawEvent->deviceId);
         break;
 
     case EventHubInterface::DEVICE_REMOVED:
-        removeDevice(rawEvent->when, rawEvent->deviceId);
+        removeDevice(rawEvent->deviceId);
+        break;
+
+    case EventHubInterface::FINISHED_DEVICE_SCAN:
+        handleConfigurationChanged();
         break;
 
     default:
@@ -239,7 +243,7 @@
     }
 }
 
-void InputReader::addDevice(nsecs_t when, int32_t deviceId) {
+void InputReader::addDevice(int32_t deviceId) {
     String8 name = mEventHub->getDeviceName(deviceId);
     uint32_t classes = mEventHub->getDeviceClasses(deviceId);
 
@@ -269,11 +273,9 @@
         delete device;
         return;
     }
-
-    handleConfigurationChanged(when);
 }
 
-void InputReader::removeDevice(nsecs_t when, int32_t deviceId) {
+void InputReader::removeDevice(int32_t deviceId) {
     bool removed = false;
     InputDevice* device = NULL;
     { // acquire device registry writer lock
@@ -303,8 +305,6 @@
     device->reset();
 
     delete device;
-
-    handleConfigurationChanged(when);
 }
 
 InputDevice* InputReader::createDevice(int32_t deviceId, const String8& name, uint32_t classes) {
@@ -372,7 +372,7 @@
     } // release device registry reader lock
 }
 
-void InputReader::handleConfigurationChanged(nsecs_t when) {
+void InputReader::handleConfigurationChanged() {
     // Reset global meta state because it depends on the list of all configured devices.
     updateGlobalMetaState();
 
@@ -380,6 +380,7 @@
     updateInputConfiguration();
 
     // Enqueue configuration changed.
+    nsecs_t when = systemTime(SYSTEM_TIME_MONOTONIC);
     mDispatcher->notifyConfigurationChanged(when);
 }
 
@@ -575,6 +576,11 @@
 }
 
 void InputReader::dump(String8& dump) {
+    mEventHub->dump(dump);
+    dump.append("\n");
+
+    dump.append("Input Reader State:\n");
+
     { // acquire device registry reader lock
         RWLock::AutoRLock _rl(mDeviceRegistryLock);
 
@@ -861,7 +867,6 @@
         AutoMutex _l(mLock);
         dump.append(INDENT2 "Keyboard Input Mapper:\n");
         dump.appendFormat(INDENT3 "AssociatedDisplayId: %d\n", mAssociatedDisplayId);
-        dump.appendFormat(INDENT3 "Sources: 0x%x\n", mSources);
         dump.appendFormat(INDENT3 "KeyboardType: %d\n", mKeyboardType);
         dump.appendFormat(INDENT3 "KeyDowns: %d keys currently down\n", mLocked.keyDowns.size());
         dump.appendFormat(INDENT3 "MetaState: 0x%0x\n", mLocked.metaState);
@@ -993,7 +998,10 @@
     int32_t keyEventAction = down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP;
     int32_t keyEventFlags = AKEY_EVENT_FLAG_FROM_SYSTEM;
     if (policyFlags & POLICY_FLAG_WOKE_HERE) {
-        keyEventFlags = keyEventFlags | AKEY_EVENT_FLAG_WOKE_HERE;
+        keyEventFlags |= AKEY_EVENT_FLAG_WOKE_HERE;
+    }
+    if (policyFlags & POLICY_FLAG_VIRTUAL) {
+        keyEventFlags |= AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY;
     }
 
     getDispatcher()->notifyKey(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, policyFlags,
@@ -2162,10 +2170,7 @@
         int32_t keyCode, int32_t scanCode, nsecs_t downTime) {
     int32_t metaState = mContext->getGlobalMetaState();
 
-    if (keyEventAction == AKEY_EVENT_ACTION_DOWN) {
-        getPolicy()->virtualKeyDownFeedback();
-    }
-
+    policyFlags |= POLICY_FLAG_VIRTUAL;
     int32_t policyActions = getPolicy()->interceptKey(when, getDeviceId(),
             keyEventAction == AKEY_EVENT_ACTION_DOWN, keyCode, scanCode, policyFlags);
 
@@ -2442,7 +2447,7 @@
         yPrecision = mLocked.orientedYPrecision;
     } // release lock
 
-    getDispatcher()->notifyMotion(when, getDeviceId(), AINPUT_SOURCE_TOUCHSCREEN, policyFlags,
+    getDispatcher()->notifyMotion(when, getDeviceId(), getSources(), policyFlags,
             motionEventAction, 0, getContext()->getGlobalMetaState(), motionEventEdgeFlags,
             pointerCount, pointerIds, pointerCoords,
             xPrecision, yPrecision, mDownTime);
diff --git a/libs/utils/Looper.cpp b/libs/utils/Looper.cpp
index d2dd6eb..a5363d6 100644
--- a/libs/utils/Looper.cpp
+++ b/libs/utils/Looper.cpp
@@ -19,16 +19,17 @@
 
 #include <unistd.h>
 #include <fcntl.h>
-#include <sys/epoll.h>
 
 
 namespace android {
 
+#ifdef LOOPER_USES_EPOLL
 // Hint for number of file descriptors to be associated with the epoll instance.
 static const int EPOLL_SIZE_HINT = 8;
 
 // Maximum number of file descriptors for which to retrieve poll events each iteration.
 static const int EPOLL_MAX_EVENTS = 16;
+#endif
 
 static pthread_once_t gTLSOnce = PTHREAD_ONCE_INIT;
 static pthread_key_t gTLSKey = 0;
@@ -36,9 +37,6 @@
 Looper::Looper(bool allowNonCallbacks) :
         mAllowNonCallbacks(allowNonCallbacks),
         mResponseIndex(0) {
-    mEpollFd = epoll_create(EPOLL_SIZE_HINT);
-    LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance.  errno=%d", errno);
-
     int wakeFds[2];
     int result = pipe(wakeFds);
     LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe.  errno=%d", errno);
@@ -54,6 +52,11 @@
     LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking.  errno=%d",
             errno);
 
+#ifdef LOOPER_USES_EPOLL
+    // Allocate the epoll instance and register the wake pipe.
+    mEpollFd = epoll_create(EPOLL_SIZE_HINT);
+    LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance.  errno=%d", errno);
+
     struct epoll_event eventItem;
     memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
     eventItem.events = EPOLLIN;
@@ -61,12 +64,45 @@
     result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem);
     LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance.  errno=%d",
             errno);
+#else
+    // Add the wake pipe to the head of the request list with a null callback.
+    struct pollfd requestedFd;
+    requestedFd.fd = mWakeReadPipeFd;
+    requestedFd.events = POLLIN;
+    mRequestedFds.push(requestedFd);
+
+    Request request;
+    request.fd = mWakeReadPipeFd;
+    request.callback = NULL;
+    request.ident = 0;
+    request.data = NULL;
+    mRequests.push(request);
+
+    mPolling = false;
+    mWaiters = 0;
+#endif
+
+#ifdef LOOPER_STATISTICS
+    mPendingWakeTime = -1;
+    mPendingWakeCount = 0;
+    mSampledWakeCycles = 0;
+    mSampledWakeCountSum = 0;
+    mSampledWakeLatencySum = 0;
+
+    mSampledPolls = 0;
+    mSampledZeroPollCount = 0;
+    mSampledZeroPollLatencySum = 0;
+    mSampledTimeoutPollCount = 0;
+    mSampledTimeoutPollLatencySum = 0;
+#endif
 }
 
 Looper::~Looper() {
     close(mWakeReadPipeFd);
     close(mWakeWritePipeFd);
+#ifdef LOOPER_USES_EPOLL
     close(mEpollFd);
+#endif
 }
 
 void Looper::initTLSKey() {
@@ -157,45 +193,61 @@
 #if DEBUG_POLL_AND_WAKE
     LOGD("%p ~ pollOnce - waiting: timeoutMillis=%d", this, timeoutMillis);
 #endif
+
+    int result = ALOOPER_POLL_WAKE;
+    mResponses.clear();
+    mResponseIndex = 0;
+
+#ifdef LOOPER_STATISTICS
+    nsecs_t pollStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+#endif
+
+#ifdef LOOPER_USES_EPOLL
     struct epoll_event eventItems[EPOLL_MAX_EVENTS];
     int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
+    bool acquiredLock = false;
+#else
+    // Wait for wakeAndLock() waiters to run then set mPolling to true.
+    mLock.lock();
+    while (mWaiters != 0) {
+        mResume.wait(mLock);
+    }
+    mPolling = true;
+    mLock.unlock();
+
+    size_t requestedCount = mRequestedFds.size();
+    int eventCount = poll(mRequestedFds.editArray(), requestedCount, timeoutMillis);
+#endif
+
     if (eventCount < 0) {
         if (errno == EINTR) {
-            return ALOOPER_POLL_WAKE;
+            goto Done;
         }
 
         LOGW("Poll failed with an unexpected error, errno=%d", errno);
-        return ALOOPER_POLL_ERROR;
+        result = ALOOPER_POLL_ERROR;
+        goto Done;
     }
 
     if (eventCount == 0) {
 #if DEBUG_POLL_AND_WAKE
         LOGD("%p ~ pollOnce - timeout", this);
 #endif
-        return ALOOPER_POLL_TIMEOUT;
+        result = ALOOPER_POLL_TIMEOUT;
+        goto Done;
     }
 
-    int result = ALOOPER_POLL_WAKE;
-    mResponses.clear();
-    mResponseIndex = 0;
-
 #if DEBUG_POLL_AND_WAKE
     LOGD("%p ~ pollOnce - handling events from %d fds", this, eventCount);
 #endif
-    bool acquiredLock = false;
+
+#ifdef LOOPER_USES_EPOLL
     for (int i = 0; i < eventCount; i++) {
         int fd = eventItems[i].data.fd;
         uint32_t epollEvents = eventItems[i].events;
         if (fd == mWakeReadPipeFd) {
             if (epollEvents & EPOLLIN) {
-#if DEBUG_POLL_AND_WAKE
-                LOGD("%p ~ pollOnce - awoken", this);
-#endif
-                char buffer[16];
-                ssize_t nRead;
-                do {
-                    nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));
-                } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));
+                awoken();
             } else {
                 LOGW("Ignoring unexpected epoll events 0x%x on wake read pipe.", epollEvents);
             }
@@ -212,11 +264,7 @@
                 if (epollEvents & EPOLLOUT) events |= ALOOPER_EVENT_OUTPUT;
                 if (epollEvents & EPOLLERR) events |= ALOOPER_EVENT_ERROR;
                 if (epollEvents & EPOLLHUP) events |= ALOOPER_EVENT_HANGUP;
-
-                Response response;
-                response.events = events;
-                response.request = mRequests.valueAt(requestIndex);
-                mResponses.push(response);
+                pushResponse(events, mRequests.valueAt(requestIndex));
             } else {
                 LOGW("Ignoring unexpected epoll events 0x%x on fd %d that is "
                         "no longer registered.", epollEvents, fd);
@@ -226,6 +274,66 @@
     if (acquiredLock) {
         mLock.unlock();
     }
+Done: ;
+#else
+    for (size_t i = 0; i < requestedCount; i++) {
+        const struct pollfd& requestedFd = mRequestedFds.itemAt(i);
+
+        short pollEvents = requestedFd.revents;
+        if (pollEvents) {
+            if (requestedFd.fd == mWakeReadPipeFd) {
+                if (pollEvents & POLLIN) {
+                    awoken();
+                } else {
+                    LOGW("Ignoring unexpected poll events 0x%x on wake read pipe.", pollEvents);
+                }
+            } else {
+                int events = 0;
+                if (pollEvents & POLLIN) events |= ALOOPER_EVENT_INPUT;
+                if (pollEvents & POLLOUT) events |= ALOOPER_EVENT_OUTPUT;
+                if (pollEvents & POLLERR) events |= ALOOPER_EVENT_ERROR;
+                if (pollEvents & POLLHUP) events |= ALOOPER_EVENT_HANGUP;
+                if (pollEvents & POLLNVAL) events |= ALOOPER_EVENT_INVALID;
+                pushResponse(events, mRequests.itemAt(i));
+            }
+            if (--eventCount == 0) {
+                break;
+            }
+        }
+    }
+
+Done:
+    // Set mPolling to false and wake up the wakeAndLock() waiters.
+    mLock.lock();
+    mPolling = false;
+    if (mWaiters != 0) {
+        mAwake.broadcast();
+    }
+    mLock.unlock();
+#endif
+
+#ifdef LOOPER_STATISTICS
+    nsecs_t pollEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    mSampledPolls += 1;
+    if (timeoutMillis == 0) {
+        mSampledZeroPollCount += 1;
+        mSampledZeroPollLatencySum += pollEndTime - pollStartTime;
+    } else if (timeoutMillis > 0 && result == ALOOPER_POLL_TIMEOUT) {
+        mSampledTimeoutPollCount += 1;
+        mSampledTimeoutPollLatencySum += pollEndTime - pollStartTime
+                - milliseconds_to_nanoseconds(timeoutMillis);
+    }
+    if (mSampledPolls == SAMPLED_POLLS_TO_AGGREGATE) {
+        LOGD("%p ~ poll latency statistics: %0.3fms zero timeout, %0.3fms non-zero timeout", this,
+                0.000001f * float(mSampledZeroPollLatencySum) / mSampledZeroPollCount,
+                0.000001f * float(mSampledTimeoutPollLatencySum) / mSampledTimeoutPollCount);
+        mSampledPolls = 0;
+        mSampledZeroPollCount = 0;
+        mSampledZeroPollLatencySum = 0;
+        mSampledTimeoutPollCount = 0;
+        mSampledTimeoutPollLatencySum = 0;
+    }
+#endif
 
     for (size_t i = 0; i < mResponses.size(); i++) {
         const Response& response = mResponses.itemAt(i);
@@ -278,6 +386,13 @@
     LOGD("%p ~ wake", this);
 #endif
 
+#ifdef LOOPER_STATISTICS
+    // FIXME: Possible race with awoken() but this code is for testing only and is rarely enabled.
+    if (mPendingWakeCount++ == 0) {
+        mPendingWakeTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    }
+#endif
+
     ssize_t nWrite;
     do {
         nWrite = write(mWakeWritePipeFd, "W", 1);
@@ -290,23 +405,51 @@
     }
 }
 
+void Looper::awoken() {
+#if DEBUG_POLL_AND_WAKE
+    LOGD("%p ~ awoken", this);
+#endif
+
+#ifdef LOOPER_STATISTICS
+    if (mPendingWakeCount == 0) {
+        LOGD("%p ~ awoken: spurious!", this);
+    } else {
+        mSampledWakeCycles += 1;
+        mSampledWakeCountSum += mPendingWakeCount;
+        mSampledWakeLatencySum += systemTime(SYSTEM_TIME_MONOTONIC) - mPendingWakeTime;
+        mPendingWakeCount = 0;
+        mPendingWakeTime = -1;
+        if (mSampledWakeCycles == SAMPLED_WAKE_CYCLES_TO_AGGREGATE) {
+            LOGD("%p ~ wake statistics: %0.3fms wake latency, %0.3f wakes per cycle", this,
+                    0.000001f * float(mSampledWakeLatencySum) / mSampledWakeCycles,
+                    float(mSampledWakeCountSum) / mSampledWakeCycles);
+            mSampledWakeCycles = 0;
+            mSampledWakeCountSum = 0;
+            mSampledWakeLatencySum = 0;
+        }
+    }
+#endif
+
+    char buffer[16];
+    ssize_t nRead;
+    do {
+        nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));
+    } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));
+}
+
+void Looper::pushResponse(int events, const Request& request) {
+    Response response;
+    response.events = events;
+    response.request = request;
+    mResponses.push(response);
+}
+
 int Looper::addFd(int fd, int ident, int events, ALooper_callbackFunc callback, void* data) {
 #if DEBUG_CALLBACKS
     LOGD("%p ~ addFd - fd=%d, ident=%d, events=0x%x, callback=%p, data=%p", this, fd, ident,
             events, callback, data);
 #endif
 
-    int epollEvents = 0;
-    if (events & ALOOPER_EVENT_INPUT) epollEvents |= EPOLLIN;
-    if (events & ALOOPER_EVENT_OUTPUT) epollEvents |= EPOLLOUT;
-    if (events & ALOOPER_EVENT_ERROR) epollEvents |= EPOLLERR;
-    if (events & ALOOPER_EVENT_HANGUP) epollEvents |= EPOLLHUP;
-
-    if (epollEvents == 0) {
-        LOGE("Invalid attempt to set a callback with no selected poll events.");
-        return -1;
-    }
-
     if (! callback) {
         if (! mAllowNonCallbacks) {
             LOGE("Invalid attempt to set NULL callback but not allowed for this looper.");
@@ -319,6 +462,11 @@
         }
     }
 
+#ifdef LOOPER_USES_EPOLL
+    int epollEvents = 0;
+    if (events & ALOOPER_EVENT_INPUT) epollEvents |= EPOLLIN;
+    if (events & ALOOPER_EVENT_OUTPUT) epollEvents |= EPOLLOUT;
+
     { // acquire lock
         AutoMutex _l(mLock);
 
@@ -350,6 +498,33 @@
             mRequests.replaceValueAt(requestIndex, request);
         }
     } // release lock
+#else
+    int pollEvents = 0;
+    if (events & ALOOPER_EVENT_INPUT) pollEvents |= POLLIN;
+    if (events & ALOOPER_EVENT_OUTPUT) pollEvents |= POLLOUT;
+
+    wakeAndLock(); // acquire lock
+
+    struct pollfd requestedFd;
+    requestedFd.fd = fd;
+    requestedFd.events = pollEvents;
+
+    Request request;
+    request.fd = fd;
+    request.ident = ident;
+    request.callback = callback;
+    request.data = data;
+    ssize_t index = getRequestIndexLocked(fd);
+    if (index < 0) {
+        mRequestedFds.push(requestedFd);
+        mRequests.push(request);
+    } else {
+        mRequestedFds.replaceAt(requestedFd, size_t(index));
+        mRequests.replaceAt(request, size_t(index));
+    }
+
+    mLock.unlock(); // release lock
+#endif
     return 1;
 }
 
@@ -358,6 +533,7 @@
     LOGD("%p ~ removeFd - fd=%d", this, fd);
 #endif
 
+#ifdef LOOPER_USES_EPOLL
     { // acquire lock
         AutoMutex _l(mLock);
         ssize_t requestIndex = mRequests.indexOfKey(fd);
@@ -372,8 +548,49 @@
         }
 
         mRequests.removeItemsAt(requestIndex);
-    } // request lock
+    } // release lock
     return 1;
+#else
+    wakeAndLock(); // acquire lock
+
+    ssize_t index = getRequestIndexLocked(fd);
+    if (index >= 0) {
+        mRequestedFds.removeAt(size_t(index));
+        mRequests.removeAt(size_t(index));
+    }
+
+    mLock.unlock(); // release lock
+    return index >= 0;
+#endif
 }
 
+#ifndef LOOPER_USES_EPOLL
+ssize_t Looper::getRequestIndexLocked(int fd) {
+    size_t requestCount = mRequestedFds.size();
+
+    for (size_t i = 0; i < requestCount; i++) {
+        if (mRequestedFds.itemAt(i).fd == fd) {
+            return i;
+        }
+    }
+
+    return -1;
+}
+
+void Looper::wakeAndLock() {
+    mLock.lock();
+
+    mWaiters += 1;
+    while (mPolling) {
+        wake();
+        mAwake.wait(mLock);
+    }
+
+    mWaiters -= 1;
+    if (mWaiters == 0) {
+        mResume.signal();
+    }
+}
+#endif
+
 } // namespace android
diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp
index 9fcae72..9b1f82f 100644
--- a/libs/utils/ZipFileRO.cpp
+++ b/libs/utils/ZipFileRO.cpp
@@ -86,6 +86,16 @@
  */
 #define kZipEntryAdj        10000
 
+ZipFileRO::~ZipFileRO() {
+    free(mHashTable);
+    if (mDirectoryMap)
+        mDirectoryMap->release();
+    if (mFd >= 0)
+        TEMP_FAILURE_RETRY(close(mFd));
+    if (mFileName)
+        free(mFileName);
+}
+
 /*
  * Convert a ZipEntryRO to a hash table index, verifying that it's in a
  * valid range.
@@ -122,7 +132,7 @@
 
     mFileLength = lseek(fd, 0, SEEK_END);
     if (mFileLength < kEOCDLen) {
-        close(fd);
+        TEMP_FAILURE_RETRY(close(fd));
         return UNKNOWN_ERROR;
     }
 
@@ -152,7 +162,7 @@
 bail:
     free(mFileName);
     mFileName = NULL;
-    close(fd);
+    TEMP_FAILURE_RETRY(close(fd));
     return UNKNOWN_ERROR;
 }
 
@@ -498,6 +508,36 @@
 
         unsigned char lfhBuf[kLFHLen];
 
+#ifdef HAVE_PREAD
+        /*
+         * This file descriptor might be from zygote's preloaded assets,
+         * so we need to do an pread() instead of a lseek() + read() to
+         * guarantee atomicity across the processes with the shared file
+         * descriptors.
+         */
+        ssize_t actual =
+                TEMP_FAILURE_RETRY(pread(mFd, lfhBuf, sizeof(lfhBuf), localHdrOffset));
+
+        if (actual != sizeof(lfhBuf)) {
+            LOGW("failed reading lfh from offset %ld\n", localHdrOffset);
+            return false;
+        }
+
+        if (get4LE(lfhBuf) != kLFHSignature) {
+            LOGW("didn't find signature at start of lfh; wanted: offset=%ld data=0x%08x; "
+                    "got: data=0x%08lx\n",
+                    localHdrOffset, kLFHSignature, get4LE(lfhBuf));
+            return false;
+        }
+#else /* HAVE_PREAD */
+        /*
+         * For hosts don't have pread() we cannot guarantee atomic reads from
+         * an offset in a file. Android should never run on those platforms.
+         * File descriptors inherited from a fork() share file offsets and
+         * there would be nothing to protect from two different processes
+         * calling lseek() concurrently.
+         */
+
         {
             AutoMutex _l(mFdLock);
 
@@ -507,18 +547,21 @@
             }
 
             ssize_t actual =
-                TEMP_FAILURE_RETRY(read(mFd, lfhBuf, sizeof(lfhBuf)));
+                    TEMP_FAILURE_RETRY(read(mFd, lfhBuf, sizeof(lfhBuf)));
             if (actual != sizeof(lfhBuf)) {
                 LOGW("failed reading lfh from offset %ld\n", localHdrOffset);
                 return false;
             }
-        }
 
-        if (get4LE(lfhBuf) != kLFHSignature) {
-            LOGW("didn't find signature at start of lfh, offset=%ld (got 0x%08lx, expected 0x%08x)\n",
-                localHdrOffset, get4LE(lfhBuf), kLFHSignature);
-            return false;
+            if (get4LE(lfhBuf) != kLFHSignature) {
+                off_t actualOffset = lseek(mFd, 0, SEEK_CUR);
+                LOGW("didn't find signature at start of lfh; wanted: offset=%ld data=0x%08x; "
+                        "got: offset=%zd data=0x%08lx\n",
+                        localHdrOffset, kLFHSignature, (size_t)actualOffset, get4LE(lfhBuf));
+                return false;
+            }
         }
+#endif /* HAVE_PREAD */
 
         off_t dataOffset = localHdrOffset + kLFHLen
             + get2LE(lfhBuf + kLFHNameLen) + get2LE(lfhBuf + kLFHExtraLen);
diff --git a/libs/utils/tests/Looper_test.cpp b/libs/utils/tests/Looper_test.cpp
index afc92f8..cea1313 100644
--- a/libs/utils/tests/Looper_test.cpp
+++ b/libs/utils/tests/Looper_test.cpp
@@ -354,14 +354,6 @@
             << "addFd should return 1 because FD was added";
 }
 
-TEST_F(LooperTest, AddFd_WhenEventsIsZero_ReturnsError) {
-    Pipe pipe;
-    int result = mLooper->addFd(pipe.receiveFd, 0, 0, NULL, NULL);
-
-    EXPECT_EQ(-1, result)
-            << "addFd should return -1 because arguments were invalid";
-}
-
 TEST_F(LooperTest, AddFd_WhenIdentIsNegativeAndCallbackIsNull_ReturnsError) {
     Pipe pipe;
     int result = mLooper->addFd(pipe.receiveFd, -1, ALOOPER_EVENT_INPUT, NULL, NULL);
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 695cbfa..a060d31 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -214,6 +214,17 @@
     }
 }
 
+void Layer::drawForSreenShot() const
+{
+    bool currentFixedSize = mFixedSize;
+    bool currentBlending = mNeedsBlending;
+    const_cast<Layer*>(this)->mFixedSize = false;
+    const_cast<Layer*>(this)->mFixedSize = true;
+    LayerBase::drawForSreenShot();
+    const_cast<Layer*>(this)->mFixedSize = currentFixedSize;
+    const_cast<Layer*>(this)->mNeedsBlending = currentBlending;
+}
+
 void Layer::onDraw(const Region& clip) const
 {
     Texture tex(mBufferManager.getActiveTexture());
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index e1d283b..263c372 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -68,6 +68,7 @@
     bool isFixedSize() const;
 
     // LayerBase interface
+    virtual void drawForSreenShot() const;
     virtual void onDraw(const Region& clip) const;
     virtual uint32_t doTransaction(uint32_t transactionFlags);
     virtual void lockPageFlip(bool& recomputeVisibleRegions);
diff --git a/services/surfaceflinger/LayerBase.cpp b/services/surfaceflinger/LayerBase.cpp
index 6fc5010..758b408 100644
--- a/services/surfaceflinger/LayerBase.cpp
+++ b/services/surfaceflinger/LayerBase.cpp
@@ -317,6 +317,12 @@
     onDraw(clip);
 }
 
+void LayerBase::drawForSreenShot() const
+{
+    const DisplayHardware& hw(graphicPlane(0).displayHardware());
+    onDraw( Region(hw.bounds()) );
+}
+
 void LayerBase::clearWithOpenGL(const Region& clip, GLclampf red,
                                 GLclampf green, GLclampf blue,
                                 GLclampf alpha) const
diff --git a/services/surfaceflinger/LayerBase.h b/services/surfaceflinger/LayerBase.h
index 8cba287..d688f65 100644
--- a/services/surfaceflinger/LayerBase.h
+++ b/services/surfaceflinger/LayerBase.h
@@ -115,6 +115,7 @@
      * to perform the actual drawing.  
      */
     virtual void draw(const Region& clip) const;
+    virtual void drawForSreenShot() const;
     
     /**
      * onDraw - draws the surface.
diff --git a/services/surfaceflinger/LayerBuffer.cpp b/services/surfaceflinger/LayerBuffer.cpp
index 0240748..87cabed 100644
--- a/services/surfaceflinger/LayerBuffer.cpp
+++ b/services/surfaceflinger/LayerBuffer.cpp
@@ -132,6 +132,12 @@
     LayerBase::unlockPageFlip(planeTransform, outDirtyRegion);    
 }
 
+void LayerBuffer::drawForSreenShot() const
+{
+    const DisplayHardware& hw(graphicPlane(0).displayHardware());
+    clearWithOpenGL( Region(hw.bounds()) );
+}
+
 void LayerBuffer::onDraw(const Region& clip) const
 {
     sp<Source> source(getSource());
diff --git a/services/surfaceflinger/LayerBuffer.h b/services/surfaceflinger/LayerBuffer.h
index 1c0bf83..fece858 100644
--- a/services/surfaceflinger/LayerBuffer.h
+++ b/services/surfaceflinger/LayerBuffer.h
@@ -64,6 +64,7 @@
     virtual sp<LayerBaseClient::Surface> createSurface() const;
     virtual status_t ditch();
     virtual void onDraw(const Region& clip) const;
+    virtual void drawForSreenShot() const;
     virtual uint32_t doTransaction(uint32_t flags);
     virtual void unlockPageFlip(const Transform& planeTransform, Region& outDirtyRegion);
 
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 2b06f6f..e5e87c6 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1546,9 +1546,117 @@
 
 // ---------------------------------------------------------------------------
 
+status_t SurfaceFlinger::captureScreenImplLocked(DisplayID dpy,
+        sp<IMemoryHeap>* heap,
+        uint32_t* w, uint32_t* h, PixelFormat* f,
+        uint32_t sw, uint32_t sh)
+{
+    status_t result = PERMISSION_DENIED;
+
+    // only one display supported for now
+    if (UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT))
+        return BAD_VALUE;
+
+    if (!GLExtensions::getInstance().haveFramebufferObject())
+        return INVALID_OPERATION;
+
+    // get screen geometry
+    const DisplayHardware& hw(graphicPlane(dpy).displayHardware());
+    const uint32_t hw_w = hw.getWidth();
+    const uint32_t hw_h = hw.getHeight();
+
+    if ((sw > hw_w) || (sh > hw_h))
+        return BAD_VALUE;
+
+    sw = (!sw) ? hw_w : sw;
+    sh = (!sh) ? hw_h : sh;
+    const size_t size = sw * sh * 4;
+
+    // make sure to clear all GL error flags
+    while ( glGetError() != GL_NO_ERROR ) ;
+
+    // create a FBO
+    GLuint name, tname;
+    glGenRenderbuffersOES(1, &tname);
+    glBindRenderbufferOES(GL_RENDERBUFFER_OES, tname);
+    glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_RGBA8_OES, sw, sh);
+    glGenFramebuffersOES(1, &name);
+    glBindFramebufferOES(GL_FRAMEBUFFER_OES, name);
+    glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES,
+            GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, tname);
+
+    GLenum status = glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES);
+    if (status == GL_FRAMEBUFFER_COMPLETE_OES) {
+
+        // invert everything, b/c glReadPixel() below will invert the FB
+        glViewport(0, 0, sw, sh);
+        glMatrixMode(GL_PROJECTION);
+        glPushMatrix();
+        glLoadIdentity();
+        glOrthof(0, hw_w, 0, hw_h, 0, 1);
+        glMatrixMode(GL_MODELVIEW);
+
+        // redraw the screen entirely...
+        glClearColor(0,0,0,1);
+        glClear(GL_COLOR_BUFFER_BIT);
+        const Vector< sp<LayerBase> >& layers(mVisibleLayersSortedByZ);
+        const size_t count = layers.size();
+        for (size_t i=0 ; i<count ; ++i) {
+            const sp<LayerBase>& layer(layers[i]);
+            layer->drawForSreenShot();
+        }
+
+        // XXX: this is needed on tegra
+        glScissor(0, 0, sw, sh);
+
+        // check for errors and return screen capture
+        if (glGetError() != GL_NO_ERROR) {
+            // error while rendering
+            result = INVALID_OPERATION;
+        } else {
+            // allocate shared memory large enough to hold the
+            // screen capture
+            sp<MemoryHeapBase> base(
+                    new MemoryHeapBase(size, 0, "screen-capture") );
+            void* const ptr = base->getBase();
+            if (ptr) {
+                // capture the screen with glReadPixels()
+                glReadPixels(0, 0, sw, sh, GL_RGBA, GL_UNSIGNED_BYTE, ptr);
+                if (glGetError() == GL_NO_ERROR) {
+                    *heap = base;
+                    *w = sw;
+                    *h = sh;
+                    *f = PIXEL_FORMAT_RGBA_8888;
+                    result = NO_ERROR;
+                }
+            } else {
+                result = NO_MEMORY;
+            }
+        }
+
+        glEnable(GL_SCISSOR_TEST);
+        glViewport(0, 0, hw_w, hw_h);
+        glMatrixMode(GL_PROJECTION);
+        glPopMatrix();
+        glMatrixMode(GL_MODELVIEW);
+
+
+    } else {
+        result = BAD_VALUE;
+    }
+
+    // release FBO resources
+    glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);
+    glDeleteRenderbuffersOES(1, &tname);
+    glDeleteFramebuffersOES(1, &name);
+    return result;
+}
+
+
 status_t SurfaceFlinger::captureScreen(DisplayID dpy,
         sp<IMemoryHeap>* heap,
-        uint32_t* width, uint32_t* height, PixelFormat* format)
+        uint32_t* width, uint32_t* height, PixelFormat* format,
+        uint32_t sw, uint32_t sh)
 {
     // only one display supported for now
     if (UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT))
@@ -1564,12 +1672,15 @@
         uint32_t* w;
         uint32_t* h;
         PixelFormat* f;
+        uint32_t sw;
+        uint32_t sh;
         status_t result;
     public:
         MessageCaptureScreen(SurfaceFlinger* flinger, DisplayID dpy,
-                sp<IMemoryHeap>* heap, uint32_t* w, uint32_t* h, PixelFormat* f)
+                sp<IMemoryHeap>* heap, uint32_t* w, uint32_t* h, PixelFormat* f,
+                uint32_t sw, uint32_t sh)
             : flinger(flinger), dpy(dpy),
-              heap(heap), w(w), h(h), f(f), result(PERMISSION_DENIED)
+              heap(heap), w(w), h(h), f(f), sw(sw), sh(sh), result(PERMISSION_DENIED)
         {
         }
         status_t getResult() const {
@@ -1582,94 +1693,15 @@
             if (flinger->mSecureFrameBuffer)
                 return true;
 
-            // make sure to clear all GL error flags
-            while ( glGetError() != GL_NO_ERROR ) ;
+            result = flinger->captureScreenImplLocked(dpy,
+                    heap, w, h, f, sw, sh);
 
-            // get screen geometry
-            const DisplayHardware& hw(flinger->graphicPlane(dpy).displayHardware());
-            const uint32_t sw = hw.getWidth();
-            const uint32_t sh = hw.getHeight();
-            const Region screenBounds(hw.bounds());
-            const size_t size = sw * sh * 4;
-
-            // create a FBO
-            GLuint name, tname;
-            glGenRenderbuffersOES(1, &tname);
-            glBindRenderbufferOES(GL_RENDERBUFFER_OES, tname);
-            glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_RGBA8_OES, sw, sh);
-            glGenFramebuffersOES(1, &name);
-            glBindFramebufferOES(GL_FRAMEBUFFER_OES, name);
-            glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES,
-                    GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, tname);
-
-            GLenum status = glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES);
-            if (status == GL_FRAMEBUFFER_COMPLETE_OES) {
-
-                // invert everything, b/c glReadPixel() below will invert the FB
-                glMatrixMode(GL_PROJECTION);
-                glPushMatrix();
-                glLoadIdentity();
-                glOrthof(0, sw, 0, sh, 0, 1);
-                glMatrixMode(GL_MODELVIEW);
-
-                // redraw the screen entirely...
-                glClearColor(0,0,0,1);
-                glClear(GL_COLOR_BUFFER_BIT);
-                const Vector< sp<LayerBase> >& layers(
-                        flinger->mVisibleLayersSortedByZ);
-                const size_t count = layers.size();
-                for (size_t i=0 ; i<count ; ++i) {
-                    const sp<LayerBase>& layer(layers[i]);
-                    if (!strcmp(layer->getTypeId(), "LayerBuffer")) {
-                        // we cannot render LayerBuffer because it doens't
-                        // use OpenGL, and won't show-up in the FBO.
-                        continue;
-                    }
-                    layer->draw(screenBounds);
-                }
-
-                glMatrixMode(GL_PROJECTION);
-                glPopMatrix();
-                glMatrixMode(GL_MODELVIEW);
-
-                // check for errors and return screen capture
-                if (glGetError() != GL_NO_ERROR) {
-                    // error while rendering
-                    result = INVALID_OPERATION;
-                } else {
-                    // allocate shared memory large enough to hold the
-                    // screen capture
-                    sp<MemoryHeapBase> base(
-                            new MemoryHeapBase(size, 0, "screen-capture") );
-                    void* const ptr = base->getBase();
-                    if (ptr) {
-                        // capture the screen with glReadPixels()
-                        glReadPixels(0, 0, sw, sh, GL_RGBA, GL_UNSIGNED_BYTE, ptr);
-                        if (glGetError() == GL_NO_ERROR) {
-                            *heap = base;
-                            *w = sw;
-                            *h = sh;
-                            *f = PIXEL_FORMAT_RGBA_8888;
-                            result = NO_ERROR;
-                        }
-                    } else {
-                        result = NO_MEMORY;
-                    }
-                }
-            } else {
-                result = BAD_VALUE;
-            }
-
-            // release FBO resources
-            glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);
-            glDeleteRenderbuffersOES(1, &tname);
-            glDeleteFramebuffersOES(1, &name);
             return true;
         }
     };
 
     sp<MessageBase> msg = new MessageCaptureScreen(this,
-            dpy, heap, width, height, format);
+            dpy, heap, width, height, format, sw, sh);
     status_t res = postMessageSync(msg);
     if (res == NO_ERROR) {
         res = static_cast<MessageCaptureScreen*>( msg.get() )->getResult();
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index f09fdbc..f0a167b 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -197,7 +197,9 @@
                                                       sp<IMemoryHeap>* heap,
                                                       uint32_t* width,
                                                       uint32_t* height,
-                                                      PixelFormat* format);
+                                                      PixelFormat* format,
+                                                      uint32_t reqWidth,
+                                                      uint32_t reqHeight);
 
             void                        screenReleased(DisplayID dpy);
             void                        screenAcquired(DisplayID dpy);
@@ -318,6 +320,11 @@
             void        commitTransaction();
 
 
+            status_t captureScreenImplLocked(DisplayID dpy,
+                    sp<IMemoryHeap>* heap,
+                    uint32_t* width, uint32_t* height, PixelFormat* format,
+                    uint32_t reqWidth = 0, uint32_t reqHeight = 0);
+
             friend class FreezeLock;
             sp<FreezeLock> getFreezeLock() const;
             inline void incFreezeCount() {
diff --git a/services/surfaceflinger/tests/screencap/screencap.cpp b/services/surfaceflinger/tests/screencap/screencap.cpp
index 9e893f4..6cf1504 100644
--- a/services/surfaceflinger/tests/screencap/screencap.cpp
+++ b/services/surfaceflinger/tests/screencap/screencap.cpp
@@ -42,7 +42,7 @@
     sp<IMemoryHeap> heap;
     uint32_t w, h;
     PixelFormat f;
-    status_t err = composer->captureScreen(0, &heap, &w, &h, &f);
+    status_t err = composer->captureScreen(0, &heap, &w, &h, &f, 0, 0);
     if (err != NO_ERROR) {
         fprintf(stderr, "screen capture failed: %s\n", strerror(-err));
         exit(0);