Merge "Extended android.os.Debug.dumpNativeHeap to include /proc/self/maps contents"
diff --git a/include/surfaceflinger/ISurfaceComposer.h b/include/surfaceflinger/ISurfaceComposer.h
index ac69a06..db57859 100644
--- a/include/surfaceflinger/ISurfaceComposer.h
+++ b/include/surfaceflinger/ISurfaceComposer.h
@@ -78,6 +78,11 @@
         eOrientationSwapMask    = 0x01
     };
     
+    enum {
+        eElectronBeamAnimationOn  = 0x01,
+        eElectronBeamAnimationOff = 0x10
+    };
+
     // flags for setOrientation
     enum {
         eOrientationAnimationDisable = 0x00000001
@@ -120,6 +125,7 @@
             uint32_t reqWidth, uint32_t reqHeight) = 0;
 
     virtual status_t turnElectronBeamOff(int32_t mode) = 0;
+    virtual status_t turnElectronBeamOn(int32_t mode) = 0;
 
     /* Signal surfaceflinger that there might be some work to do
      * This is an ASYNCHRONOUS call.
@@ -146,7 +152,8 @@
         UNFREEZE_DISPLAY,
         SIGNAL,
         CAPTURE_SCREEN,
-        TURN_ELECTRON_BEAM_OFF
+        TURN_ELECTRON_BEAM_OFF,
+        TURN_ELECTRON_BEAM_ON
     };
 
     virtual status_t    onTransact( uint32_t code,
diff --git a/include/ui/EventHub.h b/include/ui/EventHub.h
index d78e35f..1431964 100644
--- a/include/ui/EventHub.h
+++ b/include/ui/EventHub.h
@@ -187,6 +187,9 @@
     virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes,
             uint8_t* outFlags) const = 0;
 
+    virtual bool hasLed(int32_t deviceId, int32_t led) const = 0;
+    virtual void setLedState(int32_t deviceId, int32_t led, bool on) = 0;
+
     virtual void dump(String8& dump) = 0;
 };
 
@@ -198,9 +201,9 @@
     status_t errorCheck() const;
 
     virtual uint32_t getDeviceClasses(int32_t deviceId) const;
-    
+
     virtual String8 getDeviceName(int32_t deviceId) const;
-    
+
     virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
             RawAbsoluteAxisInfo* outAxisInfo) const;
 
@@ -218,6 +221,9 @@
 
     virtual bool getEvent(RawEvent* outEvent);
 
+    virtual bool hasLed(int32_t deviceId, int32_t led) const;
+    virtual void setLedState(int32_t deviceId, int32_t led, bool on);
+
     virtual void dump(String8& dump);
 
 protected:
@@ -240,7 +246,10 @@
         uint32_t        classes;
         uint8_t*        keyBitmask;
         KeyLayoutMap*   layoutMap;
-        String8         keylayoutFilename;
+        String8         keyMapName;
+        bool            defaultKeyMap;
+        String8         keyLayoutFilename;
+        String8         keyCharacterMapFilename;
         int             fd;
         device_t*       next;
         
@@ -250,13 +259,19 @@
 
     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;
     int32_t getSwitchStateLocked(device_t* device, int32_t sw) const;
     bool markSupportedKeyCodesLocked(device_t* device, size_t numCodes,
             const int32_t* keyCodes, uint8_t* outFlags) const;
 
+    void configureKeyMap(device_t* device);
+    bool probeKeyMap(device_t* device, const String8& keyMapName, bool defaultKeyMap);
+    void selectKeyMap(device_t* device, const String8& keyMapName, bool defaultKeyMap);
+    void setKeyboardProperties(device_t* device, bool firstKeyboard);
+    void clearKeyboardProperties(device_t* device, bool firstKeyboard);
+
     // Protect all internal state.
     mutable Mutex   mLock;
     
diff --git a/include/ui/Input.h b/include/ui/Input.h
index 8c6018b..1355bab 100644
--- a/include/ui/Input.h
+++ b/include/ui/Input.h
@@ -76,7 +76,7 @@
  */
 enum {
     /* These flags originate in RawEvents and are generally set in the key map.
-     * See also labels for policy flags in KeycodeLabels.h. */
+     * NOTE: If you edit these flags, also edit labels in KeycodeLabels.h. */
 
     POLICY_FLAG_WAKE = 0x00000001,
     POLICY_FLAG_WAKE_DROPPED = 0x00000002,
@@ -87,6 +87,7 @@
     POLICY_FLAG_MENU = 0x00000040,
     POLICY_FLAG_LAUNCHER = 0x00000080,
     POLICY_FLAG_VIRTUAL = 0x00000100,
+    POLICY_FLAG_FUNCTION = 0x00000200,
 
     POLICY_FLAG_RAW_MASK = 0x0000ffff,
 
diff --git a/include/ui/InputDispatcher.h b/include/ui/InputDispatcher.h
index 63185d3..b811ace 100644
--- a/include/ui/InputDispatcher.h
+++ b/include/ui/InputDispatcher.h
@@ -183,6 +183,7 @@
         TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW+12,
         TYPE_WALLPAPER          = FIRST_SYSTEM_WINDOW+13,
         TYPE_STATUS_BAR_PANEL   = FIRST_SYSTEM_WINDOW+14,
+        TYPE_SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+15,
         LAST_SYSTEM_WINDOW      = 2999,
     };
 
diff --git a/include/ui/InputReader.h b/include/ui/InputReader.h
index c15e382..eb88d8b 100644
--- a/include/ui/InputReader.h
+++ b/include/ui/InputReader.h
@@ -419,9 +419,18 @@
         Vector<KeyDown> keyDowns; // keys that are down
         int32_t metaState;
         nsecs_t downTime; // time of most recent key down
+
+        struct LedState {
+            bool avail; // led is available
+            bool on;    // we think the led is currently on
+        };
+        LedState capsLockLedState;
+        LedState numLockLedState;
+        LedState scrollLockLedState;
     } mLocked;
 
     void initializeLocked();
+    void initializeLedStateLocked(LockedState::LedState& ledState, int32_t led);
 
     bool isKeyboardOrGamepadKey(int32_t scanCode);
 
@@ -429,6 +438,10 @@
             uint32_t policyFlags);
 
     ssize_t findKeyDownLocked(int32_t scanCode);
+
+    void updateLedStateLocked(bool reset);
+    void updateLedStateForModifierLocked(LockedState::LedState& ledState, int32_t led,
+            int32_t modifier, bool reset);
 };
 
 
@@ -571,31 +584,36 @@
 
     // Immutable calibration parameters in parsed form.
     struct Calibration {
-        // Touch Area
-        enum TouchAreaCalibration {
-            TOUCH_AREA_CALIBRATION_DEFAULT,
-            TOUCH_AREA_CALIBRATION_NONE,
-            TOUCH_AREA_CALIBRATION_GEOMETRIC,
-            TOUCH_AREA_CALIBRATION_PRESSURE,
+        // Touch Size
+        enum TouchSizeCalibration {
+            TOUCH_SIZE_CALIBRATION_DEFAULT,
+            TOUCH_SIZE_CALIBRATION_NONE,
+            TOUCH_SIZE_CALIBRATION_GEOMETRIC,
+            TOUCH_SIZE_CALIBRATION_PRESSURE,
         };
 
-        TouchAreaCalibration touchAreaCalibration;
+        TouchSizeCalibration touchSizeCalibration;
 
-        // Tool Area
-        enum ToolAreaCalibration {
-            TOOL_AREA_CALIBRATION_DEFAULT,
-            TOOL_AREA_CALIBRATION_NONE,
-            TOOL_AREA_CALIBRATION_GEOMETRIC,
-            TOOL_AREA_CALIBRATION_LINEAR,
+        // Tool Size
+        enum ToolSizeCalibration {
+            TOOL_SIZE_CALIBRATION_DEFAULT,
+            TOOL_SIZE_CALIBRATION_NONE,
+            TOOL_SIZE_CALIBRATION_GEOMETRIC,
+            TOOL_SIZE_CALIBRATION_LINEAR,
+            TOOL_SIZE_CALIBRATION_AREA,
         };
 
-        ToolAreaCalibration toolAreaCalibration;
-        bool haveToolAreaLinearScale;
-        float toolAreaLinearScale;
-        bool haveToolAreaLinearBias;
-        float toolAreaLinearBias;
-        bool haveToolAreaIsSummed;
-        int32_t toolAreaIsSummed;
+        ToolSizeCalibration toolSizeCalibration;
+        bool haveToolSizeLinearScale;
+        float toolSizeLinearScale;
+        bool haveToolSizeLinearBias;
+        float toolSizeLinearBias;
+        bool haveToolSizeAreaScale;
+        float toolSizeAreaScale;
+        bool haveToolSizeAreaBias;
+        float toolSizeAreaBias;
+        bool haveToolSizeIsSummed;
+        int32_t toolSizeIsSummed;
 
         // Pressure
         enum PressureCalibration {
@@ -671,8 +689,10 @@
 
         float geometricScale;
 
-        float toolAreaLinearScale;
-        float toolAreaLinearBias;
+        float toolSizeLinearScale;
+        float toolSizeLinearBias;
+        float toolSizeAreaScale;
+        float toolSizeAreaBias;
 
         float pressureScale;
 
@@ -691,11 +711,11 @@
             bool haveSize;
             InputDeviceInfo::MotionRange size;
 
-            bool haveTouchArea;
+            bool haveTouchSize;
             InputDeviceInfo::MotionRange touchMajor;
             InputDeviceInfo::MotionRange touchMinor;
 
-            bool haveToolArea;
+            bool haveToolSize;
             InputDeviceInfo::MotionRange toolMajor;
             InputDeviceInfo::MotionRange toolMinor;
 
diff --git a/include/ui/KeycodeLabels.h b/include/ui/KeycodeLabels.h
index f71d9cd..ef2b6b3 100755
--- a/include/ui/KeycodeLabels.h
+++ b/include/ui/KeycodeLabels.h
@@ -135,6 +135,59 @@
     { "BUTTON_START", 108 },
     { "BUTTON_SELECT", 109 },
     { "BUTTON_MODE", 110 },
+    { "ESCAPE", 111 },
+    { "FORWARD_DEL", 112 },
+    { "CTRL_LEFT", 113 },
+    { "CTRL_RIGHT", 114 },
+    { "CAPS_LOCK", 115 },
+    { "SCROLL_LOCK", 116 },
+    { "META_LEFT", 117 },
+    { "META_RIGHT", 118 },
+    { "FUNCTION", 119 },
+    { "SYSRQ", 120 },
+    { "BREAK", 121 },
+    { "MOVE_HOME", 122 },
+    { "MOVE_END", 123 },
+    { "INSERT", 124 },
+    { "FORWARD", 125 },
+    { "MEDIA_PLAY", 126 },
+    { "MEDIA_PAUSE", 127 },
+    { "MEDIA_CLOSE", 128 },
+    { "MEDIA_EJECT", 129 },
+    { "MEDIA_RECORD", 130 },
+    { "F1", 131 },
+    { "F2", 132 },
+    { "F3", 133 },
+    { "F4", 134 },
+    { "F5", 135 },
+    { "F6", 136 },
+    { "F7", 137 },
+    { "F8", 138 },
+    { "F9", 139 },
+    { "F10", 140 },
+    { "F11", 141 },
+    { "F12", 142 },
+    { "NUM_LOCK", 143 },
+    { "NUMPAD_0", 144 },
+    { "NUMPAD_1", 145 },
+    { "NUMPAD_2", 146 },
+    { "NUMPAD_3", 147 },
+    { "NUMPAD_4", 148 },
+    { "NUMPAD_5", 149 },
+    { "NUMPAD_6", 150 },
+    { "NUMPAD_7", 151 },
+    { "NUMPAD_8", 152 },
+    { "NUMPAD_9", 153 },
+    { "NUMPAD_DIVIDE", 154 },
+    { "NUMPAD_MULTIPLY", 155 },
+    { "NUMPAD_SUBTRACT", 156 },
+    { "NUMPAD_ADD", 157 },
+    { "NUMPAD_DOT", 158 },
+    { "NUMPAD_COMMA", 159 },
+    { "NUMPAD_ENTER", 160 },
+    { "NUMPAD_EQUALS", 161 },
+    { "NUMPAD_LEFT_PAREN", 162 },
+    { "NUMPAD_RIGHT_PAREN", 163 },
 
     // NOTE: If you add a new keycode here you must also add it to several other files.
     //       Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
@@ -142,7 +195,7 @@
     { NULL, 0 }
 };
 
-// See also policy flags in Input.h.
+// NOTE: If you edit these flags, also edit policy flags in Input.h.
 static const KeycodeLabel FLAGS[] = {
     { "WAKE", 0x00000001 },
     { "WAKE_DROPPED", 0x00000002 },
@@ -153,6 +206,7 @@
     { "MENU", 0x00000040 },
     { "LAUNCHER", 0x00000080 },
     { "VIRTUAL", 0x00000100 },
+    { "FUNCTION", 0x00000200 },
     { NULL, 0 }
 };
 
diff --git a/include/utils/Looper.h b/include/utils/Looper.h
index cc51490..eefff31 100644
--- a/include/utils/Looper.h
+++ b/include/utils/Looper.h
@@ -24,10 +24,10 @@
 
 #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
+// When defined, uses epoll_wait() for polling, otherwise uses poll().
+#define LOOPER_USES_EPOLL
+
+// When defined, logs performance statistics for tuning and debugging purposes.
 //#define LOOPER_STATISTICS
 
 #ifdef LOOPER_USES_EPOLL
diff --git a/include/utils/ObbFile.h b/include/utils/ObbFile.h
index 5243f50..47559cd 100644
--- a/include/utils/ObbFile.h
+++ b/include/utils/ObbFile.h
@@ -27,6 +27,7 @@
 
 // OBB flags (bit 0)
 #define OBB_OVERLAY         (1 << 0)
+#define OBB_SALTED          (1 << 1)
 
 class ObbFile : public RefBase {
 protected:
@@ -70,6 +71,26 @@
         mFlags = flags;
     }
 
+    const unsigned char* getSalt(size_t* length) const {
+        if ((mFlags & OBB_SALTED) == 0) {
+            *length = 0;
+            return NULL;
+        }
+
+        *length = sizeof(mSalt);
+        return mSalt;
+    }
+
+    bool setSalt(const unsigned char* salt, size_t length) {
+        if (length != sizeof(mSalt)) {
+            return false;
+        }
+
+        memcpy(mSalt, salt, sizeof(mSalt));
+        mFlags |= OBB_SALTED;
+        return true;
+    }
+
     bool isOverlay() {
         return (mFlags & OBB_OVERLAY) == OBB_OVERLAY;
     }
@@ -103,6 +124,12 @@
     /* Flags for this OBB type. */
     int32_t mFlags;
 
+    /* Whether the file is salted. */
+    bool mSalted;
+
+    /* The encryption salt. */
+    unsigned char mSalt[8];
+
     const char* mFileName;
 
     size_t mFileSize;
diff --git a/include/utils/String8.h b/include/utils/String8.h
index ef0b51a..cef8eca 100644
--- a/include/utils/String8.h
+++ b/include/utils/String8.h
@@ -157,9 +157,12 @@
     inline  size_t              size() const;
     inline  size_t              length() const;
     inline  size_t              bytes() const;
+    inline  bool                isEmpty() const;
     
     inline  const SharedBuffer* sharedBuffer() const;
     
+            void                clear();
+
             void                setTo(const String8& other);
             status_t            setTo(const char* other);
             status_t            setTo(const char* other, size_t numChars);
@@ -345,6 +348,11 @@
     return length();
 }
 
+inline bool String8::isEmpty() const
+{
+    return length() == 0;
+}
+
 inline size_t String8::bytes() const
 {
     return SharedBuffer::sizeFromData(mString)-1;
diff --git a/libs/surfaceflinger_client/ISurfaceComposer.cpp b/libs/surfaceflinger_client/ISurfaceComposer.cpp
index d72561f..969ee79 100644
--- a/libs/surfaceflinger_client/ISurfaceComposer.cpp
+++ b/libs/surfaceflinger_client/ISurfaceComposer.cpp
@@ -151,6 +151,15 @@
         return reply.readInt32();
     }
 
+    virtual status_t turnElectronBeamOn(int32_t mode)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        data.writeInt32(mode);
+        remote()->transact(BnSurfaceComposer::TURN_ELECTRON_BEAM_ON, data, &reply);
+        return reply.readInt32();
+    }
+
     virtual void signal() const
     {
         Parcel data, reply;
@@ -239,6 +248,12 @@
             status_t res = turnElectronBeamOff(mode);
             reply->writeInt32(res);
         }
+        case TURN_ELECTRON_BEAM_ON: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            int32_t mode = data.readInt32();
+            status_t res = turnElectronBeamOn(mode);
+            reply->writeInt32(res);
+        }
         default:
             return BBinder::onTransact(code, data, reply, flags);
     }
diff --git a/libs/ui/EventHub.cpp b/libs/ui/EventHub.cpp
index c0be3a0..97a7528 100644
--- a/libs/ui/EventHub.cpp
+++ b/libs/ui/EventHub.cpp
@@ -94,7 +94,7 @@
 
 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) {
+    , keyBitmask(NULL), layoutMap(new KeyLayoutMap()), defaultKeyMap(false), fd(-1), next(NULL) {
 }
 
 EventHub::device_t::~device_t() {
@@ -103,7 +103,7 @@
 }
 
 EventHub::EventHub(void)
-    : mError(NO_INIT), mHaveFirstKeyboard(false), mFirstKeyboardId(0)
+    : mError(NO_INIT), mHaveFirstKeyboard(false), mFirstKeyboardId(-1)
     , mDevicesById(0), mNumDevicesById(0)
     , mOpeningDevices(0), mClosingDevices(0)
     , mDevices(0), mFDs(0), mFDCount(0), mOpened(false), mNeedToSendFinishedDeviceScan(false)
@@ -325,6 +325,39 @@
     mExcludedDevices.push_back(name);
 }
 
+bool EventHub::hasLed(int32_t deviceId, int32_t led) const {
+    AutoMutex _l(mLock);
+    device_t* device = getDeviceLocked(deviceId);
+    if (device) {
+        uint8_t bitmask[sizeof_bit_array(LED_MAX + 1)];
+        memset(bitmask, 0, sizeof(bitmask));
+        if (ioctl(device->fd, EVIOCGBIT(EV_LED, sizeof(bitmask)), bitmask) >= 0) {
+            if (test_bit(led, bitmask)) {
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
+void EventHub::setLedState(int32_t deviceId, int32_t led, bool on) {
+    AutoMutex _l(mLock);
+    device_t* device = getDeviceLocked(deviceId);
+    if (device) {
+        struct input_event ev;
+        ev.time.tv_sec = 0;
+        ev.time.tv_usec = 0;
+        ev.type = EV_LED;
+        ev.code = led;
+        ev.value = on ? 1 : 0;
+
+        ssize_t nWrite;
+        do {
+            nWrite = write(device->fd, &ev, sizeof(struct input_event));
+        } while (nWrite == -1 && errno == EINTR);
+    }
+}
+
 EventHub::device_t* EventHub::getDeviceLocked(int32_t deviceId) const
 {
     if (deviceId == 0) deviceId = mFirstKeyboardId;
@@ -439,11 +472,10 @@
             // Since mFDs[0] is used for inotify, we process regular events starting at index 1.
             mInputDeviceIndex += 1;
             if (mInputDeviceIndex >= mFDCount) {
-                mInputDeviceIndex = 0;
                 break;
             }
 
-            const struct pollfd &pfd = mFDs[mInputDeviceIndex];
+            const struct pollfd& pfd = mFDs[mInputDeviceIndex];
             if (pfd.revents & POLLIN) {
                 int32_t readSize = read(pfd.fd, mInputBufferData,
                         sizeof(struct input_event) * INPUT_BUFFER_SIZE);
@@ -460,11 +492,17 @@
             }
         }
 
+#if HAVE_INOTIFY
         // readNotify() will modify mFDs and mFDCount, so this must be done after
         // processing all other events.
         if(mFDs[0].revents & POLLIN) {
             readNotify(mFDs[0].fd);
+            mFDs[0].revents = 0;
+            continue; // report added or removed devices immediately
         }
+#endif
+
+        mInputDeviceIndex = 0;
 
         // Poll for events.  Mind the wake lock dance!
         // We hold a wake lock at all times except during poll().  This works due to some
@@ -482,7 +520,7 @@
 
         if (pollResult <= 0) {
             if (errno != EINTR) {
-                LOGW("select failed (errno=%d)\n", errno);
+                LOGW("poll failed (errno=%d)\n", errno);
                 usleep(100000);
             }
         }
@@ -755,54 +793,42 @@
 #endif
 
     if ((device->classes & INPUT_DEVICE_CLASS_KEYBOARD) != 0) {
-        char tmpfn[sizeof(name)];
-        char keylayoutFilename[300];
-
         // a more descriptive name
         device->name = name;
 
-        // replace all the spaces with underscores
-        strcpy(tmpfn, name);
-        for (char *p = strchr(tmpfn, ' '); p && *p; p = strchr(tmpfn, ' '))
-            *p = '_';
+        // Configure the keymap for the device.
+        configureKeyMap(device);
 
-        // find the .kl file we need for this device
-        const char* root = getenv("ANDROID_ROOT");
-        snprintf(keylayoutFilename, sizeof(keylayoutFilename),
-                 "%s/usr/keylayout/%s.kl", root, tmpfn);
-        bool defaultKeymap = false;
-        if (access(keylayoutFilename, R_OK)) {
-            snprintf(keylayoutFilename, sizeof(keylayoutFilename),
-                     "%s/usr/keylayout/%s", root, "qwerty.kl");
-            defaultKeymap = true;
-        }
-        status_t status = device->layoutMap->load(keylayoutFilename);
-        if (status) {
-            LOGE("Error %d loading key layout.", status);
-        }
-
-        // tell the world about the devname (the descriptive name)
-        if (!mHaveFirstKeyboard && !defaultKeymap && strstr(name, "-keypad")) {
+        // Tell the world about the devname (the descriptive name)
+        if (!mHaveFirstKeyboard && !device->defaultKeyMap && strstr(name, "-keypad")) {
             // the built-in keyboard has a well-known device ID of 0,
             // this device better not go away.
             mHaveFirstKeyboard = true;
             mFirstKeyboardId = device->id;
-            property_set("hw.keyboards.0.devname", name);
+            setKeyboardProperties(device, true);
         } else {
             // ensure mFirstKeyboardId is set to -something-.
-            if (mFirstKeyboardId == 0) {
+            if (mFirstKeyboardId == -1) {
                 mFirstKeyboardId = device->id;
+                setKeyboardProperties(device, true);
             }
         }
-        char propName[100];
-        sprintf(propName, "hw.keyboards.%u.devname", device->id);
-        property_set(propName, name);
+        setKeyboardProperties(device, false);
+
+        // Load the keylayout.
+        if (!device->keyLayoutFilename.isEmpty()) {
+            status_t status = device->layoutMap->load(device->keyLayoutFilename);
+            if (status) {
+                LOGE("Error %d loading key layout file '%s'.", status,
+                        device->keyLayoutFilename.string());
+            }
+        }
 
         // 'Q' key support = cheap test of whether this is an alpha-capable kbd
         if (hasKeycodeLocked(device, AKEYCODE_Q)) {
             device->classes |= INPUT_DEVICE_CLASS_ALPHAKEY;
         }
-        
+
         // See if this device has a DPAD.
         if (hasKeycodeLocked(device, AKEYCODE_DPAD_UP) &&
                 hasKeycodeLocked(device, AKEYCODE_DPAD_DOWN) &&
@@ -811,7 +837,7 @@
                 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 (hasKeycodeLocked(device, GAMEPAD_KEYCODES[i])) {
@@ -820,8 +846,9 @@
             }
         }
 
-        LOGI("New keyboard: device->id=0x%x devname='%s' propName='%s' keylayout='%s'\n",
-                device->id, name, propName, keylayoutFilename);
+        LOGI("New keyboard: device->id=0x%x devname='%s' keylayout='%s' keycharactermap='%s'\n",
+                device->id, name,
+                device->keyLayoutFilename.string(), device->keyCharacterMapFilename.string());
     }
 
     // If the device isn't recognized as something we handle, don't monitor it.
@@ -847,6 +874,109 @@
     return 0;
 }
 
+void EventHub::configureKeyMap(device_t* device) {
+    // As an initial key map name, try using the device name.
+    String8 keyMapName(device->name);
+    char* p = keyMapName.lockBuffer(keyMapName.size());
+    while (*p) {
+        if (*p == ' ') *p = '_';
+        p++;
+    }
+    keyMapName.unlockBuffer();
+
+    if (probeKeyMap(device, keyMapName, false)) return;
+
+    // TODO Consider allowing the user to configure a specific key map somehow.
+
+    // Try the Generic key map.
+    // TODO Apply some additional heuristics here to figure out what kind of
+    //      generic key map to use (US English, etc.).
+    keyMapName.setTo("Generic");
+    if (probeKeyMap(device, keyMapName, true)) return;
+
+    // Fall back on the old style catchall qwerty key map.
+    keyMapName.setTo("qwerty");
+    if (probeKeyMap(device, keyMapName, true)) return;
+
+    // Give up!
+    keyMapName.setTo("unknown");
+    selectKeyMap(device, keyMapName, true);
+    LOGE("Could not determine key map for device '%s'.", device->name.string());
+}
+
+bool EventHub::probeKeyMap(device_t* device, const String8& keyMapName, bool defaultKeyMap) {
+    const char* root = getenv("ANDROID_ROOT");
+
+    // TODO Consider also looking somewhere in a writeable partition like /data for a
+    //      custom keymap supplied by the user for this device.
+    bool haveKeyLayout = !device->keyLayoutFilename.isEmpty();
+    if (!haveKeyLayout) {
+        device->keyLayoutFilename.setTo(root);
+        device->keyLayoutFilename.append("/usr/keylayout/");
+        device->keyLayoutFilename.append(keyMapName);
+        device->keyLayoutFilename.append(".kl");
+        if (access(device->keyLayoutFilename.string(), R_OK)) {
+            device->keyLayoutFilename.clear();
+        } else {
+            haveKeyLayout = true;
+        }
+    }
+
+    bool haveKeyCharacterMap = !device->keyCharacterMapFilename.isEmpty();
+    if (!haveKeyCharacterMap) {
+        device->keyCharacterMapFilename.setTo(root);
+        device->keyCharacterMapFilename.append("/usr/keychars/");
+        device->keyCharacterMapFilename.append(keyMapName);
+        device->keyCharacterMapFilename.append(".kcm.bin");
+        if (access(device->keyCharacterMapFilename.string(), R_OK)) {
+            device->keyCharacterMapFilename.clear();
+        } else {
+            haveKeyCharacterMap = true;
+        }
+    }
+
+    if (haveKeyLayout || haveKeyCharacterMap) {
+        selectKeyMap(device, keyMapName, defaultKeyMap);
+    }
+    return haveKeyLayout && haveKeyCharacterMap;
+}
+
+void EventHub::selectKeyMap(device_t* device,
+        const String8& keyMapName, bool defaultKeyMap) {
+    if (device->keyMapName.isEmpty()) {
+        device->keyMapName.setTo(keyMapName);
+        device->defaultKeyMap = defaultKeyMap;
+    }
+}
+
+void EventHub::setKeyboardProperties(device_t* device, bool firstKeyboard) {
+    int32_t id = firstKeyboard ? 0 : device->id;
+
+    char propName[100];
+    sprintf(propName, "hw.keyboards.%u.devname", id);
+    property_set(propName, device->name.string());
+    sprintf(propName, "hw.keyboards.%u.keymap", id);
+    property_set(propName, device->keyMapName.string());
+    sprintf(propName, "hw.keyboards.%u.klfile", id);
+    property_set(propName, device->keyLayoutFilename.string());
+    sprintf(propName, "hw.keyboards.%u.kcmfile", id);
+    property_set(propName, device->keyCharacterMapFilename.string());
+}
+
+void EventHub::clearKeyboardProperties(device_t* device, bool firstKeyboard) {
+    int32_t id = firstKeyboard ? 0 : device->id;
+
+    char propName[100];
+    sprintf(propName, "hw.keyboards.%u.devname", id);
+    property_set(propName, "");
+    sprintf(propName, "hw.keyboards.%u.keymap", id);
+    property_set(propName, "");
+    sprintf(propName, "hw.keyboards.%u.klfile", id);
+    property_set(propName, "");
+    sprintf(propName, "hw.keyboards.%u.kcmfile", id);
+    property_set(propName, "");
+}
+
 bool EventHub::hasKeycodeLocked(device_t* device, int keycode) const
 {
     if (device->keyBitmask == NULL || device->layoutMap == NULL) {
@@ -904,13 +1034,10 @@
             if (device->id == mFirstKeyboardId) {
                 LOGW("built-in keyboard device %s (id=%d) is closing! the apps will not like this",
                         device->path.string(), mFirstKeyboardId);
-                mFirstKeyboardId = 0;
-                property_set("hw.keyboards.0.devname", NULL);
+                mFirstKeyboardId = -1;
+                clearKeyboardProperties(device, true);
             }
-            // clear the property
-            char propName[100];
-            sprintf(propName, "hw.keyboards.%u.devname", device->id);
-            property_set(propName, NULL);
+            clearKeyboardProperties(device, false);
             return 0;
         }
     }
@@ -1009,7 +1136,11 @@
                 }
                 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());
+                dump.appendFormat(INDENT3 "KeyMapName: %s\n", device->keyMapName.string());
+                dump.appendFormat(INDENT3 "KeyLayoutFilename: %s\n",
+                        device->keyLayoutFilename.string());
+                dump.appendFormat(INDENT3 "KeyCharacterMapFilename: %s\n",
+                        device->keyCharacterMapFilename.string());
             }
         }
     } // release lock
diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp
index 84c3db8..93bde13 100644
--- a/libs/ui/InputDispatcher.cpp
+++ b/libs/ui/InputDispatcher.cpp
@@ -149,7 +149,8 @@
 
 bool InputWindow::isTrustedOverlay() const {
     return layoutParamsType == TYPE_INPUT_METHOD
-            || layoutParamsType == TYPE_INPUT_METHOD_DIALOG;
+            || layoutParamsType == TYPE_INPUT_METHOD_DIALOG
+            || layoutParamsType == TYPE_SECURE_SYSTEM_OVERLAY;
 }
 
 
@@ -1350,7 +1351,7 @@
         target.flags = 0;
         target.xOffset = 0;
         target.yOffset = 0;
-        target.windowType = InputWindow::TYPE_SYSTEM_OVERLAY;
+        target.windowType = -1;
     }
 }
 
@@ -2535,9 +2536,9 @@
 #if DEBUG_FOCUS
                 LOGD("Touched window was removed: %s", touchedWindow.channel->getName().string());
 #endif
-                mTouchState.windows.removeAt(i);
                 synthesizeCancelationEventsForInputChannelLocked(touchedWindow.channel,
                         InputState::CANCEL_POINTER_EVENTS, "touched window was removed");
+                mTouchState.windows.removeAt(i);
             }
         }
 
diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp
index 0560bb8..01ebda9 100644
--- a/libs/ui/InputReader.cpp
+++ b/libs/ui/InputReader.cpp
@@ -70,32 +70,15 @@
     return value ? "true" : "false";
 }
 
-
-int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState) {
-    int32_t mask;
-    switch (keyCode) {
-    case AKEYCODE_ALT_LEFT:
-        mask = AMETA_ALT_LEFT_ON;
-        break;
-    case AKEYCODE_ALT_RIGHT:
-        mask = AMETA_ALT_RIGHT_ON;
-        break;
-    case AKEYCODE_SHIFT_LEFT:
-        mask = AMETA_SHIFT_LEFT_ON;
-        break;
-    case AKEYCODE_SHIFT_RIGHT:
-        mask = AMETA_SHIFT_RIGHT_ON;
-        break;
-    case AKEYCODE_SYM:
-        mask = AMETA_SYM_ON;
-        break;
-    default:
-        return oldMetaState;
+int32_t setEphemeralMetaState(int32_t mask, bool down, int32_t oldMetaState) {
+    int32_t newMetaState;
+    if (down) {
+        newMetaState = oldMetaState | mask;
+    } else {
+        newMetaState = oldMetaState &
+                ~(mask | AMETA_ALT_ON | AMETA_SHIFT_ON | AMETA_CTRL_ON | AMETA_META_ON);
     }
 
-    int32_t newMetaState = down ? oldMetaState | mask : oldMetaState & ~ mask
-            & ~ (AMETA_ALT_ON | AMETA_SHIFT_ON);
-
     if (newMetaState & (AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON)) {
         newMetaState |= AMETA_ALT_ON;
     }
@@ -104,9 +87,58 @@
         newMetaState |= AMETA_SHIFT_ON;
     }
 
+    if (newMetaState & (AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON)) {
+        newMetaState |= AMETA_CTRL_ON;
+    }
+
+    if (newMetaState & (AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON)) {
+        newMetaState |= AMETA_META_ON;
+    }
     return newMetaState;
 }
 
+int32_t toggleLockedMetaState(int32_t mask, bool down, int32_t oldMetaState) {
+    if (down) {
+        return oldMetaState;
+    } else {
+        return oldMetaState ^ mask;
+    }
+}
+
+int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState) {
+    int32_t mask;
+    switch (keyCode) {
+    case AKEYCODE_ALT_LEFT:
+        return setEphemeralMetaState(AMETA_ALT_LEFT_ON, down, oldMetaState);
+    case AKEYCODE_ALT_RIGHT:
+        return setEphemeralMetaState(AMETA_ALT_RIGHT_ON, down, oldMetaState);
+    case AKEYCODE_SHIFT_LEFT:
+        return setEphemeralMetaState(AMETA_SHIFT_LEFT_ON, down, oldMetaState);
+    case AKEYCODE_SHIFT_RIGHT:
+        return setEphemeralMetaState(AMETA_SHIFT_RIGHT_ON, down, oldMetaState);
+    case AKEYCODE_SYM:
+        return setEphemeralMetaState(AMETA_SYM_ON, down, oldMetaState);
+    case AKEYCODE_FUNCTION:
+        return setEphemeralMetaState(AMETA_FUNCTION_ON, down, oldMetaState);
+    case AKEYCODE_CTRL_LEFT:
+        return setEphemeralMetaState(AMETA_CTRL_LEFT_ON, down, oldMetaState);
+    case AKEYCODE_CTRL_RIGHT:
+        return setEphemeralMetaState(AMETA_CTRL_RIGHT_ON, down, oldMetaState);
+    case AKEYCODE_META_LEFT:
+        return setEphemeralMetaState(AMETA_META_LEFT_ON, down, oldMetaState);
+    case AKEYCODE_META_RIGHT:
+        return setEphemeralMetaState(AMETA_META_RIGHT_ON, down, oldMetaState);
+    case AKEYCODE_CAPS_LOCK:
+        return toggleLockedMetaState(AMETA_CAPS_LOCK_LATCHED, down, oldMetaState);
+    case AKEYCODE_NUM_LOCK:
+        return toggleLockedMetaState(AMETA_NUM_LOCK_LATCHED, down, oldMetaState);
+    case AKEYCODE_SCROLL_LOCK:
+        return toggleLockedMetaState(AMETA_SCROLL_LOCK_LATCHED, down, oldMetaState);
+    default:
+        return oldMetaState;
+    }
+}
+
 static const int32_t keyCodeRotationMap[][4] = {
         // key codes enumerated counter-clockwise with the original (unrotated) key first
         // no rotation,        90 degree rotation,  180 degree rotation, 270 degree rotation
@@ -842,6 +874,17 @@
 void KeyboardInputMapper::initializeLocked() {
     mLocked.metaState = AMETA_NONE;
     mLocked.downTime = 0;
+
+    initializeLedStateLocked(mLocked.capsLockLedState, LED_CAPSL);
+    initializeLedStateLocked(mLocked.numLockLedState, LED_NUML);
+    initializeLedStateLocked(mLocked.scrollLockLedState, LED_SCROLLL);
+
+    updateLedStateLocked(true);
+}
+
+void KeyboardInputMapper::initializeLedStateLocked(LockedState::LedState& ledState, int32_t led) {
+    ledState.avail = getEventHub()->hasLed(getDeviceId(), led);
+    ledState.on = false;
 }
 
 uint32_t KeyboardInputMapper::getSources() {
@@ -966,6 +1009,7 @@
         if (oldMetaState != newMetaState) {
             mLocked.metaState = newMetaState;
             metaStateChanged = true;
+            updateLedStateLocked(false);
         }
 
         downTime = mLocked.downTime;
@@ -975,6 +1019,9 @@
         getContext()->updateGlobalMetaState();
     }
 
+    if (policyFlags & POLICY_FLAG_FUNCTION) {
+        newMetaState |= AMETA_FUNCTION_ON;
+    }
     getDispatcher()->notifyKey(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, policyFlags,
             down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
             AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, newMetaState, downTime);
@@ -1010,6 +1057,26 @@
     } // release lock
 }
 
+void KeyboardInputMapper::updateLedStateLocked(bool reset) {
+    updateLedStateForModifierLocked(mLocked.capsLockLedState, LED_CAPSL,
+            AMETA_CAPS_LOCK_LATCHED, reset);
+    updateLedStateForModifierLocked(mLocked.numLockLedState, LED_NUML,
+            AMETA_NUM_LOCK_LATCHED, reset);
+    updateLedStateForModifierLocked(mLocked.scrollLockLedState, LED_SCROLLL,
+            AMETA_SCROLL_LOCK_LATCHED, reset);
+}
+
+void KeyboardInputMapper::updateLedStateForModifierLocked(LockedState::LedState& ledState,
+        int32_t led, int32_t modifier, bool reset) {
+    if (ledState.avail) {
+        bool desiredState = (mLocked.metaState & modifier) != 0;
+        if (ledState.on != desiredState) {
+            getEventHub()->setLedState(getDeviceId(), led, desiredState);
+            ledState.on = desiredState;
+        }
+    }
+}
+
 
 // --- TrackballInputMapper ---
 
@@ -1246,14 +1313,14 @@
                     mLocked.orientedRanges.size);
         }
 
-        if (mLocked.orientedRanges.haveTouchArea) {
+        if (mLocked.orientedRanges.haveTouchSize) {
             info->addMotionRange(AINPUT_MOTION_RANGE_TOUCH_MAJOR,
                     mLocked.orientedRanges.touchMajor);
             info->addMotionRange(AINPUT_MOTION_RANGE_TOUCH_MINOR,
                     mLocked.orientedRanges.touchMinor);
         }
 
-        if (mLocked.orientedRanges.haveToolArea) {
+        if (mLocked.orientedRanges.haveToolSize) {
             info->addMotionRange(AINPUT_MOTION_RANGE_TOOL_MAJOR,
                     mLocked.orientedRanges.toolMajor);
             info->addMotionRange(AINPUT_MOTION_RANGE_TOOL_MINOR,
@@ -1277,8 +1344,21 @@
         dumpRawAxes(dump);
         dumpCalibration(dump);
         dumpSurfaceLocked(dump);
-        dump.appendFormat(INDENT3 "XPrecision: %0.3f\n", mLocked.xPrecision);
-        dump.appendFormat(INDENT3 "YPrecision: %0.3f\n", mLocked.yPrecision);
+        dump.appendFormat(INDENT3 "Translation and Scaling Factors:");
+        dump.appendFormat(INDENT4 "XOrigin: %d\n", mLocked.xOrigin);
+        dump.appendFormat(INDENT4 "YOrigin: %d\n", mLocked.yOrigin);
+        dump.appendFormat(INDENT4 "XScale: %0.3f\n", mLocked.xScale);
+        dump.appendFormat(INDENT4 "YScale: %0.3f\n", mLocked.yScale);
+        dump.appendFormat(INDENT4 "XPrecision: %0.3f\n", mLocked.xPrecision);
+        dump.appendFormat(INDENT4 "YPrecision: %0.3f\n", mLocked.yPrecision);
+        dump.appendFormat(INDENT4 "GeometricScale: %0.3f\n", mLocked.geometricScale);
+        dump.appendFormat(INDENT4 "ToolSizeLinearScale: %0.3f\n", mLocked.toolSizeLinearScale);
+        dump.appendFormat(INDENT4 "ToolSizeLinearBias: %0.3f\n", mLocked.toolSizeLinearBias);
+        dump.appendFormat(INDENT4 "ToolSizeAreaScale: %0.3f\n", mLocked.toolSizeAreaScale);
+        dump.appendFormat(INDENT4 "ToolSizeAreaBias: %0.3f\n", mLocked.toolSizeAreaBias);
+        dump.appendFormat(INDENT4 "PressureScale: %0.3f\n", mLocked.pressureScale);
+        dump.appendFormat(INDENT4 "SizeScale: %0.3f\n", mLocked.sizeScale);
+        dump.appendFormat(INDENT4 "OrientationSCale: %0.3f\n", mLocked.orientationScale);
     } // release lock
 }
 
@@ -1298,8 +1378,8 @@
 
     mLocked.orientedRanges.havePressure = false;
     mLocked.orientedRanges.haveSize = false;
-    mLocked.orientedRanges.haveTouchArea = false;
-    mLocked.orientedRanges.haveToolArea = false;
+    mLocked.orientedRanges.haveTouchSize = false;
+    mLocked.orientedRanges.haveToolSize = false;
     mLocked.orientedRanges.haveOrientation = false;
 }
 
@@ -1428,8 +1508,8 @@
         float diagonalSize = pythag(width, height);
 
         // TouchMajor and TouchMinor factors.
-        if (mCalibration.touchAreaCalibration != Calibration::TOUCH_AREA_CALIBRATION_NONE) {
-            mLocked.orientedRanges.haveTouchArea = true;
+        if (mCalibration.touchSizeCalibration != Calibration::TOUCH_SIZE_CALIBRATION_NONE) {
+            mLocked.orientedRanges.haveTouchSize = true;
             mLocked.orientedRanges.touchMajor.min = 0;
             mLocked.orientedRanges.touchMajor.max = diagonalSize;
             mLocked.orientedRanges.touchMajor.flat = 0;
@@ -1438,23 +1518,46 @@
         }
 
         // ToolMajor and ToolMinor factors.
-        if (mCalibration.toolAreaCalibration != Calibration::TOOL_AREA_CALIBRATION_NONE) {
-            mLocked.toolAreaLinearScale = 0;
-            mLocked.toolAreaLinearBias = 0;
-            if (mCalibration.toolAreaCalibration == Calibration::TOOL_AREA_CALIBRATION_LINEAR) {
-                if (mCalibration.haveToolAreaLinearScale) {
-                    mLocked.toolAreaLinearScale = mCalibration.toolAreaLinearScale;
+        mLocked.toolSizeLinearScale = 0;
+        mLocked.toolSizeLinearBias = 0;
+        mLocked.toolSizeAreaScale = 0;
+        mLocked.toolSizeAreaBias = 0;
+        if (mCalibration.toolSizeCalibration != Calibration::TOOL_SIZE_CALIBRATION_NONE) {
+            if (mCalibration.toolSizeCalibration == Calibration::TOOL_SIZE_CALIBRATION_LINEAR) {
+                if (mCalibration.haveToolSizeLinearScale) {
+                    mLocked.toolSizeLinearScale = mCalibration.toolSizeLinearScale;
                 } else if (mRawAxes.toolMajor.valid && mRawAxes.toolMajor.maxValue != 0) {
-                    mLocked.toolAreaLinearScale = float(min(width, height))
+                    mLocked.toolSizeLinearScale = float(min(width, height))
                             / mRawAxes.toolMajor.maxValue;
                 }
 
-                if (mCalibration.haveToolAreaLinearBias) {
-                    mLocked.toolAreaLinearBias = mCalibration.toolAreaLinearBias;
+                if (mCalibration.haveToolSizeLinearBias) {
+                    mLocked.toolSizeLinearBias = mCalibration.toolSizeLinearBias;
+                }
+            } else if (mCalibration.toolSizeCalibration ==
+                    Calibration::TOOL_SIZE_CALIBRATION_AREA) {
+                if (mCalibration.haveToolSizeLinearScale) {
+                    mLocked.toolSizeLinearScale = mCalibration.toolSizeLinearScale;
+                } else {
+                    mLocked.toolSizeLinearScale = min(width, height);
+                }
+
+                if (mCalibration.haveToolSizeLinearBias) {
+                    mLocked.toolSizeLinearBias = mCalibration.toolSizeLinearBias;
+                }
+
+                if (mCalibration.haveToolSizeAreaScale) {
+                    mLocked.toolSizeAreaScale = mCalibration.toolSizeAreaScale;
+                } else if (mRawAxes.toolMajor.valid && mRawAxes.toolMajor.maxValue != 0) {
+                    mLocked.toolSizeAreaScale = 1.0f / mRawAxes.toolMajor.maxValue;
+                }
+
+                if (mCalibration.haveToolSizeAreaBias) {
+                    mLocked.toolSizeAreaBias = mCalibration.toolSizeAreaBias;
                 }
             }
 
-            mLocked.orientedRanges.haveToolArea = true;
+            mLocked.orientedRanges.haveToolSize = true;
             mLocked.orientedRanges.toolMajor.min = 0;
             mLocked.orientedRanges.toolMajor.max = diagonalSize;
             mLocked.orientedRanges.toolMajor.flat = 0;
@@ -1463,6 +1566,7 @@
         }
 
         // Pressure factors.
+        mLocked.pressureScale = 0;
         if (mCalibration.pressureCalibration != Calibration::PRESSURE_CALIBRATION_NONE) {
             RawAbsoluteAxisInfo rawPressureAxis;
             switch (mCalibration.pressureSource) {
@@ -1476,7 +1580,6 @@
                 rawPressureAxis.clear();
             }
 
-            mLocked.pressureScale = 0;
             if (mCalibration.pressureCalibration == Calibration::PRESSURE_CALIBRATION_PHYSICAL
                     || mCalibration.pressureCalibration
                             == Calibration::PRESSURE_CALIBRATION_AMPLITUDE) {
@@ -1495,8 +1598,8 @@
         }
 
         // Size factors.
+        mLocked.sizeScale = 0;
         if (mCalibration.sizeCalibration != Calibration::SIZE_CALIBRATION_NONE) {
-            mLocked.sizeScale = 0;
             if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_NORMALIZED) {
                 if (mRawAxes.toolMajor.valid && mRawAxes.toolMajor.maxValue != 0) {
                     mLocked.sizeScale = 1.0f / mRawAxes.toolMajor.maxValue;
@@ -1511,8 +1614,8 @@
         }
 
         // Orientation
+        mLocked.orientationScale = 0;
         if (mCalibration.orientationCalibration != Calibration::ORIENTATION_CALIBRATION_NONE) {
-            mLocked.orientationScale = 0;
             if (mCalibration.orientationCalibration
                     == Calibration::ORIENTATION_CALIBRATION_INTERPOLATED) {
                 if (mRawAxes.orientation.valid && mRawAxes.orientation.maxValue != 0) {
@@ -1647,49 +1750,55 @@
     const InputDeviceCalibration& in = getDevice()->getCalibration();
     Calibration& out = mCalibration;
 
-    // Touch Area
-    out.touchAreaCalibration = Calibration::TOUCH_AREA_CALIBRATION_DEFAULT;
-    String8 touchAreaCalibrationString;
-    if (in.tryGetProperty(String8("touch.touchArea.calibration"), touchAreaCalibrationString)) {
-        if (touchAreaCalibrationString == "none") {
-            out.touchAreaCalibration = Calibration::TOUCH_AREA_CALIBRATION_NONE;
-        } else if (touchAreaCalibrationString == "geometric") {
-            out.touchAreaCalibration = Calibration::TOUCH_AREA_CALIBRATION_GEOMETRIC;
-        } else if (touchAreaCalibrationString == "pressure") {
-            out.touchAreaCalibration = Calibration::TOUCH_AREA_CALIBRATION_PRESSURE;
-        } else if (touchAreaCalibrationString != "default") {
-            LOGW("Invalid value for touch.touchArea.calibration: '%s'",
-                    touchAreaCalibrationString.string());
+    // Touch Size
+    out.touchSizeCalibration = Calibration::TOUCH_SIZE_CALIBRATION_DEFAULT;
+    String8 touchSizeCalibrationString;
+    if (in.tryGetProperty(String8("touch.touchSize.calibration"), touchSizeCalibrationString)) {
+        if (touchSizeCalibrationString == "none") {
+            out.touchSizeCalibration = Calibration::TOUCH_SIZE_CALIBRATION_NONE;
+        } else if (touchSizeCalibrationString == "geometric") {
+            out.touchSizeCalibration = Calibration::TOUCH_SIZE_CALIBRATION_GEOMETRIC;
+        } else if (touchSizeCalibrationString == "pressure") {
+            out.touchSizeCalibration = Calibration::TOUCH_SIZE_CALIBRATION_PRESSURE;
+        } else if (touchSizeCalibrationString != "default") {
+            LOGW("Invalid value for touch.touchSize.calibration: '%s'",
+                    touchSizeCalibrationString.string());
         }
     }
 
-    // Tool Area
-    out.toolAreaCalibration = Calibration::TOOL_AREA_CALIBRATION_DEFAULT;
-    String8 toolAreaCalibrationString;
-    if (in.tryGetProperty(String8("tool.toolArea.calibration"), toolAreaCalibrationString)) {
-        if (toolAreaCalibrationString == "none") {
-            out.toolAreaCalibration = Calibration::TOOL_AREA_CALIBRATION_NONE;
-        } else if (toolAreaCalibrationString == "geometric") {
-            out.toolAreaCalibration = Calibration::TOOL_AREA_CALIBRATION_GEOMETRIC;
-        } else if (toolAreaCalibrationString == "linear") {
-            out.toolAreaCalibration = Calibration::TOOL_AREA_CALIBRATION_LINEAR;
-        } else if (toolAreaCalibrationString != "default") {
-            LOGW("Invalid value for tool.toolArea.calibration: '%s'",
-                    toolAreaCalibrationString.string());
+    // Tool Size
+    out.toolSizeCalibration = Calibration::TOOL_SIZE_CALIBRATION_DEFAULT;
+    String8 toolSizeCalibrationString;
+    if (in.tryGetProperty(String8("touch.toolSize.calibration"), toolSizeCalibrationString)) {
+        if (toolSizeCalibrationString == "none") {
+            out.toolSizeCalibration = Calibration::TOOL_SIZE_CALIBRATION_NONE;
+        } else if (toolSizeCalibrationString == "geometric") {
+            out.toolSizeCalibration = Calibration::TOOL_SIZE_CALIBRATION_GEOMETRIC;
+        } else if (toolSizeCalibrationString == "linear") {
+            out.toolSizeCalibration = Calibration::TOOL_SIZE_CALIBRATION_LINEAR;
+        } else if (toolSizeCalibrationString == "area") {
+            out.toolSizeCalibration = Calibration::TOOL_SIZE_CALIBRATION_AREA;
+        } else if (toolSizeCalibrationString != "default") {
+            LOGW("Invalid value for touch.toolSize.calibration: '%s'",
+                    toolSizeCalibrationString.string());
         }
     }
 
-    out.haveToolAreaLinearScale = in.tryGetProperty(String8("touch.toolArea.linearScale"),
-            out.toolAreaLinearScale);
-    out.haveToolAreaLinearBias = in.tryGetProperty(String8("touch.toolArea.linearBias"),
-            out.toolAreaLinearBias);
-    out.haveToolAreaIsSummed = in.tryGetProperty(String8("touch.toolArea.isSummed"),
-            out.toolAreaIsSummed);
+    out.haveToolSizeLinearScale = in.tryGetProperty(String8("touch.toolSize.linearScale"),
+            out.toolSizeLinearScale);
+    out.haveToolSizeLinearBias = in.tryGetProperty(String8("touch.toolSize.linearBias"),
+            out.toolSizeLinearBias);
+    out.haveToolSizeAreaScale = in.tryGetProperty(String8("touch.toolSize.areaScale"),
+            out.toolSizeAreaScale);
+    out.haveToolSizeAreaBias = in.tryGetProperty(String8("touch.toolSize.areaBias"),
+            out.toolSizeAreaBias);
+    out.haveToolSizeIsSummed = in.tryGetProperty(String8("touch.toolSize.isSummed"),
+            out.toolSizeIsSummed);
 
     // Pressure
     out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_DEFAULT;
     String8 pressureCalibrationString;
-    if (in.tryGetProperty(String8("tool.pressure.calibration"), pressureCalibrationString)) {
+    if (in.tryGetProperty(String8("touch.pressure.calibration"), pressureCalibrationString)) {
         if (pressureCalibrationString == "none") {
             out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_NONE;
         } else if (pressureCalibrationString == "physical") {
@@ -1697,7 +1806,7 @@
         } else if (pressureCalibrationString == "amplitude") {
             out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_AMPLITUDE;
         } else if (pressureCalibrationString != "default") {
-            LOGW("Invalid value for tool.pressure.calibration: '%s'",
+            LOGW("Invalid value for touch.pressure.calibration: '%s'",
                     pressureCalibrationString.string());
         }
     }
@@ -1721,13 +1830,13 @@
     // Size
     out.sizeCalibration = Calibration::SIZE_CALIBRATION_DEFAULT;
     String8 sizeCalibrationString;
-    if (in.tryGetProperty(String8("tool.size.calibration"), sizeCalibrationString)) {
+    if (in.tryGetProperty(String8("touch.size.calibration"), sizeCalibrationString)) {
         if (sizeCalibrationString == "none") {
             out.sizeCalibration = Calibration::SIZE_CALIBRATION_NONE;
         } else if (sizeCalibrationString == "normalized") {
             out.sizeCalibration = Calibration::SIZE_CALIBRATION_NORMALIZED;
         } else if (sizeCalibrationString != "default") {
-            LOGW("Invalid value for tool.size.calibration: '%s'",
+            LOGW("Invalid value for touch.size.calibration: '%s'",
                     sizeCalibrationString.string());
         }
     }
@@ -1735,13 +1844,13 @@
     // Orientation
     out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_DEFAULT;
     String8 orientationCalibrationString;
-    if (in.tryGetProperty(String8("tool.orientation.calibration"), orientationCalibrationString)) {
+    if (in.tryGetProperty(String8("touch.orientation.calibration"), orientationCalibrationString)) {
         if (orientationCalibrationString == "none") {
             out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_NONE;
         } else if (orientationCalibrationString == "interpolated") {
             out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_INTERPOLATED;
         } else if (orientationCalibrationString != "default") {
-            LOGW("Invalid value for tool.orientation.calibration: '%s'",
+            LOGW("Invalid value for touch.orientation.calibration: '%s'",
                     orientationCalibrationString.string());
         }
     }
@@ -1789,13 +1898,13 @@
         break;
     }
 
-    // Tool Area
-    switch (mCalibration.toolAreaCalibration) {
-    case Calibration::TOOL_AREA_CALIBRATION_DEFAULT:
+    // Tool Size
+    switch (mCalibration.toolSizeCalibration) {
+    case Calibration::TOOL_SIZE_CALIBRATION_DEFAULT:
         if (mRawAxes.toolMajor.valid) {
-            mCalibration.toolAreaCalibration = Calibration::TOOL_AREA_CALIBRATION_LINEAR;
+            mCalibration.toolSizeCalibration = Calibration::TOOL_SIZE_CALIBRATION_LINEAR;
         } else {
-            mCalibration.toolAreaCalibration = Calibration::TOOL_AREA_CALIBRATION_NONE;
+            mCalibration.toolSizeCalibration = Calibration::TOOL_SIZE_CALIBRATION_NONE;
         }
         break;
 
@@ -1803,14 +1912,14 @@
         break;
     }
 
-    // Touch Area
-    switch (mCalibration.touchAreaCalibration) {
-    case Calibration::TOUCH_AREA_CALIBRATION_DEFAULT:
+    // Touch Size
+    switch (mCalibration.touchSizeCalibration) {
+    case Calibration::TOUCH_SIZE_CALIBRATION_DEFAULT:
         if (mCalibration.pressureCalibration != Calibration::PRESSURE_CALIBRATION_NONE
-                && mCalibration.toolAreaCalibration != Calibration::TOOL_AREA_CALIBRATION_NONE) {
-            mCalibration.touchAreaCalibration = Calibration::TOUCH_AREA_CALIBRATION_PRESSURE;
+                && mCalibration.toolSizeCalibration != Calibration::TOOL_SIZE_CALIBRATION_NONE) {
+            mCalibration.touchSizeCalibration = Calibration::TOUCH_SIZE_CALIBRATION_PRESSURE;
         } else {
-            mCalibration.touchAreaCalibration = Calibration::TOUCH_AREA_CALIBRATION_NONE;
+            mCalibration.touchSizeCalibration = Calibration::TOUCH_SIZE_CALIBRATION_NONE;
         }
         break;
 
@@ -1850,49 +1959,62 @@
 void TouchInputMapper::dumpCalibration(String8& dump) {
     dump.append(INDENT3 "Calibration:\n");
 
-    // Touch Area
-    switch (mCalibration.touchAreaCalibration) {
-    case Calibration::TOUCH_AREA_CALIBRATION_NONE:
-        dump.append(INDENT4 "touch.touchArea.calibration: none\n");
+    // Touch Size
+    switch (mCalibration.touchSizeCalibration) {
+    case Calibration::TOUCH_SIZE_CALIBRATION_NONE:
+        dump.append(INDENT4 "touch.touchSize.calibration: none\n");
         break;
-    case Calibration::TOUCH_AREA_CALIBRATION_GEOMETRIC:
-        dump.append(INDENT4 "touch.touchArea.calibration: geometric\n");
+    case Calibration::TOUCH_SIZE_CALIBRATION_GEOMETRIC:
+        dump.append(INDENT4 "touch.touchSize.calibration: geometric\n");
         break;
-    case Calibration::TOUCH_AREA_CALIBRATION_PRESSURE:
-        dump.append(INDENT4 "touch.touchArea.calibration: pressure\n");
+    case Calibration::TOUCH_SIZE_CALIBRATION_PRESSURE:
+        dump.append(INDENT4 "touch.touchSize.calibration: pressure\n");
         break;
     default:
         assert(false);
     }
 
-    // Tool Area
-    switch (mCalibration.toolAreaCalibration) {
-    case Calibration::TOOL_AREA_CALIBRATION_NONE:
-        dump.append(INDENT4 "touch.toolArea.calibration: none\n");
+    // Tool Size
+    switch (mCalibration.toolSizeCalibration) {
+    case Calibration::TOOL_SIZE_CALIBRATION_NONE:
+        dump.append(INDENT4 "touch.toolSize.calibration: none\n");
         break;
-    case Calibration::TOOL_AREA_CALIBRATION_GEOMETRIC:
-        dump.append(INDENT4 "touch.toolArea.calibration: geometric\n");
+    case Calibration::TOOL_SIZE_CALIBRATION_GEOMETRIC:
+        dump.append(INDENT4 "touch.toolSize.calibration: geometric\n");
         break;
-    case Calibration::TOOL_AREA_CALIBRATION_LINEAR:
-        dump.append(INDENT4 "touch.toolArea.calibration: linear\n");
+    case Calibration::TOOL_SIZE_CALIBRATION_LINEAR:
+        dump.append(INDENT4 "touch.toolSize.calibration: linear\n");
+        break;
+    case Calibration::TOOL_SIZE_CALIBRATION_AREA:
+        dump.append(INDENT4 "touch.toolSize.calibration: area\n");
         break;
     default:
         assert(false);
     }
 
-    if (mCalibration.haveToolAreaLinearScale) {
-        dump.appendFormat(INDENT4 "touch.toolArea.linearScale: %0.3f\n",
-                mCalibration.toolAreaLinearScale);
+    if (mCalibration.haveToolSizeLinearScale) {
+        dump.appendFormat(INDENT4 "touch.toolSize.linearScale: %0.3f\n",
+                mCalibration.toolSizeLinearScale);
     }
 
-    if (mCalibration.haveToolAreaLinearBias) {
-        dump.appendFormat(INDENT4 "touch.toolArea.linearBias: %0.3f\n",
-                mCalibration.toolAreaLinearBias);
+    if (mCalibration.haveToolSizeLinearBias) {
+        dump.appendFormat(INDENT4 "touch.toolSize.linearBias: %0.3f\n",
+                mCalibration.toolSizeLinearBias);
     }
 
-    if (mCalibration.haveToolAreaIsSummed) {
-        dump.appendFormat(INDENT4 "touch.toolArea.isSummed: %d\n",
-                mCalibration.toolAreaIsSummed);
+    if (mCalibration.haveToolSizeAreaScale) {
+        dump.appendFormat(INDENT4 "touch.toolSize.areaScale: %0.3f\n",
+                mCalibration.toolSizeAreaScale);
+    }
+
+    if (mCalibration.haveToolSizeAreaBias) {
+        dump.appendFormat(INDENT4 "touch.toolSize.areaBias: %0.3f\n",
+                mCalibration.toolSizeAreaBias);
+    }
+
+    if (mCalibration.haveToolSizeIsSummed) {
+        dump.appendFormat(INDENT4 "touch.toolSize.isSummed: %d\n",
+                mCalibration.toolSizeIsSummed);
     }
 
     // Pressure
@@ -2207,8 +2329,8 @@
 
             // ToolMajor and ToolMinor
             float toolMajor, toolMinor;
-            switch (mCalibration.toolAreaCalibration) {
-            case Calibration::TOOL_AREA_CALIBRATION_GEOMETRIC:
+            switch (mCalibration.toolSizeCalibration) {
+            case Calibration::TOOL_SIZE_CALIBRATION_GEOMETRIC:
                 toolMajor = in.toolMajor * mLocked.geometricScale;
                 if (mRawAxes.toolMinor.valid) {
                     toolMinor = in.toolMinor * mLocked.geometricScale;
@@ -2216,26 +2338,36 @@
                     toolMinor = toolMajor;
                 }
                 break;
-            case Calibration::TOOL_AREA_CALIBRATION_LINEAR:
+            case Calibration::TOOL_SIZE_CALIBRATION_LINEAR:
                 toolMajor = in.toolMajor != 0
-                        ? in.toolMajor * mLocked.toolAreaLinearScale + mLocked.toolAreaLinearBias
+                        ? in.toolMajor * mLocked.toolSizeLinearScale + mLocked.toolSizeLinearBias
                         : 0;
                 if (mRawAxes.toolMinor.valid) {
                     toolMinor = in.toolMinor != 0
-                            ? in.toolMinor * mLocked.toolAreaLinearScale
-                                    + mLocked.toolAreaLinearBias
+                            ? in.toolMinor * mLocked.toolSizeLinearScale
+                                    + mLocked.toolSizeLinearBias
                             : 0;
                 } else {
                     toolMinor = toolMajor;
                 }
                 break;
+            case Calibration::TOOL_SIZE_CALIBRATION_AREA:
+                if (in.toolMajor != 0) {
+                    float diameter = sqrtf(in.toolMajor
+                            * mLocked.toolSizeAreaScale + mLocked.toolSizeAreaBias);
+                    toolMajor = diameter * mLocked.toolSizeLinearScale + mLocked.toolSizeLinearBias;
+                } else {
+                    toolMajor = 0;
+                }
+                toolMinor = toolMajor;
+                break;
             default:
                 toolMajor = 0;
                 toolMinor = 0;
                 break;
             }
 
-            if (mCalibration.haveToolAreaIsSummed && mCalibration.toolAreaIsSummed) {
+            if (mCalibration.haveToolSizeIsSummed && mCalibration.toolSizeIsSummed) {
                 toolMajor /= pointerCount;
                 toolMinor /= pointerCount;
             }
@@ -2266,8 +2398,8 @@
 
             // TouchMajor and TouchMinor
             float touchMajor, touchMinor;
-            switch (mCalibration.touchAreaCalibration) {
-            case Calibration::TOUCH_AREA_CALIBRATION_GEOMETRIC:
+            switch (mCalibration.touchSizeCalibration) {
+            case Calibration::TOUCH_SIZE_CALIBRATION_GEOMETRIC:
                 touchMajor = in.touchMajor * mLocked.geometricScale;
                 if (mRawAxes.touchMinor.valid) {
                     touchMinor = in.touchMinor * mLocked.geometricScale;
@@ -2275,7 +2407,7 @@
                     touchMinor = touchMajor;
                 }
                 break;
-            case Calibration::TOUCH_AREA_CALIBRATION_PRESSURE:
+            case Calibration::TOUCH_SIZE_CALIBRATION_PRESSURE:
                 touchMajor = toolMajor * pressure;
                 touchMinor = toolMinor * pressure;
                 break;
diff --git a/libs/ui/KeyCharacterMap.cpp b/libs/ui/KeyCharacterMap.cpp
index e891181..870a45c 100644
--- a/libs/ui/KeyCharacterMap.cpp
+++ b/libs/ui/KeyCharacterMap.cpp
@@ -156,26 +156,38 @@
 KeyCharacterMap*
 KeyCharacterMap::load(int id)
 {
-    KeyCharacterMap* rv = NULL;
+    KeyCharacterMap* map;
     char path[PATH_MAX];
     char propName[100];
     char dev[PROPERTY_VALUE_MAX];
-    char tmpfn[PROPERTY_VALUE_MAX];
+    char fn[PROPERTY_VALUE_MAX];
     int err;
+
+    // Check whether the EventHub has set a key character map filename for us already.
+    sprintf(propName, "hw.keyboards.%u.kcmfile", id);
+    err = property_get(propName, fn, "");
+    if (err > 0) {
+        map = try_file(fn);
+        if (map) {
+            return map;
+        }
+        LOGW("Error loading keycharmap file '%s'. %s='%s'", path, propName, fn);
+    }
+
+    // Try using the device name.
     const char* root = getenv("ANDROID_ROOT");
 
     sprintf(propName, "hw.keyboards.%u.devname", id);
     err = property_get(propName, dev, "");
     if (err > 0) {
         // replace all the spaces with underscores
-        strcpy(tmpfn, dev);
-        for (char *p = strchr(tmpfn, ' '); p && *p; p = strchr(tmpfn, ' '))
+        strcpy(fn, dev);
+        for (char *p = strchr(fn, ' '); p && *p; p = strchr(p + 1, ' '))
             *p = '_';
-        snprintf(path, sizeof(path), "%s/usr/keychars/%s.kcm.bin", root, tmpfn);
-        //LOGD("load: dev='%s' path='%s'\n", dev, path);
-        rv = try_file(path);
-        if (rv != NULL) {
-            return rv;
+        snprintf(path, sizeof(path), "%s/usr/keychars/%s.kcm.bin", root, fn);
+        map = try_file(path);
+        if (map) {
+            return map;
         }
         LOGW("Error loading keycharmap file '%s'. %s='%s'", path, propName, dev);
     } else {
@@ -183,14 +195,14 @@
     }
 
     snprintf(path, sizeof(path), "%s/usr/keychars/qwerty.kcm.bin", root);
-    rv = try_file(path);
-    if (rv == NULL) {
-        LOGE("Can't find any keycharmaps (also tried %s)", path);
-        return NULL;
+    map = try_file(path);
+    if (map) {
+        LOGW("Using default keymap: %s", path);
+        return map;
     }
-    LOGW("Using default keymap: %s", path);
 
-    return rv;
+    LOGE("Can't find any keycharmaps (also tried %s)", path);
+    return NULL;
 }
 
 KeyCharacterMap*
diff --git a/libs/utils/ObbFile.cpp b/libs/utils/ObbFile.cpp
index e170ab8..2c3724c 100644
--- a/libs/utils/ObbFile.cpp
+++ b/libs/utils/ObbFile.cpp
@@ -29,10 +29,11 @@
 
 #define kFooterTagSize 8  /* last two 32-bit integers */
 
-#define kFooterMinSize 25 /* 32-bit signature version (4 bytes)
+#define kFooterMinSize 33 /* 32-bit signature version (4 bytes)
                            * 32-bit package version (4 bytes)
                            * 32-bit flags (4 bytes)
-                           * 32-bit package name size (4-bytes)
+                           * 64-bit salt (8 bytes)
+                           * 32-bit package name size (4 bytes)
                            * >=1-character package name (1 byte)
                            * 32-bit footer size (4 bytes)
                            * 32-bit footer marker (4 bytes)
@@ -47,8 +48,9 @@
 /* offsets in version 1 of the header */
 #define kPackageVersionOffset 4
 #define kFlagsOffset          8
-#define kPackageNameLenOffset 12
-#define kPackageNameOffset    16
+#define kSaltOffset           12
+#define kPackageNameLenOffset 20
+#define kPackageNameOffset    24
 
 /*
  * TEMP_FAILURE_RETRY is defined by some, but not all, versions of
@@ -79,11 +81,12 @@
 
 namespace android {
 
-ObbFile::ObbFile() :
-        mPackageName(""),
-        mVersion(-1),
-        mFlags(0)
+ObbFile::ObbFile()
+        : mPackageName("")
+        , mVersion(-1)
+        , mFlags(0)
 {
+    memset(mSalt, 0, sizeof(mSalt));
 }
 
 ObbFile::~ObbFile() {
@@ -192,7 +195,7 @@
 
 #ifdef DEBUG
     for (int i = 0; i < footerSize; ++i) {
-        LOGI("char: 0x%02x", scanBuf[i]);
+        LOGI("char: 0x%02x\n", scanBuf[i]);
     }
 #endif
 
@@ -206,6 +209,8 @@
     mVersion = (int32_t) get4LE((unsigned char*)scanBuf + kPackageVersionOffset);
     mFlags = (int32_t) get4LE((unsigned char*)scanBuf + kFlagsOffset);
 
+    memcpy(&mSalt, (unsigned char*)scanBuf + kSaltOffset, sizeof(mSalt));
+
     uint32_t packageNameLen = get4LE((unsigned char*)scanBuf + kPackageNameLenOffset);
     if (packageNameLen <= 0
             || packageNameLen > (footerSize - kPackageNameOffset)) {
@@ -255,7 +260,7 @@
     my_lseek64(fd, 0, SEEK_END);
 
     if (mPackageName.size() == 0 || mVersion == -1) {
-        LOGW("tried to write uninitialized ObbFile data");
+        LOGW("tried to write uninitialized ObbFile data\n");
         return false;
     }
 
@@ -264,43 +269,48 @@
 
     put4LE(intBuf, kSigVersion);
     if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) {
-        LOGW("couldn't write signature version: %s", strerror(errno));
+        LOGW("couldn't write signature version: %s\n", strerror(errno));
         return false;
     }
 
     put4LE(intBuf, mVersion);
     if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) {
-        LOGW("couldn't write package version");
+        LOGW("couldn't write package version\n");
         return false;
     }
 
     put4LE(intBuf, mFlags);
     if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) {
-        LOGW("couldn't write package version");
+        LOGW("couldn't write package version\n");
+        return false;
+    }
+
+    if (write(fd, mSalt, sizeof(mSalt)) != (ssize_t)sizeof(mSalt)) {
+        LOGW("couldn't write salt: %s\n", strerror(errno));
         return false;
     }
 
     size_t packageNameLen = mPackageName.size();
     put4LE(intBuf, packageNameLen);
     if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) {
-        LOGW("couldn't write package name length: %s", strerror(errno));
+        LOGW("couldn't write package name length: %s\n", strerror(errno));
         return false;
     }
 
     if (write(fd, mPackageName.string(), packageNameLen) != (ssize_t)packageNameLen) {
-        LOGW("couldn't write package name: %s", strerror(errno));
+        LOGW("couldn't write package name: %s\n", strerror(errno));
         return false;
     }
 
     put4LE(intBuf, kPackageNameOffset + packageNameLen);
     if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) {
-        LOGW("couldn't write footer size: %s", strerror(errno));
+        LOGW("couldn't write footer size: %s\n", strerror(errno));
         return false;
     }
 
     put4LE(intBuf, kSignature);
     if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) {
-        LOGW("couldn't write footer magic signature: %s", strerror(errno));
+        LOGW("couldn't write footer magic signature: %s\n", strerror(errno));
         return false;
     }
 
diff --git a/libs/utils/String8.cpp b/libs/utils/String8.cpp
index 1c4f80c..6358fc4 100644
--- a/libs/utils/String8.cpp
+++ b/libs/utils/String8.cpp
@@ -292,6 +292,11 @@
     SharedBuffer::bufferFromData(mString)->release();
 }
 
+void String8::clear() {
+    SharedBuffer::bufferFromData(mString)->release();
+    mString = getEmptyString();
+}
+
 void String8::setTo(const String8& other)
 {
     SharedBuffer::bufferFromData(other.mString)->acquire();
diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp
index 9b1f82f..5ff1f8f 100644
--- a/libs/utils/ZipFileRO.cpp
+++ b/libs/utils/ZipFileRO.cpp
@@ -32,6 +32,22 @@
 #include <assert.h>
 #include <unistd.h>
 
+#if HAVE_PRINTF_ZD
+#  define ZD "%zd"
+#  define ZD_TYPE ssize_t
+#else
+#  define ZD "%ld"
+#  define ZD_TYPE long
+#endif
+
+/*
+ * We must open binary files using open(path, ... | O_BINARY) under Windows.
+ * Otherwise strange read errors will happen.
+ */
+#ifndef O_BINARY
+#  define O_BINARY  0
+#endif
+
 /*
  * TEMP_FAILURE_RETRY is defined by some, but not all, versions of
  * <unistd.h>. (Alas, it is not as standard as we'd hoped!) So, if it's
@@ -124,7 +140,7 @@
     /*
      * Open and map the specified file.
      */
-    fd = ::open(zipFileName, O_RDONLY);
+    fd = ::open(zipFileName, O_RDONLY | O_BINARY);
     if (fd < 0) {
         LOGW("Unable to open zip '%s': %s\n", zipFileName, strerror(errno));
         return NAME_NOT_FOUND;
@@ -172,8 +188,8 @@
  */
 bool ZipFileRO::mapCentralDirectory(void)
 {
-    size_t readAmount = kMaxEOCDSearch;
-    if (readAmount > (size_t) mFileLength)
+    ssize_t readAmount = kMaxEOCDSearch;
+    if (readAmount > (ssize_t) mFileLength)
         readAmount = mFileLength;
 
     unsigned char* scanBuf = (unsigned char*) malloc(readAmount);
@@ -233,7 +249,8 @@
     }
     actual = TEMP_FAILURE_RETRY(read(mFd, scanBuf, readAmount));
     if (actual != (ssize_t) readAmount) {
-        LOGW("Zip: read %zd failed: %s\n", readAmount, strerror(errno));
+        LOGW("Zip: read " ZD ", expected " ZD ". Failed: %s\n",
+            (ZD_TYPE) actual, (ZD_TYPE) readAmount, strerror(errno));
         free(scanBuf);
         return false;
     }
@@ -292,8 +309,8 @@
     }
 
     if (!mDirectoryMap->create(mFileName, mFd, dirOffset, dirSize, true)) {
-        LOGW("Unable to map '%s' (%zd to %zd): %s\n", mFileName,
-                dirOffset, dirOffset + dirSize, strerror(errno));
+        LOGW("Unable to map '%s' (" ZD " to " ZD "): %s\n", mFileName,
+                (ZD_TYPE) dirOffset, (ZD_TYPE) (dirOffset + dirSize), strerror(errno));
         return false;
     }
 
@@ -350,8 +367,8 @@
 
         ptr += kCDELen + fileNameLen + extraLen + commentLen;
         if ((size_t)(ptr - cdPtr) > cdLength) {
-            LOGW("bad CD advance (%d vs %zd) at entry %d\n",
-                (int) (ptr - cdPtr), cdLength, i);
+            LOGW("bad CD advance (%d vs " ZD ") at entry %d\n",
+                (int) (ptr - cdPtr), (ZD_TYPE) cdLength, i);
             goto bail;
         }
     }
@@ -556,8 +573,8 @@
             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));
+                        "got: offset=" ZD " data=0x%08lx\n",
+                        localHdrOffset, kLFHSignature, (ZD_TYPE) actualOffset, get4LE(lfhBuf));
                 return false;
             }
         }
@@ -572,16 +589,16 @@
 
         /* check lengths */
         if ((off_t)(dataOffset + compLen) > cdOffset) {
-            LOGW("bad compressed length in zip (%ld + %zd > %ld)\n",
-                (long) dataOffset, compLen, (long) cdOffset);
+            LOGW("bad compressed length in zip (%ld + " ZD " > %ld)\n",
+                (long) dataOffset, (ZD_TYPE) compLen, (long) cdOffset);
             return false;
         }
 
         if (method == kCompressStored &&
             (off_t)(dataOffset + uncompLen) > cdOffset)
         {
-            LOGE("ERROR: bad uncompressed length in zip (%ld + %zd > %ld)\n",
-                (long) dataOffset, uncompLen, (long) cdOffset);
+            LOGE("ERROR: bad uncompressed length in zip (%ld + " ZD " > %ld)\n",
+                (long) dataOffset, (ZD_TYPE) uncompLen, (long) cdOffset);
             return false;
         }
 
@@ -732,8 +749,8 @@
             LOGE("Write failed: %s\n", strerror(errno));
             goto unmap;
         } else if ((size_t) actual != uncompLen) {
-            LOGE("Partial write during uncompress (%zd of %zd)\n",
-                (size_t)actual, (size_t)uncompLen);
+            LOGE("Partial write during uncompress (" ZD " of " ZD ")\n",
+                (ZD_TYPE) actual, (ZD_TYPE) uncompLen);
             goto unmap;
         } else {
             LOGI("+++ successful write\n");
@@ -802,8 +819,8 @@
 
     /* paranoia */
     if (zstream.total_out != uncompLen) {
-        LOGW("Size mismatch on inflated file (%ld vs %zd)\n",
-            zstream.total_out, uncompLen);
+        LOGW("Size mismatch on inflated file (%ld vs " ZD ")\n",
+            zstream.total_out, (ZD_TYPE) uncompLen);
         goto z_bail;
     }
 
@@ -891,8 +908,8 @@
 
     /* paranoia */
     if (zstream.total_out != uncompLen) {
-        LOGW("Size mismatch on inflated file (%ld vs %zd)\n",
-            zstream.total_out, uncompLen);
+        LOGW("Size mismatch on inflated file (%ld vs " ZD ")\n",
+            zstream.total_out, (ZD_TYPE) uncompLen);
         goto z_bail;
     }
 
diff --git a/libs/utils/tests/ObbFile_test.cpp b/libs/utils/tests/ObbFile_test.cpp
index 29bb70a..46b30c2 100644
--- a/libs/utils/tests/ObbFile_test.cpp
+++ b/libs/utils/tests/ObbFile_test.cpp
@@ -23,6 +23,7 @@
 #include <gtest/gtest.h>
 
 #include <fcntl.h>
+#include <string.h>
 
 namespace android {
 
@@ -63,6 +64,10 @@
 
     mObbFile->setPackageName(String8(packageName));
     mObbFile->setVersion(versionNum);
+#define SALT_SIZE 8
+    unsigned char salt[SALT_SIZE] = {0x01, 0x10, 0x55, 0xAA, 0xFF, 0x00, 0x5A, 0xA5};
+    EXPECT_TRUE(mObbFile->setSalt(salt, SALT_SIZE))
+            << "Salt should be successfully set";
 
     EXPECT_TRUE(mObbFile->writeTo(mFileName))
             << "couldn't write to fake .obb file";
@@ -77,6 +82,19 @@
     const char* currentPackageName = mObbFile->getPackageName().string();
     EXPECT_STREQ(packageName, currentPackageName)
             << "package name didn't come out the same as it went in";
+
+    size_t saltLen;
+    const unsigned char* newSalt = mObbFile->getSalt(&saltLen);
+
+    EXPECT_EQ(sizeof(salt), saltLen)
+            << "salt sizes were not the same";
+
+    for (int i = 0; i < sizeof(salt); i++) {
+        EXPECT_EQ(salt[i], newSalt[i])
+                << "salt character " << i << " should be equal";
+    }
+    EXPECT_TRUE(memcmp(newSalt, salt, sizeof(salt)) == 0)
+            << "salts should be the same";
 }
 
 }
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp
index fe9a5ab..90865da 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp
@@ -396,7 +396,6 @@
 {
     status_t err = mDisplayEventThread->acquireScreen();
     if (err >= 0) {
-        mCanDraw = true;
         mScreenAcquired = true;
     }
 }
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 97365aa..c59f2fd 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -82,6 +82,7 @@
         mHwWorkListDirty(false),
         mDeferReleaseConsole(false),
         mFreezeDisplay(false),
+        mElectronBeamAnimationMode(0),
         mFreezeCount(0),
         mFreezeDisplayTime(0),
         mDebugRegion(0),
@@ -428,6 +429,9 @@
     int what = android_atomic_and(0, &mConsoleSignals);
     if (what & eConsoleAcquired) {
         hw.acquireScreen();
+        // this is a temporary work-around, eventually this should be called
+        // by the power-manager
+        SurfaceFlinger::turnElectronBeamOn(mElectronBeamAnimationMode);
     }
 
     if (mDeferReleaseConsole && hw.isScreenAcquired()) {
@@ -1559,6 +1563,7 @@
         case UNFREEZE_DISPLAY:
         case BOOT_FINISHED:
         case TURN_ELECTRON_BEAM_OFF:
+        case TURN_ELECTRON_BEAM_ON:
         {
             // codes that require permission check
             IPCThreadState* ipc = IPCThreadState::self();
@@ -1652,27 +1657,18 @@
     return err;
 }
 
-
 // ---------------------------------------------------------------------------
 
-status_t SurfaceFlinger::turnElectronBeamOffImplLocked()
+status_t SurfaceFlinger::renderScreenToTextureLocked(DisplayID dpy,
+        GLuint* textureName, GLfloat* uOut, GLfloat* vOut)
 {
-    status_t result = PERMISSION_DENIED;
-
     if (!GLExtensions::getInstance().haveFramebufferObject())
         return INVALID_OPERATION;
 
     // get screen geometry
-    const int dpy = 0;
     const DisplayHardware& hw(graphicPlane(dpy).displayHardware());
-    if (!hw.canDraw()) {
-        // we're already off
-        return NO_ERROR;
-    }
-
     const uint32_t hw_w = hw.getWidth();
     const uint32_t hw_h = hw.getHeight();
-    const Region screenBounds(hw.bounds());
     GLfloat u = 1;
     GLfloat v = 1;
 
@@ -1683,202 +1679,437 @@
     GLuint name, tname;
     glGenTextures(1, &tname);
     glBindTexture(GL_TEXTURE_2D, tname);
-    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, hw_w, hw_h, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,
+            hw_w, hw_h, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
     if (glGetError() != GL_NO_ERROR) {
+        while ( glGetError() != GL_NO_ERROR ) ;
         GLint tw = (2 << (31 - clz(hw_w)));
         GLint th = (2 << (31 - clz(hw_h)));
-        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, tw, th, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
+        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,
+                tw, th, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
         u = GLfloat(hw_w) / tw;
         v = GLfloat(hw_h) / th;
     }
     glGenFramebuffersOES(1, &name);
     glBindFramebufferOES(GL_FRAMEBUFFER_OES, name);
-    glFramebufferTexture2DOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_TEXTURE_2D, tname, 0);
+    glFramebufferTexture2DOES(GL_FRAMEBUFFER_OES,
+            GL_COLOR_ATTACHMENT0_OES, GL_TEXTURE_2D, tname, 0);
 
-    GLenum status = glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES);
-    if (status == GL_FRAMEBUFFER_COMPLETE_OES) {
-        // redraw the screen entirely...
-        glClearColor(0,0,0,1);
+    // 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();
+    }
+
+    // back to main framebuffer
+    glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);
+    glDisable(GL_SCISSOR_TEST);
+    glDeleteFramebuffersOES(1, &name);
+
+    *textureName = tname;
+    *uOut = u;
+    *vOut = v;
+    return NO_ERROR;
+}
+
+// ---------------------------------------------------------------------------
+
+status_t SurfaceFlinger::electronBeamOffAnimationImplLocked()
+{
+    status_t result = PERMISSION_DENIED;
+
+    if (!GLExtensions::getInstance().haveFramebufferObject())
+        return INVALID_OPERATION;
+
+    // get screen geometry
+    const DisplayHardware& hw(graphicPlane(0).displayHardware());
+    const uint32_t hw_w = hw.getWidth();
+    const uint32_t hw_h = hw.getHeight();
+    const Region screenBounds(hw.bounds());
+
+    GLfloat u, v;
+    GLuint tname;
+    result = renderScreenToTextureLocked(0, &tname, &u, &v);
+    if (result != NO_ERROR) {
+        return result;
+    }
+
+    GLfloat vtx[8];
+    const GLfloat texCoords[4][2] = { {0,v}, {0,0}, {u,0}, {u,v} };
+    glEnable(GL_TEXTURE_2D);
+    glBindTexture(GL_TEXTURE_2D, tname);
+    glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
+    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+    glVertexPointer(2, GL_FLOAT, 0, vtx);
+
+    class s_curve_interpolator {
+        const float nbFrames, s, v;
+    public:
+        s_curve_interpolator(int nbFrames, float s)
+        : nbFrames(1.0f / (nbFrames-1)), s(s),
+          v(1.0f + expf(-s + 0.5f*s)) {
+        }
+        float operator()(int f) {
+            const float x = f * nbFrames;
+            return ((1.0f/(1.0f + expf(-x*s + 0.5f*s))) - 0.5f) * v + 0.5f;
+        }
+    };
+
+    class v_stretch {
+        const GLfloat hw_w, hw_h;
+    public:
+        v_stretch(uint32_t hw_w, uint32_t hw_h)
+        : hw_w(hw_w), hw_h(hw_h) {
+        }
+        void operator()(GLfloat* vtx, float v) {
+            const GLfloat w = hw_w + (hw_w * v);
+            const GLfloat h = hw_h - (hw_h * v);
+            const GLfloat x = (hw_w - w) * 0.5f;
+            const GLfloat y = (hw_h - h) * 0.5f;
+            vtx[0] = x;         vtx[1] = y;
+            vtx[2] = x;         vtx[3] = y + h;
+            vtx[4] = x + w;     vtx[5] = y + h;
+            vtx[6] = x + w;     vtx[7] = y;
+        }
+    };
+
+    class h_stretch {
+        const GLfloat hw_w, hw_h;
+    public:
+        h_stretch(uint32_t hw_w, uint32_t hw_h)
+        : hw_w(hw_w), hw_h(hw_h) {
+        }
+        void operator()(GLfloat* vtx, float v) {
+            const GLfloat w = hw_w - (hw_w * v);
+            const GLfloat h = 1.0f;
+            const GLfloat x = (hw_w - w) * 0.5f;
+            const GLfloat y = (hw_h - h) * 0.5f;
+            vtx[0] = x;         vtx[1] = y;
+            vtx[2] = x;         vtx[3] = y + h;
+            vtx[4] = x + w;     vtx[5] = y + h;
+            vtx[6] = x + w;     vtx[7] = y;
+        }
+    };
+
+    // the full animation is 24 frames
+    const int nbFrames = 12;
+    s_curve_interpolator itr(nbFrames, 7.5f);
+    s_curve_interpolator itg(nbFrames, 8.0f);
+    s_curve_interpolator itb(nbFrames, 8.5f);
+
+    v_stretch vverts(hw_w, hw_h);
+    glEnable(GL_BLEND);
+    glBlendFunc(GL_ONE, GL_ONE);
+    for (int i=0 ; i<nbFrames ; i++) {
+        float x, y, w, h;
+        const float vr = itr(i);
+        const float vg = itg(i);
+        const float vb = itb(i);
+
+        // clear screen
+        glColorMask(1,1,1,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();
-        }
-        // back to main framebuffer
-        glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);
-        glDisable(GL_SCISSOR_TEST);
-
-        GLfloat vtx[8];
-        const GLfloat texCoords[4][2] = { {0,v}, {0,0}, {u,0}, {u,v} };
         glEnable(GL_TEXTURE_2D);
-        glBindTexture(GL_TEXTURE_2D, tname);
-        glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
-        glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
-        glEnableClientState(GL_TEXTURE_COORD_ARRAY);
-        glVertexPointer(2, GL_FLOAT, 0, vtx);
 
-        class s_curve_interpolator {
-            const float nbFrames, s, v;
-        public:
-            s_curve_interpolator(int nbFrames, float s)
-                : nbFrames(1.0f / (nbFrames-1)), s(s),
-                  v(1.0f + expf(-s + 0.5f*s)) {
-            }
-            float operator()(int f) {
-                const float x = f * nbFrames;
-                return ((1.0f/(1.0f + expf(-x*s + 0.5f*s))) - 0.5f) * v + 0.5f;
-            }
-        };
+        // draw the red plane
+        vverts(vtx, vr);
+        glColorMask(1,0,0,1);
+        glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
 
-        class v_stretch {
-            const GLfloat hw_w, hw_h;
-        public:
-            v_stretch(uint32_t hw_w, uint32_t hw_h)
-                : hw_w(hw_w), hw_h(hw_h) {
-            }
-            void operator()(GLfloat* vtx, float v) {
-                const GLfloat w = hw_w + (hw_w * v);
-                const GLfloat h = hw_h - (hw_h * v);
-                const GLfloat x = (hw_w - w) * 0.5f;
-                const GLfloat y = (hw_h - h) * 0.5f;
-                vtx[0] = x;         vtx[1] = y;
-                vtx[2] = x;         vtx[3] = y + h;
-                vtx[4] = x + w;     vtx[5] = y + h;
-                vtx[6] = x + w;     vtx[7] = y;
-            }
-        };
+        // draw the green plane
+        vverts(vtx, vg);
+        glColorMask(0,1,0,1);
+        glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
 
-        class h_stretch {
-            const GLfloat hw_w, hw_h;
-        public:
-            h_stretch(uint32_t hw_w, uint32_t hw_h)
-                : hw_w(hw_w), hw_h(hw_h) {
-            }
-            void operator()(GLfloat* vtx, float v) {
-                const GLfloat w = hw_w - (hw_w * v);
-                const GLfloat h = 1.0f;
-                const GLfloat x = (hw_w - w) * 0.5f;
-                const GLfloat y = (hw_h - h) * 0.5f;
-                vtx[0] = x;         vtx[1] = y;
-                vtx[2] = x;         vtx[3] = y + h;
-                vtx[4] = x + w;     vtx[5] = y + h;
-                vtx[6] = x + w;     vtx[7] = y;
-            }
-        };
+        // draw the blue plane
+        vverts(vtx, vb);
+        glColorMask(0,0,1,1);
+        glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
 
-        // the full animation is 24 frames
-        const int nbFrames = 12;
-
-        v_stretch vverts(hw_w, hw_h);
-        s_curve_interpolator itr(nbFrames, 7.5f);
-        s_curve_interpolator itg(nbFrames, 8.0f);
-        s_curve_interpolator itb(nbFrames, 8.5f);
-        glEnable(GL_BLEND);
-        glBlendFunc(GL_ONE, GL_ONE);
-        for (int i=0 ; i<nbFrames ; i++) {
-            float x, y, w, h;
-            const float vr = itr(i);
-            const float vg = itg(i);
-            const float vb = itb(i);
-
-            // clear screen
-            glColorMask(1,1,1,1);
-            glClear(GL_COLOR_BUFFER_BIT);
-            glEnable(GL_TEXTURE_2D);
-
-            // draw the red plane
-            vverts(vtx, vr);
-            glColorMask(1,0,0,1);
-            glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
-
-            // draw the green plane
-            vverts(vtx, vg);
-            glColorMask(0,1,0,1);
-            glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
-
-            // draw the blue plane
-            vverts(vtx, vb);
-            glColorMask(0,0,1,1);
-            glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
-
-            // draw the white highlight (we use the last vertices)
-            glDisable(GL_TEXTURE_2D);
-            glColorMask(1,1,1,1);
-            glColor4f(vg, vg, vg, 1);
-            glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
-            hw.flip(screenBounds);
-        }
-
-        h_stretch hverts(hw_w, hw_h);
-        glDisable(GL_BLEND);
+        // draw the white highlight (we use the last vertices)
         glDisable(GL_TEXTURE_2D);
         glColorMask(1,1,1,1);
-        for (int i=0 ; i<nbFrames ; i++) {
-            const float v = itg(i);
-            hverts(vtx, v);
-            glClear(GL_COLOR_BUFFER_BIT);
-            glColor4f(1-v, 1-v, 1-v, 1);
-            glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
-            hw.flip(screenBounds);
-        }
-
-        glColorMask(1,1,1,1);
-        glEnable(GL_SCISSOR_TEST);
-        glDisableClientState(GL_TEXTURE_COORD_ARRAY);
-        result = NO_ERROR;
-    } else {
-        // release FBO resources
-        glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);
-        result = BAD_VALUE;
+        glColor4f(vg, vg, vg, 1);
+        glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+        hw.flip(screenBounds);
     }
 
-    glDeleteFramebuffersOES(1, &name);
+    h_stretch hverts(hw_w, hw_h);
+    glDisable(GL_BLEND);
+    glDisable(GL_TEXTURE_2D);
+    glColorMask(1,1,1,1);
+    for (int i=0 ; i<nbFrames ; i++) {
+        const float v = itg(i);
+        hverts(vtx, v);
+        glClear(GL_COLOR_BUFFER_BIT);
+        glColor4f(1-v, 1-v, 1-v, 1);
+        glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+        hw.flip(screenBounds);
+    }
+
+    glColorMask(1,1,1,1);
+    glEnable(GL_SCISSOR_TEST);
+    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+    glDeleteTextures(1, &tname);
+    return NO_ERROR;
+}
+
+status_t SurfaceFlinger::electronBeamOnAnimationImplLocked()
+{
+    status_t result = PERMISSION_DENIED;
+
+    if (!GLExtensions::getInstance().haveFramebufferObject())
+        return INVALID_OPERATION;
+
+
+    // get screen geometry
+    const DisplayHardware& hw(graphicPlane(0).displayHardware());
+    const uint32_t hw_w = hw.getWidth();
+    const uint32_t hw_h = hw.getHeight();
+    const Region screenBounds(hw.bounds());
+
+    GLfloat u, v;
+    GLuint tname;
+    result = renderScreenToTextureLocked(0, &tname, &u, &v);
+    if (result != NO_ERROR) {
+        return result;
+    }
+
+    // back to main framebuffer
+    glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);
+    glDisable(GL_SCISSOR_TEST);
+
+    GLfloat vtx[8];
+    const GLfloat texCoords[4][2] = { {0,v}, {0,0}, {u,0}, {u,v} };
+    glEnable(GL_TEXTURE_2D);
+    glBindTexture(GL_TEXTURE_2D, tname);
+    glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
+    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+    glVertexPointer(2, GL_FLOAT, 0, vtx);
+
+    class s_curve_interpolator {
+        const float nbFrames, s, v;
+    public:
+        s_curve_interpolator(int nbFrames, float s)
+        : nbFrames(1.0f / (nbFrames-1)), s(s),
+          v(1.0f + expf(-s + 0.5f*s)) {
+        }
+        float operator()(int f) {
+            const float x = f * nbFrames;
+            return ((1.0f/(1.0f + expf(-x*s + 0.5f*s))) - 0.5f) * v + 0.5f;
+        }
+    };
+
+    class v_stretch {
+        const GLfloat hw_w, hw_h;
+    public:
+        v_stretch(uint32_t hw_w, uint32_t hw_h)
+        : hw_w(hw_w), hw_h(hw_h) {
+        }
+        void operator()(GLfloat* vtx, float v) {
+            const GLfloat w = hw_w + (hw_w * v);
+            const GLfloat h = hw_h - (hw_h * v);
+            const GLfloat x = (hw_w - w) * 0.5f;
+            const GLfloat y = (hw_h - h) * 0.5f;
+            vtx[0] = x;         vtx[1] = y;
+            vtx[2] = x;         vtx[3] = y + h;
+            vtx[4] = x + w;     vtx[5] = y + h;
+            vtx[6] = x + w;     vtx[7] = y;
+        }
+    };
+
+    class h_stretch {
+        const GLfloat hw_w, hw_h;
+    public:
+        h_stretch(uint32_t hw_w, uint32_t hw_h)
+        : hw_w(hw_w), hw_h(hw_h) {
+        }
+        void operator()(GLfloat* vtx, float v) {
+            const GLfloat w = hw_w - (hw_w * v);
+            const GLfloat h = 1.0f;
+            const GLfloat x = (hw_w - w) * 0.5f;
+            const GLfloat y = (hw_h - h) * 0.5f;
+            vtx[0] = x;         vtx[1] = y;
+            vtx[2] = x;         vtx[3] = y + h;
+            vtx[4] = x + w;     vtx[5] = y + h;
+            vtx[6] = x + w;     vtx[7] = y;
+        }
+    };
+
+    // the full animation is 12 frames
+    int nbFrames = 8;
+    s_curve_interpolator itr(nbFrames, 7.5f);
+    s_curve_interpolator itg(nbFrames, 8.0f);
+    s_curve_interpolator itb(nbFrames, 8.5f);
+
+    h_stretch hverts(hw_w, hw_h);
+    glDisable(GL_BLEND);
+    glDisable(GL_TEXTURE_2D);
+    glColorMask(1,1,1,1);
+    for (int i=nbFrames-1 ; i>=0 ; i--) {
+        const float v = itg(i);
+        hverts(vtx, v);
+        glClear(GL_COLOR_BUFFER_BIT);
+        glColor4f(1-v, 1-v, 1-v, 1);
+        glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+        hw.flip(screenBounds);
+    }
+
+    nbFrames = 4;
+    v_stretch vverts(hw_w, hw_h);
+    glEnable(GL_BLEND);
+    glBlendFunc(GL_ONE, GL_ONE);
+    for (int i=nbFrames-1 ; i>=0 ; i--) {
+        float x, y, w, h;
+        const float vr = itr(i);
+        const float vg = itg(i);
+        const float vb = itb(i);
+
+        // clear screen
+        glColorMask(1,1,1,1);
+        glClear(GL_COLOR_BUFFER_BIT);
+        glEnable(GL_TEXTURE_2D);
+
+        // draw the red plane
+        vverts(vtx, vr);
+        glColorMask(1,0,0,1);
+        glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+
+        // draw the green plane
+        vverts(vtx, vg);
+        glColorMask(0,1,0,1);
+        glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+
+        // draw the blue plane
+        vverts(vtx, vb);
+        glColorMask(0,0,1,1);
+        glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+
+        hw.flip(screenBounds);
+    }
+
+    glColorMask(1,1,1,1);
+    glEnable(GL_SCISSOR_TEST);
+    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
     glDeleteTextures(1, &tname);
 
-    if (result == NO_ERROR) {
-        DisplayHardware& hw(graphicPlane(dpy).editDisplayHardware());
-        hw.setCanDraw(false);
+    return NO_ERROR;
+}
+
+// ---------------------------------------------------------------------------
+
+status_t SurfaceFlinger::turnElectronBeamOffImplLocked(int32_t mode)
+{
+    DisplayHardware& hw(graphicPlane(0).editDisplayHardware());
+    if (!hw.canDraw()) {
+        // we're already off
+        return NO_ERROR;
+    }
+    if (mode & ISurfaceComposer::eElectronBeamAnimationOff) {
+        electronBeamOffAnimationImplLocked();
     }
 
-    return result;
+    // always clear the whole screen at the end of the animation
+    glClearColor(0,0,0,1);
+    glDisable(GL_SCISSOR_TEST);
+    glClear(GL_COLOR_BUFFER_BIT);
+    glEnable(GL_SCISSOR_TEST);
+    hw.flip( Region(hw.bounds()) );
+
+    hw.setCanDraw(false);
+    return NO_ERROR;
 }
 
 status_t SurfaceFlinger::turnElectronBeamOff(int32_t mode)
 {
-    if (!GLExtensions::getInstance().haveFramebufferObject())
-        return INVALID_OPERATION;
-
     class MessageTurnElectronBeamOff : public MessageBase {
         SurfaceFlinger* flinger;
+        int32_t mode;
         status_t result;
     public:
-        MessageTurnElectronBeamOff(SurfaceFlinger* flinger)
-            : flinger(flinger), result(PERMISSION_DENIED) {
+        MessageTurnElectronBeamOff(SurfaceFlinger* flinger, int32_t mode)
+            : flinger(flinger), mode(mode), result(PERMISSION_DENIED) {
         }
         status_t getResult() const {
             return result;
         }
         virtual bool handler() {
             Mutex::Autolock _l(flinger->mStateLock);
-            result = flinger->turnElectronBeamOffImplLocked();
+            result = flinger->turnElectronBeamOffImplLocked(mode);
             return true;
         }
     };
 
-    sp<MessageBase> msg = new MessageTurnElectronBeamOff(this);
+    sp<MessageBase> msg = new MessageTurnElectronBeamOff(this, mode);
     status_t res = postMessageSync(msg);
     if (res == NO_ERROR) {
         res = static_cast<MessageTurnElectronBeamOff*>( msg.get() )->getResult();
+
+        // work-around: when the power-manager calls us we activate the
+        // animation. eventually, the "on" animation will be called
+        // by the power-manager itself
+        mElectronBeamAnimationMode = mode;
     }
     return res;
 }
 
 // ---------------------------------------------------------------------------
 
+status_t SurfaceFlinger::turnElectronBeamOnImplLocked(int32_t mode)
+{
+    DisplayHardware& hw(graphicPlane(0).editDisplayHardware());
+    if (hw.canDraw()) {
+        // we're already on
+        return NO_ERROR;
+    }
+    if (mode & ISurfaceComposer::eElectronBeamAnimationOn) {
+        electronBeamOnAnimationImplLocked();
+    }
+    hw.setCanDraw(true);
+
+    // make sure to redraw the whole screen when the animation is done
+    mDirtyRegion.set(hw.bounds());
+    signalEvent();
+
+    return NO_ERROR;
+}
+
+status_t SurfaceFlinger::turnElectronBeamOn(int32_t mode)
+{
+    class MessageTurnElectronBeamOn : public MessageBase {
+        SurfaceFlinger* flinger;
+        int32_t mode;
+        status_t result;
+    public:
+        MessageTurnElectronBeamOn(SurfaceFlinger* flinger, int32_t mode)
+            : flinger(flinger), mode(mode), result(PERMISSION_DENIED) {
+        }
+        status_t getResult() const {
+            return result;
+        }
+        virtual bool handler() {
+            Mutex::Autolock _l(flinger->mStateLock);
+            result = flinger->turnElectronBeamOnImplLocked(mode);
+            return true;
+        }
+    };
+
+    postMessageAsync( new MessageTurnElectronBeamOn(this, mode) );
+    return NO_ERROR;
+}
+
+// ---------------------------------------------------------------------------
+
 status_t SurfaceFlinger::captureScreenImplLocked(DisplayID dpy,
         sp<IMemoryHeap>* heap,
         uint32_t* w, uint32_t* h, PixelFormat* f,
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 3470d87..dda25e8 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -202,6 +202,7 @@
                                                       uint32_t reqWidth,
                                                       uint32_t reqHeight);
     virtual status_t                    turnElectronBeamOff(int32_t mode);
+    virtual status_t                    turnElectronBeamOn(int32_t mode);
 
             void                        screenReleased(DisplayID dpy);
             void                        screenAcquired(DisplayID dpy);
@@ -328,7 +329,12 @@
                     uint32_t* width, uint32_t* height, PixelFormat* format,
                     uint32_t reqWidth = 0, uint32_t reqHeight = 0);
 
-            status_t turnElectronBeamOffImplLocked();
+            status_t turnElectronBeamOffImplLocked(int32_t mode);
+            status_t turnElectronBeamOnImplLocked(int32_t mode);
+            status_t electronBeamOffAnimationImplLocked();
+            status_t electronBeamOnAnimationImplLocked();
+            status_t renderScreenToTextureLocked(DisplayID dpy,
+                    GLuint* textureName, GLfloat* uOut, GLfloat* vOut);
 
             friend class FreezeLock;
             sp<FreezeLock> getFreezeLock() const;
@@ -391,6 +397,7 @@
                 bool                        mHwWorkListDirty;
                 bool                        mDeferReleaseConsole;
                 bool                        mFreezeDisplay;
+                int32_t                     mElectronBeamAnimationMode;
                 int32_t                     mFreezeCount;
                 nsecs_t                     mFreezeDisplayTime;
                 Vector< sp<LayerBase> >     mVisibleLayersSortedByZ;