Merge "Support for 64-bit integer type."
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/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/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/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/InputReader.h b/include/ui/InputReader.h
index e85735a..2209cb8 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.
@@ -308,9 +304,6 @@
             GetStateFunc getStateFunc);
     bool markSupportedKeyCodes(int32_t deviceId, uint32_t sourceMask, size_t numCodes,
             const int32_t* keyCodes, uint8_t* outFlags);
-
-    // dump state
-    void dumpDeviceInfo(String8& dump);
 };
 
 
@@ -340,6 +333,7 @@
 
     inline bool isIgnored() { return mMappers.isEmpty(); }
 
+    void dump(String8& dump);
     void addMapper(InputMapper* mapper);
     void configure();
     void reset();
@@ -393,6 +387,7 @@
 
     virtual uint32_t getSources() = 0;
     virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
+    virtual void dump(String8& dump);
     virtual void configure();
     virtual void reset();
     virtual void process(const RawEvent* rawEvent) = 0;
@@ -436,6 +431,7 @@
 
     virtual uint32_t getSources();
     virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
+    virtual void dump(String8& dump);
     virtual void reset();
     virtual void process(const RawEvent* rawEvent);
 
@@ -484,6 +480,7 @@
 
     virtual uint32_t getSources();
     virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
+    virtual void dump(String8& dump);
     virtual void reset();
     virtual void process(const RawEvent* rawEvent);
 
@@ -540,6 +537,7 @@
 
     virtual uint32_t getSources();
     virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
+    virtual void dump(String8& dump);
     virtual void configure();
     virtual void reset();
 
@@ -761,15 +759,16 @@
     } mLocked;
 
     virtual void configureParameters();
-    virtual void logParameters();
+    virtual void dumpParameters(String8& dump);
     virtual void configureRawAxes();
-    virtual void logRawAxes();
+    virtual void dumpRawAxes(String8& dump);
     virtual bool configureSurfaceLocked();
-    virtual void logMotionRangesLocked();
+    virtual void dumpSurfaceLocked(String8& dump);
     virtual void configureVirtualKeysLocked();
+    virtual void dumpVirtualKeysLocked(String8& dump);
     virtual void parseCalibration();
     virtual void resolveCalibration();
-    virtual void logCalibration();
+    virtual void dumpCalibration(String8& dump);
 
     enum TouchResult {
         // Dispatch the touch normally.
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/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/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/InputReader.cpp b/libs/ui/InputReader.cpp
index f2b029a..825febc 100644
--- a/libs/ui/InputReader.cpp
+++ b/libs/ui/InputReader.cpp
@@ -33,6 +33,9 @@
 #include <math.h>
 
 #define INDENT "  "
+#define INDENT2 "    "
+#define INDENT3 "      "
+#define INDENT4 "        "
 
 namespace android {
 
@@ -63,6 +66,10 @@
     return sqrtf(x * x + y * y);
 }
 
+static inline const char* toString(bool value) {
+    return value ? "true" : "false";
+}
+
 
 int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState) {
     int32_t mask;
@@ -236,16 +243,14 @@
     String8 name = mEventHub->getDeviceName(deviceId);
     uint32_t classes = mEventHub->getDeviceClasses(deviceId);
 
-    // Write a log message about the added device as a heading for subsequent log messages.
-    LOGI("Device added: id=0x%x, name=%s", deviceId, name.string());
-
     InputDevice* device = createDevice(deviceId, name, classes);
     device->configure();
 
     if (device->isIgnored()) {
-        LOGI(INDENT "Sources: none (device is ignored)");
+        LOGI("Device added: id=0x%x, name=%s (ignored non-input device)", deviceId, name.string());
     } else {
-        LOGI(INDENT "Sources: 0x%08x", device->getSources());
+        LOGI("Device added: id=0x%x, name=%s, sources=%08x", deviceId, name.string(),
+                device->getSources());
     }
 
     bool added = false;
@@ -287,7 +292,6 @@
         return;
     }
 
-    // Write a log message about the removed device as a heading for subsequent log messages.
     if (device->isIgnored()) {
         LOGI("Device removed: id=0x%x, name=%s (ignored non-input device)",
                 device->getId(), device->getName().string());
@@ -571,59 +575,15 @@
 }
 
 void InputReader::dump(String8& dump) {
-    dumpDeviceInfo(dump);
-}
+    { // acquire device registry reader lock
+        RWLock::AutoRLock _rl(mDeviceRegistryLock);
 
-static void dumpMotionRange(String8& dump,
-        const char* name, const InputDeviceInfo::MotionRange* range) {
-    if (range) {
-        dump.appendFormat("      %s = { min: %0.3f, max: %0.3f, flat: %0.3f, fuzz: %0.3f }\n",
-                name, range->min, range->max, range->flat, range->fuzz);
-    }
-}
-
-#define DUMP_MOTION_RANGE(range) \
-    dumpMotionRange(dump, #range, deviceInfo.getMotionRange(AINPUT_MOTION_RANGE_##range));
-
-void InputReader::dumpDeviceInfo(String8& dump) {
-    Vector<int32_t> deviceIds;
-    getInputDeviceIds(deviceIds);
-
-    InputDeviceInfo deviceInfo;
-    for (size_t i = 0; i < deviceIds.size(); i++) {
-        int32_t deviceId = deviceIds[i];
-
-        status_t result = getInputDeviceInfo(deviceId, & deviceInfo);
-        if (result == NAME_NOT_FOUND) {
-            continue;
-        } else if (result != OK) {
-            dump.appendFormat("  ** Unexpected error %d getting information about input devices.\n",
-                    result);
-            continue;
+        for (size_t i = 0; i < mDevices.size(); i++) {
+            mDevices.valueAt(i)->dump(dump);
         }
-
-        dump.appendFormat("  Device %d: '%s'\n",
-                deviceInfo.getId(), deviceInfo.getName().string());
-        dump.appendFormat("    sources = 0x%08x\n",
-                deviceInfo.getSources());
-        dump.appendFormat("    keyboardType = %d\n",
-                deviceInfo.getKeyboardType());
-
-        dump.append("    motion ranges:\n");
-        DUMP_MOTION_RANGE(X);
-        DUMP_MOTION_RANGE(Y);
-        DUMP_MOTION_RANGE(PRESSURE);
-        DUMP_MOTION_RANGE(SIZE);
-        DUMP_MOTION_RANGE(TOUCH_MAJOR);
-        DUMP_MOTION_RANGE(TOUCH_MINOR);
-        DUMP_MOTION_RANGE(TOOL_MAJOR);
-        DUMP_MOTION_RANGE(TOOL_MINOR);
-        DUMP_MOTION_RANGE(ORIENTATION);
-    }
+    } // release device registy reader lock
 }
 
-#undef DUMP_MOTION_RANGE
-
 
 // --- InputReaderThread ---
 
@@ -654,6 +614,43 @@
     mMappers.clear();
 }
 
+static void dumpMotionRange(String8& dump, const InputDeviceInfo& deviceInfo,
+        int32_t rangeType, const char* name) {
+    const InputDeviceInfo::MotionRange* range = deviceInfo.getMotionRange(rangeType);
+    if (range) {
+        dump.appendFormat(INDENT3 "%s: min=%0.3f, max=%0.3f, flat=%0.3f, fuzz=%0.3f\n",
+                name, range->min, range->max, range->flat, range->fuzz);
+    }
+}
+
+void InputDevice::dump(String8& dump) {
+    InputDeviceInfo deviceInfo;
+    getDeviceInfo(& deviceInfo);
+
+    dump.appendFormat(INDENT "Device 0x%x: %s\n", deviceInfo.getId(),
+            deviceInfo.getName().string());
+    dump.appendFormat(INDENT2 "Sources: 0x%08x\n", deviceInfo.getSources());
+    dump.appendFormat(INDENT2 "KeyboardType: %d\n", deviceInfo.getKeyboardType());
+    if (!deviceInfo.getMotionRanges().isEmpty()) {
+        dump.append(INDENT2 "Motion Ranges:\n");
+        dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_X, "X");
+        dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_Y, "Y");
+        dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_PRESSURE, "Pressure");
+        dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_SIZE, "Size");
+        dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_TOUCH_MAJOR, "TouchMajor");
+        dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_TOUCH_MINOR, "TouchMinor");
+        dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_TOOL_MAJOR, "ToolMajor");
+        dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_TOOL_MINOR, "ToolMinor");
+        dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_ORIENTATION, "Orientation");
+    }
+
+    size_t numMappers = mMappers.size();
+    for (size_t i = 0; i < numMappers; i++) {
+        InputMapper* mapper = mMappers[i];
+        mapper->dump(dump);
+    }
+}
+
 void InputDevice::addMapper(InputMapper* mapper) {
     mMappers.add(mapper);
 }
@@ -763,6 +760,9 @@
     info->addSource(getSources());
 }
 
+void InputMapper::dump(String8& dump) {
+}
+
 void InputMapper::configure() {
 }
 
@@ -856,6 +856,19 @@
     info->setKeyboardType(mKeyboardType);
 }
 
+void KeyboardInputMapper::dump(String8& dump) {
+    { // acquire lock
+        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);
+        dump.appendFormat(INDENT3 "DownTime: %lld\n", mLocked.downTime);
+    } // release lock
+}
+
 void KeyboardInputMapper::reset() {
     for (;;) {
         int32_t keyCode, scanCode;
@@ -980,7 +993,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,
@@ -1044,6 +1060,18 @@
     info->addMotionRange(AINPUT_MOTION_RANGE_Y, -1.0f, 1.0f, 0.0f, mYScale);
 }
 
+void TrackballInputMapper::dump(String8& dump) {
+    { // acquire lock
+        AutoMutex _l(mLock);
+        dump.append(INDENT2 "Trackball Input Mapper:\n");
+        dump.appendFormat(INDENT3 "AssociatedDisplayId: %d\n", mAssociatedDisplayId);
+        dump.appendFormat(INDENT3 "XPrecision: %0.3f\n", mXPrecision);
+        dump.appendFormat(INDENT3 "YPrecision: %0.3f\n", mYPrecision);
+        dump.appendFormat(INDENT3 "Down: %s\n", toString(mLocked.down));
+        dump.appendFormat(INDENT3 "DownTime: %lld\n", mLocked.downTime);
+    } // release lock
+}
+
 void TrackballInputMapper::initializeLocked() {
     mAccumulator.clear();
 
@@ -1275,6 +1303,21 @@
     } // release lock
 }
 
+void TouchInputMapper::dump(String8& dump) {
+    { // acquire lock
+        AutoMutex _l(mLock);
+        dump.append(INDENT2 "Touch Input Mapper:\n");
+        dump.appendFormat(INDENT3 "AssociatedDisplayId: %d\n", mAssociatedDisplayId);
+        dumpParameters(dump);
+        dumpVirtualKeysLocked(dump);
+        dumpRawAxes(dump);
+        dumpCalibration(dump);
+        dumpSurfaceLocked(dump);
+        dump.appendFormat(INDENT3 "XPrecision: %0.3f\n", mLocked.xPrecision);
+        dump.appendFormat(INDENT3 "YPrecision: %0.3f\n", mLocked.yPrecision);
+    } // release lock
+}
+
 void TouchInputMapper::initializeLocked() {
     mCurrentTouch.clear();
     mLastTouch.clear();
@@ -1301,16 +1344,13 @@
 
     // Configure basic parameters.
     configureParameters();
-    logParameters();
 
     // Configure absolute axis information.
     configureRawAxes();
-    logRawAxes();
 
     // Prepare input device calibration.
     parseCalibration();
     resolveCalibration();
-    logCalibration();
 
     { // acquire lock
         AutoMutex _l(mLock);
@@ -1326,16 +1366,13 @@
     mParameters.useJumpyTouchFilter = getPolicy()->filterJumpyTouchEvents();
 }
 
-void TouchInputMapper::logParameters() {
-    if (mParameters.useBadTouchFilter) {
-        LOGI(INDENT "Bad touch filter enabled.");
-    }
-    if (mParameters.useAveragingTouchFilter) {
-        LOGI(INDENT "Averaging touch filter enabled.");
-    }
-    if (mParameters.useJumpyTouchFilter) {
-        LOGI(INDENT "Jumpy touch filter enabled.");
-    }
+void TouchInputMapper::dumpParameters(String8& dump) {
+    dump.appendFormat(INDENT3 "UseBadTouchFilter: %s\n",
+            toString(mParameters.useBadTouchFilter));
+    dump.appendFormat(INDENT3 "UseAveragingTouchFilter: %s\n",
+            toString(mParameters.useAveragingTouchFilter));
+    dump.appendFormat(INDENT3 "UseJumpyTouchFilter: %s\n",
+            toString(mParameters.useJumpyTouchFilter));
 }
 
 void TouchInputMapper::configureRawAxes() {
@@ -1349,24 +1386,25 @@
     mRawAxes.orientation.clear();
 }
 
-static void logAxisInfo(RawAbsoluteAxisInfo axis, const char* name) {
+static void dumpAxisInfo(String8& dump, RawAbsoluteAxisInfo axis, const char* name) {
     if (axis.valid) {
-        LOGI(INDENT "Raw %s axis: min=%d, max=%d, flat=%d, fuzz=%d",
+        dump.appendFormat(INDENT4 "%s: min=%d, max=%d, flat=%d, fuzz=%d\n",
                 name, axis.minValue, axis.maxValue, axis.flat, axis.fuzz);
     } else {
-        LOGI(INDENT "Raw %s axis: unknown range", name);
+        dump.appendFormat(INDENT4 "%s: unknown range\n", name);
     }
 }
 
-void TouchInputMapper::logRawAxes() {
-    logAxisInfo(mRawAxes.x, "x");
-    logAxisInfo(mRawAxes.y, "y");
-    logAxisInfo(mRawAxes.pressure, "pressure");
-    logAxisInfo(mRawAxes.touchMajor, "touchMajor");
-    logAxisInfo(mRawAxes.touchMinor, "touchMinor");
-    logAxisInfo(mRawAxes.toolMajor, "toolMajor");
-    logAxisInfo(mRawAxes.toolMinor, "toolMinor");
-    logAxisInfo(mRawAxes.orientation, "orientation");
+void TouchInputMapper::dumpRawAxes(String8& dump) {
+    dump.append(INDENT3 "Raw Axes:\n");
+    dumpAxisInfo(dump, mRawAxes.x, "X");
+    dumpAxisInfo(dump, mRawAxes.y, "Y");
+    dumpAxisInfo(dump, mRawAxes.pressure, "Pressure");
+    dumpAxisInfo(dump, mRawAxes.touchMajor, "TouchMajor");
+    dumpAxisInfo(dump, mRawAxes.touchMinor, "TouchMinor");
+    dumpAxisInfo(dump, mRawAxes.toolMajor, "ToolMajor");
+    dumpAxisInfo(dump, mRawAxes.toolMinor, "ToolMinor");
+    dumpAxisInfo(dump, mRawAxes.orientation, "Orientation");
 }
 
 bool TouchInputMapper::configureSurfaceLocked() {
@@ -1391,10 +1429,8 @@
 
     bool sizeChanged = mLocked.surfaceWidth != width || mLocked.surfaceHeight != height;
     if (sizeChanged) {
-        LOGI("Device reconfigured (display size changed): id=0x%x, name=%s",
-                getDeviceId(), getDeviceName().string());
-        LOGI(INDENT "Width: %dpx", width);
-        LOGI(INDENT "Height: %dpx", height);
+        LOGI("Device reconfigured: id=0x%x, name=%s, display size is now %dx%d",
+                getDeviceId(), getDeviceName().string(), width, height);
 
         mLocked.surfaceWidth = width;
         mLocked.surfaceHeight = height;
@@ -1562,39 +1598,13 @@
         mLocked.orientedRanges.y.fuzz = orientedYScale;
     }
 
-    if (sizeChanged) {
-        logMotionRangesLocked();
-    }
-
     return true;
 }
 
-static void logMotionRangeInfo(InputDeviceInfo::MotionRange* range, const char* name) {
-    if (range) {
-        LOGI(INDENT "Output %s range: min=%f, max=%f, flat=%f, fuzz=%f",
-                name, range->min, range->max, range->flat, range->fuzz);
-    } else {
-        LOGI(INDENT "Output %s range: unsupported", name);
-    }
-}
-
-void TouchInputMapper::logMotionRangesLocked() {
-    logMotionRangeInfo(& mLocked.orientedRanges.x, "x");
-    logMotionRangeInfo(& mLocked.orientedRanges.y, "y");
-    logMotionRangeInfo(mLocked.orientedRanges.havePressure
-            ? & mLocked.orientedRanges.pressure : NULL, "pressure");
-    logMotionRangeInfo(mLocked.orientedRanges.haveSize
-            ? & mLocked.orientedRanges.size : NULL, "size");
-    logMotionRangeInfo(mLocked.orientedRanges.haveTouchArea
-            ? & mLocked.orientedRanges.touchMajor : NULL, "touchMajor");
-    logMotionRangeInfo(mLocked.orientedRanges.haveTouchArea
-            ? & mLocked.orientedRanges.touchMinor : NULL, "touchMinor");
-    logMotionRangeInfo(mLocked.orientedRanges.haveToolArea
-            ? & mLocked.orientedRanges.toolMajor : NULL, "toolMajor");
-    logMotionRangeInfo(mLocked.orientedRanges.haveToolArea
-            ? & mLocked.orientedRanges.toolMinor : NULL, "toolMinor");
-    logMotionRangeInfo(mLocked.orientedRanges.haveOrientation
-            ? & mLocked.orientedRanges.orientation : NULL, "orientation");
+void TouchInputMapper::dumpSurfaceLocked(String8& dump) {
+    dump.appendFormat(INDENT3 "SurfaceWidth: %dpx\n", mLocked.surfaceWidth);
+    dump.appendFormat(INDENT3 "SurfaceHeight: %dpx\n", mLocked.surfaceHeight);
+    dump.appendFormat(INDENT3 "SurfaceOrientation: %d\n", mLocked.surfaceOrientation);
 }
 
 void TouchInputMapper::configureVirtualKeysLocked() {
@@ -1651,9 +1661,21 @@
         virtualKey.hitBottom = (virtualKeyDefinition.centerY + halfHeight)
                 * touchScreenHeight / mLocked.surfaceHeight + touchScreenTop;
 
-        LOGI(INDENT "VirtualKey %d: keyCode=%d hitLeft=%d hitRight=%d hitTop=%d hitBottom=%d",
-                virtualKey.scanCode, virtualKey.keyCode,
-                virtualKey.hitLeft, virtualKey.hitRight, virtualKey.hitTop, virtualKey.hitBottom);
+    }
+}
+
+void TouchInputMapper::dumpVirtualKeysLocked(String8& dump) {
+    if (!mLocked.virtualKeys.isEmpty()) {
+        dump.append(INDENT3 "Virtual Keys:\n");
+
+        for (size_t i = 0; i < mLocked.virtualKeys.size(); i++) {
+            const VirtualKey& virtualKey = mLocked.virtualKeys.itemAt(i);
+            dump.appendFormat(INDENT4 "%d: scanCode=%d, keyCode=%d, "
+                    "hitLeft=%d, hitRight=%d, hitTop=%d, hitBottom=%d\n",
+                    i, virtualKey.scanCode, virtualKey.keyCode,
+                    virtualKey.hitLeft, virtualKey.hitRight,
+                    virtualKey.hitTop, virtualKey.hitBottom);
+        }
     }
 }
 
@@ -1861,19 +1883,19 @@
     }
 }
 
-void TouchInputMapper::logCalibration() {
-    LOGI(INDENT "Calibration:");
+void TouchInputMapper::dumpCalibration(String8& dump) {
+    dump.append(INDENT3 "Calibration:\n");
 
     // Touch Area
     switch (mCalibration.touchAreaCalibration) {
     case Calibration::TOUCH_AREA_CALIBRATION_NONE:
-        LOGI(INDENT INDENT "touch.touchArea.calibration: none");
+        dump.append(INDENT4 "touch.touchArea.calibration: none\n");
         break;
     case Calibration::TOUCH_AREA_CALIBRATION_GEOMETRIC:
-        LOGI(INDENT INDENT "touch.touchArea.calibration: geometric");
+        dump.append(INDENT4 "touch.touchArea.calibration: geometric\n");
         break;
     case Calibration::TOUCH_AREA_CALIBRATION_PRESSURE:
-        LOGI(INDENT INDENT "touch.touchArea.calibration: pressure");
+        dump.append(INDENT4 "touch.touchArea.calibration: pressure\n");
         break;
     default:
         assert(false);
@@ -1882,40 +1904,43 @@
     // Tool Area
     switch (mCalibration.toolAreaCalibration) {
     case Calibration::TOOL_AREA_CALIBRATION_NONE:
-        LOGI(INDENT INDENT "touch.toolArea.calibration: none");
+        dump.append(INDENT4 "touch.toolArea.calibration: none\n");
         break;
     case Calibration::TOOL_AREA_CALIBRATION_GEOMETRIC:
-        LOGI(INDENT INDENT "touch.toolArea.calibration: geometric");
+        dump.append(INDENT4 "touch.toolArea.calibration: geometric\n");
         break;
     case Calibration::TOOL_AREA_CALIBRATION_LINEAR:
-        LOGI(INDENT INDENT "touch.toolArea.calibration: linear");
+        dump.append(INDENT4 "touch.toolArea.calibration: linear\n");
         break;
     default:
         assert(false);
     }
 
     if (mCalibration.haveToolAreaLinearScale) {
-        LOGI(INDENT INDENT "touch.toolArea.linearScale: %f", mCalibration.toolAreaLinearScale);
+        dump.appendFormat(INDENT4 "touch.toolArea.linearScale: %0.3f\n",
+                mCalibration.toolAreaLinearScale);
     }
 
     if (mCalibration.haveToolAreaLinearBias) {
-        LOGI(INDENT INDENT "touch.toolArea.linearBias: %f", mCalibration.toolAreaLinearBias);
+        dump.appendFormat(INDENT4 "touch.toolArea.linearBias: %0.3f\n",
+                mCalibration.toolAreaLinearBias);
     }
 
     if (mCalibration.haveToolAreaIsSummed) {
-        LOGI(INDENT INDENT "touch.toolArea.isSummed: %d", mCalibration.toolAreaIsSummed);
+        dump.appendFormat(INDENT4 "touch.toolArea.isSummed: %d\n",
+                mCalibration.toolAreaIsSummed);
     }
 
     // Pressure
     switch (mCalibration.pressureCalibration) {
     case Calibration::PRESSURE_CALIBRATION_NONE:
-        LOGI(INDENT INDENT "touch.pressure.calibration: none");
+        dump.append(INDENT4 "touch.pressure.calibration: none\n");
         break;
     case Calibration::PRESSURE_CALIBRATION_PHYSICAL:
-        LOGI(INDENT INDENT "touch.pressure.calibration: physical");
+        dump.append(INDENT4 "touch.pressure.calibration: physical\n");
         break;
     case Calibration::PRESSURE_CALIBRATION_AMPLITUDE:
-        LOGI(INDENT INDENT "touch.pressure.calibration: amplitude");
+        dump.append(INDENT4 "touch.pressure.calibration: amplitude\n");
         break;
     default:
         assert(false);
@@ -1923,10 +1948,10 @@
 
     switch (mCalibration.pressureSource) {
     case Calibration::PRESSURE_SOURCE_PRESSURE:
-        LOGI(INDENT INDENT "touch.pressure.source: pressure");
+        dump.append(INDENT4 "touch.pressure.source: pressure\n");
         break;
     case Calibration::PRESSURE_SOURCE_TOUCH:
-        LOGI(INDENT INDENT "touch.pressure.source: touch");
+        dump.append(INDENT4 "touch.pressure.source: touch\n");
         break;
     case Calibration::PRESSURE_SOURCE_DEFAULT:
         break;
@@ -1935,16 +1960,17 @@
     }
 
     if (mCalibration.havePressureScale) {
-        LOGI(INDENT INDENT "touch.pressure.scale: %f", mCalibration.pressureScale);
+        dump.appendFormat(INDENT4 "touch.pressure.scale: %0.3f\n",
+                mCalibration.pressureScale);
     }
 
     // Size
     switch (mCalibration.sizeCalibration) {
     case Calibration::SIZE_CALIBRATION_NONE:
-        LOGI(INDENT INDENT "touch.size.calibration: none");
+        dump.append(INDENT4 "touch.size.calibration: none\n");
         break;
     case Calibration::SIZE_CALIBRATION_NORMALIZED:
-        LOGI(INDENT INDENT "touch.size.calibration: normalized");
+        dump.append(INDENT4 "touch.size.calibration: normalized\n");
         break;
     default:
         assert(false);
@@ -1953,10 +1979,10 @@
     // Orientation
     switch (mCalibration.orientationCalibration) {
     case Calibration::ORIENTATION_CALIBRATION_NONE:
-        LOGI(INDENT INDENT "touch.orientation.calibration: none");
+        dump.append(INDENT4 "touch.orientation.calibration: none\n");
         break;
     case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED:
-        LOGI(INDENT INDENT "touch.orientation.calibration: interpolated");
+        dump.append(INDENT4 "touch.orientation.calibration: interpolated\n");
         break;
     default:
         assert(false);
@@ -2139,10 +2165,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);
 
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 1b21a8d..fb76720 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -271,6 +271,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 188da6a..caa6d17 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -70,6 +70,7 @@
     // LayerBase interface
     virtual void setGeometry(hwc_layer_t* hwcl);
     virtual void setPerFrameData(hwc_layer_t* hwcl);
+    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 3d049a7..14191cb 100644
--- a/services/surfaceflinger/LayerBase.cpp
+++ b/services/surfaceflinger/LayerBase.cpp
@@ -326,6 +326,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 c66dc34..bdee05b 100644
--- a/services/surfaceflinger/LayerBase.h
+++ b/services/surfaceflinger/LayerBase.h
@@ -121,6 +121,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 fdf9abc..c060895 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 17b98a6..e6bdfd1 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1653,9 +1653,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))
@@ -1671,12 +1779,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 {
@@ -1689,94 +1800,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 6e9ecbd..732e57e 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);
@@ -319,6 +321,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);