am 5126f1d4: am 613ed4c6: Merge "Fixd for 5490443 Native crash while exporting a video - Add new onError callback to PreviewProgressListener, which is used to indicate video editor application for the error that has occurred during priviewing. With this modification, 

* commit '5126f1d413b4fcb0aff5b364e1e5a41c89ca665c':
  Fixd for 5490443 Native crash while exporting a video - Add new onError callback to PreviewProgressListener, which is used to indicate video editor application for the error that has occurred during priviewing. With this modification, the application must implement the onError method, and then VideoEditorPreviewTest.java is changed accordingly.
diff --git a/include/binder/CursorWindow.h b/include/binder/CursorWindow.h
index f0284de..8a2979a 100644
--- a/include/binder/CursorWindow.h
+++ b/include/binder/CursorWindow.h
@@ -31,8 +31,8 @@
 
 #else
 
-#define IF_LOG_WINDOW() IF_LOG(LOG_DEBUG, "CursorWindow")
-#define LOG_WINDOW(...) LOG(LOG_DEBUG, "CursorWindow", __VA_ARGS__)
+#define IF_LOG_WINDOW() IF_ALOG(LOG_DEBUG, "CursorWindow")
+#define LOG_WINDOW(...) ALOG(LOG_DEBUG, "CursorWindow", __VA_ARGS__)
 
 #endif
 
diff --git a/include/gui/SensorChannel.h b/include/gui/BitTube.h
similarity index 89%
rename from include/gui/SensorChannel.h
rename to include/gui/BitTube.h
index bb54618..76389a0 100644
--- a/include/gui/SensorChannel.h
+++ b/include/gui/BitTube.h
@@ -28,14 +28,15 @@
 // ----------------------------------------------------------------------------
 class Parcel;
 
-class SensorChannel : public RefBase
+class BitTube : public RefBase
 {
 public:
 
-            SensorChannel();
-            SensorChannel(const Parcel& data);
-    virtual ~SensorChannel();
+            BitTube();
+            BitTube(const Parcel& data);
+    virtual ~BitTube();
 
+    status_t initCheck() const;
     int getFd() const;
     ssize_t write(void const* vaddr, size_t size);
     ssize_t read(void* vaddr, size_t size);
diff --git a/include/gui/DisplayEventReceiver.h b/include/gui/DisplayEventReceiver.h
new file mode 100644
index 0000000..8d07c0e
--- /dev/null
+++ b/include/gui/DisplayEventReceiver.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_GUI_DISPLAY_EVENT_H
+#define ANDROID_GUI_DISPLAY_EVENT_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+#include <utils/Timers.h>
+
+#include <binder/IInterface.h>
+
+// ----------------------------------------------------------------------------
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+class BitTube;
+class IDisplayEventConnection;
+
+// ----------------------------------------------------------------------------
+
+class DisplayEventReceiver {
+public:
+    enum {
+        DISPLAY_EVENT_VSYNC = 'vsyn'
+    };
+
+    struct Event {
+
+        struct Header {
+            uint32_t type;
+            nsecs_t timestamp;
+        };
+
+        struct VSync {
+            uint32_t count;
+        };
+
+        Header header;
+        union {
+            VSync vsync;
+        };
+    };
+
+public:
+    /*
+     * DisplayEventReceiver creates and registers an event connection with
+     * SurfaceFlinger. Events start being delivered immediately.
+     */
+    DisplayEventReceiver();
+
+    /*
+     * ~DisplayEventReceiver severs the connection with SurfaceFlinger, new events
+     * stop being delivered immediately. Note that the queue could have
+     * some events pending. These will be delivered.
+     */
+    ~DisplayEventReceiver();
+
+    /*
+     * initCheck returns the state of DisplayEventReceiver after construction.
+     */
+    status_t initCheck() const;
+
+    /*
+     * getFd returns the file descriptor to use to receive events.
+     * OWNERSHIP IS RETAINED by DisplayEventReceiver. DO NOT CLOSE this
+     * file-descriptor.
+     */
+    int getFd() const;
+
+    /*
+     * getEvents reads event from the queue and returns how many events were
+     * read. Returns 0 if there are no more events or a negative error code.
+     * If NOT_ENOUGH_DATA is returned, the object has become invalid forever, it
+     * should be destroyed and getEvents() shouldn't be called again.
+     */
+    ssize_t getEvents(Event* events, size_t count);
+
+private:
+    sp<IDisplayEventConnection> mEventConnection;
+    sp<BitTube> mDataChannel;
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_GUI_DISPLAY_EVENT_H
diff --git a/include/gui/IDisplayEventConnection.h b/include/gui/IDisplayEventConnection.h
new file mode 100644
index 0000000..8728bb5
--- /dev/null
+++ b/include/gui/IDisplayEventConnection.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_GUI_IDISPLAY_EVENT_CONNECTION_H
+#define ANDROID_GUI_IDISPLAY_EVENT_CONNECTION_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+
+#include <binder/IInterface.h>
+
+namespace android {
+// ----------------------------------------------------------------------------
+
+class BitTube;
+
+class IDisplayEventConnection : public IInterface
+{
+public:
+    DECLARE_META_INTERFACE(DisplayEventConnection);
+
+    virtual sp<BitTube> getDataChannel() const = 0;
+};
+
+// ----------------------------------------------------------------------------
+
+class BnDisplayEventConnection : public BnInterface<IDisplayEventConnection>
+{
+public:
+    virtual status_t    onTransact( uint32_t code,
+                                    const Parcel& data,
+                                    Parcel* reply,
+                                    uint32_t flags = 0);
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_GUI_IDISPLAY_EVENT_CONNECTION_H
diff --git a/include/gui/ISensorEventConnection.h b/include/gui/ISensorEventConnection.h
index ed4e4cc..749065e 100644
--- a/include/gui/ISensorEventConnection.h
+++ b/include/gui/ISensorEventConnection.h
@@ -28,14 +28,14 @@
 namespace android {
 // ----------------------------------------------------------------------------
 
-class SensorChannel;
+class BitTube;
 
 class ISensorEventConnection : public IInterface
 {
 public:
     DECLARE_META_INTERFACE(SensorEventConnection);
 
-    virtual sp<SensorChannel> getSensorChannel() const = 0;
+    virtual sp<BitTube> getSensorChannel() const = 0;
     virtual status_t enableDisable(int handle, bool enabled) = 0;
     virtual status_t setEventRate(int handle, nsecs_t ns) = 0;
 };
diff --git a/include/gui/SensorEventQueue.h b/include/gui/SensorEventQueue.h
index 97dd391..ef7c6e3 100644
--- a/include/gui/SensorEventQueue.h
+++ b/include/gui/SensorEventQueue.h
@@ -24,7 +24,7 @@
 #include <utils/RefBase.h>
 #include <utils/Timers.h>
 
-#include <gui/SensorChannel.h>
+#include <gui/BitTube.h>
 
 // ----------------------------------------------------------------------------
 
@@ -71,7 +71,7 @@
 private:
     sp<Looper> getLooper() const;
     sp<ISensorEventConnection> mSensorEventConnection;
-    sp<SensorChannel> mSensorChannel;
+    sp<BitTube> mSensorChannel;
     mutable Mutex mLock;
     mutable sp<Looper> mLooper;
 };
diff --git a/include/private/gui/ComposerService.h b/include/private/gui/ComposerService.h
new file mode 100644
index 0000000..d04491a
--- /dev/null
+++ b/include/private/gui/ComposerService.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_PRIVATE_GUI_COMPOSER_SERVICE_H
+#define ANDROID_PRIVATE_GUI_COMPOSER_SERVICE_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Singleton.h>
+#include <utils/StrongPointer.h>
+
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+class IMemoryHeap;
+class ISurfaceComposer;
+class surface_flinger_cblk_t;
+
+// ---------------------------------------------------------------------------
+
+class ComposerService : public Singleton<ComposerService>
+{
+    // these are constants
+    sp<ISurfaceComposer> mComposerService;
+    sp<IMemoryHeap> mServerCblkMemory;
+    surface_flinger_cblk_t volatile* mServerCblk;
+    ComposerService();
+    friend class Singleton<ComposerService>;
+public:
+    static sp<ISurfaceComposer> getComposerService();
+    static surface_flinger_cblk_t const volatile * getControlBlock();
+};
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_PRIVATE_GUI_COMPOSER_SERVICE_H
diff --git a/include/surfaceflinger/ISurfaceComposer.h b/include/surfaceflinger/ISurfaceComposer.h
index 5eb09c7..58fd89d 100644
--- a/include/surfaceflinger/ISurfaceComposer.h
+++ b/include/surfaceflinger/ISurfaceComposer.h
@@ -33,8 +33,9 @@
 namespace android {
 // ----------------------------------------------------------------------------
 
-class IMemoryHeap;
 class ComposerState;
+class IDisplayEventConnection;
+class IMemoryHeap;
 
 class ISurfaceComposer : public IInterface
 {
@@ -124,13 +125,19 @@
             uint32_t reqWidth, uint32_t reqHeight,
             uint32_t minLayerZ, uint32_t maxLayerZ) = 0;
 
+    /* triggers screen off animation */
     virtual status_t turnElectronBeamOff(int32_t mode) = 0;
+
+    /* triggers screen on animation */
     virtual status_t turnElectronBeamOn(int32_t mode) = 0;
 
     /* verify that an ISurfaceTexture was created by SurfaceFlinger.
      */
     virtual bool authenticateSurfaceTexture(
             const sp<ISurfaceTexture>& surface) const = 0;
+
+    /* return an IDisplayEventConnection */
+    virtual sp<IDisplayEventConnection> createDisplayEventConnection() = 0;
 };
 
 // ----------------------------------------------------------------------------
@@ -151,6 +158,7 @@
         TURN_ELECTRON_BEAM_OFF,
         TURN_ELECTRON_BEAM_ON,
         AUTHENTICATE_SURFACE,
+        CREATE_DISPLAY_EVENT_CONNECTION,
     };
 
     virtual status_t    onTransact( uint32_t code,
diff --git a/include/surfaceflinger/SurfaceComposerClient.h b/include/surfaceflinger/SurfaceComposerClient.h
index 8226abe..99affda 100644
--- a/include/surfaceflinger/SurfaceComposerClient.h
+++ b/include/surfaceflinger/SurfaceComposerClient.h
@@ -28,7 +28,6 @@
 #include <utils/threads.h>
 
 #include <ui/PixelFormat.h>
-#include <ui/Region.h>
 
 #include <surfaceflinger/Surface.h>
 
@@ -39,30 +38,11 @@
 class DisplayInfo;
 class Composer;
 class IMemoryHeap;
-class ISurfaceComposer;
+class ISurfaceComposerClient;
 class Region;
-class surface_flinger_cblk_t;
-struct layer_state_t;
 
 // ---------------------------------------------------------------------------
 
-class ComposerService : public Singleton<ComposerService>
-{
-    // these are constants
-    sp<ISurfaceComposer> mComposerService;
-    sp<IMemoryHeap> mServerCblkMemory;
-    surface_flinger_cblk_t volatile* mServerCblk;
-    ComposerService();
-    friend class Singleton<ComposerService>;
-public:
-    static sp<ISurfaceComposer> getComposerService();
-    static surface_flinger_cblk_t const volatile * getControlBlock();
-};
-
-// ---------------------------------------------------------------------------
-
-class Composer;
-
 class SurfaceComposerClient : public RefBase
 {
     friend class Composer;
diff --git a/include/utils/BasicHashtable.h b/include/utils/BasicHashtable.h
new file mode 100644
index 0000000..fdf9738
--- /dev/null
+++ b/include/utils/BasicHashtable.h
@@ -0,0 +1,393 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_BASIC_HASHTABLE_H
+#define ANDROID_BASIC_HASHTABLE_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <utils/SharedBuffer.h>
+#include <utils/TypeHelpers.h>
+
+namespace android {
+
+/* Implementation type.  Nothing to see here. */
+class BasicHashtableImpl {
+protected:
+    struct Bucket {
+        // The collision flag indicates that the bucket is part of a collision chain
+        // such that at least two entries both hash to this bucket.  When true, we
+        // may need to seek further along the chain to find the entry.
+        static const uint32_t COLLISION = 0x80000000UL;
+
+        // The present flag indicates that the bucket contains an initialized entry value.
+        static const uint32_t PRESENT   = 0x40000000UL;
+
+        // Mask for 30 bits worth of the hash code that are stored within the bucket to
+        // speed up lookups and rehashing by eliminating the need to recalculate the
+        // hash code of the entry's key.
+        static const uint32_t HASH_MASK = 0x3fffffffUL;
+
+        // Combined value that stores the collision and present flags as well as
+        // a 30 bit hash code.
+        uint32_t cookie;
+
+        // Storage for the entry begins here.
+        char entry[0];
+    };
+
+    BasicHashtableImpl(size_t entrySize, bool hasTrivialDestructor,
+            size_t minimumInitialCapacity, float loadFactor);
+    BasicHashtableImpl(const BasicHashtableImpl& other);
+
+    void dispose();
+
+    inline void edit() {
+        if (mBuckets && !SharedBuffer::bufferFromData(mBuckets)->onlyOwner()) {
+            clone();
+        }
+    }
+
+    void setTo(const BasicHashtableImpl& other);
+    void clear();
+
+    ssize_t next(ssize_t index) const;
+    ssize_t find(ssize_t index, hash_t hash, const void* __restrict__ key) const;
+    size_t add(hash_t hash, const void* __restrict__ entry);
+    void removeAt(size_t index);
+    void rehash(size_t minimumCapacity, float loadFactor);
+
+    const size_t mBucketSize; // number of bytes per bucket including the entry
+    const bool mHasTrivialDestructor; // true if the entry type does not require destruction
+    size_t mCapacity;         // number of buckets that can be filled before exceeding load factor
+    float mLoadFactor;        // load factor
+    size_t mSize;             // number of elements actually in the table
+    size_t mFilledBuckets;    // number of buckets for which collision or present is true
+    size_t mBucketCount;      // number of slots in the mBuckets array
+    void* mBuckets;           // array of buckets, as a SharedBuffer
+
+    inline const Bucket& bucketAt(const void* __restrict__ buckets, size_t index) const {
+        return *reinterpret_cast<const Bucket*>(
+                static_cast<const uint8_t*>(buckets) + index * mBucketSize);
+    }
+
+    inline Bucket& bucketAt(void* __restrict__ buckets, size_t index) const {
+        return *reinterpret_cast<Bucket*>(static_cast<uint8_t*>(buckets) + index * mBucketSize);
+    }
+
+    virtual bool compareBucketKey(const Bucket& bucket, const void* __restrict__ key) const = 0;
+    virtual void initializeBucketEntry(Bucket& bucket, const void* __restrict__ entry) const = 0;
+    virtual void destroyBucketEntry(Bucket& bucket) const = 0;
+
+private:
+    void clone();
+
+    // Allocates a bucket array as a SharedBuffer.
+    void* allocateBuckets(size_t count) const;
+
+    // Releases a bucket array's associated SharedBuffer.
+    void releaseBuckets(void* __restrict__ buckets, size_t count) const;
+
+    // Destroys the contents of buckets (invokes destroyBucketEntry for each
+    // populated bucket if needed).
+    void destroyBuckets(void* __restrict__ buckets, size_t count) const;
+
+    // Copies the content of buckets (copies the cookie and invokes copyBucketEntry
+    // for each populated bucket if needed).
+    void copyBuckets(const void* __restrict__ fromBuckets,
+            void* __restrict__ toBuckets, size_t count) const;
+
+    // Determines the appropriate size of a bucket array to store a certain minimum
+    // number of entries and returns its effective capacity.
+    static void determineCapacity(size_t minimumCapacity, float loadFactor,
+            size_t* __restrict__ outBucketCount, size_t* __restrict__ outCapacity);
+
+    // Trim a hash code to 30 bits to match what we store in the bucket's cookie.
+    inline static hash_t trimHash(hash_t hash) {
+        return (hash & Bucket::HASH_MASK) ^ (hash >> 30);
+    }
+
+    // Returns the index of the first bucket that is in the collision chain
+    // for the specified hash code, given the total number of buckets.
+    // (Primary hash)
+    inline static size_t chainStart(hash_t hash, size_t count) {
+        return hash % count;
+    }
+
+    // Returns the increment to add to a bucket index to seek to the next bucket
+    // in the collision chain for the specified hash code, given the total number of buckets.
+    // (Secondary hash)
+    inline static size_t chainIncrement(hash_t hash, size_t count) {
+        return ((hash >> 7) | (hash << 25)) % (count - 1) + 1;
+    }
+
+    // Returns the index of the next bucket that is in the collision chain
+    // that is defined by the specified increment, given the total number of buckets.
+    inline static size_t chainSeek(size_t index, size_t increment, size_t count) {
+        return (index + increment) % count;
+    }
+};
+
+/*
+ * A BasicHashtable stores entries that are indexed by hash code in place
+ * within an array.  The basic operations are finding entries by key,
+ * adding new entries and removing existing entries.
+ *
+ * This class provides a very limited set of operations with simple semantics.
+ * It is intended to be used as a building block to construct more complex
+ * and interesting data structures such as HashMap.  Think very hard before
+ * adding anything extra to BasicHashtable, it probably belongs at a
+ * higher level of abstraction.
+ *
+ * TKey: The key type.
+ * TEntry: The entry type which is what is actually stored in the array.
+ *
+ * TKey must support the following contract:
+ *     bool operator==(const TKey& other) const;  // return true if equal
+ *     bool operator!=(const TKey& other) const;  // return true if unequal
+ *
+ * TEntry must support the following contract:
+ *     const TKey& getKey() const;  // get the key from the entry
+ *
+ * This class supports storing entries with duplicate keys.  Of course, it can't
+ * tell them apart during removal so only the first entry will be removed.
+ * We do this because it means that operations like add() can't fail.
+ */
+template <typename TKey, typename TEntry>
+class BasicHashtable : private BasicHashtableImpl {
+public:
+    /* Creates a hashtable with the specified minimum initial capacity.
+     * The underlying array will be created when the first entry is added.
+     *
+     * minimumInitialCapacity: The minimum initial capacity for the hashtable.
+     *     Default is 0.
+     * loadFactor: The desired load factor for the hashtable, between 0 and 1.
+     *     Default is 0.75.
+     */
+    BasicHashtable(size_t minimumInitialCapacity = 0, float loadFactor = 0.75f);
+
+    /* Copies a hashtable.
+     * The underlying storage is shared copy-on-write.
+     */
+    BasicHashtable(const BasicHashtable& other);
+
+    /* Clears and destroys the hashtable.
+     */
+    virtual ~BasicHashtable();
+
+    /* Making this hashtable a copy of the other hashtable.
+     * The underlying storage is shared copy-on-write.
+     *
+     * other: The hashtable to copy.
+     */
+    inline BasicHashtable<TKey, TEntry>& operator =(const BasicHashtable<TKey, TEntry> & other) {
+        setTo(other);
+        return *this;
+    }
+
+    /* Returns the number of entries in the hashtable.
+     */
+    inline size_t size() const {
+        return mSize;
+    }
+
+    /* Returns the capacity of the hashtable, which is the number of elements that can
+     * added to the hashtable without requiring it to be grown.
+     */
+    inline size_t capacity() const {
+        return mCapacity;
+    }
+
+    /* Returns the number of buckets that the hashtable has, which is the size of its
+     * underlying array.
+     */
+    inline size_t bucketCount() const {
+        return mBucketCount;
+    }
+
+    /* Returns the load factor of the hashtable. */
+    inline float loadFactor() const {
+        return mLoadFactor;
+    };
+
+    /* Returns a const reference to the entry at the specified index.
+     *
+     * index:   The index of the entry to retrieve.  Must be a valid index within
+     *          the bounds of the hashtable.
+     */
+    inline const TEntry& entryAt(size_t index) const {
+        return entryFor(bucketAt(mBuckets, index));
+    }
+
+    /* Returns a non-const reference to the entry at the specified index.
+     *
+     * index: The index of the entry to edit.  Must be a valid index within
+     *        the bounds of the hashtable.
+     */
+    inline TEntry& editEntryAt(size_t index) {
+        edit();
+        return entryFor(bucketAt(mBuckets, index));
+    }
+
+    /* Clears the hashtable.
+     * All entries in the hashtable are destroyed immediately.
+     * If you need to do something special with the entries in the hashtable then iterate
+     * over them and do what you need before clearing the hashtable.
+     */
+    inline void clear() {
+        BasicHashtableImpl::clear();
+    }
+
+    /* Returns the index of the next entry in the hashtable given the index of a previous entry.
+     * If the given index is -1, then returns the index of the first entry in the hashtable,
+     * if there is one, or -1 otherwise.
+     * If the given index is not -1, then returns the index of the next entry in the hashtable,
+     * in strictly increasing order, or -1 if there are none left.
+     *
+     * index:   The index of the previous entry that was iterated, or -1 to begin
+     *          iteration at the beginning of the hashtable.
+     */
+    inline ssize_t next(ssize_t index) const {
+        return BasicHashtableImpl::next(index);
+    }
+
+    /* Finds the index of an entry with the specified key.
+     * If the given index is -1, then returns the index of the first matching entry,
+     * otherwise returns the index of the next matching entry.
+     * If the hashtable contains multiple entries with keys that match the requested
+     * key, then the sequence of entries returned is arbitrary.
+     * Returns -1 if no entry was found.
+     *
+     * index:   The index of the previous entry with the specified key, or -1 to
+     *          find the first matching entry.
+     * hash:    The hashcode of the key.
+     * key:     The key.
+     */
+    inline ssize_t find(ssize_t index, hash_t hash, const TKey& key) const {
+        return BasicHashtableImpl::find(index, hash, &key);
+    }
+
+    /* Adds the entry to the hashtable.
+     * Returns the index of the newly added entry.
+     * If an entry with the same key already exists, then a duplicate entry is added.
+     * If the entry will not fit, then the hashtable's capacity is increased and
+     * its contents are rehashed.  See rehash().
+     *
+     * hash:    The hashcode of the key.
+     * entry:   The entry to add.
+     */
+    inline size_t add(hash_t hash, const TEntry& entry) {
+        return BasicHashtableImpl::add(hash, &entry);
+    }
+
+    /* Removes the entry with the specified index from the hashtable.
+     * The entry is destroyed immediately.
+     * The index must be valid.
+     *
+     * The hashtable is not compacted after an item is removed, so it is legal
+     * to continue iterating over the hashtable using next() or find().
+     *
+     * index:   The index of the entry to remove.  Must be a valid index within the
+     *          bounds of the hashtable, and it must refer to an existing entry.
+     */
+    inline void removeAt(size_t index) {
+        BasicHashtableImpl::removeAt(index);
+    }
+
+    /* Rehashes the contents of the hashtable.
+     * Grows the hashtable to at least the specified minimum capacity or the
+     * current number of elements, whichever is larger.
+     *
+     * Rehashing causes all entries to be copied and the entry indices may change.
+     * Although the hash codes are cached by the hashtable, rehashing can be an
+     * expensive operation and should be avoided unless the hashtable's size
+     * needs to be changed.
+     *
+     * Rehashing is the only way to change the capacity or load factor of the
+     * hashtable once it has been created.  It can be used to compact the
+     * hashtable by choosing a minimum capacity that is smaller than the current
+     * capacity (such as 0).
+     *
+     * minimumCapacity: The desired minimum capacity after rehashing.
+     * loadFactor: The desired load factor after rehashing.
+     */
+    inline void rehash(size_t minimumCapacity, float loadFactor) {
+        BasicHashtableImpl::rehash(minimumCapacity, loadFactor);
+    }
+
+protected:
+    static inline const TEntry& entryFor(const Bucket& bucket) {
+        return reinterpret_cast<const TEntry&>(bucket.entry);
+    }
+
+    static inline TEntry& entryFor(Bucket& bucket) {
+        return reinterpret_cast<TEntry&>(bucket.entry);
+    }
+
+    virtual bool compareBucketKey(const Bucket& bucket, const void* __restrict__ key) const;
+    virtual void initializeBucketEntry(Bucket& bucket, const void* __restrict__ entry) const;
+    virtual void destroyBucketEntry(Bucket& bucket) const;
+
+private:
+    // For dumping the raw contents of a hashtable during testing.
+    friend class BasicHashtableTest;
+    inline uint32_t cookieAt(size_t index) const {
+        return bucketAt(mBuckets, index).cookie;
+    }
+};
+
+template <typename TKey, typename TEntry>
+BasicHashtable<TKey, TEntry>::BasicHashtable(size_t minimumInitialCapacity, float loadFactor) :
+        BasicHashtableImpl(sizeof(TEntry), traits<TEntry>::has_trivial_dtor,
+                minimumInitialCapacity, loadFactor) {
+}
+
+template <typename TKey, typename TEntry>
+BasicHashtable<TKey, TEntry>::BasicHashtable(const BasicHashtable<TKey, TEntry>& other) :
+        BasicHashtableImpl(other) {
+}
+
+template <typename TKey, typename TEntry>
+BasicHashtable<TKey, TEntry>::~BasicHashtable() {
+    dispose();
+}
+
+template <typename TKey, typename TEntry>
+bool BasicHashtable<TKey, TEntry>::compareBucketKey(const Bucket& bucket,
+        const void* __restrict__ key) const {
+    return entryFor(bucket).getKey() == *static_cast<const TKey*>(key);
+}
+
+template <typename TKey, typename TEntry>
+void BasicHashtable<TKey, TEntry>::initializeBucketEntry(Bucket& bucket,
+        const void* __restrict__ entry) const {
+    if (!traits<TEntry>::has_trivial_copy) {
+        new (&entryFor(bucket)) TEntry(*(static_cast<const TEntry*>(entry)));
+    } else {
+        memcpy(&entryFor(bucket), entry, sizeof(TEntry));
+    }
+}
+
+template <typename TKey, typename TEntry>
+void BasicHashtable<TKey, TEntry>::destroyBucketEntry(Bucket& bucket) const {
+    if (!traits<TEntry>::has_trivial_dtor) {
+        entryFor(bucket).~TEntry();
+    }
+}
+
+}; // namespace android
+
+#endif // ANDROID_BASIC_HASHTABLE_H
diff --git a/include/utils/CallStack.h b/include/utils/CallStack.h
index 8817120..079e20c 100644
--- a/include/utils/CallStack.h
+++ b/include/utils/CallStack.h
@@ -21,6 +21,7 @@
 #include <sys/types.h>
 
 #include <utils/String8.h>
+#include <corkscrew/backtrace.h>
 
 // ---------------------------------------------------------------------------
 
@@ -61,11 +62,8 @@
     size_t size() const { return mCount; }
 
 private:
-    // Internal helper function
-    String8 toStringSingleLevel(const char* prefix, int32_t level) const;
-
-    size_t      mCount;
-    const void* mStack[MAX_DEPTH];
+    size_t mCount;
+    backtrace_frame_t mStack[MAX_DEPTH];
 };
 
 }; // namespace android
diff --git a/include/utils/GenerationCache.h b/include/utils/GenerationCache.h
index bb9ddd6..83cda86 100644
--- a/include/utils/GenerationCache.h
+++ b/include/utils/GenerationCache.h
@@ -34,17 +34,17 @@
 
 template<typename EntryKey, typename EntryValue>
 struct Entry: public LightRefBase<Entry<EntryKey, EntryValue> > {
-    Entry() { }
-    Entry(const Entry<EntryKey, EntryValue>& e):
-            key(e.key), value(e.value), parent(e.parent), child(e.child) { }
-    Entry(sp<Entry<EntryKey, EntryValue> > e):
-            key(e->key), value(e->value), parent(e->parent), child(e->child) { }
+    Entry(const Entry<EntryKey, EntryValue>& e) :
+            key(e.key), value(e.value),
+            parent(e.parent), child(e.child) { }
+    Entry(const EntryKey& key, const EntryValue& value) :
+            key(key), value(value) { }
 
     EntryKey key;
     EntryValue value;
 
-    sp<Entry<EntryKey, EntryValue> > parent;
-    sp<Entry<EntryKey, EntryValue> > child;
+    sp<Entry<EntryKey, EntryValue> > parent; // next older entry
+    sp<Entry<EntryKey, EntryValue> > child;  // next younger entry
 }; // struct Entry
 
 /**
@@ -62,23 +62,20 @@
 
     void setOnEntryRemovedListener(OnEntryRemoved<K, V>* listener);
 
+    size_t size() const;
+
     void clear();
 
-    bool contains(K key) const;
-    V get(K key);
-    K getKeyAt(uint32_t index) const;
-    bool put(K key, V value);
-    V remove(K key);
-    V removeOldest();
-    V getValueAt(uint32_t index) const;
+    bool contains(const K& key) const;
+    const K& getKeyAt(size_t index) const;
+    const V& getValueAt(size_t index) const;
 
-    uint32_t size() const;
+    const V& get(const K& key);
+    bool put(const K& key, const V& value);
 
-    void addToCache(sp<Entry<K, V> > entry, K key, V value);
-    void attachToCache(sp<Entry<K, V> > entry);
-    void detachFromCache(sp<Entry<K, V> > entry);
-
-    V removeAt(ssize_t index);
+    void removeAt(ssize_t index);
+    bool remove(const K& key);
+    bool removeOldest();
 
 private:
     KeyedVector<K, sp<Entry<K, V> > > mCache;
@@ -88,6 +85,9 @@
 
     sp<Entry<K, V> > mOldest;
     sp<Entry<K, V> > mYoungest;
+
+    void attachToCache(const sp<Entry<K, V> >& entry);
+    void detachFromCache(const sp<Entry<K, V> >& entry);
 }; // class GenerationCache
 
 template<typename K, typename V>
@@ -130,45 +130,44 @@
 }
 
 template<typename K, typename V>
-bool GenerationCache<K, V>::contains(K key) const {
+bool GenerationCache<K, V>::contains(const K& key) const {
     return mCache.indexOfKey(key) >= 0;
 }
 
 template<typename K, typename V>
-K GenerationCache<K, V>::getKeyAt(uint32_t index) const {
+const K& GenerationCache<K, V>::getKeyAt(size_t index) const {
     return mCache.keyAt(index);
 }
 
 template<typename K, typename V>
-V GenerationCache<K, V>::getValueAt(uint32_t index) const {
+const V& GenerationCache<K, V>::getValueAt(size_t index) const {
     return mCache.valueAt(index)->value;
 }
 
 template<typename K, typename V>
-V GenerationCache<K, V>::get(K key) {
+const V& GenerationCache<K, V>::get(const K& key) {
     ssize_t index = mCache.indexOfKey(key);
     if (index >= 0) {
-        sp<Entry<K, V> > entry = mCache.valueAt(index);
-        if (entry.get()) {
-            detachFromCache(entry);
-            attachToCache(entry);
-            return entry->value;
-        }
+        const sp<Entry<K, V> >& entry = mCache.valueAt(index);
+        detachFromCache(entry);
+        attachToCache(entry);
+        return entry->value;
     }
 
     return NULL;
 }
 
 template<typename K, typename V>
-bool GenerationCache<K, V>::put(K key, V value) {
+bool GenerationCache<K, V>::put(const K& key, const V& value) {
     if (mMaxCapacity != kUnlimitedCapacity && mCache.size() >= mMaxCapacity) {
         removeOldest();
     }
 
     ssize_t index = mCache.indexOfKey(key);
     if (index < 0) {
-        sp<Entry<K, V> > entry = new Entry<K, V>;
-        addToCache(entry, key, value);
+        sp<Entry<K, V> > entry = new Entry<K, V>(key, value);
+        mCache.add(key, entry);
+        attachToCache(entry);
         return true;
     }
 
@@ -176,49 +175,44 @@
 }
 
 template<typename K, typename V>
-void GenerationCache<K, V>::addToCache(sp<Entry<K, V> > entry, K key, V value) {
-    entry->key = key;
-    entry->value = value;
-    mCache.add(key, entry);
-    attachToCache(entry);
-}
-
-template<typename K, typename V>
-V GenerationCache<K, V>::remove(K key) {
+bool GenerationCache<K, V>::remove(const K& key) {
     ssize_t index = mCache.indexOfKey(key);
     if (index >= 0) {
-        return removeAt(index);
+        removeAt(index);
+        return true;
     }
 
-    return NULL;
+    return false;
 }
 
 template<typename K, typename V>
-V GenerationCache<K, V>::removeAt(ssize_t index) {
+void GenerationCache<K, V>::removeAt(ssize_t index) {
     sp<Entry<K, V> > entry = mCache.valueAt(index);
     if (mListener) {
         (*mListener)(entry->key, entry->value);
     }
     mCache.removeItemsAt(index, 1);
     detachFromCache(entry);
-
-    return entry->value;
 }
 
 template<typename K, typename V>
-V GenerationCache<K, V>::removeOldest() {
+bool GenerationCache<K, V>::removeOldest() {
     if (mOldest.get()) {
         ssize_t index = mCache.indexOfKey(mOldest->key);
         if (index >= 0) {
-            return removeAt(index);
+            removeAt(index);
+            return true;
         }
+        LOGE("GenerationCache: removeOldest failed to find the item in the cache "
+                "with the given key, but we know it must be in there.  "
+                "Is the key comparator kaput?");
     }
 
-    return NULL;
+    return false;
 }
 
 template<typename K, typename V>
-void GenerationCache<K, V>::attachToCache(sp<Entry<K, V> > entry) {
+void GenerationCache<K, V>::attachToCache(const sp<Entry<K, V> >& entry) {
     if (!mYoungest.get()) {
         mYoungest = mOldest = entry;
     } else {
@@ -229,20 +223,16 @@
 }
 
 template<typename K, typename V>
-void GenerationCache<K, V>::detachFromCache(sp<Entry<K, V> > entry) {
+void GenerationCache<K, V>::detachFromCache(const sp<Entry<K, V> >& entry) {
     if (entry->parent.get()) {
         entry->parent->child = entry->child;
+    } else {
+        mOldest = entry->child;
     }
 
     if (entry->child.get()) {
         entry->child->parent = entry->parent;
-    }
-
-    if (mOldest == entry) {
-        mOldest = entry->child;
-    }
-
-    if (mYoungest == entry) {
+    } else {
         mYoungest = entry->parent;
     }
 
diff --git a/include/utils/TypeHelpers.h b/include/utils/TypeHelpers.h
index a1663f3..7b4fb70 100644
--- a/include/utils/TypeHelpers.h
+++ b/include/utils/TypeHelpers.h
@@ -213,6 +213,9 @@
 
 template <typename KEY, typename VALUE>
 struct key_value_pair_t {
+    typedef KEY key_t;
+    typedef VALUE value_t;
+
     KEY     key;
     VALUE   value;
     key_value_pair_t() { }
@@ -222,6 +225,12 @@
     inline bool operator < (const key_value_pair_t& o) const {
         return strictly_order_type(key, o.key);
     }
+    inline const KEY& getKey() const {
+        return key;
+    }
+    inline const VALUE& getValue() const {
+        return value;
+    }
 };
 
 template<>
@@ -243,6 +252,41 @@
 
 // ---------------------------------------------------------------------------
 
+/*
+ * Hash codes.
+ */
+typedef uint32_t hash_t;
+
+template <typename TKey>
+hash_t hash_type(const TKey& key);
+
+/* Built-in hash code specializations.
+ * Assumes pointers are 32bit. */
+#define ANDROID_INT32_HASH(T) \
+        template <> inline hash_t hash_type(const T& value) { return hash_t(value); }
+#define ANDROID_INT64_HASH(T) \
+        template <> inline hash_t hash_type(const T& value) { \
+                return hash_t((value >> 32) ^ value); }
+#define ANDROID_REINTERPRET_HASH(T, R) \
+        template <> inline hash_t hash_type(const T& value) { \
+                return hash_type(*reinterpret_cast<const R*>(&value)); }
+
+ANDROID_INT32_HASH(bool)
+ANDROID_INT32_HASH(int8_t)
+ANDROID_INT32_HASH(uint8_t)
+ANDROID_INT32_HASH(int16_t)
+ANDROID_INT32_HASH(uint16_t)
+ANDROID_INT32_HASH(int32_t)
+ANDROID_INT32_HASH(uint32_t)
+ANDROID_INT64_HASH(int64_t)
+ANDROID_INT64_HASH(uint64_t)
+ANDROID_REINTERPRET_HASH(float, uint32_t)
+ANDROID_REINTERPRET_HASH(double, uint64_t)
+
+template <typename T> inline hash_t hash_type(const T*& value) {
+    return hash_type(uintptr_t(value));
+}
+
 }; // namespace android
 
 // ---------------------------------------------------------------------------
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index 5de87ec..e8fb1d9 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -24,8 +24,8 @@
 
 #include <stdio.h>
 
-//#undef LOGV
-//#define LOGV(...) fprintf(stderr, __VA_ARGS__)
+//#undef ALOGV
+//#define ALOGV(...) fprintf(stderr, __VA_ARGS__)
 
 namespace android {
 
@@ -73,7 +73,7 @@
 void BpBinder::ObjectManager::kill()
 {
     const size_t N = mObjects.size();
-    LOGV("Killing %d objects in manager %p", N, this);
+    ALOGV("Killing %d objects in manager %p", N, this);
     for (size_t i=0; i<N; i++) {
         const entry_t& e = mObjects.valueAt(i);
         if (e.func != NULL) {
@@ -92,7 +92,7 @@
     , mObitsSent(0)
     , mObituaries(NULL)
 {
-    LOGV("Creating BpBinder %p handle %d\n", this, mHandle);
+    ALOGV("Creating BpBinder %p handle %d\n", this, mHandle);
 
     extendObjectLifetime(OBJECT_LIFETIME_WEAK);
     IPCThreadState::self()->incWeakHandle(handle);
@@ -190,7 +190,7 @@
                 if (!mObituaries) {
                     return NO_MEMORY;
                 }
-                LOGV("Requesting death notification: %p handle %d\n", this, mHandle);
+                ALOGV("Requesting death notification: %p handle %d\n", this, mHandle);
                 getWeakRefs()->incWeak(this);
                 IPCThreadState* self = IPCThreadState::self();
                 self->requestDeathNotification(mHandle, this);
@@ -226,7 +226,7 @@
             }
             mObituaries->removeAt(i);
             if (mObituaries->size() == 0) {
-                LOGV("Clearing death notification: %p handle %d\n", this, mHandle);
+                ALOGV("Clearing death notification: %p handle %d\n", this, mHandle);
                 IPCThreadState* self = IPCThreadState::self();
                 self->clearDeathNotification(mHandle, this);
                 self->flushCommands();
@@ -242,7 +242,7 @@
 
 void BpBinder::sendObituary()
 {
-    LOGV("Sending obituary for proxy %p handle %d, mObitsSent=%s\n",
+    ALOGV("Sending obituary for proxy %p handle %d, mObitsSent=%s\n",
         this, mHandle, mObitsSent ? "true" : "false");
 
     mAlive = 0;
@@ -251,7 +251,7 @@
     mLock.lock();
     Vector<Obituary>* obits = mObituaries;
     if(obits != NULL) {
-        LOGV("Clearing sent death notification: %p handle %d\n", this, mHandle);
+        ALOGV("Clearing sent death notification: %p handle %d\n", this, mHandle);
         IPCThreadState* self = IPCThreadState::self();
         self->clearDeathNotification(mHandle, this);
         self->flushCommands();
@@ -260,7 +260,7 @@
     mObitsSent = 1;
     mLock.unlock();
 
-    LOGV("Reporting death of proxy %p for %d recipients\n",
+    ALOGV("Reporting death of proxy %p for %d recipients\n",
         this, obits ? obits->size() : 0);
 
     if (obits != NULL) {
@@ -276,7 +276,7 @@
 void BpBinder::reportOneDeath(const Obituary& obit)
 {
     sp<DeathRecipient> recipient = obit.recipient.promote();
-    LOGV("Reporting death to recipient: %p\n", recipient.get());
+    ALOGV("Reporting death to recipient: %p\n", recipient.get());
     if (recipient == NULL) return;
 
     recipient->binderDied(this);
@@ -288,7 +288,7 @@
     object_cleanup_func func)
 {
     AutoMutex _l(mLock);
-    LOGV("Attaching object %p to binder %p (manager=%p)", object, this, &mObjects);
+    ALOGV("Attaching object %p to binder %p (manager=%p)", object, this, &mObjects);
     mObjects.attach(objectID, object, cleanupCookie, func);
 }
 
@@ -311,7 +311,7 @@
 
 BpBinder::~BpBinder()
 {
-    LOGV("Destroying BpBinder %p handle %d\n", this, mHandle);
+    ALOGV("Destroying BpBinder %p handle %d\n", this, mHandle);
 
     IPCThreadState* ipc = IPCThreadState::self();
 
@@ -338,15 +338,15 @@
 
 void BpBinder::onFirstRef()
 {
-    LOGV("onFirstRef BpBinder %p handle %d\n", this, mHandle);
+    ALOGV("onFirstRef BpBinder %p handle %d\n", this, mHandle);
     IPCThreadState* ipc = IPCThreadState::self();
     if (ipc) ipc->incStrongHandle(mHandle);
 }
 
 void BpBinder::onLastStrongRef(const void* id)
 {
-    LOGV("onLastStrongRef BpBinder %p handle %d\n", this, mHandle);
-    IF_LOGV() {
+    ALOGV("onLastStrongRef BpBinder %p handle %d\n", this, mHandle);
+    IF_ALOGV() {
         printRefs();
     }
     IPCThreadState* ipc = IPCThreadState::self();
@@ -355,7 +355,7 @@
 
 bool BpBinder::onIncStrongAttempted(uint32_t flags, const void* id)
 {
-    LOGV("onIncStrongAttempted BpBinder %p handle %d\n", this, mHandle);
+    ALOGV("onIncStrongAttempted BpBinder %p handle %d\n", this, mHandle);
     IPCThreadState* ipc = IPCThreadState::self();
     return ipc ? ipc->attemptIncStrongHandle(mHandle) == NO_ERROR : false;
 }
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 5d34787..641134a 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -56,12 +56,12 @@
 
 #else
 
-#define IF_LOG_TRANSACTIONS() IF_LOG(LOG_VERBOSE, "transact")
-#define IF_LOG_COMMANDS() IF_LOG(LOG_VERBOSE, "ipc")
-#define LOG_REMOTEREFS(...) LOG(LOG_DEBUG, "remoterefs", __VA_ARGS__)
-#define IF_LOG_REMOTEREFS() IF_LOG(LOG_DEBUG, "remoterefs")
-#define LOG_THREADPOOL(...) LOG(LOG_DEBUG, "threadpool", __VA_ARGS__)
-#define LOG_ONEWAY(...) LOG(LOG_DEBUG, "ipc", __VA_ARGS__)
+#define IF_LOG_TRANSACTIONS() IF_ALOG(LOG_VERBOSE, "transact")
+#define IF_LOG_COMMANDS() IF_ALOG(LOG_VERBOSE, "ipc")
+#define LOG_REMOTEREFS(...) ALOG(LOG_DEBUG, "remoterefs", __VA_ARGS__)
+#define IF_LOG_REMOTEREFS() IF_ALOG(LOG_DEBUG, "remoterefs")
+#define LOG_THREADPOOL(...) ALOG(LOG_DEBUG, "threadpool", __VA_ARGS__)
+#define LOG_ONEWAY(...) ALOG(LOG_DEBUG, "ipc", __VA_ARGS__)
 
 #endif
 
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 6b4c1a6..6cd43aa 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -44,7 +44,7 @@
 #endif
 
 #define LOG_REFS(...)
-//#define LOG_REFS(...) LOG(LOG_DEBUG, "Parcel", __VA_ARGS__)
+//#define LOG_REFS(...) ALOG(LOG_DEBUG, "Parcel", __VA_ARGS__)
 
 // ---------------------------------------------------------------------------
 
@@ -330,7 +330,7 @@
     err = continueWrite(size);
     if (err == NO_ERROR) {
         mDataSize = size;
-        LOGV("setDataSize Setting data size of %p to %d\n", this, mDataSize);
+        ALOGV("setDataSize Setting data size of %p to %d\n", this, mDataSize);
     }
     return err;
 }
@@ -534,10 +534,10 @@
 {
     //printf("Finish write of %d\n", len);
     mDataPos += len;
-    LOGV("finishWrite Setting data pos of %p to %d\n", this, mDataPos);
+    ALOGV("finishWrite Setting data pos of %p to %d\n", this, mDataPos);
     if (mDataPos > mDataSize) {
         mDataSize = mDataPos;
-        LOGV("finishWrite Setting data size of %p to %d\n", this, mDataSize);
+        ALOGV("finishWrite Setting data size of %p to %d\n", this, mDataSize);
     }
     //printf("New pos=%d, size=%d\n", mDataPos, mDataSize);
     return NO_ERROR;
@@ -722,7 +722,15 @@
 
 status_t Parcel::writeDupFileDescriptor(int fd)
 {
-    return writeFileDescriptor(dup(fd), true /*takeOwnership*/);
+    int dupFd = dup(fd);
+    if (dupFd < 0) {
+        return -errno;
+    }
+    status_t err = writeFileDescriptor(dupFd, true /*takeOwnership*/);
+    if (err) {
+        close(dupFd);
+    }
+    return err;
 }
 
 status_t Parcel::writeBlob(size_t len, WritableBlob* outBlob)
@@ -730,7 +738,7 @@
     status_t status;
 
     if (!mAllowFds || len <= IN_PLACE_BLOB_LIMIT) {
-        LOGV("writeBlob: write in place");
+        ALOGV("writeBlob: write in place");
         status = writeInt32(0);
         if (status) return status;
 
@@ -741,7 +749,7 @@
         return NO_ERROR;
     }
 
-    LOGV("writeBlob: write to ashmem");
+    ALOGV("writeBlob: write to ashmem");
     int fd = ashmem_create_region("Parcel Blob", len);
     if (fd < 0) return NO_MEMORY;
 
@@ -865,7 +873,7 @@
     if ((mDataPos+PAD_SIZE(len)) >= mDataPos && (mDataPos+PAD_SIZE(len)) <= mDataSize) {
         memcpy(outData, mData+mDataPos, len);
         mDataPos += PAD_SIZE(len);
-        LOGV("read Setting data pos of %p to %d\n", this, mDataPos);
+        ALOGV("read Setting data pos of %p to %d\n", this, mDataPos);
         return NO_ERROR;
     }
     return NOT_ENOUGH_DATA;
@@ -876,7 +884,7 @@
     if ((mDataPos+PAD_SIZE(len)) >= mDataPos && (mDataPos+PAD_SIZE(len)) <= mDataSize) {
         const void* data = mData+mDataPos;
         mDataPos += PAD_SIZE(len);
-        LOGV("readInplace Setting data pos of %p to %d\n", this, mDataPos);
+        ALOGV("readInplace Setting data pos of %p to %d\n", this, mDataPos);
         return data;
     }
     return NULL;
@@ -987,7 +995,7 @@
         if (eos) {
             const size_t len = eos - str;
             mDataPos += PAD_SIZE(len+1);
-            LOGV("readCString Setting data pos of %p to %d\n", this, mDataPos);
+            ALOGV("readCString Setting data pos of %p to %d\n", this, mDataPos);
             return str;
         }
     }
@@ -1102,7 +1110,7 @@
     if (status) return status;
 
     if (!useAshmem) {
-        LOGV("readBlob: read in place");
+        ALOGV("readBlob: read in place");
         const void* ptr = readInplace(len);
         if (!ptr) return BAD_VALUE;
 
@@ -1110,7 +1118,7 @@
         return NO_ERROR;
     }
 
-    LOGV("readBlob: read from ashmem");
+    ALOGV("readBlob: read from ashmem");
     int fd = readFileDescriptor();
     if (fd == int(BAD_TYPE)) return BAD_VALUE;
 
@@ -1164,7 +1172,7 @@
             // When transferring a NULL object, we don't write it into
             // the object list, so we don't want to check for it when
             // reading.
-            LOGV("readObject Setting data pos of %p to %d\n", this, mDataPos);
+            ALOGV("readObject Setting data pos of %p to %d\n", this, mDataPos);
             return obj;
         }
         
@@ -1174,7 +1182,7 @@
         size_t opos = mNextObjectHint;
         
         if (N > 0) {
-            LOGV("Parcel %p looking for obj at %d, hint=%d\n",
+            ALOGV("Parcel %p looking for obj at %d, hint=%d\n",
                  this, DPOS, opos);
             
             // Start at the current hint position, looking for an object at
@@ -1188,10 +1196,10 @@
             }
             if (OBJS[opos] == DPOS) {
                 // Found it!
-                LOGV("Parcel found obj %d at index %d with forward search",
+                ALOGV("Parcel found obj %d at index %d with forward search",
                      this, DPOS, opos);
                 mNextObjectHint = opos+1;
-                LOGV("readObject Setting data pos of %p to %d\n", this, mDataPos);
+                ALOGV("readObject Setting data pos of %p to %d\n", this, mDataPos);
                 return obj;
             }
         
@@ -1201,10 +1209,10 @@
             }
             if (OBJS[opos] == DPOS) {
                 // Found it!
-                LOGV("Parcel found obj %d at index %d with backward search",
+                ALOGV("Parcel found obj %d at index %d with backward search",
                      this, DPOS, opos);
                 mNextObjectHint = opos+1;
-                LOGV("readObject Setting data pos of %p to %d\n", this, mDataPos);
+                ALOGV("readObject Setting data pos of %p to %d\n", this, mDataPos);
                 return obj;
             }
         }
@@ -1260,7 +1268,7 @@
     mDataSize = mDataCapacity = dataSize;
     //LOGI("setDataReference Setting data size of %p to %lu (pid=%d)\n", this, mDataSize, getpid());
     mDataPos = 0;
-    LOGV("setDataReference Setting data pos of %p to %d\n", this, mDataPos);
+    ALOGV("setDataReference Setting data pos of %p to %d\n", this, mDataPos);
     mObjects = const_cast<size_t*>(objects);
     mObjectsSize = mObjectsCapacity = objectsCount;
     mNextObjectHint = 0;
@@ -1370,8 +1378,8 @@
     }
     
     mDataSize = mDataPos = 0;
-    LOGV("restartWrite Setting data size of %p to %d\n", this, mDataSize);
-    LOGV("restartWrite Setting data pos of %p to %d\n", this, mDataPos);
+    ALOGV("restartWrite Setting data size of %p to %d\n", this, mDataSize);
+    ALOGV("restartWrite Setting data pos of %p to %d\n", this, mDataPos);
         
     free(mObjects);
     mObjects = NULL;
@@ -1445,7 +1453,7 @@
         mData = data;
         mObjects = objects;
         mDataSize = (mDataSize < desired) ? mDataSize : desired;
-        LOGV("continueWrite Setting data size of %p to %d\n", this, mDataSize);
+        ALOGV("continueWrite Setting data size of %p to %d\n", this, mDataSize);
         mDataCapacity = desired;
         mObjectsSize = mObjectsCapacity = objectsSize;
         mNextObjectHint = 0;
@@ -1485,11 +1493,11 @@
         } else {
             if (mDataSize > desired) {
                 mDataSize = desired;
-                LOGV("continueWrite Setting data size of %p to %d\n", this, mDataSize);
+                ALOGV("continueWrite Setting data size of %p to %d\n", this, mDataSize);
             }
             if (mDataPos > desired) {
                 mDataPos = desired;
-                LOGV("continueWrite Setting data pos of %p to %d\n", this, mDataPos);
+                ALOGV("continueWrite Setting data pos of %p to %d\n", this, mDataPos);
             }
         }
         
@@ -1508,8 +1516,8 @@
         
         mData = data;
         mDataSize = mDataPos = 0;
-        LOGV("continueWrite Setting data size of %p to %d\n", this, mDataSize);
-        LOGV("continueWrite Setting data pos of %p to %d\n", this, mDataPos);
+        ALOGV("continueWrite Setting data size of %p to %d\n", this, mDataSize);
+        ALOGV("continueWrite Setting data pos of %p to %d\n", this, mDataPos);
         mDataCapacity = desired;
     }
 
@@ -1523,8 +1531,8 @@
     mDataSize = 0;
     mDataCapacity = 0;
     mDataPos = 0;
-    LOGV("initState Setting data size of %p to %d\n", this, mDataSize);
-    LOGV("initState Setting data pos of %p to %d\n", this, mDataPos);
+    ALOGV("initState Setting data size of %p to %d\n", this, mDataSize);
+    ALOGV("initState Setting data pos of %p to %d\n", this, mDataPos);
     mObjects = NULL;
     mObjectsSize = 0;
     mObjectsCapacity = 0;
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index f5288c8..f06a59e 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -288,7 +288,7 @@
         int32_t s = android_atomic_add(1, &mThreadPoolSeq);
         char buf[32];
         sprintf(buf, "Binder Thread #%d", s);
-        LOGV("Spawning new pooled thread, name=%s\n", buf);
+        ALOGV("Spawning new pooled thread, name=%s\n", buf);
         sp<Thread> t = new PoolThread(isMain);
         t->run(buf);
     }
diff --git a/libs/gui/Android.mk b/libs/gui/Android.mk
index 9767568..b8be67d 100644
--- a/libs/gui/Android.mk
+++ b/libs/gui/Android.mk
@@ -2,11 +2,13 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES:= \
+	BitTube.cpp \
+	DisplayEventReceiver.cpp \
+	IDisplayEventConnection.cpp \
 	ISensorEventConnection.cpp \
 	ISensorServer.cpp \
 	ISurfaceTexture.cpp \
 	Sensor.cpp \
-	SensorChannel.cpp \
 	SensorEventQueue.cpp \
 	SensorManager.cpp \
 	SurfaceTexture.cpp \
diff --git a/libs/gui/BitTube.cpp b/libs/gui/BitTube.cpp
new file mode 100644
index 0000000..fa8d0ea
--- /dev/null
+++ b/libs/gui/BitTube.cpp
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <utils/Errors.h>
+
+#include <binder/Parcel.h>
+
+#include <gui/BitTube.h>
+
+namespace android {
+// ----------------------------------------------------------------------------
+
+BitTube::BitTube()
+    : mSendFd(-1), mReceiveFd(-1)
+{
+    int fds[2];
+    if (pipe(fds) == 0) {
+        mReceiveFd = fds[0];
+        mSendFd = fds[1];
+        fcntl(mReceiveFd, F_SETFL, O_NONBLOCK);
+        fcntl(mSendFd, F_SETFL, O_NONBLOCK);
+    } else {
+        mReceiveFd = -errno;
+        LOGE("BitTube: pipe creation failed (%s)", strerror(-mReceiveFd));
+    }
+}
+
+BitTube::BitTube(const Parcel& data)
+    : mSendFd(-1), mReceiveFd(-1)
+{
+    mReceiveFd = dup(data.readFileDescriptor());
+    if (mReceiveFd >= 0) {
+        fcntl(mReceiveFd, F_SETFL, O_NONBLOCK);
+    } else {
+        mReceiveFd = -errno;
+        LOGE("BitTube(Parcel): can't dup filedescriptor (%s)",
+                strerror(-mReceiveFd));
+    }
+}
+
+BitTube::~BitTube()
+{
+    if (mSendFd >= 0)
+        close(mSendFd);
+
+    if (mReceiveFd >= 0)
+        close(mReceiveFd);
+}
+
+status_t BitTube::initCheck() const
+{
+    if (mReceiveFd < 0) {
+        return status_t(mReceiveFd);
+    }
+    return NO_ERROR;
+}
+
+int BitTube::getFd() const
+{
+    return mReceiveFd;
+}
+
+ssize_t BitTube::write(void const* vaddr, size_t size)
+{
+    ssize_t err, len;
+    do {
+        len = ::write(mSendFd, vaddr, size);
+        err = len < 0 ? errno : 0;
+    } while (err == EINTR);
+    return err == 0 ? len : -err;
+
+}
+
+ssize_t BitTube::read(void* vaddr, size_t size)
+{
+    ssize_t err, len;
+    do {
+        len = ::read(mReceiveFd, vaddr, size);
+        err = len < 0 ? errno : 0;
+    } while (err == EINTR);
+    if (err == EAGAIN || err == EWOULDBLOCK) {
+        // EAGAIN means that we have non-blocking I/O but there was
+        // no data to be read. Nothing the client should care about.
+        return 0;
+    }
+    return err == 0 ? len : -err;
+}
+
+status_t BitTube::writeToParcel(Parcel* reply) const
+{
+    if (mReceiveFd < 0)
+        return -EINVAL;
+
+    status_t result = reply->writeDupFileDescriptor(mReceiveFd);
+    close(mReceiveFd);
+    mReceiveFd = -1;
+    return result;
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
diff --git a/libs/gui/DisplayEventReceiver.cpp b/libs/gui/DisplayEventReceiver.cpp
new file mode 100644
index 0000000..3b29a11
--- /dev/null
+++ b/libs/gui/DisplayEventReceiver.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string.h>
+
+#include <utils/Errors.h>
+
+#include <gui/BitTube.h>
+#include <gui/DisplayEventReceiver.h>
+#include <gui/IDisplayEventConnection.h>
+
+#include <private/gui/ComposerService.h>
+
+#include <surfaceflinger/ISurfaceComposer.h>
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+DisplayEventReceiver::DisplayEventReceiver() {
+    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+    if (sf != NULL) {
+        mEventConnection = sf->createDisplayEventConnection();
+        if (mEventConnection != NULL) {
+            mDataChannel = mEventConnection->getDataChannel();
+        }
+    }
+}
+
+DisplayEventReceiver::~DisplayEventReceiver() {
+}
+
+status_t DisplayEventReceiver::initCheck() const {
+    if (mDataChannel != NULL)
+        return NO_ERROR;
+    return NO_INIT;
+}
+
+int DisplayEventReceiver::getFd() const {
+    if (mDataChannel == NULL)
+        return NO_INIT;
+
+    return mDataChannel->getFd();
+}
+
+ssize_t DisplayEventReceiver::getEvents(DisplayEventReceiver::Event* events,
+        size_t count) {
+    ssize_t size = mDataChannel->read(events, sizeof(events[0])*count);
+    LOGE_IF(size<0,
+            "DisplayEventReceiver::getEvents error (%s)",
+            strerror(-size));
+    if (size >= 0) {
+        // Note: if (size % sizeof(events[0])) != 0, we've got a
+        // partial read. This can happen if the queue filed up (ie: if we
+        // didn't pull from it fast enough).
+        // We discard the partial event and rely on the sender to
+        // re-send the event if appropriate (some events, like VSYNC
+        // can be lost forever).
+
+        // returns number of events read
+        size /= sizeof(events[0]);
+    }
+    return size;
+}
+
+// ---------------------------------------------------------------------------
+
+}; // namespace android
diff --git a/libs/gui/IDisplayEventConnection.cpp b/libs/gui/IDisplayEventConnection.cpp
new file mode 100644
index 0000000..44127fb
--- /dev/null
+++ b/libs/gui/IDisplayEventConnection.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+#include <utils/Timers.h>
+
+#include <binder/Parcel.h>
+#include <binder/IInterface.h>
+
+#include <gui/IDisplayEventConnection.h>
+#include <gui/BitTube.h>
+
+namespace android {
+// ----------------------------------------------------------------------------
+
+enum {
+    GET_DATA_CHANNEL = IBinder::FIRST_CALL_TRANSACTION,
+};
+
+class BpDisplayEventConnection : public BpInterface<IDisplayEventConnection>
+{
+public:
+    BpDisplayEventConnection(const sp<IBinder>& impl)
+        : BpInterface<IDisplayEventConnection>(impl)
+    {
+    }
+
+    virtual sp<BitTube> getDataChannel() const
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IDisplayEventConnection::getInterfaceDescriptor());
+        remote()->transact(GET_DATA_CHANNEL, data, &reply);
+        return new BitTube(reply);
+    }
+};
+
+IMPLEMENT_META_INTERFACE(DisplayEventConnection, "android.gui.DisplayEventConnection");
+
+// ----------------------------------------------------------------------------
+
+status_t BnDisplayEventConnection::onTransact(
+    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+    switch(code) {
+        case GET_DATA_CHANNEL: {
+            CHECK_INTERFACE(IDisplayEventConnection, data, reply);
+            sp<BitTube> channel(getDataChannel());
+            channel->writeToParcel(reply);
+            return NO_ERROR;
+        } break;
+    }
+    return BBinder::onTransact(code, data, reply, flags);
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
diff --git a/libs/gui/ISensorEventConnection.cpp b/libs/gui/ISensorEventConnection.cpp
index a5083fe..0e51e8e 100644
--- a/libs/gui/ISensorEventConnection.cpp
+++ b/libs/gui/ISensorEventConnection.cpp
@@ -25,7 +25,7 @@
 #include <binder/IInterface.h>
 
 #include <gui/ISensorEventConnection.h>
-#include <gui/SensorChannel.h>
+#include <gui/BitTube.h>
 
 namespace android {
 // ----------------------------------------------------------------------------
@@ -44,12 +44,12 @@
     {
     }
 
-    virtual sp<SensorChannel> getSensorChannel() const
+    virtual sp<BitTube> getSensorChannel() const
     {
         Parcel data, reply;
         data.writeInterfaceToken(ISensorEventConnection::getInterfaceDescriptor());
         remote()->transact(GET_SENSOR_CHANNEL, data, &reply);
-        return new SensorChannel(reply);
+        return new BitTube(reply);
     }
 
     virtual status_t enableDisable(int handle, bool enabled)
@@ -83,7 +83,7 @@
     switch(code) {
         case GET_SENSOR_CHANNEL: {
             CHECK_INTERFACE(ISensorEventConnection, data, reply);
-            sp<SensorChannel> channel(getSensorChannel());
+            sp<BitTube> channel(getSensorChannel());
             channel->writeToParcel(reply);
             return NO_ERROR;
         } break;
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 86bc62a..db32827 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -29,6 +29,9 @@
 
 #include <surfaceflinger/ISurfaceComposer.h>
 
+#include <gui/BitTube.h>
+#include <gui/IDisplayEventConnection.h>
+
 #include <ui/DisplayInfo.h>
 
 #include <gui/ISurfaceTexture.h>
@@ -44,6 +47,8 @@
 
 namespace android {
 
+class IDisplayEventConnection;
+
 class BpSurfaceComposer : public BpInterface<ISurfaceComposer>
 {
 public:
@@ -174,6 +179,27 @@
         }
         return result != 0;
     }
+
+    virtual sp<IDisplayEventConnection> createDisplayEventConnection()
+    {
+        Parcel data, reply;
+        sp<IDisplayEventConnection> result;
+        int err = data.writeInterfaceToken(
+                ISurfaceComposer::getInterfaceDescriptor());
+        if (err != NO_ERROR) {
+            return result;
+        }
+        err = remote()->transact(
+                BnSurfaceComposer::CREATE_DISPLAY_EVENT_CONNECTION,
+                data, &reply);
+        if (err != NO_ERROR) {
+            LOGE("ISurfaceComposer::createDisplayEventConnection: error performing "
+                    "transaction: %s (%d)", strerror(-err), -err);
+            return result;
+        }
+        result = interface_cast<IDisplayEventConnection>(reply.readStrongBinder());
+        return result;
+    }
 };
 
 IMPLEMENT_META_INTERFACE(SurfaceComposer, "android.ui.ISurfaceComposer");
@@ -254,6 +280,12 @@
             int32_t result = authenticateSurfaceTexture(surfaceTexture) ? 1 : 0;
             reply->writeInt32(result);
         } break;
+        case CREATE_DISPLAY_EVENT_CONNECTION: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            sp<IDisplayEventConnection> connection(createDisplayEventConnection());
+            reply->writeStrongBinder(connection->asBinder());
+            return NO_ERROR;
+        } break;
         default:
             return BBinder::onTransact(code, data, reply, flags);
     }
diff --git a/libs/gui/SensorChannel.cpp b/libs/gui/SensorChannel.cpp
deleted file mode 100644
index 147e1c2..0000000
--- a/libs/gui/SensorChannel.cpp
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <unistd.h>
-#include <fcntl.h>
-
-#include <utils/Errors.h>
-
-#include <binder/Parcel.h>
-
-#include <gui/SensorChannel.h>
-
-namespace android {
-// ----------------------------------------------------------------------------
-
-SensorChannel::SensorChannel()
-    : mSendFd(-1), mReceiveFd(-1)
-{
-    int fds[2];
-    if (pipe(fds) == 0) {
-        mReceiveFd = fds[0];
-        mSendFd = fds[1];
-        fcntl(mReceiveFd, F_SETFL, O_NONBLOCK);
-        fcntl(mSendFd, F_SETFL, O_NONBLOCK);
-    }
-}
-
-SensorChannel::SensorChannel(const Parcel& data)
-    : mSendFd(-1), mReceiveFd(-1)
-{
-    mReceiveFd = dup(data.readFileDescriptor());
-    fcntl(mReceiveFd, F_SETFL, O_NONBLOCK);
-}
-
-SensorChannel::~SensorChannel()
-{
-    if (mSendFd >= 0)
-        close(mSendFd);
-
-    if (mReceiveFd >= 0)
-        close(mReceiveFd);
-}
-
-int SensorChannel::getFd() const
-{
-    return mReceiveFd;
-}
-
-ssize_t SensorChannel::write(void const* vaddr, size_t size)
-{
-    ssize_t len = ::write(mSendFd, vaddr, size);
-    if (len < 0)
-        return -errno;
-    return len;
-}
-
-ssize_t SensorChannel::read(void* vaddr, size_t size)
-{
-    ssize_t len = ::read(mReceiveFd, vaddr, size);
-    if (len < 0)
-        return -errno;
-    return len;
-}
-
-status_t SensorChannel::writeToParcel(Parcel* reply) const
-{
-    if (mReceiveFd < 0)
-        return -EINVAL;
-
-    status_t result = reply->writeDupFileDescriptor(mReceiveFd);
-    close(mReceiveFd);
-    mReceiveFd = -1;
-    return result;
-}
-
-// ----------------------------------------------------------------------------
-}; // namespace android
diff --git a/libs/gui/SensorEventQueue.cpp b/libs/gui/SensorEventQueue.cpp
index f935524..ee21c45 100644
--- a/libs/gui/SensorEventQueue.cpp
+++ b/libs/gui/SensorEventQueue.cpp
@@ -24,7 +24,7 @@
 #include <utils/Looper.h>
 
 #include <gui/Sensor.h>
-#include <gui/SensorChannel.h>
+#include <gui/BitTube.h>
 #include <gui/SensorEventQueue.h>
 #include <gui/ISensorEventConnection.h>
 
@@ -104,7 +104,7 @@
     do {
         result = looper->pollOnce(-1);
         if (result == ALOOPER_EVENT_ERROR) {
-            LOGE("SensorChannel::waitForEvent error (errno=%d)", errno);
+            LOGE("SensorEventQueue::waitForEvent error (errno=%d)", errno);
             result = -EPIPE; // unknown error, so we make up one
             break;
         }
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 4ad6c22..699438c 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -39,6 +39,7 @@
 #include <private/surfaceflinger/LayerState.h>
 #include <private/surfaceflinger/SharedBufferStack.h>
 
+#include <private/gui/ComposerService.h>
 
 namespace android {
 // ---------------------------------------------------------------------------
diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp
index fcd287c..6f3051a 100644
--- a/libs/gui/SurfaceTexture.cpp
+++ b/libs/gui/SurfaceTexture.cpp
@@ -29,6 +29,8 @@
 
 #include <hardware/hardware.h>
 
+#include <private/gui/ComposerService.h>
+
 #include <surfaceflinger/ISurfaceComposer.h>
 #include <surfaceflinger/SurfaceComposerClient.h>
 #include <surfaceflinger/IGraphicBufferAlloc.h>
@@ -60,7 +62,7 @@
 #endif
 
 // Macros for including the SurfaceTexture name in log messages
-#define ST_LOGV(x, ...) LOGV("[%s] "x, mName.string(), ##__VA_ARGS__)
+#define ST_LOGV(x, ...) ALOGV("[%s] "x, mName.string(), ##__VA_ARGS__)
 #define ST_LOGD(x, ...) LOGD("[%s] "x, mName.string(), ##__VA_ARGS__)
 #define ST_LOGI(x, ...) LOGI("[%s] "x, mName.string(), ##__VA_ARGS__)
 #define ST_LOGW(x, ...) LOGW("[%s] "x, mName.string(), ##__VA_ARGS__)
diff --git a/libs/gui/SurfaceTextureClient.cpp b/libs/gui/SurfaceTextureClient.cpp
index 48070d6..691b52d 100644
--- a/libs/gui/SurfaceTextureClient.cpp
+++ b/libs/gui/SurfaceTextureClient.cpp
@@ -23,6 +23,8 @@
 
 #include <utils/Log.h>
 
+#include <private/gui/ComposerService.h>
+
 namespace android {
 
 SurfaceTextureClient::SurfaceTextureClient(
@@ -136,13 +138,13 @@
 }
 
 int SurfaceTextureClient::dequeueBuffer(android_native_buffer_t** buffer) {
-    LOGV("SurfaceTextureClient::dequeueBuffer");
+    ALOGV("SurfaceTextureClient::dequeueBuffer");
     Mutex::Autolock lock(mMutex);
     int buf = -1;
     status_t result = mSurfaceTexture->dequeueBuffer(&buf, mReqWidth, mReqHeight,
             mReqFormat, mReqUsage);
     if (result < 0) {
-        LOGV("dequeueBuffer: ISurfaceTexture::dequeueBuffer(%d, %d, %d, %d)"
+        ALOGV("dequeueBuffer: ISurfaceTexture::dequeueBuffer(%d, %d, %d, %d)"
              "failed: %d", mReqWidth, mReqHeight, mReqFormat, mReqUsage,
              result);
         return result;
@@ -165,7 +167,7 @@
 }
 
 int SurfaceTextureClient::cancelBuffer(android_native_buffer_t* buffer) {
-    LOGV("SurfaceTextureClient::cancelBuffer");
+    ALOGV("SurfaceTextureClient::cancelBuffer");
     Mutex::Autolock lock(mMutex);
     int i = getSlotFromBufferLocked(buffer);
     if (i < 0) {
@@ -205,18 +207,18 @@
 }
 
 int SurfaceTextureClient::lockBuffer(android_native_buffer_t* buffer) {
-    LOGV("SurfaceTextureClient::lockBuffer");
+    ALOGV("SurfaceTextureClient::lockBuffer");
     Mutex::Autolock lock(mMutex);
     return OK;
 }
 
 int SurfaceTextureClient::queueBuffer(android_native_buffer_t* buffer) {
-    LOGV("SurfaceTextureClient::queueBuffer");
+    ALOGV("SurfaceTextureClient::queueBuffer");
     Mutex::Autolock lock(mMutex);
     int64_t timestamp;
     if (mTimestamp == NATIVE_WINDOW_TIMESTAMP_AUTO) {
         timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
-        LOGV("SurfaceTextureClient::queueBuffer making up timestamp: %.2f ms",
+        ALOGV("SurfaceTextureClient::queueBuffer making up timestamp: %.2f ms",
              timestamp / 1000000.f);
     } else {
         timestamp = mTimestamp;
@@ -234,7 +236,7 @@
 }
 
 int SurfaceTextureClient::query(int what, int* value) const {
-    LOGV("SurfaceTextureClient::query");
+    ALOGV("SurfaceTextureClient::query");
     { // scope for the lock
         Mutex::Autolock lock(mMutex);
         switch (what) {
@@ -402,7 +404,7 @@
 
 
 int SurfaceTextureClient::connect(int api) {
-    LOGV("SurfaceTextureClient::connect");
+    ALOGV("SurfaceTextureClient::connect");
     Mutex::Autolock lock(mMutex);
     int err = mSurfaceTexture->connect(api,
             &mDefaultWidth, &mDefaultHeight, &mTransformHint);
@@ -413,7 +415,7 @@
 }
 
 int SurfaceTextureClient::disconnect(int api) {
-    LOGV("SurfaceTextureClient::disconnect");
+    ALOGV("SurfaceTextureClient::disconnect");
     Mutex::Autolock lock(mMutex);
     freeAllBuffers();
     int err = mSurfaceTexture->disconnect(api);
@@ -431,7 +433,7 @@
 
 int SurfaceTextureClient::setUsage(uint32_t reqUsage)
 {
-    LOGV("SurfaceTextureClient::setUsage");
+    ALOGV("SurfaceTextureClient::setUsage");
     Mutex::Autolock lock(mMutex);
     mReqUsage = reqUsage;
     return OK;
@@ -439,7 +441,7 @@
 
 int SurfaceTextureClient::setCrop(Rect const* rect)
 {
-    LOGV("SurfaceTextureClient::setCrop");
+    ALOGV("SurfaceTextureClient::setCrop");
     Mutex::Autolock lock(mMutex);
 
     Rect realRect;
@@ -457,7 +459,7 @@
 
 int SurfaceTextureClient::setBufferCount(int bufferCount)
 {
-    LOGV("SurfaceTextureClient::setBufferCount");
+    ALOGV("SurfaceTextureClient::setBufferCount");
     Mutex::Autolock lock(mMutex);
 
     status_t err = mSurfaceTexture->setBufferCount(bufferCount);
@@ -473,7 +475,7 @@
 
 int SurfaceTextureClient::setBuffersDimensions(int w, int h)
 {
-    LOGV("SurfaceTextureClient::setBuffersDimensions");
+    ALOGV("SurfaceTextureClient::setBuffersDimensions");
     Mutex::Autolock lock(mMutex);
 
     if (w<0 || h<0)
@@ -493,7 +495,7 @@
 
 int SurfaceTextureClient::setBuffersFormat(int format)
 {
-    LOGV("SurfaceTextureClient::setBuffersFormat");
+    ALOGV("SurfaceTextureClient::setBuffersFormat");
     Mutex::Autolock lock(mMutex);
 
     if (format<0)
@@ -506,7 +508,7 @@
 
 int SurfaceTextureClient::setScalingMode(int mode)
 {
-    LOGV("SurfaceTextureClient::setScalingMode(%d)", mode);
+    ALOGV("SurfaceTextureClient::setScalingMode(%d)", mode);
     Mutex::Autolock lock(mMutex);
     // mode is validated on the server
     status_t err = mSurfaceTexture->setScalingMode(mode);
@@ -518,7 +520,7 @@
 
 int SurfaceTextureClient::setBuffersTransform(int transform)
 {
-    LOGV("SurfaceTextureClient::setBuffersTransform");
+    ALOGV("SurfaceTextureClient::setBuffersTransform");
     Mutex::Autolock lock(mMutex);
     status_t err = mSurfaceTexture->setTransform(transform);
     return err;
@@ -526,7 +528,7 @@
 
 int SurfaceTextureClient::setBuffersTimestamp(int64_t timestamp)
 {
-    LOGV("SurfaceTextureClient::setBuffersTimestamp");
+    ALOGV("SurfaceTextureClient::setBuffersTimestamp");
     Mutex::Autolock lock(mMutex);
     mTimestamp = timestamp;
     return NO_ERROR;
diff --git a/libs/gui/tests/SurfaceTexture_test.cpp b/libs/gui/tests/SurfaceTexture_test.cpp
index c313904..b18e7b0 100644
--- a/libs/gui/tests/SurfaceTexture_test.cpp
+++ b/libs/gui/tests/SurfaceTexture_test.cpp
@@ -1399,12 +1399,12 @@
         // test.
         void waitForFrame() {
             Mutex::Autolock lock(mMutex);
-            LOGV("+waitForFrame");
+            ALOGV("+waitForFrame");
             while (!mFrameAvailable) {
                 mFrameAvailableCondition.wait(mMutex);
             }
             mFrameAvailable = false;
-            LOGV("-waitForFrame");
+            ALOGV("-waitForFrame");
         }
 
         // Allow the producer to return from its swapBuffers call and continue
@@ -1412,23 +1412,23 @@
         // thread once for every frame expected by the test.
         void finishFrame() {
             Mutex::Autolock lock(mMutex);
-            LOGV("+finishFrame");
+            ALOGV("+finishFrame");
             mFrameFinished = true;
             mFrameFinishCondition.signal();
-            LOGV("-finishFrame");
+            ALOGV("-finishFrame");
         }
 
         // This should be called by SurfaceTexture on the producer thread.
         virtual void onFrameAvailable() {
             Mutex::Autolock lock(mMutex);
-            LOGV("+onFrameAvailable");
+            ALOGV("+onFrameAvailable");
             mFrameAvailable = true;
             mFrameAvailableCondition.signal();
             while (!mFrameFinished) {
                 mFrameFinishCondition.wait(mMutex);
             }
             mFrameFinished = false;
-            LOGV("-onFrameAvailable");
+            ALOGV("-onFrameAvailable");
         }
 
     protected:
@@ -1514,9 +1514,9 @@
             for (int i = 0; i < NUM_ITERATIONS; i++) {
                 glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
                 glClear(GL_COLOR_BUFFER_BIT);
-                LOGV("+swapBuffers");
+                ALOGV("+swapBuffers");
                 swapBuffers();
-                LOGV("-swapBuffers");
+                ALOGV("-swapBuffers");
             }
         }
     };
@@ -1525,9 +1525,9 @@
 
     for (int i = 0; i < NUM_ITERATIONS; i++) {
         mFC->waitForFrame();
-        LOGV("+updateTexImage");
+        ALOGV("+updateTexImage");
         mST->updateTexImage();
-        LOGV("-updateTexImage");
+        ALOGV("-updateTexImage");
         mFC->finishFrame();
 
         // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported!
@@ -1543,9 +1543,9 @@
             for (int i = 0; i < NUM_ITERATIONS; i++) {
                 glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
                 glClear(GL_COLOR_BUFFER_BIT);
-                LOGV("+swapBuffers");
+                ALOGV("+swapBuffers");
                 swapBuffers();
-                LOGV("-swapBuffers");
+                ALOGV("-swapBuffers");
             }
         }
     };
@@ -1555,9 +1555,9 @@
     for (int i = 0; i < NUM_ITERATIONS; i++) {
         mFC->waitForFrame();
         mFC->finishFrame();
-        LOGV("+updateTexImage");
+        ALOGV("+updateTexImage");
         mST->updateTexImage();
-        LOGV("-updateTexImage");
+        ALOGV("-updateTexImage");
 
         // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported!
     }
@@ -1573,9 +1573,9 @@
             for (int i = 0; i < NUM_ITERATIONS; i++) {
                 glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
                 glClear(GL_COLOR_BUFFER_BIT);
-                LOGV("+swapBuffers");
+                ALOGV("+swapBuffers");
                 swapBuffers();
-                LOGV("-swapBuffers");
+                ALOGV("-swapBuffers");
             }
         }
     };
@@ -1624,9 +1624,9 @@
     for (int i = 0; i < NUM_ITERATIONS-3; i++) {
         mFC->waitForFrame();
         mFC->finishFrame();
-        LOGV("+updateTexImage");
+        ALOGV("+updateTexImage");
         mST->updateTexImage();
-        LOGV("-updateTexImage");
+        ALOGV("-updateTexImage");
     }
 }
 
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 693b7b8..ea52750 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -22,6 +22,8 @@
 #include <surfaceflinger/SurfaceComposerClient.h>
 #include <utils/String8.h>
 
+#include <private/gui/ComposerService.h>
+
 namespace android {
 
 class SurfaceTest : public ::testing::Test {
diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk
index 831d9e3..544ab74 100644
--- a/libs/utils/Android.mk
+++ b/libs/utils/Android.mk
@@ -21,6 +21,7 @@
 	Asset.cpp \
 	AssetDir.cpp \
 	AssetManager.cpp \
+	BasicHashtable.cpp \
 	BlobCache.cpp \
 	BufferedTextOutput.cpp \
 	CallStack.cpp \
@@ -105,7 +106,8 @@
 	libz \
 	liblog \
 	libcutils \
-	libdl
+	libdl \
+	libcorkscrew
 
 LOCAL_MODULE:= libutils
 include $(BUILD_SHARED_LIBRARY)
diff --git a/libs/utils/Asset.cpp b/libs/utils/Asset.cpp
index a18294b..7fd2c87 100644
--- a/libs/utils/Asset.cpp
+++ b/libs/utils/Asset.cpp
@@ -585,7 +585,7 @@
             return NULL;
         }
 
-        LOGV("Asset %p allocating buffer size %d (smaller than threshold)", this, (int)allocLen);
+        ALOGV("Asset %p allocating buffer size %d (smaller than threshold)", this, (int)allocLen);
         if (mLength > 0) {
             long oldPosn = ftell(mFp);
             fseek(mFp, mStart, SEEK_SET);
@@ -597,7 +597,7 @@
             fseek(mFp, oldPosn, SEEK_SET);
         }
 
-        LOGV(" getBuffer: loaded into buffer\n");
+        ALOGV(" getBuffer: loaded into buffer\n");
 
         mBuf = buf;
         return mBuf;
@@ -610,7 +610,7 @@
             return NULL;
         }
 
-        LOGV(" getBuffer: mapped\n");
+        ALOGV(" getBuffer: mapped\n");
 
         mMap = map;
         if (!wordAligned) {
@@ -648,13 +648,13 @@
     if ((((size_t)data)&0x3) == 0) {
         // We can return this directly if it is aligned on a word
         // boundary.
-        LOGV("Returning aligned FileAsset %p (%s).", this,
+        ALOGV("Returning aligned FileAsset %p (%s).", this,
                 getAssetSource());
         return data;
     }
     // If not aligned on a word boundary, then we need to copy it into
     // our own buffer.
-    LOGV("Copying FileAsset %p (%s) to buffer size %d to make it aligned.", this,
+    ALOGV("Copying FileAsset %p (%s) to buffer size %d to make it aligned.", this,
             getAssetSource(), (int)mLength);
     unsigned char* buf = new unsigned char[mLength];
     if (buf == NULL) {
diff --git a/libs/utils/AssetManager.cpp b/libs/utils/AssetManager.cpp
index 22034c5..203e6fa 100644
--- a/libs/utils/AssetManager.cpp
+++ b/libs/utils/AssetManager.cpp
@@ -167,7 +167,7 @@
         }
     }
 
-    LOGV("In %p Asset %s path: %s", this,
+    ALOGV("In %p Asset %s path: %s", this,
          ap.type == kFileTypeDirectory ? "dir" : "zip", ap.path.string());
 
     mAssetPaths.add(ap);
@@ -498,7 +498,7 @@
     size_t i = mAssetPaths.size();
     while (i > 0) {
         i--;
-        LOGV("Looking for asset '%s' in '%s'\n",
+        ALOGV("Looking for asset '%s' in '%s'\n",
                 assetName.string(), mAssetPaths.itemAt(i).path.string());
         Asset* pAsset = openNonAssetInPathLocked(assetName.string(), mode, mAssetPaths.itemAt(i));
         if (pAsset != NULL) {
@@ -532,7 +532,7 @@
     size_t i = mAssetPaths.size();
     while (i > 0) {
         i--;
-        LOGV("Looking for non-asset '%s' in '%s'\n", fileName, mAssetPaths.itemAt(i).path.string());
+        ALOGV("Looking for non-asset '%s' in '%s'\n", fileName, mAssetPaths.itemAt(i).path.string());
         Asset* pAsset = openNonAssetInPathLocked(
             fileName, mode, mAssetPaths.itemAt(i));
         if (pAsset != NULL) {
@@ -556,7 +556,7 @@
         loadFileNameCacheLocked();
 
     if (which < mAssetPaths.size()) {
-        LOGV("Looking for non-asset '%s' in '%s'\n", fileName,
+        ALOGV("Looking for non-asset '%s' in '%s'\n", fileName,
                 mAssetPaths.itemAt(which).path.string());
         Asset* pAsset = openNonAssetInPathLocked(
             fileName, mode, mAssetPaths.itemAt(which));
@@ -621,7 +621,7 @@
         bool shared = true;
         const asset_path& ap = mAssetPaths.itemAt(i);
         Asset* idmap = openIdmapLocked(ap);
-        LOGV("Looking for resource asset in '%s'\n", ap.path.string());
+        ALOGV("Looking for resource asset in '%s'\n", ap.path.string());
         if (ap.type != kFileTypeDirectory) {
             if (i == 0) {
                 // The first item is typically the framework resources,
@@ -633,7 +633,7 @@
                 ass = const_cast<AssetManager*>(this)->
                     mZipSet.getZipResourceTableAsset(ap.path);
                 if (ass == NULL) {
-                    LOGV("loading resource table %s\n", ap.path.string());
+                    ALOGV("loading resource table %s\n", ap.path.string());
                     ass = const_cast<AssetManager*>(this)->
                         openNonAssetInPathLocked("resources.arsc",
                                                  Asset::ACCESS_BUFFER,
@@ -648,7 +648,7 @@
                     // If this is the first resource table in the asset
                     // manager, then we are going to cache it so that we
                     // can quickly copy it out for others.
-                    LOGV("Creating shared resources for %s", ap.path.string());
+                    ALOGV("Creating shared resources for %s", ap.path.string());
                     sharedRes = new ResTable();
                     sharedRes->add(ass, (void*)(i+1), false, idmap);
                     sharedRes = const_cast<AssetManager*>(this)->
@@ -656,7 +656,7 @@
                 }
             }
         } else {
-            LOGV("loading resource table %s\n", ap.path.string());
+            ALOGV("loading resource table %s\n", ap.path.string());
             Asset* ass = const_cast<AssetManager*>(this)->
                 openNonAssetInPathLocked("resources.arsc",
                                          Asset::ACCESS_BUFFER,
@@ -668,12 +668,12 @@
                 mResources = rt = new ResTable();
                 updateResourceParamsLocked();
             }
-            LOGV("Installing resource asset %p in to table %p\n", ass, mResources);
+            ALOGV("Installing resource asset %p in to table %p\n", ass, mResources);
             if (sharedRes != NULL) {
-                LOGV("Copying existing resources for %s", ap.path.string());
+                ALOGV("Copying existing resources for %s", ap.path.string());
                 rt->add(sharedRes);
             } else {
-                LOGV("Parsing resources for %s", ap.path.string());
+                ALOGV("Parsing resources for %s", ap.path.string());
                 rt->add(ass, (void*)(i+1), !shared, idmap);
             }
 
@@ -725,7 +725,7 @@
         ass = const_cast<AssetManager*>(this)->
             openAssetFromFileLocked(ap.idmap, Asset::ACCESS_BUFFER);
         if (ass) {
-            LOGV("loading idmap %s\n", ap.idmap.string());
+            ALOGV("loading idmap %s\n", ap.idmap.string());
         } else {
             LOGW("failed to load idmap %s\n", ap.idmap.string());
         }
@@ -1019,7 +1019,7 @@
  */
 ZipFileRO* AssetManager::getZipFileLocked(const asset_path& ap)
 {
-    LOGV("getZipFileLocked() in %p\n", this);
+    ALOGV("getZipFileLocked() in %p\n", this);
 
     return mZipSet.getZip(ap.path);
 }
@@ -1086,12 +1086,12 @@
 
     if (method == ZipFileRO::kCompressStored) {
         pAsset = Asset::createFromUncompressedMap(dataMap, mode);
-        LOGV("Opened uncompressed entry %s in zip %s mode %d: %p", entryName.string(),
+        ALOGV("Opened uncompressed entry %s in zip %s mode %d: %p", entryName.string(),
                 dataMap->getFileName(), mode, pAsset);
     } else {
         pAsset = Asset::createFromCompressedMap(dataMap, method,
             uncompressedLen, mode);
-        LOGV("Opened compressed entry %s in zip %s mode %d: %p", entryName.string(),
+        ALOGV("Opened compressed entry %s in zip %s mode %d: %p", entryName.string(),
                 dataMap->getFileName(), mode, pAsset);
     }
     if (pAsset == NULL) {
@@ -1146,10 +1146,10 @@
         i--;
         const asset_path& ap = mAssetPaths.itemAt(i);
         if (ap.type == kFileTypeRegular) {
-            LOGV("Adding directory %s from zip %s", dirName, ap.path.string());
+            ALOGV("Adding directory %s from zip %s", dirName, ap.path.string());
             scanAndMergeZipLocked(pMergedInfo, ap, kAssetsRoot, dirName);
         } else {
-            LOGV("Adding directory %s from dir %s", dirName, ap.path.string());
+            ALOGV("Adding directory %s from dir %s", dirName, ap.path.string());
             scanAndMergeDirLocked(pMergedInfo, ap, kAssetsRoot, dirName);
         }
     }
@@ -1200,10 +1200,10 @@
     if (which < mAssetPaths.size()) {
         const asset_path& ap = mAssetPaths.itemAt(which);
         if (ap.type == kFileTypeRegular) {
-            LOGV("Adding directory %s from zip %s", dirName, ap.path.string());
+            ALOGV("Adding directory %s from zip %s", dirName, ap.path.string());
             scanAndMergeZipLocked(pMergedInfo, ap, NULL, dirName);
         } else {
-            LOGV("Adding directory %s from dir %s", dirName, ap.path.string());
+            ALOGV("Adding directory %s from dir %s", dirName, ap.path.string());
             scanAndMergeDirLocked(pMergedInfo, ap, NULL, dirName);
         }
     }
@@ -1325,7 +1325,7 @@
 
             matchIdx = AssetDir::FileInfo::findEntry(pMergedInfo, match);
             if (matchIdx > 0) {
-                LOGV("Excluding '%s' [%s]\n",
+                ALOGV("Excluding '%s' [%s]\n",
                     pMergedInfo->itemAt(matchIdx).getFileName().string(),
                     pMergedInfo->itemAt(matchIdx).getSourceName().string());
                 pMergedInfo->removeAt(matchIdx);
@@ -1365,7 +1365,7 @@
     struct dirent* entry;
     FileType fileType;
 
-    LOGV("Scanning dir '%s'\n", path.string());
+    ALOGV("Scanning dir '%s'\n", path.string());
 
     dir = opendir(path.string());
     if (dir == NULL)
@@ -1782,7 +1782,7 @@
 {
     //LOGI("Creating SharedZip %p %s\n", this, (const char*)mPath);
     mZipFile = new ZipFileRO;
-    LOGV("+++ opening zip '%s'\n", mPath.string());
+    ALOGV("+++ opening zip '%s'\n", mPath.string());
     if (mZipFile->open(mPath.string()) != NO_ERROR) {
         LOGD("failed to open Zip archive '%s'\n", mPath.string());
         delete mZipFile;
@@ -1811,7 +1811,7 @@
 
 Asset* AssetManager::SharedZip::getResourceTableAsset()
 {
-    LOGV("Getting from SharedZip %p resource asset %p\n", this, mResourceTableAsset);
+    ALOGV("Getting from SharedZip %p resource asset %p\n", this, mResourceTableAsset);
     return mResourceTableAsset;
 }
 
@@ -1833,7 +1833,7 @@
 
 ResTable* AssetManager::SharedZip::getResourceTable()
 {
-    LOGV("Getting from SharedZip %p resource table %p\n", this, mResourceTable);
+    ALOGV("Getting from SharedZip %p resource table %p\n", this, mResourceTable);
     return mResourceTable;
 }
 
@@ -1867,7 +1867,7 @@
     }
     if (mZipFile != NULL) {
         delete mZipFile;
-        LOGV("Closed '%s'\n", mPath.string());
+        ALOGV("Closed '%s'\n", mPath.string());
     }
 }
 
diff --git a/libs/utils/BasicHashtable.cpp b/libs/utils/BasicHashtable.cpp
new file mode 100644
index 0000000..fb8ec9f
--- /dev/null
+++ b/libs/utils/BasicHashtable.cpp
@@ -0,0 +1,338 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "BasicHashtable"
+
+#include <math.h>
+
+#include <utils/Log.h>
+#include <utils/BasicHashtable.h>
+#include <utils/misc.h>
+
+namespace android {
+
+BasicHashtableImpl::BasicHashtableImpl(size_t entrySize, bool hasTrivialDestructor,
+        size_t minimumInitialCapacity, float loadFactor) :
+        mBucketSize(entrySize + sizeof(Bucket)), mHasTrivialDestructor(hasTrivialDestructor),
+        mLoadFactor(loadFactor), mSize(0),
+        mFilledBuckets(0), mBuckets(NULL) {
+    determineCapacity(minimumInitialCapacity, mLoadFactor, &mBucketCount, &mCapacity);
+}
+
+BasicHashtableImpl::BasicHashtableImpl(const BasicHashtableImpl& other) :
+        mBucketSize(other.mBucketSize), mHasTrivialDestructor(other.mHasTrivialDestructor),
+        mCapacity(other.mCapacity), mLoadFactor(other.mLoadFactor),
+        mSize(other.mSize), mFilledBuckets(other.mFilledBuckets),
+        mBucketCount(other.mBucketCount), mBuckets(other.mBuckets) {
+    if (mBuckets) {
+        SharedBuffer::bufferFromData(mBuckets)->acquire();
+    }
+}
+
+void BasicHashtableImpl::dispose() {
+    if (mBuckets) {
+        releaseBuckets(mBuckets, mBucketCount);
+    }
+}
+
+void BasicHashtableImpl::clone() {
+    if (mBuckets) {
+        void* newBuckets = allocateBuckets(mBucketCount);
+        copyBuckets(mBuckets, newBuckets, mBucketCount);
+        releaseBuckets(mBuckets, mBucketCount);
+        mBuckets = newBuckets;
+    }
+}
+
+void BasicHashtableImpl::setTo(const BasicHashtableImpl& other) {
+    if (mBuckets) {
+        releaseBuckets(mBuckets, mBucketCount);
+    }
+
+    mCapacity = other.mCapacity;
+    mLoadFactor = other.mLoadFactor;
+    mSize = other.mSize;
+    mFilledBuckets = other.mFilledBuckets;
+    mBucketCount = other.mBucketCount;
+    mBuckets = other.mBuckets;
+
+    if (mBuckets) {
+        SharedBuffer::bufferFromData(mBuckets)->acquire();
+    }
+}
+
+void BasicHashtableImpl::clear() {
+    if (mBuckets) {
+        if (mFilledBuckets) {
+            SharedBuffer* sb = SharedBuffer::bufferFromData(mBuckets);
+            if (sb->onlyOwner()) {
+                destroyBuckets(mBuckets, mBucketCount);
+                for (size_t i = 0; i < mSize; i++) {
+                    Bucket& bucket = bucketAt(mBuckets, i);
+                    bucket.cookie = 0;
+                }
+            } else {
+                releaseBuckets(mBuckets, mBucketCount);
+                mBuckets = NULL;
+            }
+            mFilledBuckets = 0;
+        }
+        mSize = 0;
+    }
+}
+
+ssize_t BasicHashtableImpl::next(ssize_t index) const {
+    if (mSize) {
+        while (size_t(++index) < mBucketCount) {
+            const Bucket& bucket = bucketAt(mBuckets, index);
+            if (bucket.cookie & Bucket::PRESENT) {
+                return index;
+            }
+        }
+    }
+    return -1;
+}
+
+ssize_t BasicHashtableImpl::find(ssize_t index, hash_t hash,
+        const void* __restrict__ key) const {
+    if (!mSize) {
+        return -1;
+    }
+
+    hash = trimHash(hash);
+    if (index < 0) {
+        index = chainStart(hash, mBucketCount);
+
+        const Bucket& bucket = bucketAt(mBuckets, size_t(index));
+        if (bucket.cookie & Bucket::PRESENT) {
+            if (compareBucketKey(bucket, key)) {
+                return index;
+            }
+        } else {
+            if (!(bucket.cookie & Bucket::COLLISION)) {
+                return -1;
+            }
+        }
+    }
+
+    size_t inc = chainIncrement(hash, mBucketCount);
+    for (;;) {
+        index = chainSeek(index, inc, mBucketCount);
+
+        const Bucket& bucket = bucketAt(mBuckets, size_t(index));
+        if (bucket.cookie & Bucket::PRESENT) {
+            if ((bucket.cookie & Bucket::HASH_MASK) == hash
+                    && compareBucketKey(bucket, key)) {
+                return index;
+            }
+        }
+        if (!(bucket.cookie & Bucket::COLLISION)) {
+            return -1;
+        }
+    }
+}
+
+size_t BasicHashtableImpl::add(hash_t hash, const void* entry) {
+    if (!mBuckets) {
+        mBuckets = allocateBuckets(mBucketCount);
+    } else {
+        edit();
+    }
+
+    hash = trimHash(hash);
+    for (;;) {
+        size_t index = chainStart(hash, mBucketCount);
+        Bucket* bucket = &bucketAt(mBuckets, size_t(index));
+        if (bucket->cookie & Bucket::PRESENT) {
+            size_t inc = chainIncrement(hash, mBucketCount);
+            do {
+                bucket->cookie |= Bucket::COLLISION;
+                index = chainSeek(index, inc, mBucketCount);
+                bucket = &bucketAt(mBuckets, size_t(index));
+            } while (bucket->cookie & Bucket::PRESENT);
+        }
+
+        uint32_t collision = bucket->cookie & Bucket::COLLISION;
+        if (!collision) {
+            if (mFilledBuckets >= mCapacity) {
+                rehash(mCapacity * 2, mLoadFactor);
+                continue;
+            }
+            mFilledBuckets += 1;
+        }
+
+        bucket->cookie = collision | Bucket::PRESENT | hash;
+        mSize += 1;
+        initializeBucketEntry(*bucket, entry);
+        return index;
+    }
+}
+
+void BasicHashtableImpl::removeAt(size_t index) {
+    edit();
+
+    Bucket& bucket = bucketAt(mBuckets, index);
+    bucket.cookie &= ~Bucket::PRESENT;
+    if (!(bucket.cookie & Bucket::COLLISION)) {
+        mFilledBuckets -= 1;
+    }
+    mSize -= 1;
+    if (!mHasTrivialDestructor) {
+        destroyBucketEntry(bucket);
+    }
+}
+
+void BasicHashtableImpl::rehash(size_t minimumCapacity, float loadFactor) {
+    if (minimumCapacity < mSize) {
+        minimumCapacity = mSize;
+    }
+    size_t newBucketCount, newCapacity;
+    determineCapacity(minimumCapacity, loadFactor, &newBucketCount, &newCapacity);
+
+    if (newBucketCount != mBucketCount || newCapacity != mCapacity) {
+        if (mBuckets) {
+            void* newBuckets;
+            if (mSize) {
+                newBuckets = allocateBuckets(newBucketCount);
+                for (size_t i = 0; i < mBucketCount; i++) {
+                    const Bucket& fromBucket = bucketAt(mBuckets, i);
+                    if (fromBucket.cookie & Bucket::PRESENT) {
+                        hash_t hash = fromBucket.cookie & Bucket::HASH_MASK;
+                        size_t index = chainStart(hash, newBucketCount);
+                        Bucket* toBucket = &bucketAt(newBuckets, size_t(index));
+                        if (toBucket->cookie & Bucket::PRESENT) {
+                            size_t inc = chainIncrement(hash, newBucketCount);
+                            do {
+                                toBucket->cookie |= Bucket::COLLISION;
+                                index = chainSeek(index, inc, newBucketCount);
+                                toBucket = &bucketAt(newBuckets, size_t(index));
+                            } while (toBucket->cookie & Bucket::PRESENT);
+                        }
+                        toBucket->cookie = Bucket::PRESENT | hash;
+                        initializeBucketEntry(*toBucket, fromBucket.entry);
+                    }
+                }
+            } else {
+                newBuckets = NULL;
+            }
+            releaseBuckets(mBuckets, mBucketCount);
+            mBuckets = newBuckets;
+            mFilledBuckets = mSize;
+        }
+        mBucketCount = newBucketCount;
+        mCapacity = newCapacity;
+    }
+    mLoadFactor = loadFactor;
+}
+
+void* BasicHashtableImpl::allocateBuckets(size_t count) const {
+    size_t bytes = count * mBucketSize;
+    SharedBuffer* sb = SharedBuffer::alloc(bytes);
+    LOG_ALWAYS_FATAL_IF(!sb, "Could not allocate %u bytes for hashtable with %u buckets.",
+            uint32_t(bytes), uint32_t(count));
+    void* buckets = sb->data();
+    for (size_t i = 0; i < count; i++) {
+        Bucket& bucket = bucketAt(buckets, i);
+        bucket.cookie = 0;
+    }
+    return buckets;
+}
+
+void BasicHashtableImpl::releaseBuckets(void* __restrict__ buckets, size_t count) const {
+    SharedBuffer* sb = SharedBuffer::bufferFromData(buckets);
+    if (sb->release(SharedBuffer::eKeepStorage) == 1) {
+        destroyBuckets(buckets, count);
+        SharedBuffer::dealloc(sb);
+    }
+}
+
+void BasicHashtableImpl::destroyBuckets(void* __restrict__ buckets, size_t count) const {
+    if (!mHasTrivialDestructor) {
+        for (size_t i = 0; i < count; i++) {
+            Bucket& bucket = bucketAt(buckets, i);
+            if (bucket.cookie & Bucket::PRESENT) {
+                destroyBucketEntry(bucket);
+            }
+        }
+    }
+}
+
+void BasicHashtableImpl::copyBuckets(const void* __restrict__ fromBuckets,
+        void* __restrict__ toBuckets, size_t count) const {
+    for (size_t i = 0; i < count; i++) {
+        const Bucket& fromBucket = bucketAt(fromBuckets, i);
+        Bucket& toBucket = bucketAt(toBuckets, i);
+        toBucket.cookie = fromBucket.cookie;
+        if (fromBucket.cookie & Bucket::PRESENT) {
+            initializeBucketEntry(toBucket, fromBucket.entry);
+        }
+    }
+}
+
+// Table of 31-bit primes where each prime is no less than twice as large
+// as the previous one.  Generated by "primes.py".
+static size_t PRIMES[] = {
+    5,
+    11,
+    23,
+    47,
+    97,
+    197,
+    397,
+    797,
+    1597,
+    3203,
+    6421,
+    12853,
+    25717,
+    51437,
+    102877,
+    205759,
+    411527,
+    823117,
+    1646237,
+    3292489,
+    6584983,
+    13169977,
+    26339969,
+    52679969,
+    105359939,
+    210719881,
+    421439783,
+    842879579,
+    1685759167,
+    0,
+};
+
+void BasicHashtableImpl::determineCapacity(size_t minimumCapacity, float loadFactor,
+        size_t* __restrict__ outBucketCount, size_t* __restrict__ outCapacity) {
+    LOG_ALWAYS_FATAL_IF(loadFactor <= 0.0f || loadFactor > 1.0f,
+            "Invalid load factor %0.3f.  Must be in the range (0, 1].", loadFactor);
+
+    size_t count = ceilf(minimumCapacity / loadFactor) + 1;
+    size_t i = 0;
+    while (count > PRIMES[i] && i < NELEM(PRIMES)) {
+        i++;
+    }
+    count = PRIMES[i];
+    LOG_ALWAYS_FATAL_IF(!count, "Could not determine required number of buckets for "
+            "hashtable with minimum capacity %u and load factor %0.3f.",
+            uint32_t(minimumCapacity), loadFactor);
+    *outBucketCount = count;
+    *outCapacity = ceilf((count - 1) * loadFactor);
+}
+
+}; // namespace android
diff --git a/libs/utils/BlobCache.cpp b/libs/utils/BlobCache.cpp
index d38aae9..4970828 100644
--- a/libs/utils/BlobCache.cpp
+++ b/libs/utils/BlobCache.cpp
@@ -48,23 +48,23 @@
     mRandState[1] = (now >> 16) & 0xFFFF;
     mRandState[2] = (now >> 32) & 0xFFFF;
 #endif
-    LOGV("initializing random seed using %lld", now);
+    ALOGV("initializing random seed using %lld", now);
 }
 
 void BlobCache::set(const void* key, size_t keySize, const void* value,
         size_t valueSize) {
     if (mMaxKeySize < keySize) {
-        LOGV("set: not caching because the key is too large: %d (limit: %d)",
+        ALOGV("set: not caching because the key is too large: %d (limit: %d)",
                 keySize, mMaxKeySize);
         return;
     }
     if (mMaxValueSize < valueSize) {
-        LOGV("set: not caching because the value is too large: %d (limit: %d)",
+        ALOGV("set: not caching because the value is too large: %d (limit: %d)",
                 valueSize, mMaxValueSize);
         return;
     }
     if (mMaxTotalSize < keySize + valueSize) {
-        LOGV("set: not caching because the combined key/value size is too "
+        ALOGV("set: not caching because the combined key/value size is too "
                 "large: %d (limit: %d)", keySize + valueSize, mMaxTotalSize);
         return;
     }
@@ -93,7 +93,7 @@
                     clean();
                     continue;
                 } else {
-                    LOGV("set: not caching new key/value pair because the "
+                    ALOGV("set: not caching new key/value pair because the "
                             "total cache size limit would be exceeded: %d "
                             "(limit: %d)",
                             keySize + valueSize, mMaxTotalSize);
@@ -102,7 +102,7 @@
             }
             mCacheEntries.add(CacheEntry(keyBlob, valueBlob));
             mTotalSize = newTotalSize;
-            LOGV("set: created new cache entry with %d byte key and %d byte value",
+            ALOGV("set: created new cache entry with %d byte key and %d byte value",
                     keySize, valueSize);
         } else {
             // Update the existing cache entry.
@@ -115,7 +115,7 @@
                     clean();
                     continue;
                 } else {
-                    LOGV("set: not caching new value because the total cache "
+                    ALOGV("set: not caching new value because the total cache "
                             "size limit would be exceeded: %d (limit: %d)",
                             keySize + valueSize, mMaxTotalSize);
                     break;
@@ -123,7 +123,7 @@
             }
             mCacheEntries.editItemAt(index).setValue(valueBlob);
             mTotalSize = newTotalSize;
-            LOGV("set: updated existing cache entry with %d byte key and %d byte "
+            ALOGV("set: updated existing cache entry with %d byte key and %d byte "
                     "value", keySize, valueSize);
         }
         break;
@@ -133,7 +133,7 @@
 size_t BlobCache::get(const void* key, size_t keySize, void* value,
         size_t valueSize) {
     if (mMaxKeySize < keySize) {
-        LOGV("get: not searching because the key is too large: %d (limit %d)",
+        ALOGV("get: not searching because the key is too large: %d (limit %d)",
                 keySize, mMaxKeySize);
         return 0;
     }
@@ -141,7 +141,7 @@
     CacheEntry dummyEntry(dummyKey, NULL);
     ssize_t index = mCacheEntries.indexOf(dummyEntry);
     if (index < 0) {
-        LOGV("get: no cache entry found for key of size %d", keySize);
+        ALOGV("get: no cache entry found for key of size %d", keySize);
         return 0;
     }
 
@@ -150,10 +150,10 @@
     sp<Blob> valueBlob(mCacheEntries[index].getValue());
     size_t valueBlobSize = valueBlob->getSize();
     if (valueBlobSize <= valueSize) {
-        LOGV("get: copying %d bytes to caller's buffer", valueBlobSize);
+        ALOGV("get: copying %d bytes to caller's buffer", valueBlobSize);
         memcpy(value, valueBlob->getData(), valueBlobSize);
     } else {
-        LOGV("get: caller's buffer is too small for value: %d (needs %d)",
+        ALOGV("get: caller's buffer is too small for value: %d (needs %d)",
                 valueSize, valueBlobSize);
     }
     return valueBlobSize;
diff --git a/libs/utils/CallStack.cpp b/libs/utils/CallStack.cpp
index 55b6024..c2a5e55 100644
--- a/libs/utils/CallStack.cpp
+++ b/libs/utils/CallStack.cpp
@@ -17,218 +17,33 @@
 #define LOG_TAG "CallStack"
 
 #include <string.h>
-#include <stdlib.h>
-#include <stdio.h>
-
-#if HAVE_DLADDR
-#include <dlfcn.h>
-#endif
-
-#if HAVE_CXXABI
-#include <cxxabi.h>
-#endif
-
-#include <unwind.h>
 
 #include <utils/Log.h>
 #include <utils/Errors.h>
 #include <utils/CallStack.h>
-#include <utils/threads.h>
-
+#include <corkscrew/backtrace.h>
 
 /*****************************************************************************/
 namespace android {
 
-
-typedef struct {
-    size_t count;
-    size_t ignore;
-    const void** addrs;
-} stack_crawl_state_t;
-
-static
-_Unwind_Reason_Code trace_function(_Unwind_Context *context, void *arg)
-{
-    stack_crawl_state_t* state = (stack_crawl_state_t*)arg;
-    if (state->count) {
-        void* ip = (void*)_Unwind_GetIP(context);
-        if (ip) {
-            if (state->ignore) {
-                state->ignore--;
-            } else {
-                state->addrs[0] = ip; 
-                state->addrs++;
-                state->count--;
-            }
-        }
-    }
-    return _URC_NO_REASON;
+CallStack::CallStack() :
+        mCount(0) {
 }
 
-static
-int backtrace(const void** addrs, size_t ignore, size_t size)
-{
-    stack_crawl_state_t state;
-    state.count = size;
-    state.ignore = ignore;
-    state.addrs = addrs;
-    _Unwind_Backtrace(trace_function, (void*)&state);
-    return size - state.count;
-}
-
-/*****************************************************************************/
-
-static 
-const char *lookup_symbol(const void* addr, void **offset, char* name, size_t bufSize)
-{
-#if HAVE_DLADDR
-    Dl_info info;
-    if (dladdr(addr, &info)) {
-        *offset = info.dli_saddr;
-        return info.dli_sname;
-    }
-#endif
-    return NULL;
-}
-
-static 
-int32_t linux_gcc_demangler(const char *mangled_name, char *unmangled_name, size_t buffersize)
-{
-    size_t out_len = 0;
-#if HAVE_CXXABI
-    int status = 0;
-    char *demangled = abi::__cxa_demangle(mangled_name, 0, &out_len, &status);
-    if (status == 0) {
-        // OK
-        if (out_len < buffersize) memcpy(unmangled_name, demangled, out_len);
-        else out_len = 0;
-        free(demangled);
-    } else {
-        out_len = 0;
-    }
-#endif
-    return out_len;
-}
-
-/*****************************************************************************/
-
-class MapInfo {
-    struct mapinfo {
-        struct mapinfo *next;
-        uint64_t start;
-        uint64_t end;
-        char name[];
-    };
-
-    const char *map_to_name(uint64_t pc, const char* def, uint64_t* start) {
-        mapinfo* mi = getMapInfoList();
-        while(mi) {
-            if ((pc >= mi->start) && (pc < mi->end)) {
-                if (start) 
-                    *start = mi->start;
-                return mi->name;
-            }
-            mi = mi->next;
-        }
-        if (start) 
-            *start = 0;
-        return def;
-    }
-
-    mapinfo *parse_maps_line(char *line) {
-        mapinfo *mi;
-        int len = strlen(line);
-        if (len < 1) return 0;
-        line[--len] = 0;
-        if (len < 50) return 0;
-        if (line[20] != 'x') return 0;
-        mi = (mapinfo*)malloc(sizeof(mapinfo) + (len - 47));
-        if (mi == 0) return 0;
-        mi->start = strtoull(line, 0, 16);
-        mi->end = strtoull(line + 9, 0, 16);
-        mi->next = 0;
-        strcpy(mi->name, line + 49);
-        return mi;
-    }
-
-    mapinfo* getMapInfoList() {
-        Mutex::Autolock _l(mLock);
-        if (milist == 0) {
-            char data[1024];
-            FILE *fp;
-            sprintf(data, "/proc/%d/maps", getpid());
-            fp = fopen(data, "r");
-            if (fp) {
-                while(fgets(data, 1024, fp)) {
-                    mapinfo *mi = parse_maps_line(data);
-                    if(mi) {
-                        mi->next = milist;
-                        milist = mi;
-                    }
-                }
-                fclose(fp);
-            }
-        }
-        return milist;
-    }
-    mapinfo*    milist;
-    Mutex       mLock;
-    static MapInfo sMapInfo;
-
-public:
-    MapInfo()
-     : milist(0) {
-    }
-
-    ~MapInfo() {
-        while (milist) {
-            mapinfo *next = milist->next;
-            free(milist);
-            milist = next;
-        }
-    }
-    
-    static const char *mapAddressToName(const void* pc, const char* def,
-            void const** start) 
-    {
-        uint64_t s;
-        char const* name = sMapInfo.map_to_name(uint64_t(uintptr_t(pc)), def, &s);
-        if (start) {
-            *start = (void*)s;
-        }
-        return name;
-    }
-
-};
-
-/*****************************************************************************/
-
-MapInfo MapInfo::sMapInfo;
-
-/*****************************************************************************/
-
-CallStack::CallStack()
-    : mCount(0)
-{
-}
-
-CallStack::CallStack(const CallStack& rhs)
-    : mCount(rhs.mCount)
-{
+CallStack::CallStack(const CallStack& rhs) :
+        mCount(rhs.mCount) {
     if (mCount) {
-        memcpy(mStack, rhs.mStack, mCount*sizeof(void*));
+        memcpy(mStack, rhs.mStack, mCount * sizeof(backtrace_frame_t));
     }
 }
 
-CallStack::~CallStack()
-{
+CallStack::~CallStack() {
 }
 
-CallStack& CallStack::operator = (const CallStack& rhs)
-{
+CallStack& CallStack::operator = (const CallStack& rhs) {
     mCount = rhs.mCount;
     if (mCount) {
-        memcpy(mStack, rhs.mStack, mCount*sizeof(void*));
+        memcpy(mStack, rhs.mStack, mCount * sizeof(backtrace_frame_t));
     }
     return *this;
 }
@@ -236,7 +51,7 @@
 bool CallStack::operator == (const CallStack& rhs) const {
     if (mCount != rhs.mCount)
         return false;
-    return !mCount || (memcmp(mStack, rhs.mStack, mCount*sizeof(void*)) == 0);
+    return !mCount || memcmp(mStack, rhs.mStack, mCount * sizeof(backtrace_frame_t)) == 0;
 }
 
 bool CallStack::operator != (const CallStack& rhs) const {
@@ -246,7 +61,7 @@
 bool CallStack::operator < (const CallStack& rhs) const {
     if (mCount != rhs.mCount)
         return mCount < rhs.mCount;
-    return memcmp(mStack, rhs.mStack, mCount*sizeof(void*)) < 0;
+    return memcmp(mStack, rhs.mStack, mCount * sizeof(backtrace_frame_t)) < 0;
 }
 
 bool CallStack::operator >= (const CallStack& rhs) const {
@@ -256,7 +71,7 @@
 bool CallStack::operator > (const CallStack& rhs) const {
     if (mCount != rhs.mCount)
         return mCount > rhs.mCount;
-    return memcmp(mStack, rhs.mStack, mCount*sizeof(void*)) > 0;
+    return memcmp(mStack, rhs.mStack, mCount * sizeof(backtrace_frame_t)) > 0;
 }
 
 bool CallStack::operator <= (const CallStack& rhs) const {
@@ -266,84 +81,49 @@
 const void* CallStack::operator [] (int index) const {
     if (index >= int(mCount))
         return 0;
-    return mStack[index];
+    return reinterpret_cast<const void*>(mStack[index].absolute_pc);
 }
 
-
-void CallStack::clear()
-{
+void CallStack::clear() {
     mCount = 0;
 }
 
-void CallStack::update(int32_t ignoreDepth, int32_t maxDepth)
-{
-    if (maxDepth > MAX_DEPTH)
+void CallStack::update(int32_t ignoreDepth, int32_t maxDepth) {
+    if (maxDepth > MAX_DEPTH) {
         maxDepth = MAX_DEPTH;
-    mCount = backtrace(mStack, ignoreDepth, maxDepth);
-}
-
-// Return the stack frame name on the designated level
-String8 CallStack::toStringSingleLevel(const char* prefix, int32_t level) const
-{
-    String8 res;
-    char namebuf[1024];
-    char tmp[256];
-    char tmp1[32];
-    char tmp2[32];
-    void *offs;
-
-    const void* ip = mStack[level];
-    if (!ip) return res;
-
-    if (prefix) res.append(prefix);
-    snprintf(tmp1, 32, "#%02d  ", level);
-    res.append(tmp1);
-
-    const char* name = lookup_symbol(ip, &offs, namebuf, sizeof(namebuf));
-    if (name) {
-        if (linux_gcc_demangler(name, tmp, 256) != 0)
-            name = tmp;
-        snprintf(tmp1, 32, "0x%p: <", ip);
-        snprintf(tmp2, 32, ">+0x%p", offs);
-        res.append(tmp1);
-        res.append(name);
-        res.append(tmp2);
-    } else { 
-        void const* start = 0;
-        name = MapInfo::mapAddressToName(ip, "<unknown>", &start);
-        snprintf(tmp, 256, "pc %08lx  %s", 
-                long(uintptr_t(ip)-uintptr_t(start)), name);
-        res.append(tmp);
     }
-    res.append("\n");
-
-    return res;
+    ssize_t count = unwind_backtrace(mStack, ignoreDepth + 1, maxDepth);
+    mCount = count > 0 ? count : 0;
 }
 
-// Dump a stack trace to the log
-void CallStack::dump(const char* prefix) const
-{
-    /* 
-     * Sending a single long log may be truncated since the stack levels can
-     * get very deep. So we request function names of each frame individually.
-     */
-    for (int i=0; i<int(mCount); i++) {
-        LOGD("%s", toStringSingleLevel(prefix, i).string());
+void CallStack::dump(const char* prefix) const {
+    backtrace_symbol_t symbols[mCount];
+
+    get_backtrace_symbols(mStack, mCount, symbols);
+    for (size_t i = 0; i < mCount; i++) {
+        char line[MAX_BACKTRACE_LINE_LENGTH];
+        format_backtrace_line(i, &mStack[i], &symbols[i],
+                line, MAX_BACKTRACE_LINE_LENGTH);
+        LOGD("%s%s", prefix, line);
     }
+    free_backtrace_symbols(symbols, mCount);
 }
 
-// Return a string (possibly very long) containing the complete stack trace
-String8 CallStack::toString(const char* prefix) const
-{
-    String8 res;
+String8 CallStack::toString(const char* prefix) const {
+    String8 str;
+    backtrace_symbol_t symbols[mCount];
 
-    for (int i=0; i<int(mCount); i++) {
-        res.append(toStringSingleLevel(prefix, i).string());
+    get_backtrace_symbols(mStack, mCount, symbols);
+    for (size_t i = 0; i < mCount; i++) {
+        char line[MAX_BACKTRACE_LINE_LENGTH];
+        format_backtrace_line(i, &mStack[i], &symbols[i],
+                line, MAX_BACKTRACE_LINE_LENGTH);
+        str.append(prefix);
+        str.append(line);
+        str.append("\n");
     }
-
-    return res;
+    free_backtrace_symbols(symbols, mCount);
+    return str;
 }
 
-/*****************************************************************************/
-
 }; // namespace android
diff --git a/libs/utils/FileMap.cpp b/libs/utils/FileMap.cpp
index c220a90..294f7b6 100644
--- a/libs/utils/FileMap.cpp
+++ b/libs/utils/FileMap.cpp
@@ -190,7 +190,7 @@
 
     assert(mBasePtr != NULL);
 
-    LOGV("MAP: base %p/%d data %p/%d\n",
+    ALOGV("MAP: base %p/%d data %p/%d\n",
         mBasePtr, (int) mBaseLength, mDataPtr, (int) mDataLength);
 
     return true;
diff --git a/libs/utils/RefBase.cpp b/libs/utils/RefBase.cpp
index 37d061c..959b382 100644
--- a/libs/utils/RefBase.cpp
+++ b/libs/utils/RefBase.cpp
@@ -421,7 +421,7 @@
             // destroy the object now.
             delete impl->mBase;
         } else {
-            // LOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase);
+            // ALOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase);
             delete impl;
         }
     } else {
diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp
index 6cf01c8..6a9e91d 100644
--- a/libs/utils/ResourceTypes.cpp
+++ b/libs/utils/ResourceTypes.cpp
@@ -69,7 +69,7 @@
 
 static void printToLogFunc(void* cookie, const char* txt)
 {
-    LOGV("%s", txt);
+    ALOGV("%s", txt);
 }
 
 // Standard C isspace() is only required to look at the low byte of its input, so
@@ -1867,7 +1867,7 @@
     const bool notDeviceEndian = htods(0xf0) != 0xf0;
 
     LOAD_TABLE_NOISY(
-        LOGV("Adding resources to ResTable: data=%p, size=0x%x, cookie=%p, asset=%p, copy=%d "
+        ALOGV("Adding resources to ResTable: data=%p, size=0x%x, cookie=%p, asset=%p, copy=%d "
              "idmap=%p\n", data, size, cookie, asset, copyData, idmap));
     
     if (copyData || notDeviceEndian) {
@@ -2122,7 +2122,7 @@
                                           resID, &overlayResID);
             if (retval == NO_ERROR && overlayResID != 0x0) {
                 // for this loop iteration, this is the type and entry we really want
-                LOGV("resource map 0x%08x -> 0x%08x\n", resID, overlayResID);
+                ALOGV("resource map 0x%08x -> 0x%08x\n", resID, overlayResID);
                 T = Res_GETTYPE(overlayResID);
                 E = Res_GETENTRY(overlayResID);
             } else {
@@ -2401,7 +2401,7 @@
                                           resID, &overlayResID);
             if (retval == NO_ERROR && overlayResID != 0x0) {
                 // for this loop iteration, this is the type and entry we really want
-                LOGV("resource map 0x%08x -> 0x%08x\n", resID, overlayResID);
+                ALOGV("resource map 0x%08x -> 0x%08x\n", resID, overlayResID);
                 T = Res_GETTYPE(overlayResID);
                 E = Res_GETENTRY(overlayResID);
             } else {
@@ -2413,9 +2413,9 @@
         const ResTable_type* type;
         const ResTable_entry* entry;
         const Type* typeClass;
-        LOGV("Getting entry pkg=%p, t=%d, e=%d\n", package, T, E);
+        ALOGV("Getting entry pkg=%p, t=%d, e=%d\n", package, T, E);
         ssize_t offset = getEntry(package, T, E, &mParams, &type, &entry, &typeClass);
-        LOGV("Resulting offset=%d\n", offset);
+        ALOGV("Resulting offset=%d\n", offset);
         if (offset <= 0) {
             // No {entry, appropriate config} pair found in package. If this
             // package is an overlay package (ip != 0), this simply means the
@@ -3898,9 +3898,9 @@
 void ResTable::getLocales(Vector<String8>* locales) const
 {
     Vector<ResTable_config> configs;
-    LOGV("calling getConfigurations");
+    ALOGV("calling getConfigurations");
     getConfigurations(&configs);
-    LOGV("called getConfigurations size=%d", (int)configs.size());
+    ALOGV("called getConfigurations size=%d", (int)configs.size());
     const size_t I = configs.size();
     for (size_t i=0; i<I; i++) {
         char locale[6];
@@ -3924,13 +3924,13 @@
     const ResTable_type** outType, const ResTable_entry** outEntry,
     const Type** outTypeClass) const
 {
-    LOGV("Getting entry from package %p\n", package);
+    ALOGV("Getting entry from package %p\n", package);
     const ResTable_package* const pkg = package->package;
 
     const Type* allTypes = package->getType(typeIndex);
-    LOGV("allTypes=%p\n", allTypes);
+    ALOGV("allTypes=%p\n", allTypes);
     if (allTypes == NULL) {
-        LOGV("Skipping entry type index 0x%02x because type is NULL!\n", typeIndex);
+        ALOGV("Skipping entry type index 0x%02x because type is NULL!\n", typeIndex);
         return 0;
     }
 
diff --git a/libs/utils/StreamingZipInflater.cpp b/libs/utils/StreamingZipInflater.cpp
index 00498bd..59a46f9 100644
--- a/libs/utils/StreamingZipInflater.cpp
+++ b/libs/utils/StreamingZipInflater.cpp
@@ -77,7 +77,7 @@
 }
 
 void StreamingZipInflater::initInflateState() {
-    LOGV("Initializing inflate state");
+    ALOGV("Initializing inflate state");
 
     memset(&mInflateState, 0, sizeof(mInflateState));
     mInflateState.zalloc = Z_NULL;
@@ -152,13 +152,13 @@
             mInflateState.avail_out = mOutBufSize;
 
             /*
-            LOGV("Inflating to outbuf: avail_in=%u avail_out=%u next_in=%p next_out=%p",
+            ALOGV("Inflating to outbuf: avail_in=%u avail_out=%u next_in=%p next_out=%p",
                     mInflateState.avail_in, mInflateState.avail_out,
                     mInflateState.next_in, mInflateState.next_out);
             */
             int result = Z_OK;
             if (mStreamNeedsInit) {
-                LOGV("Initializing zlib to inflate");
+                ALOGV("Initializing zlib to inflate");
                 result = inflateInit2(&mInflateState, -MAX_WBITS);
                 mStreamNeedsInit = false;
             }
@@ -192,7 +192,7 @@
         size_t toRead = min_of(mInBufSize, mInTotalSize - mInNextChunkOffset);
         if (toRead > 0) {
             ssize_t didRead = ::read(mFd, mInBuf, toRead);
-            //LOGV("Reading input chunk, size %08x didread %08x", toRead, didRead);
+            //ALOGV("Reading input chunk, size %08x didread %08x", toRead, didRead);
             if (didRead < 0) {
                 // TODO: error
                 LOGE("Error reading asset data");
diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp
index 5dbcb75..fe4b8e6 100644
--- a/libs/utils/Threads.cpp
+++ b/libs/utils/Threads.cpp
@@ -205,7 +205,7 @@
 
     delete pDetails;
 
-    LOG(LOG_VERBOSE, "thread", "thread exiting\n");
+    ALOG(LOG_VERBOSE, "thread", "thread exiting\n");
     return (unsigned int) result;
 }
 
@@ -232,7 +232,7 @@
     if (hThread == NULL)
 #endif
     {
-        LOG(LOG_WARN, "thread", "WARNING: thread create failed\n");
+        ALOG(LOG_WARN, "thread", "WARNING: thread create failed\n");
         return false;
     }
 
@@ -470,7 +470,7 @@
 void Mutex::unlock()
 {
     if (!ReleaseMutex((HANDLE) mState))
-        LOG(LOG_WARN, "thread", "WARNING: bad result from unlocking mutex\n");
+        ALOG(LOG_WARN, "thread", "WARNING: bad result from unlocking mutex\n");
 }
 
 status_t Mutex::tryLock()
@@ -479,7 +479,7 @@
 
     dwWaitResult = WaitForSingleObject((HANDLE) mState, 0);
     if (dwWaitResult != WAIT_OBJECT_0 && dwWaitResult != WAIT_TIMEOUT)
-        LOG(LOG_WARN, "thread", "WARNING: bad result from try-locking mutex\n");
+        ALOG(LOG_WARN, "thread", "WARNING: bad result from try-locking mutex\n");
     return (dwWaitResult == WAIT_OBJECT_0) ? 0 : -1;
 }
 
diff --git a/libs/utils/Timers.cpp b/libs/utils/Timers.cpp
index 64a29f5..64b4701 100644
--- a/libs/utils/Timers.cpp
+++ b/libs/utils/Timers.cpp
@@ -113,7 +113,7 @@
 /*static*/ void DurationTimer::addToTimeval(struct timeval* ptv, long usec)
 {
     if (usec < 0) {
-        LOG(LOG_WARN, "", "Negative values not supported in addToTimeval\n");
+        ALOG(LOG_WARN, "", "Negative values not supported in addToTimeval\n");
         return;
     }
 
diff --git a/libs/utils/VectorImpl.cpp b/libs/utils/VectorImpl.cpp
index bfb37a6..4a90296 100644
--- a/libs/utils/VectorImpl.cpp
+++ b/libs/utils/VectorImpl.cpp
@@ -346,7 +346,7 @@
 
 void* VectorImpl::_grow(size_t where, size_t amount)
 {
-//    LOGV("_grow(this=%p, where=%d, amount=%d) count=%d, capacity=%d",
+//    ALOGV("_grow(this=%p, where=%d, amount=%d) count=%d, capacity=%d",
 //        this, (int)where, (int)amount, (int)mCount, (int)capacity());
 
     LOG_ASSERT(where <= mCount,
@@ -356,7 +356,7 @@
     const size_t new_size = mCount + amount;
     if (capacity() < new_size) {
         const size_t new_capacity = max(kMinVectorCapacity, ((new_size*3)+1)/2);
-//        LOGV("grow vector %p, new_capacity=%d", this, (int)new_capacity);
+//        ALOGV("grow vector %p, new_capacity=%d", this, (int)new_capacity);
         if ((mStorage) &&
             (mCount==where) &&
             (mFlags & HAS_TRIVIAL_COPY) &&
@@ -399,7 +399,7 @@
     if (!mStorage)
         return;
 
-//    LOGV("_shrink(this=%p, where=%d, amount=%d) count=%d, capacity=%d",
+//    ALOGV("_shrink(this=%p, where=%d, amount=%d) count=%d, capacity=%d",
 //        this, (int)where, (int)amount, (int)mCount, (int)capacity());
 
     LOG_ASSERT(where + amount <= mCount,
@@ -409,7 +409,7 @@
     const size_t new_size = mCount - amount;
     if (new_size*3 < capacity()) {
         const size_t new_capacity = max(kMinVectorCapacity, new_size*2);
-//        LOGV("shrink vector %p, new_capacity=%d", this, (int)new_capacity);
+//        ALOGV("shrink vector %p, new_capacity=%d", this, (int)new_capacity);
         if ((where == new_size) &&
             (mFlags & HAS_TRIVIAL_COPY) &&
             (mFlags & HAS_TRIVIAL_DTOR))
diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp
index b18c383..d880f55 100644
--- a/libs/utils/ZipFileRO.cpp
+++ b/libs/utils/ZipFileRO.cpp
@@ -222,7 +222,7 @@
             free(scanBuf);
             return false;
         } else if (header != kLFHSignature) {
-            LOGV("Not a Zip archive (found 0x%08x)\n", header);
+            ALOGV("Not a Zip archive (found 0x%08x)\n", header);
             free(scanBuf);
             return false;
         }
@@ -264,7 +264,7 @@
     int i;
     for (i = readAmount - kEOCDLen; i >= 0; i--) {
         if (scanBuf[i] == 0x50 && get4LE(&scanBuf[i]) == kEOCDSignature) {
-            LOGV("+++ Found EOCD at buf+%d\n", i);
+            ALOGV("+++ Found EOCD at buf+%d\n", i);
             break;
         }
     }
@@ -299,7 +299,7 @@
         return false;
     }
 
-    LOGV("+++ numEntries=%d dirSize=%d dirOffset=%d\n",
+    ALOGV("+++ numEntries=%d dirSize=%d dirOffset=%d\n",
         numEntries, dirSize, dirOffset);
 
     mDirectoryMap = new FileMap();
@@ -372,7 +372,7 @@
             goto bail;
         }
     }
-    LOGV("+++ zip good scan %d entries\n", numEntries);
+    ALOGV("+++ zip good scan %d entries\n", numEntries);
     result = true;
 
 bail:
diff --git a/libs/utils/ZipUtils.cpp b/libs/utils/ZipUtils.cpp
index 9138878..76725b4 100644
--- a/libs/utils/ZipUtils.cpp
+++ b/libs/utils/ZipUtils.cpp
@@ -95,7 +95,7 @@
         if (zstream.avail_in == 0) {
             getSize = (compRemaining > kReadBufSize) ?
                         kReadBufSize : compRemaining;
-            LOGV("+++ reading %ld bytes (%ld left)\n",
+            ALOGV("+++ reading %ld bytes (%ld left)\n",
                 getSize, compRemaining);
 
             int cc = read(fd, readBuf, getSize);
@@ -207,7 +207,7 @@
         if (zstream.avail_in == 0) {
             getSize = (compRemaining > kReadBufSize) ?
                         kReadBufSize : compRemaining;
-            LOGV("+++ reading %ld bytes (%ld left)\n",
+            ALOGV("+++ reading %ld bytes (%ld left)\n",
                 getSize, compRemaining);
 
             int cc = fread(readBuf, 1, getSize, fp);
diff --git a/libs/utils/primes.py b/libs/utils/primes.py
new file mode 100755
index 0000000..e161dd8
--- /dev/null
+++ b/libs/utils/primes.py
@@ -0,0 +1,47 @@
+#!/usr/bin/env python2.6
+#
+# Copyright (C) 2011 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#
+# Generates a table of prime numbers for use in BasicHashtable.cpp.
+#
+# Each prime is chosen such that it is a little more than twice as large as
+# the previous prime in the table.  This makes it easier to choose a new
+# hashtable size when the underlying array is grown by as nominal factor
+# of two each time.
+#
+
+def is_odd_prime(n):
+  limit = (n - 1) / 2
+  d = 3
+  while d <= limit:
+    if n % d == 0:
+      return False
+    d += 2
+  return True
+
+print "static size_t PRIMES[] = {"
+
+n = 5
+max = 2**31 - 1
+while n < max:
+  print "    %d," % (n)
+  n = n * 2 + 1
+  while not is_odd_prime(n):
+    n += 2
+
+print "    0,"
+print "};"
diff --git a/libs/utils/tests/Android.mk b/libs/utils/tests/Android.mk
index b97f52f..58230f4 100644
--- a/libs/utils/tests/Android.mk
+++ b/libs/utils/tests/Android.mk
@@ -4,9 +4,10 @@
 
 # Build the unit tests.
 test_src_files := \
+	BasicHashtable_test.cpp \
 	BlobCache_test.cpp \
-	ObbFile_test.cpp \
 	Looper_test.cpp \
+	ObbFile_test.cpp \
 	String8_test.cpp \
 	Unicode_test.cpp \
 	ZipFileRO_test.cpp \
diff --git a/libs/utils/tests/BasicHashtable_test.cpp b/libs/utils/tests/BasicHashtable_test.cpp
new file mode 100644
index 0000000..764082d
--- /dev/null
+++ b/libs/utils/tests/BasicHashtable_test.cpp
@@ -0,0 +1,577 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "BasicHashtable_test"
+
+#include <utils/BasicHashtable.h>
+#include <cutils/log.h>
+#include <gtest/gtest.h>
+#include <unistd.h>
+
+namespace android {
+
+typedef int SimpleKey;
+typedef int SimpleValue;
+typedef key_value_pair_t<SimpleKey, SimpleValue> SimpleEntry;
+typedef BasicHashtable<SimpleKey, SimpleEntry> SimpleHashtable;
+
+struct ComplexKey {
+    int k;
+
+    explicit ComplexKey(int k) : k(k) {
+        instanceCount += 1;
+    }
+
+    ComplexKey(const ComplexKey& other) : k(other.k) {
+        instanceCount += 1;
+    }
+
+    ~ComplexKey() {
+        instanceCount -= 1;
+    }
+
+    bool operator ==(const ComplexKey& other) const {
+        return k == other.k;
+    }
+
+    bool operator !=(const ComplexKey& other) const {
+        return k != other.k;
+    }
+
+    static ssize_t instanceCount;
+};
+
+ssize_t ComplexKey::instanceCount = 0;
+
+template<> inline hash_t hash_type(const ComplexKey& value) {
+    return hash_type(value.k);
+}
+
+struct ComplexValue {
+    int v;
+
+    explicit ComplexValue(int v) : v(v) {
+        instanceCount += 1;
+    }
+
+    ComplexValue(const ComplexValue& other) : v(other.v) {
+        instanceCount += 1;
+    }
+
+    ~ComplexValue() {
+        instanceCount -= 1;
+    }
+
+    static ssize_t instanceCount;
+};
+
+ssize_t ComplexValue::instanceCount = 0;
+
+typedef key_value_pair_t<ComplexKey, ComplexValue> ComplexEntry;
+typedef BasicHashtable<ComplexKey, ComplexEntry> ComplexHashtable;
+
+class BasicHashtableTest : public testing::Test {
+protected:
+    virtual void SetUp() {
+        ComplexKey::instanceCount = 0;
+        ComplexValue::instanceCount = 0;
+    }
+
+    virtual void TearDown() {
+        ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0));
+    }
+
+    void assertInstanceCount(ssize_t keys, ssize_t values) {
+        if (keys != ComplexKey::instanceCount || values != ComplexValue::instanceCount) {
+            FAIL() << "Expected " << keys << " keys and " << values << " values "
+                    "but there were actually " << ComplexKey::instanceCount << " keys and "
+                    << ComplexValue::instanceCount << " values";
+        }
+    }
+
+public:
+    template <typename TKey, typename TEntry>
+    static void cookieAt(const BasicHashtable<TKey, TEntry>& h, size_t index,
+            bool* collision, bool* present, hash_t* hash) {
+        uint32_t cookie = h.cookieAt(index);
+        *collision = cookie & BasicHashtable<TKey, TEntry>::Bucket::COLLISION;
+        *present = cookie & BasicHashtable<TKey, TEntry>::Bucket::PRESENT;
+        *hash = cookie & BasicHashtable<TKey, TEntry>::Bucket::HASH_MASK;
+    }
+
+    template <typename TKey, typename TEntry>
+    static const void* getBuckets(const BasicHashtable<TKey, TEntry>& h) {
+        return h.mBuckets;
+    }
+};
+
+template <typename TKey, typename TValue>
+static size_t add(BasicHashtable<TKey, key_value_pair_t<TKey, TValue> >& h,
+        const TKey& key, const TValue& value) {
+    return h.add(hash_type(key), key_value_pair_t<TKey, TValue>(key, value));
+}
+
+template <typename TKey, typename TValue>
+static ssize_t find(BasicHashtable<TKey, key_value_pair_t<TKey, TValue> >& h,
+        ssize_t index, const TKey& key) {
+    return h.find(index, hash_type(key), key);
+}
+
+template <typename TKey, typename TValue>
+static bool remove(BasicHashtable<TKey, key_value_pair_t<TKey, TValue> >& h,
+        const TKey& key) {
+    ssize_t index = find(h, -1, key);
+    if (index >= 0) {
+        h.removeAt(index);
+        return true;
+    }
+    return false;
+}
+
+template <typename TEntry>
+static void getKeyValue(const TEntry& entry, int* key, int* value);
+
+template <> void getKeyValue(const SimpleEntry& entry, int* key, int* value) {
+    *key = entry.key;
+    *value = entry.value;
+}
+
+template <> void getKeyValue(const ComplexEntry& entry, int* key, int* value) {
+    *key = entry.key.k;
+    *value = entry.value.v;
+}
+
+template <typename TKey, typename TValue>
+static void dump(BasicHashtable<TKey, key_value_pair_t<TKey, TValue> >& h) {
+    LOGD("hashtable %p, size=%u, capacity=%u, bucketCount=%u",
+            &h, h.size(), h.capacity(), h.bucketCount());
+    for (size_t i = 0; i < h.bucketCount(); i++) {
+        bool collision, present;
+        hash_t hash;
+        BasicHashtableTest::cookieAt(h, i, &collision, &present, &hash);
+        if (present) {
+            int key, value;
+            getKeyValue(h.entryAt(i), &key, &value);
+            LOGD("  [%3u] = collision=%d, present=%d, hash=0x%08x, key=%3d, value=%3d, "
+                    "hash_type(key)=0x%08x",
+                    i, collision, present, hash, key, value, hash_type(key));
+        } else {
+            LOGD("  [%3u] = collision=%d, present=%d",
+                    i, collision, present);
+        }
+    }
+}
+
+TEST_F(BasicHashtableTest, DefaultConstructor_WithDefaultProperties) {
+    SimpleHashtable h;
+
+    EXPECT_EQ(0U, h.size());
+    EXPECT_EQ(3U, h.capacity());
+    EXPECT_EQ(5U, h.bucketCount());
+    EXPECT_EQ(0.75f, h.loadFactor());
+}
+
+TEST_F(BasicHashtableTest, Constructor_WithNonUnityLoadFactor) {
+    SimpleHashtable h(52, 0.8f);
+
+    EXPECT_EQ(0U, h.size());
+    EXPECT_EQ(77U, h.capacity());
+    EXPECT_EQ(97U, h.bucketCount());
+    EXPECT_EQ(0.8f, h.loadFactor());
+}
+
+TEST_F(BasicHashtableTest, Constructor_WithUnityLoadFactorAndExactCapacity) {
+    SimpleHashtable h(46, 1.0f);
+
+    EXPECT_EQ(0U, h.size());
+    EXPECT_EQ(46U, h.capacity()); // must be one less than bucketCount because loadFactor == 1.0f
+    EXPECT_EQ(47U, h.bucketCount());
+    EXPECT_EQ(1.0f, h.loadFactor());
+}
+
+TEST_F(BasicHashtableTest, Constructor_WithUnityLoadFactorAndInexactCapacity) {
+    SimpleHashtable h(42, 1.0f);
+
+    EXPECT_EQ(0U, h.size());
+    EXPECT_EQ(46U, h.capacity()); // must be one less than bucketCount because loadFactor == 1.0f
+    EXPECT_EQ(47U, h.bucketCount());
+    EXPECT_EQ(1.0f, h.loadFactor());
+}
+
+TEST_F(BasicHashtableTest, FindAddFindRemoveFind_OneEntry) {
+    SimpleHashtable h;
+    ssize_t index = find(h, -1, 8);
+    ASSERT_EQ(-1, index);
+
+    index = add(h, 8, 1);
+    ASSERT_EQ(1U, h.size());
+
+    ASSERT_EQ(index, find(h, -1, 8));
+    ASSERT_EQ(8, h.entryAt(index).key);
+    ASSERT_EQ(1, h.entryAt(index).value);
+
+    index = find(h, index, 8);
+    ASSERT_EQ(-1, index);
+
+    ASSERT_TRUE(remove(h, 8));
+    ASSERT_EQ(0U, h.size());
+
+    index = find(h, -1, 8);
+    ASSERT_EQ(-1, index);
+}
+
+TEST_F(BasicHashtableTest, FindAddFindRemoveFind_MultipleEntryWithUniqueKey) {
+    const size_t N = 11;
+
+    SimpleHashtable h;
+    for (size_t i = 0; i < N; i++) {
+        ssize_t index = find(h, -1, int(i));
+        ASSERT_EQ(-1, index);
+
+        index = add(h, int(i), int(i * 10));
+        ASSERT_EQ(i + 1, h.size());
+
+        ASSERT_EQ(index, find(h, -1, int(i)));
+        ASSERT_EQ(int(i), h.entryAt(index).key);
+        ASSERT_EQ(int(i * 10), h.entryAt(index).value);
+
+        index = find(h, index, int(i));
+        ASSERT_EQ(-1, index);
+    }
+
+    for (size_t i = N; --i > 0; ) {
+        ASSERT_TRUE(remove(h, int(i))) << "i = " << i;
+        ASSERT_EQ(i, h.size());
+
+        ssize_t index = find(h, -1, int(i));
+        ASSERT_EQ(-1, index);
+    }
+}
+
+TEST_F(BasicHashtableTest, FindAddFindRemoveFind_MultipleEntryWithDuplicateKey) {
+    const size_t N = 11;
+    const int K = 1;
+
+    SimpleHashtable h;
+    for (size_t i = 0; i < N; i++) {
+        ssize_t index = find(h, -1, K);
+        if (i == 0) {
+            ASSERT_EQ(-1, index);
+        } else {
+            ASSERT_NE(-1, index);
+        }
+
+        add(h, K, int(i));
+        ASSERT_EQ(i + 1, h.size());
+
+        index = -1;
+        int values = 0;
+        for (size_t j = 0; j <= i; j++) {
+            index = find(h, index, K);
+            ASSERT_GE(index, 0);
+            ASSERT_EQ(K, h.entryAt(index).key);
+            values |= 1 << h.entryAt(index).value;
+        }
+        ASSERT_EQ(values, (1 << (i + 1)) - 1);
+
+        index = find(h, index, K);
+        ASSERT_EQ(-1, index);
+    }
+
+    for (size_t i = N; --i > 0; ) {
+        ASSERT_TRUE(remove(h, K)) << "i = " << i;
+        ASSERT_EQ(i, h.size());
+
+        ssize_t index = -1;
+        for (size_t j = 0; j < i; j++) {
+            index = find(h, index, K);
+            ASSERT_GE(index, 0);
+            ASSERT_EQ(K, h.entryAt(index).key);
+        }
+
+        index = find(h, index, K);
+        ASSERT_EQ(-1, index);
+    }
+}
+
+TEST_F(BasicHashtableTest, Clear_WhenAlreadyEmpty_DoesNothing) {
+    SimpleHashtable h;
+    h.clear();
+
+    EXPECT_EQ(0U, h.size());
+    EXPECT_EQ(3U, h.capacity());
+    EXPECT_EQ(5U, h.bucketCount());
+    EXPECT_EQ(0.75f, h.loadFactor());
+}
+
+TEST_F(BasicHashtableTest, Clear_AfterElementsAdded_RemovesThem) {
+    SimpleHashtable h;
+    add(h, 0, 0);
+    add(h, 1, 0);
+    h.clear();
+
+    EXPECT_EQ(0U, h.size());
+    EXPECT_EQ(3U, h.capacity());
+    EXPECT_EQ(5U, h.bucketCount());
+    EXPECT_EQ(0.75f, h.loadFactor());
+}
+
+TEST_F(BasicHashtableTest, Clear_AfterElementsAdded_DestroysThem) {
+    ComplexHashtable h;
+    add(h, ComplexKey(0), ComplexValue(0));
+    add(h, ComplexKey(1), ComplexValue(0));
+    ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2));
+
+    h.clear();
+    ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0));
+
+    EXPECT_EQ(0U, h.size());
+    EXPECT_EQ(3U, h.capacity());
+    EXPECT_EQ(5U, h.bucketCount());
+    EXPECT_EQ(0.75f, h.loadFactor());
+}
+
+TEST_F(BasicHashtableTest, Remove_AfterElementsAdded_DestroysThem) {
+    ComplexHashtable h;
+    add(h, ComplexKey(0), ComplexValue(0));
+    add(h, ComplexKey(1), ComplexValue(0));
+    ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2));
+
+    ASSERT_TRUE(remove(h, ComplexKey(0)));
+    ASSERT_NO_FATAL_FAILURE(assertInstanceCount(1, 1));
+
+    ASSERT_TRUE(remove(h, ComplexKey(1)));
+    ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0));
+
+    EXPECT_EQ(0U, h.size());
+    EXPECT_EQ(3U, h.capacity());
+    EXPECT_EQ(5U, h.bucketCount());
+    EXPECT_EQ(0.75f, h.loadFactor());
+}
+
+TEST_F(BasicHashtableTest, Destructor_AfterElementsAdded_DestroysThem) {
+    {
+        ComplexHashtable h;
+        add(h, ComplexKey(0), ComplexValue(0));
+        add(h, ComplexKey(1), ComplexValue(0));
+        ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2));
+    } // h is destroyed here
+
+    ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0));
+}
+
+TEST_F(BasicHashtableTest, Next_WhenEmpty_ReturnsMinusOne) {
+    SimpleHashtable h;
+
+    ASSERT_EQ(-1, h.next(-1));
+}
+
+TEST_F(BasicHashtableTest, Next_WhenNonEmpty_IteratesOverAllEntries) {
+    const int N = 88;
+
+    SimpleHashtable h;
+    for (int i = 0; i < N; i++) {
+        add(h, i, i * 10);
+    }
+
+    bool set[N];
+    memset(set, 0, sizeof(bool) * N);
+    int count = 0;
+    for (ssize_t index = -1; (index = h.next(index)) != -1; ) {
+        ASSERT_GE(index, 0);
+        ASSERT_LT(size_t(index), h.bucketCount());
+
+        const SimpleEntry& entry = h.entryAt(index);
+        ASSERT_GE(entry.key, 0);
+        ASSERT_LT(entry.key, N);
+        ASSERT_EQ(false, set[entry.key]);
+        ASSERT_EQ(entry.key * 10, entry.value);
+
+        set[entry.key] = true;
+        count += 1;
+    }
+    ASSERT_EQ(N, count);
+}
+
+TEST_F(BasicHashtableTest, Add_RehashesOnDemand) {
+    SimpleHashtable h;
+    size_t initialCapacity = h.capacity();
+    size_t initialBucketCount = h.bucketCount();
+
+    for (size_t i = 0; i < initialCapacity; i++) {
+        add(h, int(i), 0);
+    }
+
+    EXPECT_EQ(initialCapacity, h.size());
+    EXPECT_EQ(initialCapacity, h.capacity());
+    EXPECT_EQ(initialBucketCount, h.bucketCount());
+
+    add(h, -1, -1);
+
+    EXPECT_EQ(initialCapacity + 1, h.size());
+    EXPECT_GT(h.capacity(), initialCapacity);
+    EXPECT_GT(h.bucketCount(), initialBucketCount);
+    EXPECT_GT(h.bucketCount(), h.capacity());
+}
+
+TEST_F(BasicHashtableTest, Rehash_WhenCapacityAndBucketCountUnchanged_DoesNothing) {
+    ComplexHashtable h;
+    add(h, ComplexKey(0), ComplexValue(0));
+    const void* oldBuckets = getBuckets(h);
+    ASSERT_NE((void*)NULL, oldBuckets);
+    ASSERT_NO_FATAL_FAILURE(assertInstanceCount(1, 1));
+
+    h.rehash(h.capacity(), h.loadFactor());
+
+    ASSERT_EQ(oldBuckets, getBuckets(h));
+    ASSERT_NO_FATAL_FAILURE(assertInstanceCount(1, 1));
+}
+
+TEST_F(BasicHashtableTest, Rehash_WhenEmptyAndHasNoBuckets_ButDoesNotAllocateBuckets) {
+    ComplexHashtable h;
+    ASSERT_EQ((void*)NULL, getBuckets(h));
+    ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0));
+
+    h.rehash(9, 1.0f);
+
+    EXPECT_EQ(0U, h.size());
+    EXPECT_EQ(10U, h.capacity());
+    EXPECT_EQ(11U, h.bucketCount());
+    EXPECT_EQ(1.0f, h.loadFactor());
+    EXPECT_EQ((void*)NULL, getBuckets(h));
+    ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0));
+}
+
+TEST_F(BasicHashtableTest, Rehash_WhenEmptyAndHasBuckets_ReleasesBucketsAndSetsCapacity) {
+    ComplexHashtable h(10);
+    add(h, ComplexKey(0), ComplexValue(0));
+    ASSERT_TRUE(remove(h, ComplexKey(0)));
+    ASSERT_NE((void*)NULL, getBuckets(h));
+    ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0));
+
+    h.rehash(0, 0.75f);
+
+    EXPECT_EQ(0U, h.size());
+    EXPECT_EQ(3U, h.capacity());
+    EXPECT_EQ(5U, h.bucketCount());
+    EXPECT_EQ(0.75f, h.loadFactor());
+    EXPECT_EQ((void*)NULL, getBuckets(h));
+    ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0));
+}
+
+TEST_F(BasicHashtableTest, Rehash_WhenLessThanCurrentCapacity_ShrinksBuckets) {
+    ComplexHashtable h(10);
+    add(h, ComplexKey(0), ComplexValue(0));
+    add(h, ComplexKey(1), ComplexValue(1));
+    const void* oldBuckets = getBuckets(h);
+    ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2));
+
+    h.rehash(0, 0.75f);
+
+    EXPECT_EQ(2U, h.size());
+    EXPECT_EQ(3U, h.capacity());
+    EXPECT_EQ(5U, h.bucketCount());
+    EXPECT_EQ(0.75f, h.loadFactor());
+    EXPECT_NE(oldBuckets, getBuckets(h));
+    ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2));
+}
+
+TEST_F(BasicHashtableTest, CopyOnWrite) {
+    ComplexHashtable h1;
+    add(h1, ComplexKey(0), ComplexValue(0));
+    add(h1, ComplexKey(1), ComplexValue(1));
+    const void* originalBuckets = getBuckets(h1);
+    ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2));
+    ssize_t index0 = find(h1, -1, ComplexKey(0));
+    EXPECT_GE(index0, 0);
+
+    // copy constructor acquires shared reference
+    ComplexHashtable h2(h1);
+    ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2));
+    ASSERT_EQ(originalBuckets, getBuckets(h2));
+    EXPECT_EQ(h1.size(), h2.size());
+    EXPECT_EQ(h1.capacity(), h2.capacity());
+    EXPECT_EQ(h1.bucketCount(), h2.bucketCount());
+    EXPECT_EQ(h1.loadFactor(), h2.loadFactor());
+    EXPECT_EQ(index0, find(h2, -1, ComplexKey(0)));
+
+    // operator= acquires shared reference
+    ComplexHashtable h3;
+    h3 = h2;
+    ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2));
+    ASSERT_EQ(originalBuckets, getBuckets(h3));
+    EXPECT_EQ(h1.size(), h3.size());
+    EXPECT_EQ(h1.capacity(), h3.capacity());
+    EXPECT_EQ(h1.bucketCount(), h3.bucketCount());
+    EXPECT_EQ(h1.loadFactor(), h3.loadFactor());
+    EXPECT_EQ(index0, find(h3, -1, ComplexKey(0)));
+
+    // editEntryAt copies shared contents
+    h1.editEntryAt(index0).value.v = 42;
+    ASSERT_NO_FATAL_FAILURE(assertInstanceCount(4, 4));
+    ASSERT_NE(originalBuckets, getBuckets(h1));
+    EXPECT_EQ(42, h1.entryAt(index0).value.v);
+    EXPECT_EQ(0, h2.entryAt(index0).value.v);
+    EXPECT_EQ(0, h3.entryAt(index0).value.v);
+
+    // clear releases reference to shared contents
+    h2.clear();
+    ASSERT_NO_FATAL_FAILURE(assertInstanceCount(4, 4));
+    EXPECT_EQ(0U, h2.size());
+    ASSERT_NE(originalBuckets, getBuckets(h2));
+
+    // operator= acquires shared reference, destroys unshared contents
+    h1 = h3;
+    ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2));
+    ASSERT_EQ(originalBuckets, getBuckets(h1));
+    EXPECT_EQ(h3.size(), h1.size());
+    EXPECT_EQ(h3.capacity(), h1.capacity());
+    EXPECT_EQ(h3.bucketCount(), h1.bucketCount());
+    EXPECT_EQ(h3.loadFactor(), h1.loadFactor());
+    EXPECT_EQ(index0, find(h1, -1, ComplexKey(0)));
+
+    // add copies shared contents
+    add(h1, ComplexKey(2), ComplexValue(2));
+    ASSERT_NO_FATAL_FAILURE(assertInstanceCount(5, 5));
+    ASSERT_NE(originalBuckets, getBuckets(h1));
+    EXPECT_EQ(3U, h1.size());
+    EXPECT_EQ(0U, h2.size());
+    EXPECT_EQ(2U, h3.size());
+
+    // remove copies shared contents
+    h1 = h3;
+    ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2));
+    ASSERT_EQ(originalBuckets, getBuckets(h1));
+    h1.removeAt(index0);
+    ASSERT_NO_FATAL_FAILURE(assertInstanceCount(3, 3));
+    ASSERT_NE(originalBuckets, getBuckets(h1));
+    EXPECT_EQ(1U, h1.size());
+    EXPECT_EQ(0U, h2.size());
+    EXPECT_EQ(2U, h3.size());
+
+    // rehash copies shared contents
+    h1 = h3;
+    ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2));
+    ASSERT_EQ(originalBuckets, getBuckets(h1));
+    h1.rehash(10, 1.0f);
+    ASSERT_NO_FATAL_FAILURE(assertInstanceCount(4, 4));
+    ASSERT_NE(originalBuckets, getBuckets(h1));
+    EXPECT_EQ(2U, h1.size());
+    EXPECT_EQ(0U, h2.size());
+    EXPECT_EQ(2U, h3.size());
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index f63c0c1..95d651a 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -2,19 +2,22 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES:= \
-    Layer.cpp 								\
-    LayerBase.cpp 							\
-    LayerDim.cpp 							\
-    LayerScreenshot.cpp						\
-    DdmConnection.cpp						\
-    DisplayHardware/DisplayHardware.cpp 	\
+    EventThread.cpp                         \
+    Layer.cpp                               \
+    LayerBase.cpp                           \
+    LayerDim.cpp                            \
+    LayerScreenshot.cpp                     \
+    DdmConnection.cpp                       \
+    DisplayHardware/DisplayHardware.cpp     \
     DisplayHardware/DisplayHardwareBase.cpp \
-    DisplayHardware/HWComposer.cpp 			\
-    GLExtensions.cpp 						\
-    MessageQueue.cpp 						\
-    SurfaceFlinger.cpp 						\
-    SurfaceTextureLayer.cpp 				\
-    Transform.cpp 							\
+    DisplayHardware/HWComposer.cpp          \
+    DisplayHardware/VSyncBarrier.cpp        \
+    DisplayEventConnection.cpp              \
+    GLExtensions.cpp                        \
+    MessageQueue.cpp                        \
+    SurfaceFlinger.cpp                      \
+    SurfaceTextureLayer.cpp                 \
+    Transform.cpp                           \
     
 
 LOCAL_CFLAGS:= -DLOG_TAG=\"SurfaceFlinger\"
diff --git a/services/surfaceflinger/DisplayEventConnection.cpp b/services/surfaceflinger/DisplayEventConnection.cpp
new file mode 100644
index 0000000..a0aa9c0
--- /dev/null
+++ b/services/surfaceflinger/DisplayEventConnection.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <gui/IDisplayEventConnection.h>
+#include <gui/BitTube.h>
+#include <gui/DisplayEventReceiver.h>
+
+#include <utils/Errors.h>
+
+#include "SurfaceFlinger.h"
+#include "DisplayEventConnection.h"
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+DisplayEventConnection::DisplayEventConnection(
+        const sp<SurfaceFlinger>& flinger)
+    : mFlinger(flinger), mChannel(new BitTube())
+{
+}
+
+DisplayEventConnection::~DisplayEventConnection() {
+    mFlinger->cleanupDisplayEventConnection(this);
+}
+
+void DisplayEventConnection::onFirstRef() {
+    // nothing to do here for now.
+}
+
+sp<BitTube> DisplayEventConnection::getDataChannel() const {
+    return mChannel;
+}
+
+status_t DisplayEventConnection::postEvent(const DisplayEventReceiver::Event& event)
+{
+    ssize_t size = mChannel->write(&event, sizeof(DisplayEventReceiver::Event));
+    return size < 0 ? status_t(size) : status_t(NO_ERROR);
+}
+
+
+// ---------------------------------------------------------------------------
+
+}; // namespace android
diff --git a/services/surfaceflinger/DisplayEventConnection.h b/services/surfaceflinger/DisplayEventConnection.h
new file mode 100644
index 0000000..46cf64b
--- /dev/null
+++ b/services/surfaceflinger/DisplayEventConnection.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SURFACE_FLINGER_DISPLAY_EVENT_CONNECTION_H
+#define ANDROID_SURFACE_FLINGER_DISPLAY_EVENT_CONNECTION_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <gui/IDisplayEventConnection.h>
+
+#include <utils/Errors.h>
+#include <gui/DisplayEventReceiver.h>
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+class BitTube;
+class SurfaceFlinger;
+
+// ---------------------------------------------------------------------------
+
+class DisplayEventConnection : public BnDisplayEventConnection {
+public:
+    DisplayEventConnection(const sp<SurfaceFlinger>& flinger);
+
+    status_t postEvent(const DisplayEventReceiver::Event& event);
+
+private:
+    virtual ~DisplayEventConnection();
+    virtual void onFirstRef();
+    virtual sp<BitTube> getDataChannel() const;
+
+    sp<SurfaceFlinger> const mFlinger;
+    sp<BitTube> const mChannel;
+};
+
+// ---------------------------------------------------------------------------
+
+}; // namespace android
+
+// ---------------------------------------------------------------------------
+
+#endif /* ANDROID_SURFACE_FLINGER_DISPLAY_EVENT_CONNECTION_H */
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
index f94d321..3bbc75e 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
@@ -140,6 +140,7 @@
     mDpiX = mNativeWindow->xdpi;
     mDpiY = mNativeWindow->ydpi;
     mRefreshRate = fbDev->fps;
+    mNextFakeVSync = 0;
 
 
 /* FIXME: this is a temporary HACK until we are able to report the refresh rate
@@ -152,6 +153,8 @@
 #warning "refresh rate set via makefile to REFRESH_RATE"
 #endif
 
+    mRefreshPeriod = nsecs_t(1e9 / mRefreshRate);
+
     EGLint w, h, dummy;
     EGLint numConfigs=0;
     EGLSurface surface;
@@ -346,6 +349,37 @@
     return mPageFlipCount;
 }
 
+// this needs to be thread safe
+nsecs_t DisplayHardware::waitForVSync() const {
+    nsecs_t timestamp;
+    if (mVSync.wait(&timestamp) < 0) {
+        // vsync not supported!
+        usleep( getDelayToNextVSyncUs(&timestamp) );
+    }
+    return timestamp;
+}
+
+int32_t DisplayHardware::getDelayToNextVSyncUs(nsecs_t* timestamp) const {
+    Mutex::Autolock _l(mFakeVSyncMutex);
+    const nsecs_t period = mRefreshPeriod;
+    const nsecs_t now = systemTime(CLOCK_MONOTONIC);
+    nsecs_t next_vsync = mNextFakeVSync;
+    nsecs_t sleep = next_vsync - now;
+    if (sleep < 0) {
+        // we missed, find where the next vsync should be
+        sleep = (period - ((now - next_vsync) % period));
+        next_vsync = now + sleep;
+    }
+    mNextFakeVSync = next_vsync + period;
+    timestamp[0] = next_vsync;
+
+    // round to next microsecond
+    int32_t sleep_us = (sleep + 999LL) / 1000LL;
+
+    // guaranteed to be > 0
+    return sleep_us;
+}
+
 status_t DisplayHardware::compositionComplete() const {
     return mNativeWindow->compositionComplete();
 }
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardware.h b/services/surfaceflinger/DisplayHardware/DisplayHardware.h
index f02c954..45d4b45 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayHardware.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.h
@@ -32,6 +32,7 @@
 #include "GLExtensions.h"
 
 #include "DisplayHardware/DisplayHardwareBase.h"
+#include "DisplayHardware/VSyncBarrier.h"
 
 namespace android {
 
@@ -74,6 +75,9 @@
     uint32_t    getMaxTextureSize() const;
     uint32_t    getMaxViewportDims() const;
 
+    // waits for the next vsync and returns the timestamp of when it happened
+    nsecs_t        waitForVSync() const;
+
     uint32_t getPageFlipCount() const;
     EGLDisplay getEGLDisplay() const { return mDisplay; }
 
@@ -95,6 +99,7 @@
 private:
     void init(uint32_t displayIndex) __attribute__((noinline));
     void fini() __attribute__((noinline));
+    int32_t getDelayToNextVSyncUs(nsecs_t* timestamp) const;
 
     sp<SurfaceFlinger> mFlinger;
     EGLDisplay      mDisplay;
@@ -112,7 +117,12 @@
     mutable uint32_t mPageFlipCount;
     GLint           mMaxViewportDims[2];
     GLint           mMaxTextureSize;
-    
+    VSyncBarrier    mVSync;
+
+    mutable Mutex   mFakeVSyncMutex;
+    mutable nsecs_t mNextFakeVSync;
+    nsecs_t         mRefreshPeriod;
+
     HWComposer*     mHwc;
 
     sp<FramebufferNativeWindow> mNativeWindow;
diff --git a/services/surfaceflinger/DisplayHardware/VSyncBarrier.cpp b/services/surfaceflinger/DisplayHardware/VSyncBarrier.cpp
new file mode 100644
index 0000000..187da20
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/VSyncBarrier.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <linux/fb.h>
+
+#include "DisplayHardware/VSyncBarrier.h"
+
+#ifndef FBIO_WAITFORVSYNC
+#define FBIO_WAITFORVSYNC   _IOW('F', 0x20, __u32)
+#endif
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+VSyncBarrier::VSyncBarrier() : mFd(-EINVAL) {
+#if HAS_WAITFORVSYNC
+    mFd = open("/dev/graphics/fb0", O_RDWR);
+    if (mFd < 0) {
+        mFd = -errno;
+    }
+    // try to see if FBIO_WAITFORVSYNC is supported
+    uint32_t crt = 0;
+    int err = ioctl(mFd, FBIO_WAITFORVSYNC, &crt);
+    if (err < 0) {
+        close(mFd);
+        mFd = -EINVAL;
+    }
+#endif
+}
+
+VSyncBarrier::~VSyncBarrier() {
+    if (mFd >= 0) {
+        close(mFd);
+    }
+}
+
+status_t VSyncBarrier::initCheck() const {
+    return mFd < 0 ? mFd : status_t(NO_ERROR);
+}
+
+// this must be thread-safe
+status_t VSyncBarrier::wait(nsecs_t* timestamp) const {
+    if (mFd < 0) {
+        return mFd;
+    }
+
+    int err;
+    uint32_t crt = 0;
+    do {
+        err = ioctl(mFd, FBIO_WAITFORVSYNC, &crt);
+    } while (err<0 && errno==EINTR);
+    if (err < 0) {
+        return -errno;
+    }
+    // ideally this would come from the driver
+    timestamp[0] = systemTime();
+    return NO_ERROR;
+}
+
+// ---------------------------------------------------------------------------
+}; // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/VSyncBarrier.h b/services/surfaceflinger/DisplayHardware/VSyncBarrier.h
new file mode 100644
index 0000000..3c32950
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/VSyncBarrier.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SURFACE_FLINGER_VSYNCBARRIER_H_
+#define ANDROID_SURFACE_FLINGER_VSYNCBARRIER_H_
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/Timers.h>
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+class VSyncBarrier {
+    int mFd;
+public:
+    VSyncBarrier();
+    ~VSyncBarrier();
+    status_t initCheck() const;
+    status_t wait(nsecs_t* timestamp) const;
+};
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+
+#endif /* ANDROID_SURFACE_FLINGER_VSYNCBARRIER_H_ */
diff --git a/services/surfaceflinger/EventThread.cpp b/services/surfaceflinger/EventThread.cpp
new file mode 100644
index 0000000..edb06ba
--- /dev/null
+++ b/services/surfaceflinger/EventThread.cpp
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <gui/IDisplayEventConnection.h>
+#include <gui/DisplayEventReceiver.h>
+
+#include <utils/Errors.h>
+
+#include "DisplayHardware/DisplayHardware.h"
+#include "DisplayEventConnection.h"
+#include "EventThread.h"
+#include "SurfaceFlinger.h"
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+EventThread::EventThread(const sp<SurfaceFlinger>& flinger)
+    : mFlinger(flinger),
+      mHw(flinger->graphicPlane(0).displayHardware()),
+      mDeliveredEvents(0)
+{
+}
+
+void EventThread::onFirstRef() {
+    run("EventThread", PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE);
+}
+
+status_t EventThread::registerDisplayEventConnection(
+        const sp<DisplayEventConnection>& connection) {
+    Mutex::Autolock _l(mLock);
+    mDisplayEventConnections.add(connection);
+    mCondition.signal();
+    return NO_ERROR;
+}
+
+status_t EventThread::unregisterDisplayEventConnection(
+        const wp<DisplayEventConnection>& connection) {
+    Mutex::Autolock _l(mLock);
+    mDisplayEventConnections.remove(connection);
+    mCondition.signal();
+    return NO_ERROR;
+}
+
+bool EventThread::threadLoop() {
+
+    nsecs_t timestamp;
+    Mutex::Autolock _l(mLock);
+    do {
+        // wait for listeners
+        while (!mDisplayEventConnections.size()) {
+            mCondition.wait(mLock);
+        }
+
+        // wait for vsync
+        mLock.unlock();
+        timestamp = mHw.waitForVSync();
+        mLock.lock();
+
+        // make sure we still have some listeners
+    } while (!mDisplayEventConnections.size());
+
+
+    // dispatch vsync events to listeners...
+    mDeliveredEvents++;
+    const size_t count = mDisplayEventConnections.size();
+
+    DisplayEventReceiver::Event vsync;
+    vsync.header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
+    vsync.header.timestamp = timestamp;
+    vsync.vsync.count = mDeliveredEvents;
+
+    for (size_t i=0 ; i<count ; i++) {
+        sp<DisplayEventConnection> conn(mDisplayEventConnections.itemAt(i).promote());
+        // make sure the connection didn't die
+        if (conn != NULL) {
+            status_t err = conn->postEvent(vsync);
+            if (err == -EAGAIN || err == -EWOULDBLOCK) {
+                // The destination doesn't accept events anymore, it's probably
+                // full. For now, we just drop the events on the floor.
+                // Note that some events cannot be dropped and would have to be
+                // re-sent later. Right-now we don't have the ability to do
+                // this, but it doesn't matter for VSYNC.
+            } else if (err < 0) {
+                // handle any other error on the pipe as fatal. the only
+                // reasonable thing to do is to clean-up this connection.
+                // The most common error we'll get here is -EPIPE.
+                mDisplayEventConnections.remove(conn);
+            }
+        }
+    }
+
+    return true;
+}
+
+status_t EventThread::readyToRun() {
+    LOGI("EventThread ready to run.");
+    return NO_ERROR;
+}
+
+void EventThread::dump(String8& result, char* buffer, size_t SIZE) const {
+    Mutex::Autolock _l(mLock);
+    result.append("VSYNC state:\n");
+    snprintf(buffer, SIZE, "  numListeners=%u, events-delivered: %u\n",
+            mDisplayEventConnections.size(), mDeliveredEvents);
+    result.append(buffer);
+}
+
+// ---------------------------------------------------------------------------
+
+}; // namespace android
diff --git a/services/surfaceflinger/EventThread.h b/services/surfaceflinger/EventThread.h
new file mode 100644
index 0000000..0482ab7
--- /dev/null
+++ b/services/surfaceflinger/EventThread.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SURFACE_FLINGER_EVENT_THREAD_H
+#define ANDROID_SURFACE_FLINGER_EVENT_THREAD_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <gui/IDisplayEventConnection.h>
+
+#include <utils/Errors.h>
+#include <utils/threads.h>
+#include <utils/SortedVector.h>
+
+#include "DisplayEventConnection.h"
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+class SurfaceFlinger;
+class DisplayHardware;
+
+// ---------------------------------------------------------------------------
+
+class EventThread : public Thread {
+    friend class DisplayEventConnection;
+
+public:
+    EventThread(const sp<SurfaceFlinger>& flinger);
+
+    status_t registerDisplayEventConnection(
+            const sp<DisplayEventConnection>& connection);
+
+    status_t unregisterDisplayEventConnection(
+            const wp<DisplayEventConnection>& connection);
+
+    void dump(String8& result, char* buffer, size_t SIZE) const;
+
+private:
+    virtual bool        threadLoop();
+    virtual status_t    readyToRun();
+    virtual void        onFirstRef();
+
+    // constants
+    sp<SurfaceFlinger> mFlinger;
+    const DisplayHardware& mHw;
+
+    mutable Mutex mLock;
+    mutable Condition mCondition;
+
+    // protected by mLock
+    SortedVector<wp<DisplayEventConnection> > mDisplayEventConnections;
+    size_t mDeliveredEvents;
+};
+
+// ---------------------------------------------------------------------------
+
+}; // namespace android
+
+// ---------------------------------------------------------------------------
+
+#endif /* ANDROID_SURFACE_FLINGER_EVENT_THREAD_H */
diff --git a/services/surfaceflinger/MessageQueue.cpp b/services/surfaceflinger/MessageQueue.cpp
index aebe1b8..1846ccb 100644
--- a/services/surfaceflinger/MessageQueue.cpp
+++ b/services/surfaceflinger/MessageQueue.cpp
@@ -29,169 +29,81 @@
 
 // ---------------------------------------------------------------------------
 
-void MessageList::insert(const sp<MessageBase>& node) 
-{
-    LIST::iterator cur(mList.begin());
-    LIST::iterator end(mList.end());
-    while (cur != end) {
-        if (*node < **cur) {
-            mList.insert(cur, node);
-            return;
-        }
-        ++cur;
-    }
-    mList.insert(++end, node);
+MessageBase::MessageBase()
+    : MessageHandler() {
 }
 
-void MessageList::remove(MessageList::LIST::iterator pos) 
-{
-    mList.erase(pos);
+MessageBase::~MessageBase() {
 }
 
+void MessageBase::handleMessage(const Message&) {
+    this->handler();
+    barrier.open();
+};
+
 // ---------------------------------------------------------------------------
 
 MessageQueue::MessageQueue()
-    : mInvalidate(false)
-{
-    mInvalidateMessage = new MessageBase(INVALIDATE);
-}
-
-MessageQueue::~MessageQueue()
+    : mLooper(new Looper(true)),
+      mInvalidatePending(0)
 {
 }
 
-sp<MessageBase> MessageQueue::waitMessage(nsecs_t timeout)
-{
-    sp<MessageBase> result;
+MessageQueue::~MessageQueue() {
+}
 
-    bool again;
+void MessageQueue::waitMessage() {
     do {
-        const nsecs_t timeoutTime = systemTime() + timeout;
-        while (true) {
-            Mutex::Autolock _l(mLock);
-            nsecs_t now = systemTime();
-            nsecs_t nextEventTime = -1;
-
-            LIST::iterator cur(mMessages.begin());
-            if (cur != mMessages.end()) {
-                result = *cur;
-            }
-            
-            if (result != 0) {
-                if (result->when <= now) {
-                    // there is a message to deliver
-                    mMessages.remove(cur);
-                    break;
-                }
-                nextEventTime = result->when;
-                result = 0;
-            }
-
-            // see if we have an invalidate message
-            if (mInvalidate) {
-                mInvalidate = false;
-                mInvalidateMessage->when = now;
-                result = mInvalidateMessage;
-                break;
-            }
-
-            if (timeout >= 0) {
-                if (timeoutTime < now) {
-                    // we timed-out, return a NULL message
-                    result = 0;
-                    break;
-                }
-                if (nextEventTime > 0) {
-                    if (nextEventTime > timeoutTime) {
-                        nextEventTime = timeoutTime;
-                    }
-                } else {
-                    nextEventTime = timeoutTime;
-                }
-            }
-
-            if (nextEventTime >= 0) {
-                //LOGD("nextEventTime = %lld ms", nextEventTime);
-                if (nextEventTime > 0) {
-                    // we're about to wait, flush the binder command buffer
-                    IPCThreadState::self()->flushCommands();
-                    const nsecs_t reltime = nextEventTime - systemTime();
-                    if (reltime > 0) {
-                        mCondition.waitRelative(mLock, reltime);
-                    }
-                }
-            } else {
-                //LOGD("going to wait");
-                // we're about to wait, flush the binder command buffer
-                IPCThreadState::self()->flushCommands();
-                mCondition.wait(mLock);
-            }
-        } 
-        // here we're not holding the lock anymore
-
-        if (result == 0)
+        // handle invalidate events first
+        if (android_atomic_and(0, &mInvalidatePending) != 0)
             break;
 
-        again = result->handler();
-        if (again) {
-            // the message has been processed. release our reference to it
-            // without holding the lock.
-            result->notify();
-            result = 0;
-        }
-        
-    } while (again);
+        IPCThreadState::self()->flushCommands();
 
-    return result;
+        int32_t ret = mLooper->pollOnce(-1);
+        switch (ret) {
+            case ALOOPER_POLL_WAKE:
+                // we got woken-up there is work to do in the main loop
+                continue;
+
+            case ALOOPER_POLL_CALLBACK:
+                // callback was handled, loop again
+                continue;
+
+            case ALOOPER_POLL_TIMEOUT:
+                // timeout (should not happen)
+                continue;
+
+            case ALOOPER_POLL_ERROR:
+                LOGE("ALOOPER_POLL_ERROR");
+                continue;
+
+            default:
+                // should not happen
+                LOGE("Looper::pollOnce() returned unknown status %d", ret);
+                continue;
+        }
+    } while (true);
 }
 
 status_t MessageQueue::postMessage(
-        const sp<MessageBase>& message, nsecs_t relTime, uint32_t flags)
+        const sp<MessageBase>& messageHandler, nsecs_t relTime)
 {
-    return queueMessage(message, relTime, flags);
+    const Message dummyMessage;
+    if (relTime > 0) {
+        mLooper->sendMessageDelayed(relTime, messageHandler, dummyMessage);
+    } else {
+        mLooper->sendMessage(messageHandler, dummyMessage);
+    }
+    return NO_ERROR;
 }
 
 status_t MessageQueue::invalidate() {
-    Mutex::Autolock _l(mLock);
-    mInvalidate = true;
-    mCondition.signal();
+    android_atomic_or(1, &mInvalidatePending);
+    mLooper->wake();
     return NO_ERROR;
 }
 
-status_t MessageQueue::queueMessage(
-        const sp<MessageBase>& message, nsecs_t relTime, uint32_t flags)
-{
-    Mutex::Autolock _l(mLock);
-    message->when = systemTime() + relTime;
-    mMessages.insert(message);
-    
-    //LOGD("MessageQueue::queueMessage time = %lld ms", message->when);
-    //dumpLocked(message);
-
-    mCondition.signal();
-    return NO_ERROR;
-}
-
-void MessageQueue::dump(const sp<MessageBase>& message)
-{
-    Mutex::Autolock _l(mLock);
-    dumpLocked(message);
-}
-
-void MessageQueue::dumpLocked(const sp<MessageBase>& message)
-{
-    LIST::const_iterator cur(mMessages.begin());
-    LIST::const_iterator end(mMessages.end());
-    int c = 0;
-    while (cur != end) {
-        const char tick = (*cur == message) ? '>' : ' ';
-        LOGD("%c %d: msg{.what=%08x, when=%lld}",
-                tick, c, (*cur)->what, (*cur)->when);
-        ++cur;
-        c++;
-    }
-}
-
 // ---------------------------------------------------------------------------
 
 }; // namespace android
diff --git a/services/surfaceflinger/MessageQueue.h b/services/surfaceflinger/MessageQueue.h
index 890f809..25030a6 100644
--- a/services/surfaceflinger/MessageQueue.h
+++ b/services/surfaceflinger/MessageQueue.h
@@ -23,7 +23,7 @@
 
 #include <utils/threads.h>
 #include <utils/Timers.h>
-#include <utils/List.h>
+#include <utils/Looper.h>
 
 #include "Barrier.h"
 
@@ -31,92 +31,39 @@
 
 // ---------------------------------------------------------------------------
 
-class MessageBase;
-
-class MessageList 
-{
-    List< sp<MessageBase> > mList;
-    typedef List< sp<MessageBase> > LIST;
-public:
-    inline LIST::iterator begin()                { return mList.begin(); }
-    inline LIST::const_iterator begin() const    { return mList.begin(); }
-    inline LIST::iterator end()                  { return mList.end(); }
-    inline LIST::const_iterator end() const      { return mList.end(); }
-    inline bool isEmpty() const { return mList.empty(); }
-    void insert(const sp<MessageBase>& node);
-    void remove(LIST::iterator pos);
-};
-
-// ============================================================================
-
-class MessageBase : 
-    public LightRefBase<MessageBase>
+class MessageBase : public MessageHandler
 {
 public:
-    nsecs_t     when;
-    uint32_t    what;
-    int32_t     arg0;    
-
-    MessageBase() : when(0), what(0), arg0(0) { }
-    MessageBase(uint32_t what, int32_t arg0=0)
-        : when(0), what(what), arg0(arg0) { }
+    MessageBase();
     
     // return true if message has a handler
-    virtual bool handler() { return false; }
+    virtual bool handler() = 0;
 
     // waits for the handler to be processed
     void wait() const { barrier.wait(); }
-    
-    // releases all waiters. this is done automatically if
-    // handler returns true
-    void notify() const { barrier.open(); }
 
 protected:
-    virtual ~MessageBase() { }
+    virtual ~MessageBase();
 
 private:
-    mutable Barrier barrier;
-    friend class LightRefBase<MessageBase>;
-};
+    virtual void handleMessage(const Message& message);
 
-inline bool operator < (const MessageBase& lhs, const MessageBase& rhs) {
-    return lhs.when < rhs.when;
-}
+    mutable Barrier barrier;
+};
 
 // ---------------------------------------------------------------------------
 
-class MessageQueue
-{
-    typedef List< sp<MessageBase> > LIST;
-public:
+class MessageQueue {
+    sp<Looper> mLooper;
+    volatile int32_t mInvalidatePending;
 
+public:
     MessageQueue();
     ~MessageQueue();
 
-    // pre-defined messages
-    enum {
-        INVALIDATE = '_upd'
-    };
-
-    sp<MessageBase> waitMessage(nsecs_t timeout = -1);
-    
-    status_t postMessage(const sp<MessageBase>& message,
-            nsecs_t reltime=0, uint32_t flags = 0);
-
+    void waitMessage();
+    status_t postMessage(const sp<MessageBase>& message, nsecs_t reltime=0);
     status_t invalidate();
-    
-    void dump(const sp<MessageBase>& message);
-
-private:
-    status_t queueMessage(const sp<MessageBase>& message,
-            nsecs_t reltime, uint32_t flags);
-    void dumpLocked(const sp<MessageBase>& message);
-    
-    Mutex           mLock;
-    Condition       mCondition;
-    MessageList     mMessages;
-    bool            mInvalidate;
-    sp<MessageBase> mInvalidateMessage;
 };
 
 // ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index f38e948..51c2be8 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -34,6 +34,8 @@
 #include <binder/MemoryHeapBase.h>
 #include <binder/PermissionCache.h>
 
+#include <gui/IDisplayEventConnection.h>
+
 #include <utils/String8.h>
 #include <utils/String16.h>
 #include <utils/StopWatch.h>
@@ -46,6 +48,8 @@
 #include <GLES/gl.h>
 
 #include "clz.h"
+#include "DisplayEventConnection.h"
+#include "EventThread.h"
 #include "GLExtensions.h"
 #include "DdmConnection.h"
 #include "Layer.h"
@@ -295,12 +299,16 @@
     // put the origin in the left-bottom corner
     glOrthof(0, w, 0, h, 0, 1); // l=0, r=w ; b=0, t=h
 
-    mReadyToRunBarrier.open();
+
+    // start the EventThread
+    mEventThread = new EventThread(this);
 
     /*
      *  We're now ready to accept clients...
      */
 
+    mReadyToRunBarrier.open();
+
     // start boot animation
     property_set("ctl.start", "bootanim");
 
@@ -313,25 +321,30 @@
 #pragma mark Events Handler
 #endif
 
-void SurfaceFlinger::waitForEvent()
-{
-    while (true) {
-        nsecs_t timeout = -1;
-        sp<MessageBase> msg = mEventQueue.waitMessage(timeout);
-        if (msg != 0) {
-            switch (msg->what) {
-                case MessageQueue::INVALIDATE:
-                    // invalidate message, just return to the main loop
-                    return;
-            }
-        }
-    }
+void SurfaceFlinger::waitForEvent() {
+    mEventQueue.waitMessage();
 }
 
 void SurfaceFlinger::signalEvent() {
     mEventQueue.invalidate();
 }
 
+status_t SurfaceFlinger::postMessageAsync(const sp<MessageBase>& msg,
+        nsecs_t reltime, uint32_t flags) {
+    return mEventQueue.postMessage(msg, reltime);
+}
+
+status_t SurfaceFlinger::postMessageSync(const sp<MessageBase>& msg,
+        nsecs_t reltime, uint32_t flags) {
+    status_t res = mEventQueue.postMessage(msg, reltime);
+    if (res == NO_ERROR) {
+        msg->wait();
+    }
+    return res;
+}
+
+// ----------------------------------------------------------------------------
+
 bool SurfaceFlinger::authenticateSurfaceTexture(
         const sp<ISurfaceTexture>& surfaceTexture) const {
     Mutex::Autolock _l(mStateLock);
@@ -373,20 +386,17 @@
     return false;
 }
 
-status_t SurfaceFlinger::postMessageAsync(const sp<MessageBase>& msg,
-        nsecs_t reltime, uint32_t flags)
-{
-    return mEventQueue.postMessage(msg, reltime, flags);
+// ----------------------------------------------------------------------------
+
+sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection() {
+    sp<DisplayEventConnection> result(new DisplayEventConnection(this));
+    mEventThread->registerDisplayEventConnection(result);
+    return result;
 }
 
-status_t SurfaceFlinger::postMessageSync(const sp<MessageBase>& msg,
-        nsecs_t reltime, uint32_t flags)
-{
-    status_t res = mEventQueue.postMessage(msg, reltime, flags);
-    if (res == NO_ERROR) {
-        msg->wait();
-    }
-    return res;
+void SurfaceFlinger::cleanupDisplayEventConnection(
+        const wp<DisplayEventConnection>& connection) {
+    mEventThread->unregisterDisplayEventConnection(connection);
 }
 
 // ----------------------------------------------------------------------------
@@ -445,7 +455,7 @@
     } else {
         // pretend we did the post
         hw.compositionComplete();
-        usleep(16667); // 60 fps period
+        hw.waitForVSync();
     }
     return true;
 }
@@ -1591,9 +1601,16 @@
         }
 
         /*
+         * VSYNC state
+         */
+        mEventThread->dump(result, buffer, SIZE);
+
+        /*
          * Dump HWComposer state
          */
         HWComposer& hwc(hw.getHwComposer());
+        snprintf(buffer, SIZE, "h/w composer state:\n");
+        result.append(buffer);
         snprintf(buffer, SIZE, "  h/w composer %s and %s\n",
                 hwc.initCheck()==NO_ERROR ? "present" : "not present",
                 (mDebugDisableHWC || mDebugRegion) ? "disabled" : "enabled");
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 17028db..1039f47 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -46,6 +46,8 @@
 
 class Client;
 class DisplayHardware;
+class DisplayEventConnection;
+class EventThread;
 class Layer;
 class LayerDim;
 class LayerScreenshot;
@@ -171,6 +173,7 @@
                                                             int orientation, uint32_t flags);
     virtual int                         setOrientation(DisplayID dpy, int orientation, uint32_t flags);
     virtual bool                        authenticateSurfaceTexture(const sp<ISurfaceTexture>& surface) const;
+    virtual sp<IDisplayEventConnection> createDisplayEventConnection();
 
     virtual status_t captureScreen(DisplayID dpy,
             sp<IMemoryHeap>* heap,
@@ -222,6 +225,7 @@
 
 private:
     friend class Client;
+    friend class DisplayEventConnection;
     friend class LayerBase;
     friend class LayerBaseClient;
     friend class Layer;
@@ -331,6 +335,9 @@
             status_t electronBeamOffAnimationImplLocked();
             status_t electronBeamOnAnimationImplLocked();
 
+            void cleanupDisplayEventConnection(
+                    const wp<DisplayEventConnection>& connection);
+
             void        debugFlashRegions();
             void        debugShowFPS() const;
             void        drawWormhole() const;
@@ -361,6 +368,7 @@
                 GLuint                      mWormholeTexName;
                 GLuint                      mProtectedTexName;
                 nsecs_t                     mBootTime;
+                sp<EventThread>             mEventThread;
 
                 // Can only accessed from the main thread, these members
                 // don't need synchronization
diff --git a/services/surfaceflinger/tests/vsync/Android.mk b/services/surfaceflinger/tests/vsync/Android.mk
new file mode 100644
index 0000000..9181760
--- /dev/null
+++ b/services/surfaceflinger/tests/vsync/Android.mk
@@ -0,0 +1,18 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+	vsync.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+	libcutils \
+	libutils \
+	libbinder \
+    libui \
+    libgui
+
+LOCAL_MODULE:= test-vsync-events
+
+LOCAL_MODULE_TAGS := tests
+
+include $(BUILD_EXECUTABLE)
diff --git a/services/surfaceflinger/tests/vsync/vsync.cpp b/services/surfaceflinger/tests/vsync/vsync.cpp
new file mode 100644
index 0000000..4f79080
--- /dev/null
+++ b/services/surfaceflinger/tests/vsync/vsync.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gui/DisplayEventReceiver.h>
+#include <utils/Looper.h>
+
+using namespace android;
+
+int receiver(int fd, int events, void* data)
+{
+    DisplayEventReceiver* q = (DisplayEventReceiver*)data;
+
+    ssize_t n;
+    DisplayEventReceiver::Event buffer[1];
+
+    static nsecs_t oldTimeStamp = 0;
+
+    while ((n = q->getEvents(buffer, 1)) > 0) {
+        for (int i=0 ; i<n ; i++) {
+            if (buffer[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
+                printf("event vsync: count=%d\t", buffer[i].vsync.count);
+            }
+            if (oldTimeStamp) {
+                float t = float(buffer[i].header.timestamp - oldTimeStamp) / s2ns(1);
+                printf("%f ms (%f Hz)\n", t*1000, 1.0/t);
+            }
+            oldTimeStamp = buffer[i].header.timestamp;
+        }
+    }
+    if (n<0) {
+        printf("error reading events (%s)\n", strerror(-n));
+    }
+    return 1;
+}
+
+int main(int argc, char** argv)
+{
+    DisplayEventReceiver myDisplayEvent;
+
+
+    sp<Looper> loop = new Looper(false);
+    loop->addFd(myDisplayEvent.getFd(), 0, ALOOPER_EVENT_INPUT, receiver,
+            &myDisplayEvent);
+
+    do {
+        //printf("about to poll...\n");
+        int32_t ret = loop->pollOnce(-1);
+        switch (ret) {
+            case ALOOPER_POLL_WAKE:
+                //("ALOOPER_POLL_WAKE\n");
+                break;
+            case ALOOPER_POLL_CALLBACK:
+                //("ALOOPER_POLL_CALLBACK\n");
+                break;
+            case ALOOPER_POLL_TIMEOUT:
+                printf("ALOOPER_POLL_TIMEOUT\n");
+                break;
+            case ALOOPER_POLL_ERROR:
+                printf("ALOOPER_POLL_TIMEOUT\n");
+                break;
+            default:
+                printf("ugh? poll returned %d\n", ret);
+                break;
+        }
+    } while (1);
+
+    return 0;
+}
diff --git a/services/surfaceflinger/tests/waitforvsync/Android.mk b/services/surfaceflinger/tests/waitforvsync/Android.mk
new file mode 100644
index 0000000..c25f5ab
--- /dev/null
+++ b/services/surfaceflinger/tests/waitforvsync/Android.mk
@@ -0,0 +1,14 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+	waitforvsync.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+	libcutils \
+
+LOCAL_MODULE:= test-waitforvsync
+
+LOCAL_MODULE_TAGS := tests
+
+include $(BUILD_EXECUTABLE)
diff --git a/services/surfaceflinger/tests/waitforvsync/waitforvsync.cpp b/services/surfaceflinger/tests/waitforvsync/waitforvsync.cpp
new file mode 100644
index 0000000..279b88b
--- /dev/null
+++ b/services/surfaceflinger/tests/waitforvsync/waitforvsync.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <linux/fb.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+
+#ifndef FBIO_WAITFORVSYNC
+#define FBIO_WAITFORVSYNC   _IOW('F', 0x20, __u32)
+#endif
+
+int main(int argc, char** argv) {
+    int fd = open("/dev/graphics/fb0", O_RDWR);
+    if (fd >= 0) {
+        do {
+            uint32_t crt = 0;
+           int err = ioctl(fd, FBIO_WAITFORVSYNC, &crt);
+           if (err < 0) {
+               printf("FBIO_WAITFORVSYNC error: %s\n", strerror(errno));
+               break;
+           }
+        } while(1);
+        close(fd);
+    }
+    return 0;
+}