Merge "Fix IPv6->IPv4 fallback for HTTPS."
diff --git a/GenerationCache.h b/GenerationCache.h
new file mode 100644
index 0000000..42e6d9b
--- /dev/null
+++ b/GenerationCache.h
@@ -0,0 +1,245 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HWUI_GENERATION_CACHE_H
+#define ANDROID_HWUI_GENERATION_CACHE_H
+
+#include <utils/KeyedVector.h>
+#include <utils/RefBase.h>
+
+namespace android {
+namespace uirenderer {
+
+template<typename EntryKey, typename EntryValue>
+class OnEntryRemoved {
+public:
+ virtual ~OnEntryRemoved() { };
+ virtual void operator()(EntryKey& key, EntryValue& value) = 0;
+}; // class OnEntryRemoved
+
+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) { }
+
+ EntryKey key;
+ EntryValue value;
+
+ sp<Entry<EntryKey, EntryValue> > parent;
+ sp<Entry<EntryKey, EntryValue> > child;
+}; // struct Entry
+
+template<typename K, typename V>
+class GenerationCache {
+public:
+ GenerationCache(uint32_t maxCapacity);
+ virtual ~GenerationCache();
+
+ enum Capacity {
+ kUnlimitedCapacity,
+ };
+
+ void setOnEntryRemovedListener(OnEntryRemoved<K, V>* listener);
+
+ 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;
+
+ uint32_t size() const;
+
+ 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);
+
+ KeyedVector<K, sp<Entry<K, V> > > mCache;
+ uint32_t mMaxCapacity;
+
+ OnEntryRemoved<K, V>* mListener;
+
+ sp<Entry<K, V> > mOldest;
+ sp<Entry<K, V> > mYoungest;
+}; // class GenerationCache
+
+template<typename K, typename V>
+GenerationCache<K, V>::GenerationCache(uint32_t maxCapacity): mMaxCapacity(maxCapacity), mListener(NULL) {
+};
+
+template<typename K, typename V>
+GenerationCache<K, V>::~GenerationCache() {
+ clear();
+};
+
+template<typename K, typename V>
+uint32_t GenerationCache<K, V>::size() const {
+ return mCache.size();
+}
+
+template<typename K, typename V>
+void GenerationCache<K, V>::setOnEntryRemovedListener(OnEntryRemoved<K, V>* listener) {
+ mListener = listener;
+}
+
+template<typename K, typename V>
+void GenerationCache<K, V>::clear() {
+ if (mListener) {
+ for (uint32_t i = 0; i < mCache.size(); i++) {
+ sp<Entry<K, V> > entry = mCache.valueAt(i);
+ if (mListener) {
+ (*mListener)(entry->key, entry->value);
+ }
+ }
+ }
+ mCache.clear();
+ mYoungest.clear();
+ mOldest.clear();
+}
+
+template<typename K, typename V>
+bool GenerationCache<K, V>::contains(K key) const {
+ return mCache.indexOfKey(key) >= 0;
+}
+
+template<typename K, typename V>
+K GenerationCache<K, V>::getKeyAt(uint32_t index) const {
+ return mCache.keyAt(index);
+}
+
+template<typename K, typename V>
+V GenerationCache<K, V>::getValueAt(uint32_t index) const {
+ return mCache.valueAt(index)->value;
+}
+
+template<typename K, typename V>
+V GenerationCache<K, V>::get(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;
+ }
+ }
+
+ return NULL;
+}
+
+template<typename K, typename V>
+bool GenerationCache<K, V>::put(K key, 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);
+ return true;
+ }
+
+ return false;
+}
+
+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) {
+ ssize_t index = mCache.indexOfKey(key);
+ if (index >= 0) {
+ return removeAt(index);
+ }
+
+ return NULL;
+}
+
+template<typename K, typename V>
+V 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() {
+ if (mOldest.get()) {
+ ssize_t index = mCache.indexOfKey(mOldest->key);
+ if (index >= 0) {
+ return removeAt(index);
+ }
+ }
+
+ return NULL;
+}
+
+template<typename K, typename V>
+void GenerationCache<K, V>::attachToCache(sp<Entry<K, V> > entry) {
+ if (!mYoungest.get()) {
+ mYoungest = mOldest = entry;
+ } else {
+ entry->parent = mYoungest;
+ mYoungest->child = entry;
+ mYoungest = entry;
+ }
+}
+
+template<typename K, typename V>
+void GenerationCache<K, V>::detachFromCache(sp<Entry<K, V> > entry) {
+ if (entry->parent.get()) {
+ entry->parent->child = entry->child;
+ }
+
+ if (entry->child.get()) {
+ entry->child->parent = entry->parent;
+ }
+
+ if (mOldest == entry) {
+ mOldest = entry->child;
+ }
+
+ if (mYoungest == entry) {
+ mYoungest = entry->parent;
+ }
+
+ entry->parent.clear();
+ entry->child.clear();
+}
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_GENERATION_CACHE_H
diff --git a/include/binder/IBinder.h b/include/binder/IBinder.h
index 749a977..81b56c2 100644
--- a/include/binder/IBinder.h
+++ b/include/binder/IBinder.h
@@ -98,7 +98,7 @@
* Register the @a recipient for a notification if this binder
* goes away. If this binder object unexpectedly goes away
* (typically because its hosting process has been killed),
- * then DeathRecipient::binderDied() will be called with a referene
+ * then DeathRecipient::binderDied() will be called with a reference
* to this.
*
* The @a cookie is optional -- if non-NULL, it should be a
diff --git a/include/gui/SurfaceTexture.h b/include/gui/SurfaceTexture.h
index 31615d0..9bf38f7 100644
--- a/include/gui/SurfaceTexture.h
+++ b/include/gui/SurfaceTexture.h
@@ -37,7 +37,8 @@
class SurfaceTexture : public BnSurfaceTexture {
public:
- enum { MIN_BUFFER_SLOTS = 3 };
+ enum { MIN_UNDEQUEUED_BUFFERS = 2 };
+ enum { MIN_BUFFER_SLOTS = MIN_UNDEQUEUED_BUFFERS + 1 };
enum { NUM_BUFFER_SLOTS = 32 };
struct FrameAvailableListener : public virtual RefBase {
diff --git a/include/gui/SurfaceTextureClient.h b/include/gui/SurfaceTextureClient.h
index ff2251d..4cdece9 100644
--- a/include/gui/SurfaceTextureClient.h
+++ b/include/gui/SurfaceTextureClient.h
@@ -40,40 +40,41 @@
SurfaceTextureClient(const SurfaceTextureClient& rhs);
// ANativeWindow hooks
- static int setSwapInterval(ANativeWindow* window, int interval);
- static int dequeueBuffer(ANativeWindow* window, android_native_buffer_t** buffer);
static int cancelBuffer(ANativeWindow* window, android_native_buffer_t* buffer);
+ static int dequeueBuffer(ANativeWindow* window, android_native_buffer_t** buffer);
static int lockBuffer(ANativeWindow* window, android_native_buffer_t* buffer);
- static int queueBuffer(ANativeWindow* window, android_native_buffer_t* buffer);
- static int query(ANativeWindow* window, int what, int* value);
static int perform(ANativeWindow* window, int operation, ...);
+ static int query(ANativeWindow* window, int what, int* value);
+ static int queueBuffer(ANativeWindow* window, android_native_buffer_t* buffer);
+ static int setSwapInterval(ANativeWindow* window, int interval);
- int setSwapInterval(int interval);
+ int cancelBuffer(android_native_buffer_t* buffer);
int dequeueBuffer(android_native_buffer_t** buffer);
int lockBuffer(android_native_buffer_t* buffer);
- int queueBuffer(android_native_buffer_t* buffer);
- int cancelBuffer(android_native_buffer_t* buffer);
- int query(int what, int* value);
int perform(int operation, va_list args);
+ int query(int what, int* value);
+ int queueBuffer(android_native_buffer_t* buffer);
+ int setSwapInterval(int interval);
- int dispatchSetUsage(va_list args);
int dispatchConnect(va_list args);
int dispatchDisconnect(va_list args);
- int dispatchSetCrop(va_list args);
int dispatchSetBufferCount(va_list args);
int dispatchSetBuffersGeometry(va_list args);
int dispatchSetBuffersTransform(va_list args);
+ int dispatchSetCrop(va_list args);
+ int dispatchSetUsage(va_list args);
int connect(int api);
int disconnect(int api);
- int setUsage(uint32_t reqUsage);
- int setCrop(Rect const* rect);
int setBufferCount(int bufferCount);
int setBuffersGeometry(int w, int h, int format);
int setBuffersTransform(int transform);
+ int setCrop(Rect const* rect);
+ int setUsage(uint32_t reqUsage);
void freeAllBuffers();
+ enum { MIN_UNDEQUEUED_BUFFERS = SurfaceTexture::MIN_UNDEQUEUED_BUFFERS };
enum { MIN_BUFFER_SLOTS = SurfaceTexture::MIN_BUFFER_SLOTS };
enum { NUM_BUFFER_SLOTS = SurfaceTexture::NUM_BUFFER_SLOTS };
enum { DEFAULT_FORMAT = PIXEL_FORMAT_RGBA_8888 };
diff --git a/include/private/surfaceflinger/SharedBufferStack.h b/include/private/surfaceflinger/SharedBufferStack.h
index eb599b5..717f837 100644
--- a/include/private/surfaceflinger/SharedBufferStack.h
+++ b/include/private/surfaceflinger/SharedBufferStack.h
@@ -65,7 +65,7 @@
// When changing these values, the COMPILE_TIME_ASSERT at the end of this
// file need to be updated.
static const unsigned int NUM_LAYERS_MAX = 31;
- static const unsigned int NUM_BUFFER_MAX = 16;
+ static const unsigned int NUM_BUFFER_MAX = 32;
static const unsigned int NUM_BUFFER_MIN = 2;
static const unsigned int NUM_DISPLAY_MAX = 4;
@@ -123,7 +123,7 @@
// ----------------------------------------------------------------------------
-// 32 KB max
+// 64 KB max
class SharedClient
{
public:
@@ -394,7 +394,7 @@
// ---------------------------------------------------------------------------
-COMPILE_TIME_ASSERT(sizeof(SharedClient) <= 32768)
+COMPILE_TIME_ASSERT(sizeof(SharedClient) <= 65536)
COMPILE_TIME_ASSERT(sizeof(surface_flinger_cblk_t) <= 4096)
// ---------------------------------------------------------------------------
diff --git a/include/surfaceflinger/Surface.h b/include/surfaceflinger/Surface.h
index d783caf..9e0b5bb 100644
--- a/include/surfaceflinger/Surface.h
+++ b/include/surfaceflinger/Surface.h
@@ -242,6 +242,10 @@
status_t validate(bool inCancelBuffer = false) const;
sp<ISurface> getISurface() const;
+ // When the buffer pool is a fixed size we want to make sure SurfaceFlinger
+ // won't stall clients, so we require an extra buffer.
+ enum { MIN_UNDEQUEUED_BUFFERS = 2 };
+
inline const GraphicBufferMapper& getBufferMapper() const { return mBufferMapper; }
inline GraphicBufferMapper& getBufferMapper() { return mBufferMapper; }
diff --git a/include/ui/Input.h b/include/ui/Input.h
index cb9327e..e92d7f5 100644
--- a/include/ui/Input.h
+++ b/include/ui/Input.h
@@ -52,6 +52,14 @@
};
/*
+ * SystemUiVisibility constants from View.
+ */
+enum {
+ ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE = 0,
+ ASYSTEM_UI_VISIBILITY_STATUS_BAR_HIDDEN = 0x00000001,
+};
+
+/*
* Maximum number of pointers supported per motion event.
* Smallest number of pointers is 1.
* (We want at least 10 but some touch controllers obstensibly configured for 10 pointers
@@ -170,10 +178,10 @@
* Pointer coordinate data.
*/
struct PointerCoords {
- enum { MAX_AXES = 15 }; // 15 so that sizeof(PointerCoords) == 16 * 4 == 64
+ enum { MAX_AXES = 14 }; // 14 so that sizeof(PointerCoords) == 64
// Bitfield of axes that are present in this structure.
- uint32_t bits; // 32bits are enough for now, can raise to 64bit when needed
+ uint64_t bits;
// Values of axes that are stored in this structure packed in order by axis id
// for each axis that is present in the structure according to 'bits'.
@@ -183,41 +191,9 @@
bits = 0;
}
- inline float getAxisValue(int32_t axis) const {
- uint32_t axisBit = 1 << axis;
- if (!(bits & axisBit)) {
- return 0;
- }
- uint32_t index = __builtin_popcount(bits & (axisBit - 1));
- return values[index];
- }
-
- inline status_t setAxisValue(int32_t axis, float value) {
- uint32_t axisBit = 1 << axis;
- uint32_t index = __builtin_popcount(bits & (axisBit - 1));
- if (!(bits & axisBit)) {
- uint32_t count = __builtin_popcount(bits);
- if (count >= MAX_AXES) {
- tooManyAxes(axis);
- return NO_MEMORY;
- }
- bits |= axisBit;
- for (uint32_t i = count; i > index; i--) {
- values[i] = values[i - 1];
- }
- }
- values[index] = value;
- return OK;
- }
-
- inline float* editAxisValue(int32_t axis) {
- uint32_t axisBit = 1 << axis;
- if (!(bits & axisBit)) {
- return NULL;
- }
- uint32_t index = __builtin_popcount(bits & (axisBit - 1));
- return &values[index];
- }
+ float getAxisValue(int32_t axis) const;
+ status_t setAxisValue(int32_t axis, float value);
+ float* editAxisValue(int32_t axis);
#ifdef HAVE_ANDROID_OS
status_t readFromParcel(Parcel* parcel);
@@ -500,6 +476,11 @@
status_t writeToParcel(Parcel* parcel) const;
#endif
+ static bool isTouchEvent(int32_t source, int32_t action);
+ inline bool isTouchEvent() const {
+ return isTouchEvent(mSource, mAction);
+ }
+
// Low-level accessors.
inline const int32_t* getPointerIds() const { return mPointerIds.array(); }
inline const nsecs_t* getSampleEventTimes() const { return mSampleEventTimes.array(); }
diff --git a/include/ui/KeyLayoutMap.h b/include/ui/KeyLayoutMap.h
index f0a6d00..904c8f3 100644
--- a/include/ui/KeyLayoutMap.h
+++ b/include/ui/KeyLayoutMap.h
@@ -25,7 +25,7 @@
namespace android {
/**
- * Describes a mapping from keyboard scan codes to Android key codes.
+ * Describes a mapping from keyboard scan codes and joystick axes to Android key codes and axes.
*/
class KeyLayoutMap {
public:
@@ -33,8 +33,10 @@
static status_t load(const String8& filename, KeyLayoutMap** outMap);
- status_t map(int32_t scanCode, int32_t* keyCode, uint32_t* flags) const;
- status_t findScanCodes(int32_t keyCode, Vector<int32_t>* outScanCodes) const;
+ status_t mapKey(int32_t scanCode, int32_t* keyCode, uint32_t* flags) const;
+ status_t findScanCodesForKey(int32_t keyCode, Vector<int32_t>* outScanCodes) const;
+
+ status_t mapAxis(int32_t scanCode, int32_t* axis) const;
private:
struct Key {
@@ -42,7 +44,8 @@
uint32_t flags;
};
- KeyedVector<int32_t,Key> mKeys;
+ KeyedVector<int32_t, Key> mKeys;
+ KeyedVector<int32_t, int32_t> mAxes;
KeyLayoutMap();
@@ -57,6 +60,7 @@
private:
status_t parseKey();
+ status_t parseAxis();
};
};
diff --git a/include/ui/Keyboard.h b/include/ui/Keyboard.h
index 50296e2..609f319 100644
--- a/include/ui/Keyboard.h
+++ b/include/ui/Keyboard.h
@@ -111,10 +111,27 @@
extern uint32_t getKeyFlagByLabel(const char* label);
/**
+ * Gets a axis by its short form label, eg. "X".
+ * Returns -1 if unknown.
+ */
+extern int32_t getAxisByLabel(const char* label);
+
+/**
+ * Gets a axis label by its id.
+ * Returns NULL if unknown.
+ */
+extern const char* getAxisLabel(int32_t axisId);
+
+/**
* Updates a meta state field when a key is pressed or released.
*/
extern int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState);
+/**
+ * Returns true if a key is a meta key like ALT or CAPS_LOCK.
+ */
+extern bool isMetaKey(int32_t keyCode);
+
} // namespace android
#endif // _UI_KEYBOARD_H
diff --git a/include/ui/KeycodeLabels.h b/include/ui/KeycodeLabels.h
index dbccf29..bdfbf7c 100755
--- a/include/ui/KeycodeLabels.h
+++ b/include/ui/KeycodeLabels.h
@@ -250,4 +250,47 @@
{ NULL, 0 }
};
+static const KeycodeLabel AXES[] = {
+ { "X", 0 },
+ { "Y", 1 },
+ { "PRESSURE", 2 },
+ { "SIZE", 3 },
+ { "TOUCH_MAJOR", 4 },
+ { "TOUCH_MINOR", 5 },
+ { "TOOL_MAJOR", 6 },
+ { "TOOL_MINOR", 7 },
+ { "ORIENTATION", 8 },
+ { "VSCROLL", 9 },
+ { "HSCROLL", 10 },
+ { "Z", 11 },
+ { "RX", 12 },
+ { "RY", 13 },
+ { "RZ", 14 },
+ { "HAT_X", 15 },
+ { "HAT_Y", 16 },
+ { "LTRIGGER", 17 },
+ { "RTRIGGER", 18 },
+ { "GENERIC_1", 32 },
+ { "GENERIC_2", 33 },
+ { "GENERIC_3", 34 },
+ { "GENERIC_4", 35 },
+ { "GENERIC_5", 36 },
+ { "GENERIC_6", 37 },
+ { "GENERIC_7", 38 },
+ { "GENERIC_8", 39 },
+ { "GENERIC_9", 40 },
+ { "GENERIC_10", 41 },
+ { "GENERIC_11", 42 },
+ { "GENERIC_12", 43 },
+ { "GENERIC_13", 44 },
+ { "GENERIC_14", 45 },
+ { "GENERIC_15", 46 },
+ { "GENERIC_16", 47 },
+
+ // NOTE: If you add a new axis here you must also add it to several other files.
+ // Refer to frameworks/base/core/java/android/view/MotionEvent.java for the full list.
+
+ { NULL, -1 }
+};
+
#endif // _UI_KEYCODE_LABELS_H
diff --git a/include/ui/egl/android_natives.h b/include/ui/egl/android_natives.h
index fdc8105..fd83f46 100644
--- a/include/ui/egl/android_natives.h
+++ b/include/ui/egl/android_natives.h
@@ -75,6 +75,26 @@
NATIVE_WINDOW_WIDTH = 0,
NATIVE_WINDOW_HEIGHT,
NATIVE_WINDOW_FORMAT,
+
+ /* The minimum number of buffers that must remain un-dequeued after a buffer
+ * has been queued. This value applies only if set_buffer_count was used to
+ * override the number of buffers and if a buffer has since been queued.
+ * Users of the set_buffer_count ANativeWindow method should query this
+ * value before calling set_buffer_count. If it is necessary to have N
+ * buffers simultaneously dequeued as part of the steady-state operation,
+ * and this query returns M then N+M buffers should be requested via
+ * native_window_set_buffer_count.
+ *
+ * Note that this value does NOT apply until a single buffer has been
+ * queued. In particular this means that it is possible to:
+ *
+ * 1. Query M = min undequeued buffers
+ * 2. Set the buffer count to N + M
+ * 3. Dequeue all N + M buffers
+ * 4. Cancel M buffers
+ * 5. Queue, dequeue, queue, dequeue, ad infinitum
+ */
+ NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
};
/* valid operations for the (*perform)() hook */
diff --git a/include/utils/GenerationCache.h b/include/utils/GenerationCache.h
new file mode 100644
index 0000000..bb9ddd6
--- /dev/null
+++ b/include/utils/GenerationCache.h
@@ -0,0 +1,255 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_UTILS_GENERATION_CACHE_H
+#define ANDROID_UTILS_GENERATION_CACHE_H
+
+#include <utils/KeyedVector.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+/**
+ * GenerationCache callback used when an item is removed
+ */
+template<typename EntryKey, typename EntryValue>
+class OnEntryRemoved {
+public:
+ virtual ~OnEntryRemoved() { };
+ virtual void operator()(EntryKey& key, EntryValue& value) = 0;
+}; // class OnEntryRemoved
+
+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) { }
+
+ EntryKey key;
+ EntryValue value;
+
+ sp<Entry<EntryKey, EntryValue> > parent;
+ sp<Entry<EntryKey, EntryValue> > child;
+}; // struct Entry
+
+/**
+ * A LRU type cache
+ */
+template<typename K, typename V>
+class GenerationCache {
+public:
+ GenerationCache(uint32_t maxCapacity);
+ virtual ~GenerationCache();
+
+ enum Capacity {
+ kUnlimitedCapacity,
+ };
+
+ void setOnEntryRemovedListener(OnEntryRemoved<K, V>* listener);
+
+ 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;
+
+ uint32_t size() const;
+
+ 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);
+
+private:
+ KeyedVector<K, sp<Entry<K, V> > > mCache;
+ uint32_t mMaxCapacity;
+
+ OnEntryRemoved<K, V>* mListener;
+
+ sp<Entry<K, V> > mOldest;
+ sp<Entry<K, V> > mYoungest;
+}; // class GenerationCache
+
+template<typename K, typename V>
+GenerationCache<K, V>::GenerationCache(uint32_t maxCapacity): mMaxCapacity(maxCapacity),
+ mListener(NULL) {
+};
+
+template<typename K, typename V>
+GenerationCache<K, V>::~GenerationCache() {
+ clear();
+};
+
+template<typename K, typename V>
+uint32_t GenerationCache<K, V>::size() const {
+ return mCache.size();
+}
+
+/**
+ * Should be set by the user of the Cache so that the callback is called whenever an item is
+ * removed from the cache
+ */
+template<typename K, typename V>
+void GenerationCache<K, V>::setOnEntryRemovedListener(OnEntryRemoved<K, V>* listener) {
+ mListener = listener;
+}
+
+template<typename K, typename V>
+void GenerationCache<K, V>::clear() {
+ if (mListener) {
+ for (uint32_t i = 0; i < mCache.size(); i++) {
+ sp<Entry<K, V> > entry = mCache.valueAt(i);
+ if (mListener) {
+ (*mListener)(entry->key, entry->value);
+ }
+ }
+ }
+ mCache.clear();
+ mYoungest.clear();
+ mOldest.clear();
+}
+
+template<typename K, typename V>
+bool GenerationCache<K, V>::contains(K key) const {
+ return mCache.indexOfKey(key) >= 0;
+}
+
+template<typename K, typename V>
+K GenerationCache<K, V>::getKeyAt(uint32_t index) const {
+ return mCache.keyAt(index);
+}
+
+template<typename K, typename V>
+V GenerationCache<K, V>::getValueAt(uint32_t index) const {
+ return mCache.valueAt(index)->value;
+}
+
+template<typename K, typename V>
+V GenerationCache<K, V>::get(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;
+ }
+ }
+
+ return NULL;
+}
+
+template<typename K, typename V>
+bool GenerationCache<K, V>::put(K key, 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);
+ return true;
+ }
+
+ return false;
+}
+
+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) {
+ ssize_t index = mCache.indexOfKey(key);
+ if (index >= 0) {
+ return removeAt(index);
+ }
+
+ return NULL;
+}
+
+template<typename K, typename V>
+V 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() {
+ if (mOldest.get()) {
+ ssize_t index = mCache.indexOfKey(mOldest->key);
+ if (index >= 0) {
+ return removeAt(index);
+ }
+ }
+
+ return NULL;
+}
+
+template<typename K, typename V>
+void GenerationCache<K, V>::attachToCache(sp<Entry<K, V> > entry) {
+ if (!mYoungest.get()) {
+ mYoungest = mOldest = entry;
+ } else {
+ entry->parent = mYoungest;
+ mYoungest->child = entry;
+ mYoungest = entry;
+ }
+}
+
+template<typename K, typename V>
+void GenerationCache<K, V>::detachFromCache(sp<Entry<K, V> > entry) {
+ if (entry->parent.get()) {
+ entry->parent->child = entry->child;
+ }
+
+ if (entry->child.get()) {
+ entry->child->parent = entry->parent;
+ }
+
+ if (mOldest == entry) {
+ mOldest = entry->child;
+ }
+
+ if (mYoungest == entry) {
+ mYoungest = entry->parent;
+ }
+
+ entry->parent.clear();
+ entry->child.clear();
+}
+
+}; // namespace android
+
+#endif // ANDROID_UTILS_GENERATION_CACHE_H
diff --git a/include/utils/Looper.h b/include/utils/Looper.h
index eefff31..3c2905d 100644
--- a/include/utils/Looper.h
+++ b/include/utils/Looper.h
@@ -45,6 +45,51 @@
namespace android {
/**
+ * A message that can be posted to a Looper.
+ */
+struct Message {
+ Message() : what(0) { }
+ Message(int what) : what(what) { }
+
+ /* The message type. (interpretation is left up to the handler) */
+ int what;
+};
+
+
+/**
+ * Interface for a Looper message handler.
+ *
+ * The Looper holds a strong reference to the message handler whenever it has
+ * a message to deliver to it. Make sure to call Looper::removeMessages
+ * to remove any pending messages destined for the handler so that the handler
+ * can be destroyed.
+ */
+class MessageHandler : public virtual RefBase {
+protected:
+ virtual ~MessageHandler() { }
+
+public:
+ /**
+ * Handles a message.
+ */
+ virtual void handleMessage(const Message& message) = 0;
+};
+
+
+/**
+ * A simple proxy that holds a weak reference to a message handler.
+ */
+class WeakMessageHandler : public MessageHandler {
+public:
+ WeakMessageHandler(const wp<MessageHandler>& handler);
+ virtual void handleMessage(const Message& message);
+
+private:
+ wp<MessageHandler> mHandler;
+};
+
+
+/**
* A polling loop that supports monitoring file descriptor events, optionally
* using callbacks. The implementation uses epoll() internally.
*
@@ -166,6 +211,52 @@
int removeFd(int fd);
/**
+ * Enqueues a message to be processed by the specified handler.
+ *
+ * The handler must not be null.
+ * This method can be called on any thread.
+ */
+ void sendMessage(const sp<MessageHandler>& handler, const Message& message);
+
+ /**
+ * Enqueues a message to be processed by the specified handler after all pending messages
+ * after the specified delay.
+ *
+ * The time delay is specified in uptime nanoseconds.
+ * The handler must not be null.
+ * This method can be called on any thread.
+ */
+ void sendMessageDelayed(nsecs_t uptimeDelay, const sp<MessageHandler>& handler,
+ const Message& message);
+
+ /**
+ * Enqueues a message to be processed by the specified handler after all pending messages
+ * at the specified time.
+ *
+ * The time is specified in uptime nanoseconds.
+ * The handler must not be null.
+ * This method can be called on any thread.
+ */
+ void sendMessageAtTime(nsecs_t uptime, const sp<MessageHandler>& handler,
+ const Message& message);
+
+ /**
+ * Removes all messages for the specified handler from the queue.
+ *
+ * The handler must not be null.
+ * This method can be called on any thread.
+ */
+ void removeMessages(const sp<MessageHandler>& handler);
+
+ /**
+ * Removes all messages of a particular type for the specified handler from the queue.
+ *
+ * The handler must not be null.
+ * This method can be called on any thread.
+ */
+ void removeMessages(const sp<MessageHandler>& handler, int what);
+
+ /**
* Prepares a looper associated with the calling thread, and returns it.
* If the thread already has a looper, it is returned. Otherwise, a new
* one is created, associated with the thread, and returned.
@@ -201,12 +292,27 @@
Request request;
};
+ struct MessageEnvelope {
+ MessageEnvelope() : uptime(0) { }
+
+ MessageEnvelope(nsecs_t uptime, const sp<MessageHandler> handler,
+ const Message& message) : uptime(uptime), handler(handler), message(message) {
+ }
+
+ nsecs_t uptime;
+ sp<MessageHandler> handler;
+ Message message;
+ };
+
const bool mAllowNonCallbacks; // immutable
int mWakeReadPipeFd; // immutable
int mWakeWritePipeFd; // immutable
Mutex mLock;
+ Vector<MessageEnvelope> mMessageEnvelopes; // guarded by mLock
+ bool mSendingMessage; // guarded by mLock
+
#ifdef LOOPER_USES_EPOLL
int mEpollFd; // immutable
@@ -256,6 +362,7 @@
// it runs on a single thread.
Vector<Response> mResponses;
size_t mResponseIndex;
+ nsecs_t mNextMessageUptime; // set to LLONG_MAX when none
int pollInner(int timeoutMillis);
void awoken();
diff --git a/include/utils/RefBase.h b/include/utils/RefBase.h
index c24c0db..f355087 100644
--- a/include/utils/RefBase.h
+++ b/include/utils/RefBase.h
@@ -18,16 +18,19 @@
#define ANDROID_REF_BASE_H
#include <cutils/atomic.h>
-#include <utils/TextOutput.h>
#include <stdint.h>
#include <sys/types.h>
#include <stdlib.h>
+#include <string.h>
+
+#include <utils/StrongPointer.h>
// ---------------------------------------------------------------------------
namespace android {
-template<typename T> class wp;
+class TextOutput;
+TextOutput& printWeakPointer(TextOutput& to, const void* val);
// ---------------------------------------------------------------------------
@@ -47,15 +50,15 @@
return m_ptr _op_ o; \
}
-#define COMPARE(_op_) \
-COMPARE_WEAK(_op_) \
-inline bool operator _op_ (const wp<T>& o) const { \
- return m_ptr _op_ o.m_ptr; \
-} \
-template<typename U> \
-inline bool operator _op_ (const wp<U>& o) const { \
- return m_ptr _op_ o.m_ptr; \
-}
+// ---------------------------------------------------------------------------
+
+class ReferenceMover;
+class ReferenceConverterBase {
+public:
+ virtual size_t getReferenceTypeSize() const = 0;
+ virtual void* getReferenceBase(void const*) const = 0;
+ inline virtual ~ReferenceConverterBase() { }
+};
// ---------------------------------------------------------------------------
@@ -112,6 +115,8 @@
getWeakRefs()->trackMe(enable, retain);
}
+ typedef RefBase basetype;
+
protected:
RefBase();
virtual ~RefBase();
@@ -135,6 +140,11 @@
virtual void onLastWeakRef(const void* id);
private:
+ friend class ReferenceMover;
+ static void moveReferences(void* d, void const* s, size_t n,
+ const ReferenceConverterBase& caster);
+
+private:
friend class weakref_type;
class weakref_impl;
@@ -163,10 +173,17 @@
inline int32_t getStrongCount() const {
return mCount;
}
-
+
+ typedef LightRefBase<T> basetype;
+
protected:
inline ~LightRefBase() { }
-
+
+private:
+ friend class ReferenceMover;
+ inline static void moveReferences(void* d, void const* s, size_t n,
+ const ReferenceConverterBase& caster) { }
+
private:
mutable volatile int32_t mCount;
};
@@ -174,66 +191,6 @@
// ---------------------------------------------------------------------------
template <typename T>
-class sp
-{
-public:
- typedef typename RefBase::weakref_type weakref_type;
-
- inline sp() : m_ptr(0) { }
-
- sp(T* other);
- sp(const sp<T>& other);
- template<typename U> sp(U* other);
- template<typename U> sp(const sp<U>& other);
-
- ~sp();
-
- // Assignment
-
- sp& operator = (T* other);
- sp& operator = (const sp<T>& other);
-
- template<typename U> sp& operator = (const sp<U>& other);
- template<typename U> sp& operator = (U* other);
-
- //! Special optimization for use by ProcessState (and nobody else).
- void force_set(T* other);
-
- // Reset
-
- void clear();
-
- // Accessors
-
- inline T& operator* () const { return *m_ptr; }
- inline T* operator-> () const { return m_ptr; }
- inline T* get() const { return m_ptr; }
-
- // Operators
-
- COMPARE(==)
- COMPARE(!=)
- COMPARE(>)
- COMPARE(<)
- COMPARE(<=)
- COMPARE(>=)
-
-private:
- template<typename Y> friend class sp;
- template<typename Y> friend class wp;
-
- // Optimization for wp::promote().
- sp(T* p, weakref_type* refs);
-
- T* m_ptr;
-};
-
-template <typename T>
-TextOutput& operator<<(TextOutput& to, const sp<T>& val);
-
-// ---------------------------------------------------------------------------
-
-template <typename T>
class wp
{
public:
@@ -326,114 +283,12 @@
template <typename T>
TextOutput& operator<<(TextOutput& to, const wp<T>& val);
-#undef COMPARE
#undef COMPARE_WEAK
// ---------------------------------------------------------------------------
// No user serviceable parts below here.
template<typename T>
-sp<T>::sp(T* other)
- : m_ptr(other)
-{
- if (other) other->incStrong(this);
-}
-
-template<typename T>
-sp<T>::sp(const sp<T>& other)
- : m_ptr(other.m_ptr)
-{
- if (m_ptr) m_ptr->incStrong(this);
-}
-
-template<typename T> template<typename U>
-sp<T>::sp(U* other) : m_ptr(other)
-{
- if (other) other->incStrong(this);
-}
-
-template<typename T> template<typename U>
-sp<T>::sp(const sp<U>& other)
- : m_ptr(other.m_ptr)
-{
- if (m_ptr) m_ptr->incStrong(this);
-}
-
-template<typename T>
-sp<T>::~sp()
-{
- if (m_ptr) m_ptr->decStrong(this);
-}
-
-template<typename T>
-sp<T>& sp<T>::operator = (const sp<T>& other) {
- T* otherPtr(other.m_ptr);
- if (otherPtr) otherPtr->incStrong(this);
- if (m_ptr) m_ptr->decStrong(this);
- m_ptr = otherPtr;
- return *this;
-}
-
-template<typename T>
-sp<T>& sp<T>::operator = (T* other)
-{
- if (other) other->incStrong(this);
- if (m_ptr) m_ptr->decStrong(this);
- m_ptr = other;
- return *this;
-}
-
-template<typename T> template<typename U>
-sp<T>& sp<T>::operator = (const sp<U>& other)
-{
- U* otherPtr(other.m_ptr);
- if (otherPtr) otherPtr->incStrong(this);
- if (m_ptr) m_ptr->decStrong(this);
- m_ptr = otherPtr;
- return *this;
-}
-
-template<typename T> template<typename U>
-sp<T>& sp<T>::operator = (U* other)
-{
- if (other) other->incStrong(this);
- if (m_ptr) m_ptr->decStrong(this);
- m_ptr = other;
- return *this;
-}
-
-template<typename T>
-void sp<T>::force_set(T* other)
-{
- other->forceIncStrong(this);
- m_ptr = other;
-}
-
-template<typename T>
-void sp<T>::clear()
-{
- if (m_ptr) {
- m_ptr->decStrong(this);
- m_ptr = 0;
- }
-}
-
-template<typename T>
-sp<T>::sp(T* p, weakref_type* refs)
- : m_ptr((p && refs->attemptIncStrong(this)) ? p : 0)
-{
-}
-
-template <typename T>
-inline TextOutput& operator<<(TextOutput& to, const sp<T>& val)
-{
- to << "sp<>(" << val.get() << ")";
- return to;
-}
-
-// ---------------------------------------------------------------------------
-
-template<typename T>
wp<T>::wp(T* other)
: m_ptr(other)
{
@@ -570,7 +425,11 @@
template<typename T>
sp<T> wp<T>::promote() const
{
- return sp<T>(m_ptr, m_refs);
+ sp<T> result;
+ if (m_ptr && m_refs->attemptIncStrong(&result)) {
+ result.set_pointer(m_ptr);
+ }
+ return result;
}
template<typename T>
@@ -585,10 +444,80 @@
template <typename T>
inline TextOutput& operator<<(TextOutput& to, const wp<T>& val)
{
- to << "wp<>(" << val.unsafe_get() << ")";
- return to;
+ return printWeakPointer(to, val.unsafe_get());
}
+// ---------------------------------------------------------------------------
+
+// this class just serves as a namespace so TYPE::moveReferences can stay
+// private.
+
+class ReferenceMover {
+ // StrongReferenceCast and WeakReferenceCast do the impedance matching
+ // between the generic (void*) implementation in Refbase and the strongly typed
+ // template specializations below.
+
+ template <typename TYPE>
+ struct StrongReferenceCast : public ReferenceConverterBase {
+ virtual size_t getReferenceTypeSize() const { return sizeof( sp<TYPE> ); }
+ virtual void* getReferenceBase(void const* p) const {
+ sp<TYPE> const* sptr(reinterpret_cast<sp<TYPE> const*>(p));
+ return static_cast<typename TYPE::basetype *>(sptr->get());
+ }
+ };
+
+ template <typename TYPE>
+ struct WeakReferenceCast : public ReferenceConverterBase {
+ virtual size_t getReferenceTypeSize() const { return sizeof( wp<TYPE> ); }
+ virtual void* getReferenceBase(void const* p) const {
+ wp<TYPE> const* sptr(reinterpret_cast<wp<TYPE> const*>(p));
+ return static_cast<typename TYPE::basetype *>(sptr->unsafe_get());
+ }
+ };
+
+public:
+ template<typename TYPE> static inline
+ void move_references(sp<TYPE>* d, sp<TYPE> const* s, size_t n) {
+ memmove(d, s, n*sizeof(sp<TYPE>));
+ StrongReferenceCast<TYPE> caster;
+ TYPE::moveReferences(d, s, n, caster);
+ }
+ template<typename TYPE> static inline
+ void move_references(wp<TYPE>* d, wp<TYPE> const* s, size_t n) {
+ memmove(d, s, n*sizeof(wp<TYPE>));
+ WeakReferenceCast<TYPE> caster;
+ TYPE::moveReferences(d, s, n, caster);
+ }
+};
+
+// specialization for moving sp<> and wp<> types.
+// these are used by the [Sorted|Keyed]Vector<> implementations
+// sp<> and wp<> need to be handled specially, because they do not
+// have trivial copy operation in the general case (see RefBase.cpp
+// when DEBUG ops are enabled), but can be implemented very
+// efficiently in most cases.
+
+template<typename TYPE> inline
+void move_forward_type(sp<TYPE>* d, sp<TYPE> const* s, size_t n) {
+ ReferenceMover::move_references(d, s, n);
+}
+
+template<typename TYPE> inline
+void move_backward_type(sp<TYPE>* d, sp<TYPE> const* s, size_t n) {
+ ReferenceMover::move_references(d, s, n);
+}
+
+template<typename TYPE> inline
+void move_forward_type(wp<TYPE>* d, wp<TYPE> const* s, size_t n) {
+ ReferenceMover::move_references(d, s, n);
+}
+
+template<typename TYPE> inline
+void move_backward_type(wp<TYPE>* d, wp<TYPE> const* s, size_t n) {
+ ReferenceMover::move_references(d, s, n);
+}
+
+
}; // namespace android
// ---------------------------------------------------------------------------
diff --git a/include/utils/StrongPointer.h b/include/utils/StrongPointer.h
new file mode 100644
index 0000000..49fa3a8
--- /dev/null
+++ b/include/utils/StrongPointer.h
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2005 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_STRONG_POINTER_H
+#define ANDROID_STRONG_POINTER_H
+
+#include <cutils/atomic.h>
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <stdlib.h>
+
+// ---------------------------------------------------------------------------
+namespace android {
+
+class TextOutput;
+TextOutput& printStrongPointer(TextOutput& to, const void* val);
+
+template<typename T> class wp;
+
+// ---------------------------------------------------------------------------
+
+#define COMPARE(_op_) \
+inline bool operator _op_ (const sp<T>& o) const { \
+ return m_ptr _op_ o.m_ptr; \
+} \
+inline bool operator _op_ (const T* o) const { \
+ return m_ptr _op_ o; \
+} \
+template<typename U> \
+inline bool operator _op_ (const sp<U>& o) const { \
+ return m_ptr _op_ o.m_ptr; \
+} \
+template<typename U> \
+inline bool operator _op_ (const U* o) const { \
+ return m_ptr _op_ o; \
+} \
+inline bool operator _op_ (const wp<T>& o) const { \
+ return m_ptr _op_ o.m_ptr; \
+} \
+template<typename U> \
+inline bool operator _op_ (const wp<U>& o) const { \
+ return m_ptr _op_ o.m_ptr; \
+}
+
+// ---------------------------------------------------------------------------
+
+template <typename T>
+class sp
+{
+public:
+ inline sp() : m_ptr(0) { }
+
+ sp(T* other);
+ sp(const sp<T>& other);
+ template<typename U> sp(U* other);
+ template<typename U> sp(const sp<U>& other);
+
+ ~sp();
+
+ // Assignment
+
+ sp& operator = (T* other);
+ sp& operator = (const sp<T>& other);
+
+ template<typename U> sp& operator = (const sp<U>& other);
+ template<typename U> sp& operator = (U* other);
+
+ //! Special optimization for use by ProcessState (and nobody else).
+ void force_set(T* other);
+
+ // Reset
+
+ void clear();
+
+ // Accessors
+
+ inline T& operator* () const { return *m_ptr; }
+ inline T* operator-> () const { return m_ptr; }
+ inline T* get() const { return m_ptr; }
+
+ // Operators
+
+ COMPARE(==)
+ COMPARE(!=)
+ COMPARE(>)
+ COMPARE(<)
+ COMPARE(<=)
+ COMPARE(>=)
+
+private:
+ template<typename Y> friend class sp;
+ template<typename Y> friend class wp;
+ void set_pointer(T* ptr);
+ T* m_ptr;
+};
+
+#undef COMPARE
+
+template <typename T>
+TextOutput& operator<<(TextOutput& to, const sp<T>& val);
+
+// ---------------------------------------------------------------------------
+// No user serviceable parts below here.
+
+template<typename T>
+sp<T>::sp(T* other)
+: m_ptr(other)
+ {
+ if (other) other->incStrong(this);
+ }
+
+template<typename T>
+sp<T>::sp(const sp<T>& other)
+: m_ptr(other.m_ptr)
+ {
+ if (m_ptr) m_ptr->incStrong(this);
+ }
+
+template<typename T> template<typename U>
+sp<T>::sp(U* other) : m_ptr(other)
+{
+ if (other) ((T*)other)->incStrong(this);
+}
+
+template<typename T> template<typename U>
+sp<T>::sp(const sp<U>& other)
+: m_ptr(other.m_ptr)
+ {
+ if (m_ptr) m_ptr->incStrong(this);
+ }
+
+template<typename T>
+sp<T>::~sp()
+{
+ if (m_ptr) m_ptr->decStrong(this);
+}
+
+template<typename T>
+sp<T>& sp<T>::operator = (const sp<T>& other) {
+ T* otherPtr(other.m_ptr);
+ if (otherPtr) otherPtr->incStrong(this);
+ if (m_ptr) m_ptr->decStrong(this);
+ m_ptr = otherPtr;
+ return *this;
+}
+
+template<typename T>
+sp<T>& sp<T>::operator = (T* other)
+{
+ if (other) other->incStrong(this);
+ if (m_ptr) m_ptr->decStrong(this);
+ m_ptr = other;
+ return *this;
+}
+
+template<typename T> template<typename U>
+sp<T>& sp<T>::operator = (const sp<U>& other)
+{
+ T* otherPtr(other.m_ptr);
+ if (otherPtr) otherPtr->incStrong(this);
+ if (m_ptr) m_ptr->decStrong(this);
+ m_ptr = otherPtr;
+ return *this;
+}
+
+template<typename T> template<typename U>
+sp<T>& sp<T>::operator = (U* other)
+{
+ if (other) ((T*)other)->incStrong(this);
+ if (m_ptr) m_ptr->decStrong(this);
+ m_ptr = other;
+ return *this;
+}
+
+template<typename T>
+void sp<T>::force_set(T* other)
+{
+ other->forceIncStrong(this);
+ m_ptr = other;
+}
+
+template<typename T>
+void sp<T>::clear()
+{
+ if (m_ptr) {
+ m_ptr->decStrong(this);
+ m_ptr = 0;
+ }
+}
+
+template<typename T>
+void sp<T>::set_pointer(T* ptr) {
+ m_ptr = ptr;
+}
+
+template <typename T>
+inline TextOutput& operator<<(TextOutput& to, const sp<T>& val)
+{
+ return printStrongPointer(to, val.get());
+}
+
+}; // namespace android
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_STRONG_POINTER_H
diff --git a/include/utils/TypeHelpers.h b/include/utils/TypeHelpers.h
index 2ff2749..a1663f3 100644
--- a/include/utils/TypeHelpers.h
+++ b/include/utils/TypeHelpers.h
@@ -37,18 +37,6 @@
template <typename T> struct trait_pointer { enum { value = false }; };
template <typename T> struct trait_pointer<T*> { enum { value = true }; };
-// sp<> can be trivially moved
-template <typename T> class sp;
-template <typename T> struct trait_trivial_move< sp<T> >{
- enum { value = true };
-};
-
-// wp<> can be trivially moved
-template <typename T> class wp;
-template <typename T> struct trait_trivial_move< wp<T> >{
- enum { value = true };
-};
-
template <typename TYPE>
struct traits {
enum {
@@ -217,7 +205,6 @@
}
}
-
// ---------------------------------------------------------------------------
/*
diff --git a/include/utils/threads.h b/include/utils/threads.h
index 1bcfaed..41e5766 100644
--- a/include/utils/threads.h
+++ b/include/utils/threads.h
@@ -527,9 +527,10 @@
static int _threadLoop(void* user);
const bool mCanCallJava;
thread_id_t mThread;
- Mutex mLock;
+ mutable Mutex mLock;
Condition mThreadExitedCondition;
status_t mStatus;
+ // note that all accesses of mExitPending and mRunning need to hold mLock
volatile bool mExitPending;
volatile bool mRunning;
sp<Thread> mHoldSelf;
diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp
index 223cf09..3bed959 100644
--- a/libs/gui/SurfaceTexture.cpp
+++ b/libs/gui/SurfaceTexture.cpp
@@ -96,6 +96,11 @@
status_t SurfaceTexture::setBufferCount(int bufferCount) {
LOGV("SurfaceTexture::setBufferCount");
+
+ if (bufferCount < MIN_BUFFER_SLOTS) {
+ return BAD_VALUE;
+ }
+
Mutex::Autolock lock(mMutex);
freeAllBuffers();
mBufferCount = bufferCount;
diff --git a/libs/gui/SurfaceTextureClient.cpp b/libs/gui/SurfaceTextureClient.cpp
index ee14ac9..43b330c 100644
--- a/libs/gui/SurfaceTextureClient.cpp
+++ b/libs/gui/SurfaceTextureClient.cpp
@@ -143,8 +143,21 @@
int SurfaceTextureClient::query(int what, int* value) {
LOGV("SurfaceTextureClient::query");
Mutex::Autolock lock(mMutex);
- // XXX: Implement this!
- return INVALID_OPERATION;
+ switch (what) {
+ case NATIVE_WINDOW_WIDTH:
+ case NATIVE_WINDOW_HEIGHT:
+ // XXX: How should SurfaceTexture behave if setBuffersGeometry didn't
+ // override the size?
+ *value = 0;
+ return NO_ERROR;
+ case NATIVE_WINDOW_FORMAT:
+ *value = DEFAULT_FORMAT;
+ return NO_ERROR;
+ case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS:
+ *value = MIN_UNDEQUEUED_BUFFERS;
+ return NO_ERROR;
+ }
+ return BAD_VALUE;
}
int SurfaceTextureClient::perform(int operation, va_list args)
diff --git a/libs/surfaceflinger_client/Surface.cpp b/libs/surfaceflinger_client/Surface.cpp
index 1e9bd74..818114a 100644
--- a/libs/surfaceflinger_client/Surface.cpp
+++ b/libs/surfaceflinger_client/Surface.cpp
@@ -709,6 +709,9 @@
case NATIVE_WINDOW_FORMAT:
*value = int(mFormat);
return NO_ERROR;
+ case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS:
+ *value = MIN_UNDEQUEUED_BUFFERS;
+ return NO_ERROR;
}
return BAD_VALUE;
}
@@ -1040,7 +1043,7 @@
// e.g. if GraphicBuffer is used to wrap an android_native_buffer_t that
// was dequeued from an ANativeWindow.
for (size_t i = 0; i < mBuffers.size(); i++) {
- if (buffer->handle == mBuffers[i]->handle) {
+ if (mBuffers[i] != 0 && buffer->handle == mBuffers[i]->handle) {
idx = mBuffers[i]->getIndex();
break;
}
diff --git a/libs/ui/Input.cpp b/libs/ui/Input.cpp
index e3107d5..0ed0866 100644
--- a/libs/ui/Input.cpp
+++ b/libs/ui/Input.cpp
@@ -250,11 +250,59 @@
// --- PointerCoords ---
+float PointerCoords::getAxisValue(int32_t axis) const {
+ if (axis < 0 || axis > 63) {
+ return 0;
+ }
+
+ uint64_t axisBit = 1LL << axis;
+ if (!(bits & axisBit)) {
+ return 0;
+ }
+ uint32_t index = __builtin_popcountll(bits & (axisBit - 1LL));
+ return values[index];
+}
+
+status_t PointerCoords::setAxisValue(int32_t axis, float value) {
+ if (axis < 0 || axis > 63) {
+ return NAME_NOT_FOUND;
+ }
+
+ uint64_t axisBit = 1LL << axis;
+ uint32_t index = __builtin_popcountll(bits & (axisBit - 1LL));
+ if (!(bits & axisBit)) {
+ uint32_t count = __builtin_popcountll(bits);
+ if (count >= MAX_AXES) {
+ tooManyAxes(axis);
+ return NO_MEMORY;
+ }
+ bits |= axisBit;
+ for (uint32_t i = count; i > index; i--) {
+ values[i] = values[i - 1];
+ }
+ }
+ values[index] = value;
+ return OK;
+}
+
+float* PointerCoords::editAxisValue(int32_t axis) {
+ if (axis < 0 || axis > 63) {
+ return NULL;
+ }
+
+ uint64_t axisBit = 1LL << axis;
+ if (!(bits & axisBit)) {
+ return NULL;
+ }
+ uint32_t index = __builtin_popcountll(bits & (axisBit - 1LL));
+ return &values[index];
+}
+
#ifdef HAVE_ANDROID_OS
status_t PointerCoords::readFromParcel(Parcel* parcel) {
- bits = parcel->readInt32();
+ bits = parcel->readInt64();
- uint32_t count = __builtin_popcount(bits);
+ uint32_t count = __builtin_popcountll(bits);
if (count > MAX_AXES) {
return BAD_VALUE;
}
@@ -266,9 +314,9 @@
}
status_t PointerCoords::writeToParcel(Parcel* parcel) const {
- parcel->writeInt32(bits);
+ parcel->writeInt64(bits);
- uint32_t count = __builtin_popcount(bits);
+ uint32_t count = __builtin_popcountll(bits);
for (uint32_t i = 0; i < count; i++) {
parcel->writeInt32(values[i]);
}
@@ -568,6 +616,23 @@
}
#endif
+bool MotionEvent::isTouchEvent(int32_t source, int32_t action) {
+ if (source & AINPUT_SOURCE_CLASS_POINTER) {
+ // Specifically excludes HOVER_MOVE and SCROLL.
+ switch (action & AMOTION_EVENT_ACTION_MASK) {
+ case AMOTION_EVENT_ACTION_DOWN:
+ case AMOTION_EVENT_ACTION_MOVE:
+ case AMOTION_EVENT_ACTION_UP:
+ case AMOTION_EVENT_ACTION_POINTER_DOWN:
+ case AMOTION_EVENT_ACTION_POINTER_UP:
+ case AMOTION_EVENT_ACTION_CANCEL:
+ case AMOTION_EVENT_ACTION_OUTSIDE:
+ return true;
+ }
+ }
+ return false;
+}
+
// --- InputDeviceInfo ---
diff --git a/libs/ui/InputTransport.cpp b/libs/ui/InputTransport.cpp
index 83d9556..5c57a76 100644
--- a/libs/ui/InputTransport.cpp
+++ b/libs/ui/InputTransport.cpp
@@ -412,7 +412,8 @@
// Cache essential information about the motion event to ensure that a malicious consumer
// cannot confuse the publisher by modifying the contents of the shared memory buffer while
// it is being updated.
- if (action == AMOTION_EVENT_ACTION_MOVE) {
+ if (action == AMOTION_EVENT_ACTION_MOVE
+ || action == AMOTION_EVENT_ACTION_HOVER_MOVE) {
mMotionEventPointerCount = pointerCount;
mMotionEventSampleDataStride = InputMessage::sampleDataStride(pointerCount);
mMotionEventSampleDataTail = InputMessage::sampleDataPtrIncrement(
diff --git a/libs/ui/KeyLayoutMap.cpp b/libs/ui/KeyLayoutMap.cpp
index 56bc26f..2ed0e66 100644
--- a/libs/ui/KeyLayoutMap.cpp
+++ b/libs/ui/KeyLayoutMap.cpp
@@ -82,11 +82,11 @@
return status;
}
-status_t KeyLayoutMap::map(int32_t scanCode, int32_t* keyCode, uint32_t* flags) const {
+status_t KeyLayoutMap::mapKey(int32_t scanCode, int32_t* keyCode, uint32_t* flags) const {
ssize_t index = mKeys.indexOfKey(scanCode);
if (index < 0) {
#if DEBUG_MAPPING
- LOGD("map: scanCode=%d ~ Failed.", scanCode);
+ LOGD("mapKey: scanCode=%d ~ Failed.", scanCode);
#endif
*keyCode = AKEYCODE_UNKNOWN;
*flags = 0;
@@ -98,12 +98,12 @@
*flags = k.flags;
#if DEBUG_MAPPING
- LOGD("map: scanCode=%d ~ Result keyCode=%d, flags=0x%08x.", scanCode, *keyCode, *flags);
+ LOGD("mapKey: scanCode=%d ~ Result keyCode=%d, flags=0x%08x.", scanCode, *keyCode, *flags);
#endif
return NO_ERROR;
}
-status_t KeyLayoutMap::findScanCodes(int32_t keyCode, Vector<int32_t>* outScanCodes) const {
+status_t KeyLayoutMap::findScanCodesForKey(int32_t keyCode, Vector<int32_t>* outScanCodes) const {
const size_t N = mKeys.size();
for (size_t i=0; i<N; i++) {
if (mKeys.valueAt(i).keyCode == keyCode) {
@@ -113,6 +113,25 @@
return NO_ERROR;
}
+status_t KeyLayoutMap::mapAxis(int32_t scanCode, int32_t* axis) const {
+ ssize_t index = mAxes.indexOfKey(scanCode);
+ if (index < 0) {
+#if DEBUG_MAPPING
+ LOGD("mapAxis: scanCode=%d ~ Failed.", scanCode);
+#endif
+ *axis = -1;
+ return NAME_NOT_FOUND;
+ }
+
+ *axis = mAxes.valueAt(index);
+
+#if DEBUG_MAPPING
+ LOGD("mapAxis: scanCode=%d ~ Result axis=%d.", scanCode, *axis);
+#endif
+ return NO_ERROR;
+}
+
+
// --- KeyLayoutMap::Parser ---
KeyLayoutMap::Parser::Parser(KeyLayoutMap* map, Tokenizer* tokenizer) :
@@ -137,6 +156,10 @@
mTokenizer->skipDelimiters(WHITESPACE);
status_t status = parseKey();
if (status) return status;
+ } else if (keywordToken == "axis") {
+ mTokenizer->skipDelimiters(WHITESPACE);
+ status_t status = parseAxis();
+ if (status) return status;
} else {
LOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().string(),
keywordToken.string());
@@ -162,12 +185,12 @@
char* end;
int32_t scanCode = int32_t(strtol(scanCodeToken.string(), &end, 0));
if (*end) {
- LOGE("%s: Expected scan code number, got '%s'.", mTokenizer->getLocation().string(),
+ LOGE("%s: Expected key scan code number, got '%s'.", mTokenizer->getLocation().string(),
scanCodeToken.string());
return BAD_VALUE;
}
if (mMap->mKeys.indexOfKey(scanCode) >= 0) {
- LOGE("%s: Duplicate entry for scan code '%s'.", mTokenizer->getLocation().string(),
+ LOGE("%s: Duplicate entry for key scan code '%s'.", mTokenizer->getLocation().string(),
scanCodeToken.string());
return BAD_VALUE;
}
@@ -189,12 +212,12 @@
String8 flagToken = mTokenizer->nextToken(WHITESPACE);
uint32_t flag = getKeyFlagByLabel(flagToken.string());
if (!flag) {
- LOGE("%s: Expected flag label, got '%s'.", mTokenizer->getLocation().string(),
+ LOGE("%s: Expected key flag label, got '%s'.", mTokenizer->getLocation().string(),
flagToken.string());
return BAD_VALUE;
}
if (flags & flag) {
- LOGE("%s: Duplicate flag '%s'.", mTokenizer->getLocation().string(),
+ LOGE("%s: Duplicate key flag '%s'.", mTokenizer->getLocation().string(),
flagToken.string());
return BAD_VALUE;
}
@@ -211,4 +234,35 @@
return NO_ERROR;
}
+status_t KeyLayoutMap::Parser::parseAxis() {
+ String8 scanCodeToken = mTokenizer->nextToken(WHITESPACE);
+ char* end;
+ int32_t scanCode = int32_t(strtol(scanCodeToken.string(), &end, 0));
+ if (*end) {
+ LOGE("%s: Expected axis scan code number, got '%s'.", mTokenizer->getLocation().string(),
+ scanCodeToken.string());
+ return BAD_VALUE;
+ }
+ if (mMap->mAxes.indexOfKey(scanCode) >= 0) {
+ LOGE("%s: Duplicate entry for axis scan code '%s'.", mTokenizer->getLocation().string(),
+ scanCodeToken.string());
+ return BAD_VALUE;
+ }
+
+ mTokenizer->skipDelimiters(WHITESPACE);
+ String8 axisToken = mTokenizer->nextToken(WHITESPACE);
+ int32_t axis = getAxisByLabel(axisToken.string());
+ if (axis < 0) {
+ LOGE("%s: Expected axis label, got '%s'.", mTokenizer->getLocation().string(),
+ axisToken.string());
+ return BAD_VALUE;
+ }
+
+#if DEBUG_PARSER
+ LOGD("Parsed axis: scanCode=%d, axis=%d.", scanCode, axis);
+#endif
+ mMap->mAxes.add(scanCode, axis);
+ return NO_ERROR;
+}
+
};
diff --git a/libs/ui/Keyboard.cpp b/libs/ui/Keyboard.cpp
index 6faa600..600a951 100644
--- a/libs/ui/Keyboard.cpp
+++ b/libs/ui/Keyboard.cpp
@@ -217,7 +217,7 @@
return NAME_NOT_FOUND;
}
-static int lookupLabel(const char* literal, const KeycodeLabel *list) {
+static int lookupValueByLabel(const char* literal, const KeycodeLabel *list) {
while (list->literal) {
if (strcmp(literal, list->literal) == 0) {
return list->value;
@@ -227,12 +227,30 @@
return list->value;
}
+static const char* lookupLabelByValue(int value, const KeycodeLabel *list) {
+ while (list->literal) {
+ if (list->value == value) {
+ return list->literal;
+ }
+ list++;
+ }
+ return NULL;
+}
+
int32_t getKeyCodeByLabel(const char* label) {
- return int32_t(lookupLabel(label, KEYCODES));
+ return int32_t(lookupValueByLabel(label, KEYCODES));
}
uint32_t getKeyFlagByLabel(const char* label) {
- return uint32_t(lookupLabel(label, FLAGS));
+ return uint32_t(lookupValueByLabel(label, FLAGS));
+}
+
+int32_t getAxisByLabel(const char* label) {
+ return int32_t(lookupValueByLabel(label, AXES));
+}
+
+const char* getAxisLabel(int32_t axisId) {
+ return lookupLabelByValue(axisId, AXES);
}
static int32_t setEphemeralMetaState(int32_t mask, bool down, int32_t oldMetaState) {
@@ -304,5 +322,26 @@
}
}
+bool isMetaKey(int32_t keyCode) {
+ switch (keyCode) {
+ case AKEYCODE_ALT_LEFT:
+ case AKEYCODE_ALT_RIGHT:
+ case AKEYCODE_SHIFT_LEFT:
+ case AKEYCODE_SHIFT_RIGHT:
+ case AKEYCODE_SYM:
+ case AKEYCODE_FUNCTION:
+ case AKEYCODE_CTRL_LEFT:
+ case AKEYCODE_CTRL_RIGHT:
+ case AKEYCODE_META_LEFT:
+ case AKEYCODE_META_RIGHT:
+ case AKEYCODE_CAPS_LOCK:
+ case AKEYCODE_NUM_LOCK:
+ case AKEYCODE_SCROLL_LOCK:
+ return true;
+ default:
+ return false;
+ }
+}
+
} // namespace android
diff --git a/libs/ui/tests/InputEvent_test.cpp b/libs/ui/tests/InputEvent_test.cpp
index 7b15c38..b77489e 100644
--- a/libs/ui/tests/InputEvent_test.cpp
+++ b/libs/ui/tests/InputEvent_test.cpp
@@ -18,6 +18,9 @@
#include <gtest/gtest.h>
#include <binder/Parcel.h>
+#include <math.h>
+#include <SkMatrix.h>
+
namespace android {
class BaseTest : public testing::Test {
@@ -35,7 +38,7 @@
PointerCoords coords;
coords.clear();
- ASSERT_EQ(0U, coords.bits);
+ ASSERT_EQ(0ULL, coords.bits);
}
TEST_F(PointerCoordsTest, AxisValues) {
@@ -54,7 +57,7 @@
// Set first axis.
ASSERT_EQ(OK, coords.setAxisValue(1, 5));
- ASSERT_EQ(0x00000002U, coords.bits);
+ ASSERT_EQ(0x00000002ULL, coords.bits);
ASSERT_EQ(5, coords.values[0]);
ASSERT_EQ(0, coords.getAxisValue(0))
@@ -64,7 +67,7 @@
// Set an axis with a higher id than all others. (appending value at the end)
ASSERT_EQ(OK, coords.setAxisValue(3, 2));
- ASSERT_EQ(0x0000000aU, coords.bits);
+ ASSERT_EQ(0x0000000aULL, coords.bits);
ASSERT_EQ(5, coords.values[0]);
ASSERT_EQ(2, coords.values[1]);
@@ -79,7 +82,7 @@
// Set an axis with an id lower than all others. (prepending value at beginning)
ASSERT_EQ(OK, coords.setAxisValue(0, 4));
- ASSERT_EQ(0x0000000bU, coords.bits);
+ ASSERT_EQ(0x0000000bULL, coords.bits);
ASSERT_EQ(4, coords.values[0]);
ASSERT_EQ(5, coords.values[1]);
ASSERT_EQ(2, coords.values[2]);
@@ -104,7 +107,7 @@
// Set an axis with an id between the others. (inserting value in the middle)
ASSERT_EQ(OK, coords.setAxisValue(2, 1));
- ASSERT_EQ(0x0000000fU, coords.bits);
+ ASSERT_EQ(0x0000000fULL, coords.bits);
ASSERT_EQ(4, coords.values[0]);
ASSERT_EQ(7, coords.values[1]);
ASSERT_EQ(1, coords.values[2]);
@@ -121,7 +124,7 @@
// Set an existing axis value in place.
ASSERT_EQ(OK, coords.setAxisValue(1, 6));
- ASSERT_EQ(0x0000000fU, coords.bits);
+ ASSERT_EQ(0x0000000fULL, coords.bits);
ASSERT_EQ(4, coords.values[0]);
ASSERT_EQ(6, coords.values[1]);
ASSERT_EQ(1, coords.values[2]);
@@ -140,15 +143,15 @@
for (size_t axis = 4; axis < PointerCoords::MAX_AXES; axis++) {
ASSERT_EQ(OK, coords.setAxisValue(axis, axis));
}
- ASSERT_EQ(PointerCoords::MAX_AXES, __builtin_popcount(coords.bits));
+ ASSERT_EQ(PointerCoords::MAX_AXES, __builtin_popcountll(coords.bits));
// Try to set one more axis beyond maximum number.
// Ensure bits are unchanged.
ASSERT_EQ(NO_MEMORY, coords.setAxisValue(PointerCoords::MAX_AXES, 100));
- ASSERT_EQ(PointerCoords::MAX_AXES, __builtin_popcount(coords.bits));
+ ASSERT_EQ(PointerCoords::MAX_AXES, __builtin_popcountll(coords.bits));
}
-TEST_F(PointerCoordsTest, ReadAndWriteParcel) {
+TEST_F(PointerCoordsTest, Parcel) {
Parcel parcel;
PointerCoords inCoords;
@@ -160,7 +163,7 @@
parcel.setDataPosition(0);
outCoords.readFromParcel(&parcel);
- ASSERT_EQ(0U, outCoords.bits);
+ ASSERT_EQ(0ULL, outCoords.bits);
// Round trip with some values.
parcel.freeData();
@@ -213,16 +216,22 @@
// --- MotionEventTest ---
class MotionEventTest : public BaseTest {
+protected:
+ static const nsecs_t ARBITRARY_DOWN_TIME;
+ static const nsecs_t ARBITRARY_EVENT_TIME;
+ static const float X_OFFSET;
+ static const float Y_OFFSET;
+
+ void initializeEventWithHistory(MotionEvent* event);
+ void assertEqualsEventWithHistory(const MotionEvent* event);
};
-TEST_F(MotionEventTest, Properties) {
- MotionEvent event;
+const nsecs_t MotionEventTest::ARBITRARY_DOWN_TIME = 1;
+const nsecs_t MotionEventTest::ARBITRARY_EVENT_TIME = 2;
+const float MotionEventTest::X_OFFSET = 1.0f;
+const float MotionEventTest::Y_OFFSET = 1.1f;
- // Initialize, add samples and get properties.
- const nsecs_t ARBITRARY_DOWN_TIME = 1;
- const nsecs_t ARBITRARY_EVENT_TIME = 2;
- const float X_OFFSET = 1.0f;
- const float Y_OFFSET = 1.1f;
+void MotionEventTest::initializeEventWithHistory(MotionEvent* event) {
int32_t pointerIds[] = { 1, 2 };
PointerCoords pointerCoords[2];
pointerCoords[0].clear();
@@ -245,7 +254,7 @@
pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 26);
pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 27);
pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 28);
- event.initialize(2, AINPUT_SOURCE_TOUCHSCREEN, AMOTION_EVENT_ACTION_MOVE,
+ event->initialize(2, AINPUT_SOURCE_TOUCHSCREEN, AMOTION_EVENT_ACTION_MOVE,
AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED,
AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON,
X_OFFSET, Y_OFFSET, 2.0f, 2.1f,
@@ -270,7 +279,7 @@
pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 126);
pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 127);
pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 128);
- event.addSample(ARBITRARY_EVENT_TIME + 1, pointerCoords);
+ event->addSample(ARBITRARY_EVENT_TIME + 1, pointerCoords);
pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 210);
pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 211);
@@ -290,128 +299,284 @@
pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 226);
pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 227);
pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 228);
- event.addSample(ARBITRARY_EVENT_TIME + 2, pointerCoords);
+ event->addSample(ARBITRARY_EVENT_TIME + 2, pointerCoords);
+}
- ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event.getType());
- ASSERT_EQ(2, event.getDeviceId());
- ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, event.getSource());
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, event.getAction());
- ASSERT_EQ(AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, event.getFlags());
- ASSERT_EQ(AMOTION_EVENT_EDGE_FLAG_TOP, event.getEdgeFlags());
- ASSERT_EQ(AMETA_ALT_ON, event.getMetaState());
- ASSERT_EQ(X_OFFSET, event.getXOffset());
- ASSERT_EQ(Y_OFFSET, event.getYOffset());
- ASSERT_EQ(2.0f, event.getXPrecision());
- ASSERT_EQ(2.1f, event.getYPrecision());
- ASSERT_EQ(ARBITRARY_DOWN_TIME, event.getDownTime());
+void MotionEventTest::assertEqualsEventWithHistory(const MotionEvent* event) {
+ // Check properties.
+ ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType());
+ ASSERT_EQ(2, event->getDeviceId());
+ ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, event->getSource());
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, event->getAction());
+ ASSERT_EQ(AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, event->getFlags());
+ ASSERT_EQ(AMOTION_EVENT_EDGE_FLAG_TOP, event->getEdgeFlags());
+ ASSERT_EQ(AMETA_ALT_ON, event->getMetaState());
+ ASSERT_EQ(X_OFFSET, event->getXOffset());
+ ASSERT_EQ(Y_OFFSET, event->getYOffset());
+ ASSERT_EQ(2.0f, event->getXPrecision());
+ ASSERT_EQ(2.1f, event->getYPrecision());
+ ASSERT_EQ(ARBITRARY_DOWN_TIME, event->getDownTime());
- ASSERT_EQ(2U, event.getPointerCount());
- ASSERT_EQ(1, event.getPointerId(0));
- ASSERT_EQ(2, event.getPointerId(1));
+ ASSERT_EQ(2U, event->getPointerCount());
+ ASSERT_EQ(1, event->getPointerId(0));
+ ASSERT_EQ(2, event->getPointerId(1));
- ASSERT_EQ(2U, event.getHistorySize());
+ ASSERT_EQ(2U, event->getHistorySize());
- // Get data.
- ASSERT_EQ(ARBITRARY_EVENT_TIME, event.getHistoricalEventTime(0));
- ASSERT_EQ(ARBITRARY_EVENT_TIME + 1, event.getHistoricalEventTime(1));
- ASSERT_EQ(ARBITRARY_EVENT_TIME + 2, event.getEventTime());
+ // Check data.
+ ASSERT_EQ(ARBITRARY_EVENT_TIME, event->getHistoricalEventTime(0));
+ ASSERT_EQ(ARBITRARY_EVENT_TIME + 1, event->getHistoricalEventTime(1));
+ ASSERT_EQ(ARBITRARY_EVENT_TIME + 2, event->getEventTime());
- ASSERT_EQ(11, event.getHistoricalRawPointerCoords(0, 0)->
+ ASSERT_EQ(11, event->getHistoricalRawPointerCoords(0, 0)->
getAxisValue(AMOTION_EVENT_AXIS_Y));
- ASSERT_EQ(21, event.getHistoricalRawPointerCoords(1, 0)->
+ ASSERT_EQ(21, event->getHistoricalRawPointerCoords(1, 0)->
getAxisValue(AMOTION_EVENT_AXIS_Y));
- ASSERT_EQ(111, event.getHistoricalRawPointerCoords(0, 1)->
+ ASSERT_EQ(111, event->getHistoricalRawPointerCoords(0, 1)->
getAxisValue(AMOTION_EVENT_AXIS_Y));
- ASSERT_EQ(121, event.getHistoricalRawPointerCoords(1, 1)->
+ ASSERT_EQ(121, event->getHistoricalRawPointerCoords(1, 1)->
getAxisValue(AMOTION_EVENT_AXIS_Y));
- ASSERT_EQ(211, event.getRawPointerCoords(0)->
+ ASSERT_EQ(211, event->getRawPointerCoords(0)->
getAxisValue(AMOTION_EVENT_AXIS_Y));
- ASSERT_EQ(221, event.getRawPointerCoords(1)->
+ ASSERT_EQ(221, event->getRawPointerCoords(1)->
getAxisValue(AMOTION_EVENT_AXIS_Y));
- ASSERT_EQ(11, event.getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 0));
- ASSERT_EQ(21, event.getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 0));
- ASSERT_EQ(111, event.getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 1));
- ASSERT_EQ(121, event.getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 1));
- ASSERT_EQ(211, event.getRawAxisValue(AMOTION_EVENT_AXIS_Y, 0));
- ASSERT_EQ(221, event.getRawAxisValue(AMOTION_EVENT_AXIS_Y, 1));
+ ASSERT_EQ(11, event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 0));
+ ASSERT_EQ(21, event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 0));
+ ASSERT_EQ(111, event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 1));
+ ASSERT_EQ(121, event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 1));
+ ASSERT_EQ(211, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 0));
+ ASSERT_EQ(221, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 1));
- ASSERT_EQ(10, event.getHistoricalRawX(0, 0));
- ASSERT_EQ(20, event.getHistoricalRawX(1, 0));
- ASSERT_EQ(110, event.getHistoricalRawX(0, 1));
- ASSERT_EQ(120, event.getHistoricalRawX(1, 1));
- ASSERT_EQ(210, event.getRawX(0));
- ASSERT_EQ(220, event.getRawX(1));
+ ASSERT_EQ(10, event->getHistoricalRawX(0, 0));
+ ASSERT_EQ(20, event->getHistoricalRawX(1, 0));
+ ASSERT_EQ(110, event->getHistoricalRawX(0, 1));
+ ASSERT_EQ(120, event->getHistoricalRawX(1, 1));
+ ASSERT_EQ(210, event->getRawX(0));
+ ASSERT_EQ(220, event->getRawX(1));
- ASSERT_EQ(11, event.getHistoricalRawY(0, 0));
- ASSERT_EQ(21, event.getHistoricalRawY(1, 0));
- ASSERT_EQ(111, event.getHistoricalRawY(0, 1));
- ASSERT_EQ(121, event.getHistoricalRawY(1, 1));
- ASSERT_EQ(211, event.getRawY(0));
- ASSERT_EQ(221, event.getRawY(1));
+ ASSERT_EQ(11, event->getHistoricalRawY(0, 0));
+ ASSERT_EQ(21, event->getHistoricalRawY(1, 0));
+ ASSERT_EQ(111, event->getHistoricalRawY(0, 1));
+ ASSERT_EQ(121, event->getHistoricalRawY(1, 1));
+ ASSERT_EQ(211, event->getRawY(0));
+ ASSERT_EQ(221, event->getRawY(1));
- ASSERT_EQ(X_OFFSET + 10, event.getHistoricalX(0, 0));
- ASSERT_EQ(X_OFFSET + 20, event.getHistoricalX(1, 0));
- ASSERT_EQ(X_OFFSET + 110, event.getHistoricalX(0, 1));
- ASSERT_EQ(X_OFFSET + 120, event.getHistoricalX(1, 1));
- ASSERT_EQ(X_OFFSET + 210, event.getX(0));
- ASSERT_EQ(X_OFFSET + 220, event.getX(1));
+ ASSERT_EQ(X_OFFSET + 10, event->getHistoricalX(0, 0));
+ ASSERT_EQ(X_OFFSET + 20, event->getHistoricalX(1, 0));
+ ASSERT_EQ(X_OFFSET + 110, event->getHistoricalX(0, 1));
+ ASSERT_EQ(X_OFFSET + 120, event->getHistoricalX(1, 1));
+ ASSERT_EQ(X_OFFSET + 210, event->getX(0));
+ ASSERT_EQ(X_OFFSET + 220, event->getX(1));
- ASSERT_EQ(Y_OFFSET + 11, event.getHistoricalY(0, 0));
- ASSERT_EQ(Y_OFFSET + 21, event.getHistoricalY(1, 0));
- ASSERT_EQ(Y_OFFSET + 111, event.getHistoricalY(0, 1));
- ASSERT_EQ(Y_OFFSET + 121, event.getHistoricalY(1, 1));
- ASSERT_EQ(Y_OFFSET + 211, event.getY(0));
- ASSERT_EQ(Y_OFFSET + 221, event.getY(1));
+ ASSERT_EQ(Y_OFFSET + 11, event->getHistoricalY(0, 0));
+ ASSERT_EQ(Y_OFFSET + 21, event->getHistoricalY(1, 0));
+ ASSERT_EQ(Y_OFFSET + 111, event->getHistoricalY(0, 1));
+ ASSERT_EQ(Y_OFFSET + 121, event->getHistoricalY(1, 1));
+ ASSERT_EQ(Y_OFFSET + 211, event->getY(0));
+ ASSERT_EQ(Y_OFFSET + 221, event->getY(1));
- ASSERT_EQ(12, event.getHistoricalPressure(0, 0));
- ASSERT_EQ(22, event.getHistoricalPressure(1, 0));
- ASSERT_EQ(112, event.getHistoricalPressure(0, 1));
- ASSERT_EQ(122, event.getHistoricalPressure(1, 1));
+ ASSERT_EQ(12, event->getHistoricalPressure(0, 0));
+ ASSERT_EQ(22, event->getHistoricalPressure(1, 0));
+ ASSERT_EQ(112, event->getHistoricalPressure(0, 1));
+ ASSERT_EQ(122, event->getHistoricalPressure(1, 1));
+ ASSERT_EQ(212, event->getPressure(0));
+ ASSERT_EQ(222, event->getPressure(1));
+
+ ASSERT_EQ(13, event->getHistoricalSize(0, 0));
+ ASSERT_EQ(23, event->getHistoricalSize(1, 0));
+ ASSERT_EQ(113, event->getHistoricalSize(0, 1));
+ ASSERT_EQ(123, event->getHistoricalSize(1, 1));
+ ASSERT_EQ(213, event->getSize(0));
+ ASSERT_EQ(223, event->getSize(1));
+
+ ASSERT_EQ(14, event->getHistoricalTouchMajor(0, 0));
+ ASSERT_EQ(24, event->getHistoricalTouchMajor(1, 0));
+ ASSERT_EQ(114, event->getHistoricalTouchMajor(0, 1));
+ ASSERT_EQ(124, event->getHistoricalTouchMajor(1, 1));
+ ASSERT_EQ(214, event->getTouchMajor(0));
+ ASSERT_EQ(224, event->getTouchMajor(1));
+
+ ASSERT_EQ(15, event->getHistoricalTouchMinor(0, 0));
+ ASSERT_EQ(25, event->getHistoricalTouchMinor(1, 0));
+ ASSERT_EQ(115, event->getHistoricalTouchMinor(0, 1));
+ ASSERT_EQ(125, event->getHistoricalTouchMinor(1, 1));
+ ASSERT_EQ(215, event->getTouchMinor(0));
+ ASSERT_EQ(225, event->getTouchMinor(1));
+
+ ASSERT_EQ(16, event->getHistoricalToolMajor(0, 0));
+ ASSERT_EQ(26, event->getHistoricalToolMajor(1, 0));
+ ASSERT_EQ(116, event->getHistoricalToolMajor(0, 1));
+ ASSERT_EQ(126, event->getHistoricalToolMajor(1, 1));
+ ASSERT_EQ(216, event->getToolMajor(0));
+ ASSERT_EQ(226, event->getToolMajor(1));
+
+ ASSERT_EQ(17, event->getHistoricalToolMinor(0, 0));
+ ASSERT_EQ(27, event->getHistoricalToolMinor(1, 0));
+ ASSERT_EQ(117, event->getHistoricalToolMinor(0, 1));
+ ASSERT_EQ(127, event->getHistoricalToolMinor(1, 1));
+ ASSERT_EQ(217, event->getToolMinor(0));
+ ASSERT_EQ(227, event->getToolMinor(1));
+
+ ASSERT_EQ(18, event->getHistoricalOrientation(0, 0));
+ ASSERT_EQ(28, event->getHistoricalOrientation(1, 0));
+ ASSERT_EQ(118, event->getHistoricalOrientation(0, 1));
+ ASSERT_EQ(128, event->getHistoricalOrientation(1, 1));
+ ASSERT_EQ(218, event->getOrientation(0));
+ ASSERT_EQ(228, event->getOrientation(1));
+}
+
+TEST_F(MotionEventTest, Properties) {
+ MotionEvent event;
+
+ // Initialize, add samples and check properties.
+ initializeEventWithHistory(&event);
+ ASSERT_NO_FATAL_FAILURE(assertEqualsEventWithHistory(&event));
+
+ // Set source.
+ event.setSource(AINPUT_SOURCE_JOYSTICK);
+ ASSERT_EQ(AINPUT_SOURCE_JOYSTICK, event.getSource());
+
+ // Set action.
+ event.setAction(AMOTION_EVENT_ACTION_CANCEL);
+ ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, event.getAction());
+
+ // Set meta state.
+ event.setMetaState(AMETA_CTRL_ON);
+ ASSERT_EQ(AMETA_CTRL_ON, event.getMetaState());
+}
+
+TEST_F(MotionEventTest, CopyFrom_KeepHistory) {
+ MotionEvent event;
+ initializeEventWithHistory(&event);
+
+ MotionEvent copy;
+ copy.copyFrom(&event, true /*keepHistory*/);
+
+ ASSERT_NO_FATAL_FAILURE(assertEqualsEventWithHistory(&event));
+}
+
+TEST_F(MotionEventTest, CopyFrom_DoNotKeepHistory) {
+ MotionEvent event;
+ initializeEventWithHistory(&event);
+
+ MotionEvent copy;
+ copy.copyFrom(&event, false /*keepHistory*/);
+
+ ASSERT_EQ(event.getPointerCount(), copy.getPointerCount());
+ ASSERT_EQ(0U, copy.getHistorySize());
+
+ ASSERT_EQ(event.getPointerId(0), copy.getPointerId(0));
+ ASSERT_EQ(event.getPointerId(1), copy.getPointerId(1));
+
+ ASSERT_EQ(event.getEventTime(), copy.getEventTime());
+
+ ASSERT_EQ(event.getX(0), copy.getX(0));
+}
+
+TEST_F(MotionEventTest, OffsetLocation) {
+ MotionEvent event;
+ initializeEventWithHistory(&event);
+
+ event.offsetLocation(5.0f, -2.0f);
+
+ ASSERT_EQ(X_OFFSET + 5.0f, event.getXOffset());
+ ASSERT_EQ(Y_OFFSET - 2.0f, event.getYOffset());
+}
+
+TEST_F(MotionEventTest, Scale) {
+ MotionEvent event;
+ initializeEventWithHistory(&event);
+
+ event.scale(2.0f);
+
+ ASSERT_EQ(X_OFFSET * 2, event.getXOffset());
+ ASSERT_EQ(Y_OFFSET * 2, event.getYOffset());
+
+ ASSERT_EQ(210 * 2, event.getRawX(0));
+ ASSERT_EQ(211 * 2, event.getRawY(0));
+ ASSERT_EQ((X_OFFSET + 210) * 2, event.getX(0));
+ ASSERT_EQ((Y_OFFSET + 211) * 2, event.getY(0));
ASSERT_EQ(212, event.getPressure(0));
- ASSERT_EQ(222, event.getPressure(1));
-
- ASSERT_EQ(13, event.getHistoricalSize(0, 0));
- ASSERT_EQ(23, event.getHistoricalSize(1, 0));
- ASSERT_EQ(113, event.getHistoricalSize(0, 1));
- ASSERT_EQ(123, event.getHistoricalSize(1, 1));
ASSERT_EQ(213, event.getSize(0));
- ASSERT_EQ(223, event.getSize(1));
-
- ASSERT_EQ(14, event.getHistoricalTouchMajor(0, 0));
- ASSERT_EQ(24, event.getHistoricalTouchMajor(1, 0));
- ASSERT_EQ(114, event.getHistoricalTouchMajor(0, 1));
- ASSERT_EQ(124, event.getHistoricalTouchMajor(1, 1));
- ASSERT_EQ(214, event.getTouchMajor(0));
- ASSERT_EQ(224, event.getTouchMajor(1));
-
- ASSERT_EQ(15, event.getHistoricalTouchMinor(0, 0));
- ASSERT_EQ(25, event.getHistoricalTouchMinor(1, 0));
- ASSERT_EQ(115, event.getHistoricalTouchMinor(0, 1));
- ASSERT_EQ(125, event.getHistoricalTouchMinor(1, 1));
- ASSERT_EQ(215, event.getTouchMinor(0));
- ASSERT_EQ(225, event.getTouchMinor(1));
-
- ASSERT_EQ(16, event.getHistoricalToolMajor(0, 0));
- ASSERT_EQ(26, event.getHistoricalToolMajor(1, 0));
- ASSERT_EQ(116, event.getHistoricalToolMajor(0, 1));
- ASSERT_EQ(126, event.getHistoricalToolMajor(1, 1));
- ASSERT_EQ(216, event.getToolMajor(0));
- ASSERT_EQ(226, event.getToolMajor(1));
-
- ASSERT_EQ(17, event.getHistoricalToolMinor(0, 0));
- ASSERT_EQ(27, event.getHistoricalToolMinor(1, 0));
- ASSERT_EQ(117, event.getHistoricalToolMinor(0, 1));
- ASSERT_EQ(127, event.getHistoricalToolMinor(1, 1));
- ASSERT_EQ(217, event.getToolMinor(0));
- ASSERT_EQ(227, event.getToolMinor(1));
-
- ASSERT_EQ(18, event.getHistoricalOrientation(0, 0));
- ASSERT_EQ(28, event.getHistoricalOrientation(1, 0));
- ASSERT_EQ(118, event.getHistoricalOrientation(0, 1));
- ASSERT_EQ(128, event.getHistoricalOrientation(1, 1));
+ ASSERT_EQ(214 * 2, event.getTouchMajor(0));
+ ASSERT_EQ(215 * 2, event.getTouchMinor(0));
+ ASSERT_EQ(216 * 2, event.getToolMajor(0));
+ ASSERT_EQ(217 * 2, event.getToolMinor(0));
ASSERT_EQ(218, event.getOrientation(0));
- ASSERT_EQ(228, event.getOrientation(1));
+}
+
+TEST_F(MotionEventTest, Parcel) {
+ Parcel parcel;
+
+ MotionEvent inEvent;
+ initializeEventWithHistory(&inEvent);
+ MotionEvent outEvent;
+
+ // Round trip.
+ inEvent.writeToParcel(&parcel);
+ parcel.setDataPosition(0);
+ outEvent.readFromParcel(&parcel);
+
+ ASSERT_NO_FATAL_FAILURE(assertEqualsEventWithHistory(&outEvent));
+}
+
+TEST_F(MotionEventTest, Transform) {
+ // Generate some points on a circle.
+ // Each point 'i' is a point on a circle of radius ROTATION centered at (3,2) at an angle
+ // of ARC * i degrees clockwise relative to the Y axis.
+ // The geometrical representation is irrelevant to the test, it's just easy to generate
+ // and check rotation. We set the orientation to the same angle.
+ // Coordinate system: down is increasing Y, right is increasing X.
+ const float PI_180 = float(M_PI / 180);
+ const float RADIUS = 10;
+ const float ARC = 36;
+ const float ROTATION = ARC * 2;
+
+ const size_t pointerCount = 11;
+ int pointerIds[pointerCount];
+ PointerCoords pointerCoords[pointerCount];
+ for (size_t i = 0; i < pointerCount; i++) {
+ float angle = float(i * ARC * PI_180);
+ pointerIds[i] = i;
+ pointerCoords[i].clear();
+ pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X, sinf(angle) * RADIUS + 3);
+ pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y, -cosf(angle) * RADIUS + 2);
+ pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, angle);
+ }
+ MotionEvent event;
+ event.initialize(0, 0, AMOTION_EVENT_ACTION_MOVE, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, pointerCount, pointerIds, pointerCoords);
+ float originalRawX = 0 + 3;
+ float originalRawY = -RADIUS + 2;
+
+ // Check original raw X and Y assumption.
+ ASSERT_NEAR(originalRawX, event.getRawX(0), 0.001);
+ ASSERT_NEAR(originalRawY, event.getRawY(0), 0.001);
+
+ // Now translate the motion event so the circle's origin is at (0,0).
+ event.offsetLocation(-3, -2);
+
+ // Offsetting the location should preserve the raw X and Y of the first point.
+ ASSERT_NEAR(originalRawX, event.getRawX(0), 0.001);
+ ASSERT_NEAR(originalRawY, event.getRawY(0), 0.001);
+
+ // Apply a rotation about the origin by ROTATION degrees clockwise.
+ SkMatrix matrix;
+ matrix.setRotate(ROTATION);
+ event.transform(&matrix);
+
+ // Check the points.
+ for (size_t i = 0; i < pointerCount; i++) {
+ float angle = float((i * ARC + ROTATION) * PI_180);
+ ASSERT_NEAR(sinf(angle) * RADIUS, event.getX(i), 0.001);
+ ASSERT_NEAR(-cosf(angle) * RADIUS, event.getY(i), 0.001);
+ ASSERT_NEAR(tanf(angle), tanf(event.getOrientation(i)), 0.1);
+ }
+
+ // Applying the transformation should preserve the raw X and Y of the first point.
+ ASSERT_NEAR(originalRawX, event.getRawX(0), 0.001);
+ ASSERT_NEAR(originalRawY, event.getRawY(0), 0.001);
}
} // namespace android
diff --git a/libs/utils/Looper.cpp b/libs/utils/Looper.cpp
index a5363d6..18f858b 100644
--- a/libs/utils/Looper.cpp
+++ b/libs/utils/Looper.cpp
@@ -19,10 +19,27 @@
#include <unistd.h>
#include <fcntl.h>
+#include <limits.h>
namespace android {
+// --- WeakMessageHandler ---
+
+WeakMessageHandler::WeakMessageHandler(const wp<MessageHandler>& handler) :
+ mHandler(handler) {
+}
+
+void WeakMessageHandler::handleMessage(const Message& message) {
+ sp<MessageHandler> handler = mHandler.promote();
+ if (handler != NULL) {
+ handler->handleMessage(message);
+ }
+}
+
+
+// --- Looper ---
+
#ifdef LOOPER_USES_EPOLL
// Hint for number of file descriptors to be associated with the epoll instance.
static const int EPOLL_SIZE_HINT = 8;
@@ -35,8 +52,8 @@
static pthread_key_t gTLSKey = 0;
Looper::Looper(bool allowNonCallbacks) :
- mAllowNonCallbacks(allowNonCallbacks),
- mResponseIndex(0) {
+ mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),
+ mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
int wakeFds[2];
int result = pipe(wakeFds);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe. errno=%d", errno);
@@ -161,17 +178,21 @@
for (;;) {
while (mResponseIndex < mResponses.size()) {
const Response& response = mResponses.itemAt(mResponseIndex++);
- if (! response.request.callback) {
+ ALooper_callbackFunc callback = response.request.callback;
+ if (!callback) {
+ int ident = response.request.ident;
+ int fd = response.request.fd;
+ int events = response.events;
+ void* data = response.request.data;
#if DEBUG_POLL_AND_WAKE
LOGD("%p ~ pollOnce - returning signalled identifier %d: "
- "fd=%d, events=0x%x, data=%p", this,
- response.request.ident, response.request.fd,
- response.events, response.request.data);
+ "fd=%d, events=0x%x, data=%p",
+ this, ident, fd, events, data);
#endif
- if (outFd != NULL) *outFd = response.request.fd;
- if (outEvents != NULL) *outEvents = response.events;
- if (outData != NULL) *outData = response.request.data;
- return response.request.ident;
+ if (outFd != NULL) *outFd = fd;
+ if (outEvents != NULL) *outEvents = events;
+ if (outData != NULL) *outData = data;
+ return ident;
}
}
@@ -194,6 +215,25 @@
LOGD("%p ~ pollOnce - waiting: timeoutMillis=%d", this, timeoutMillis);
#endif
+ // Adjust the timeout based on when the next message is due.
+ if (timeoutMillis != 0 && mNextMessageUptime != LLONG_MAX) {
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ if (mNextMessageUptime <= now) {
+ timeoutMillis = 0;
+ } else {
+ uint64_t delay = (mNextMessageUptime - now + 999999LL) / 1000000LL;
+ if (delay < INT_MAX
+ && (timeoutMillis < 0 || int(delay) < timeoutMillis)) {
+ timeoutMillis = int(delay);
+ }
+ }
+#if DEBUG_POLL_AND_WAKE
+ LOGD("%p ~ pollOnce - next message in %lldns, adjusted timeout: timeoutMillis=%d",
+ this, mNextMessageUptime - now, timeoutMillis);
+#endif
+ }
+
+ // Poll.
int result = ALOOPER_POLL_WAKE;
mResponses.clear();
mResponseIndex = 0;
@@ -205,7 +245,6 @@
#ifdef LOOPER_USES_EPOLL
struct epoll_event eventItems[EPOLL_MAX_EVENTS];
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
- bool acquiredLock = false;
#else
// Wait for wakeAndLock() waiters to run then set mPolling to true.
mLock.lock();
@@ -219,16 +258,20 @@
int eventCount = poll(mRequestedFds.editArray(), requestedCount, timeoutMillis);
#endif
+ // Acquire lock.
+ mLock.lock();
+
+ // Check for poll error.
if (eventCount < 0) {
if (errno == EINTR) {
goto Done;
}
-
LOGW("Poll failed with an unexpected error, errno=%d", errno);
result = ALOOPER_POLL_ERROR;
goto Done;
}
+ // Check for poll timeout.
if (eventCount == 0) {
#if DEBUG_POLL_AND_WAKE
LOGD("%p ~ pollOnce - timeout", this);
@@ -237,6 +280,7 @@
goto Done;
}
+ // Handle all events.
#if DEBUG_POLL_AND_WAKE
LOGD("%p ~ pollOnce - handling events from %d fds", this, eventCount);
#endif
@@ -252,11 +296,6 @@
LOGW("Ignoring unexpected epoll events 0x%x on wake read pipe.", epollEvents);
}
} else {
- if (! acquiredLock) {
- mLock.lock();
- acquiredLock = true;
- }
-
ssize_t requestIndex = mRequests.indexOfKey(fd);
if (requestIndex >= 0) {
int events = 0;
@@ -271,9 +310,6 @@
}
}
}
- if (acquiredLock) {
- mLock.unlock();
- }
Done: ;
#else
for (size_t i = 0; i < requestedCount; i++) {
@@ -301,15 +337,12 @@
}
}
}
-
Done:
// Set mPolling to false and wake up the wakeAndLock() waiters.
- mLock.lock();
mPolling = false;
if (mWaiters != 0) {
mAwake.broadcast();
}
- mLock.unlock();
#endif
#ifdef LOOPER_STATISTICS
@@ -335,19 +368,59 @@
}
#endif
+ // Invoke pending message callbacks.
+ mNextMessageUptime = LLONG_MAX;
+ while (mMessageEnvelopes.size() != 0) {
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(0);
+ if (messageEnvelope.uptime <= now) {
+ // Remove the envelope from the list.
+ // We keep a strong reference to the handler until the call to handleMessage
+ // finishes. Then we drop it so that the handler can be deleted *before*
+ // we reacquire our lock.
+ { // obtain handler
+ sp<MessageHandler> handler = messageEnvelope.handler;
+ Message message = messageEnvelope.message;
+ mMessageEnvelopes.removeAt(0);
+ mSendingMessage = true;
+ mLock.unlock();
+
+#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS
+ LOGD("%p ~ pollOnce - sending message: handler=%p, what=%d",
+ this, handler.get(), message.what);
+#endif
+ handler->handleMessage(message);
+ } // release handler
+
+ mLock.lock();
+ mSendingMessage = false;
+ result = ALOOPER_POLL_CALLBACK;
+ } else {
+ // The last message left at the head of the queue determines the next wakeup time.
+ mNextMessageUptime = messageEnvelope.uptime;
+ break;
+ }
+ }
+
+ // Release lock.
+ mLock.unlock();
+
+ // Invoke all response callbacks.
for (size_t i = 0; i < mResponses.size(); i++) {
const Response& response = mResponses.itemAt(i);
- if (response.request.callback) {
+ ALooper_callbackFunc callback = response.request.callback;
+ if (callback) {
+ int fd = response.request.fd;
+ int events = response.events;
+ void* data = response.request.data;
#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS
- LOGD("%p ~ pollOnce - invoking callback: fd=%d, events=0x%x, data=%p", this,
- response.request.fd, response.events, response.request.data);
+ LOGD("%p ~ pollOnce - invoking fd event callback %p: fd=%d, events=0x%x, data=%p",
+ this, callback, fd, events, data);
#endif
- int callbackResult = response.request.callback(
- response.request.fd, response.events, response.request.data);
+ int callbackResult = callback(fd, events, data);
if (callbackResult == 0) {
- removeFd(response.request.fd);
+ removeFd(fd);
}
-
result = ALOOPER_POLL_CALLBACK;
}
}
@@ -593,4 +666,83 @@
}
#endif
+void Looper::sendMessage(const sp<MessageHandler>& handler, const Message& message) {
+ sendMessageAtTime(LLONG_MIN, handler, message);
+}
+
+void Looper::sendMessageDelayed(nsecs_t uptimeDelay, const sp<MessageHandler>& handler,
+ const Message& message) {
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ sendMessageAtTime(now + uptimeDelay, handler, message);
+}
+
+void Looper::sendMessageAtTime(nsecs_t uptime, const sp<MessageHandler>& handler,
+ const Message& message) {
+#if DEBUG_CALLBACKS
+ LOGD("%p ~ sendMessageAtTime - uptime=%lld, handler=%p, what=%d",
+ this, uptime, handler.get(), message.what);
+#endif
+
+ size_t i = 0;
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ size_t messageCount = mMessageEnvelopes.size();
+ while (i < messageCount && uptime >= mMessageEnvelopes.itemAt(i).uptime) {
+ i += 1;
+ }
+
+ MessageEnvelope messageEnvelope(uptime, handler, message);
+ mMessageEnvelopes.insertAt(messageEnvelope, i, 1);
+
+ // Optimization: If the Looper is currently sending a message, then we can skip
+ // the call to wake() because the next thing the Looper will do after processing
+ // messages is to decide when the next wakeup time should be. In fact, it does
+ // not even matter whether this code is running on the Looper thread.
+ if (mSendingMessage) {
+ return;
+ }
+ } // release lock
+
+ // Wake the poll loop only when we enqueue a new message at the head.
+ if (i == 0) {
+ wake();
+ }
+}
+
+void Looper::removeMessages(const sp<MessageHandler>& handler) {
+#if DEBUG_CALLBACKS
+ LOGD("%p ~ removeMessages - handler=%p", this, handler.get());
+#endif
+
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ for (size_t i = mMessageEnvelopes.size(); i != 0; ) {
+ const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(--i);
+ if (messageEnvelope.handler == handler) {
+ mMessageEnvelopes.removeAt(i);
+ }
+ }
+ } // release lock
+}
+
+void Looper::removeMessages(const sp<MessageHandler>& handler, int what) {
+#if DEBUG_CALLBACKS
+ LOGD("%p ~ removeMessages - handler=%p, what=%d", this, handler.get(), what);
+#endif
+
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ for (size_t i = mMessageEnvelopes.size(); i != 0; ) {
+ const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(--i);
+ if (messageEnvelope.handler == handler
+ && messageEnvelope.message.what == what) {
+ mMessageEnvelopes.removeAt(i);
+ }
+ }
+ } // release lock
+}
+
} // namespace android
diff --git a/libs/utils/RefBase.cpp b/libs/utils/RefBase.cpp
index 0bd1af4..bb6c125 100644
--- a/libs/utils/RefBase.cpp
+++ b/libs/utils/RefBase.cpp
@@ -20,9 +20,9 @@
#include <utils/Atomic.h>
#include <utils/CallStack.h>
-#include <utils/KeyedVector.h>
#include <utils/Log.h>
#include <utils/threads.h>
+#include <utils/TextOutput.h>
#include <stdlib.h>
#include <stdio.h>
@@ -34,6 +34,7 @@
// compile with refcounting debugging enabled
#define DEBUG_REFS 0
+#define DEBUG_REFS_FATAL_SANITY_CHECKS 0
#define DEBUG_REFS_ENABLED_BY_DEFAULT 1
#define DEBUG_REFS_CALLSTACK_ENABLED 1
@@ -69,8 +70,10 @@
void addStrongRef(const void* /*id*/) { }
void removeStrongRef(const void* /*id*/) { }
+ void renameStrongRefId(const void* /*old_id*/, const void* /*new_id*/) { }
void addWeakRef(const void* /*id*/) { }
void removeWeakRef(const void* /*id*/) { }
+ void renameWeakRefId(const void* /*old_id*/, const void* /*new_id*/) { }
void printRefs() const { }
void trackMe(bool, bool) { }
@@ -86,39 +89,91 @@
, mTrackEnabled(!!DEBUG_REFS_ENABLED_BY_DEFAULT)
, mRetain(false)
{
- //LOGI("NEW weakref_impl %p for RefBase %p", this, base);
}
~weakref_impl()
{
- LOG_ALWAYS_FATAL_IF(!mRetain && mStrongRefs != NULL, "Strong references remain!");
- LOG_ALWAYS_FATAL_IF(!mRetain && mWeakRefs != NULL, "Weak references remain!");
+ bool dumpStack = false;
+ if (!mRetain && mStrongRefs != NULL) {
+ dumpStack = true;
+#if DEBUG_REFS_FATAL_SANITY_CHECKS
+ LOG_ALWAYS_FATAL("Strong references remain!");
+#else
+ LOGE("Strong references remain:");
+#endif
+ ref_entry* refs = mStrongRefs;
+ while (refs) {
+ char inc = refs->ref >= 0 ? '+' : '-';
+ LOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref);
+#if DEBUG_REFS_CALLSTACK_ENABLED
+ refs->stack.dump();
+#endif;
+ refs = refs->next;
+ }
+ }
+
+ if (!mRetain && mWeakRefs != NULL) {
+ dumpStack = true;
+#if DEBUG_REFS_FATAL_SANITY_CHECKS
+ LOG_ALWAYS_FATAL("Weak references remain:");
+#else
+ LOGE("Weak references remain!");
+#endif
+ ref_entry* refs = mWeakRefs;
+ while (refs) {
+ char inc = refs->ref >= 0 ? '+' : '-';
+ LOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref);
+#if DEBUG_REFS_CALLSTACK_ENABLED
+ refs->stack.dump();
+#endif;
+ refs = refs->next;
+ }
+ }
+ if (dumpStack) {
+ LOGE("above errors at:");
+ CallStack stack;
+ stack.update();
+ stack.dump();
+ }
}
- void addStrongRef(const void* id)
- {
+ void addStrongRef(const void* id) {
+ //LOGD_IF(mTrackEnabled,
+ // "addStrongRef: RefBase=%p, id=%p", mBase, id);
addRef(&mStrongRefs, id, mStrong);
}
- void removeStrongRef(const void* id)
- {
- if (!mRetain)
+ void removeStrongRef(const void* id) {
+ //LOGD_IF(mTrackEnabled,
+ // "removeStrongRef: RefBase=%p, id=%p", mBase, id);
+ if (!mRetain) {
removeRef(&mStrongRefs, id);
- else
+ } else {
addRef(&mStrongRefs, id, -mStrong);
+ }
}
- void addWeakRef(const void* id)
- {
+ void renameStrongRefId(const void* old_id, const void* new_id) {
+ //LOGD_IF(mTrackEnabled,
+ // "renameStrongRefId: RefBase=%p, oid=%p, nid=%p",
+ // mBase, old_id, new_id);
+ renameRefsId(mStrongRefs, old_id, new_id);
+ }
+
+ void addWeakRef(const void* id) {
addRef(&mWeakRefs, id, mWeak);
}
- void removeWeakRef(const void* id)
- {
- if (!mRetain)
+ void removeWeakRef(const void* id) {
+ if (!mRetain) {
removeRef(&mWeakRefs, id);
- else
+ } else {
addRef(&mWeakRefs, id, -mWeak);
+ }
+ }
+
+ void renameWeakRefId(const void* old_id, const void* new_id) {
+ renameRefsId(mWeakRefs, old_id, new_id);
}
void trackMe(bool track, bool retain)
@@ -132,8 +187,7 @@
String8 text;
{
- AutoMutex _l(const_cast<weakref_impl*>(this)->mMutex);
-
+ Mutex::Autolock _l(const_cast<weakref_impl*>(this)->mMutex);
char buf[128];
sprintf(buf, "Strong references on RefBase %p (weakref_type %p):\n", mBase, this);
text.append(buf);
@@ -172,6 +226,7 @@
{
if (mTrackEnabled) {
AutoMutex _l(mMutex);
+
ref_entry* ref = new ref_entry;
// Reference count at the time of the snapshot, but before the
// update. Positive value means we increment, negative--we
@@ -181,7 +236,6 @@
#if DEBUG_REFS_CALLSTACK_ENABLED
ref->stack.update(2);
#endif
-
ref->next = *refs;
*refs = ref;
}
@@ -192,20 +246,52 @@
if (mTrackEnabled) {
AutoMutex _l(mMutex);
- ref_entry* ref = *refs;
+ ref_entry* const head = *refs;
+ ref_entry* ref = head;
while (ref != NULL) {
if (ref->id == id) {
*refs = ref->next;
delete ref;
return;
}
-
refs = &ref->next;
ref = *refs;
}
-
- LOG_ALWAYS_FATAL("RefBase: removing id %p on RefBase %p (weakref_type %p) that doesn't exist!",
- id, mBase, this);
+
+#if DEBUG_REFS_FATAL_SANITY_CHECKS
+ LOG_ALWAYS_FATAL("RefBase: removing id %p on RefBase %p"
+ "(weakref_type %p) that doesn't exist!",
+ id, mBase, this);
+#endif
+
+ LOGE("RefBase: removing id %p on RefBase %p"
+ "(weakref_type %p) that doesn't exist!",
+ id, mBase, this);
+
+ ref = head;
+ while (ref) {
+ char inc = ref->ref >= 0 ? '+' : '-';
+ LOGD("\t%c ID %p (ref %d):", inc, ref->id, ref->ref);
+ ref = ref->next;
+ }
+
+ CallStack stack;
+ stack.update();
+ stack.dump();
+ }
+ }
+
+ void renameRefsId(ref_entry* r, const void* old_id, const void* new_id)
+ {
+ if (mTrackEnabled) {
+ AutoMutex _l(mMutex);
+ ref_entry* ref = r;
+ while (ref != NULL) {
+ if (ref->id == old_id) {
+ ref->id = new_id;
+ }
+ ref = ref->next;
+ }
}
}
@@ -235,44 +321,6 @@
// on removeref that match the address ones.
bool mRetain;
-#if 0
- void addRef(KeyedVector<const void*, int32_t>* refs, const void* id)
- {
- AutoMutex _l(mMutex);
- ssize_t i = refs->indexOfKey(id);
- if (i >= 0) {
- ++(refs->editValueAt(i));
- } else {
- i = refs->add(id, 1);
- }
- }
-
- void removeRef(KeyedVector<const void*, int32_t>* refs, const void* id)
- {
- AutoMutex _l(mMutex);
- ssize_t i = refs->indexOfKey(id);
- LOG_ALWAYS_FATAL_IF(i < 0, "RefBase: removing id %p that doesn't exist!", id);
- if (i >= 0) {
- int32_t val = --(refs->editValueAt(i));
- if (val == 0) {
- refs->removeItemsAt(i);
- }
- }
- }
-
- void printRefs(const KeyedVector<const void*, int32_t>& refs)
- {
- const size_t N=refs.size();
- for (size_t i=0; i<N; i++) {
- printf("\tID %p: %d remain\n", refs.keyAt(i), refs.valueAt(i));
- }
- }
-
- mutable Mutex mMutex;
- KeyedVector<const void*, int32_t> mStrongRefs;
- KeyedVector<const void*, int32_t> mWeakRefs;
-#endif
-
#endif
};
@@ -281,7 +329,6 @@
void RefBase::incStrong(const void* id) const
{
weakref_impl* const refs = mRefs;
- refs->addWeakRef(id);
refs->incWeak(id);
refs->addStrongRef(id);
@@ -313,14 +360,12 @@
delete this;
}
}
- refs->removeWeakRef(id);
refs->decWeak(id);
}
void RefBase::forceIncStrong(const void* id) const
{
weakref_impl* const refs = mRefs;
- refs->addWeakRef(id);
refs->incWeak(id);
refs->addStrongRef(id);
@@ -372,7 +417,7 @@
if (impl->mStrong == INITIAL_STRONG_VALUE)
delete impl->mBase;
else {
-// LOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase);
+ // LOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase);
delete impl;
}
} else {
@@ -432,7 +477,6 @@
}
}
- impl->addWeakRef(id);
impl->addStrongRef(id);
#if PRINT_REFS
@@ -450,7 +494,7 @@
bool RefBase::weakref_type::attemptIncWeak(const void* id)
{
weakref_impl* const impl = static_cast<weakref_impl*>(this);
-
+
int32_t curCount = impl->mWeak;
LOG_ASSERT(curCount >= 0, "attemptIncWeak called on %p after underflow",
this);
@@ -497,14 +541,11 @@
RefBase::RefBase()
: mRefs(new weakref_impl(this))
{
-// LOGV("Creating refs %p with RefBase %p\n", mRefs, this);
}
RefBase::~RefBase()
{
-// LOGV("Destroying RefBase %p (refs %p)\n", this, mRefs);
if (mRefs->mWeak == 0) {
-// LOGV("Freeing refs %p of old RefBase %p\n", mRefs, this);
delete mRefs;
}
}
@@ -530,5 +571,37 @@
void RefBase::onLastWeakRef(const void* /*id*/)
{
}
-
+
+// ---------------------------------------------------------------------------
+
+void RefBase::moveReferences(void* dst, void const* src, size_t n,
+ const ReferenceConverterBase& caster)
+{
+#if DEBUG_REFS
+ const size_t itemSize = caster.getReferenceTypeSize();
+ for (size_t i=0 ; i<n ; i++) {
+ void* d = reinterpret_cast<void *>(intptr_t(dst) + i*itemSize);
+ void const* s = reinterpret_cast<void const*>(intptr_t(src) + i*itemSize);
+ RefBase* ref(reinterpret_cast<RefBase*>(caster.getReferenceBase(d)));
+ ref->mRefs->renameStrongRefId(s, d);
+ ref->mRefs->renameWeakRefId(s, d);
+ }
+#endif
+}
+
+// ---------------------------------------------------------------------------
+
+TextOutput& printStrongPointer(TextOutput& to, const void* val)
+{
+ to << "sp<>(" << val << ")";
+ return to;
+}
+
+TextOutput& printWeakPointer(TextOutput& to, const void* val)
+{
+ to << "wp<>(" << val << ")";
+ return to;
+}
+
+
}; // namespace android
diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp
index 35dcbcb..8b5da0e 100644
--- a/libs/utils/Threads.cpp
+++ b/libs/utils/Threads.cpp
@@ -675,6 +675,9 @@
mLock("Thread::mLock"),
mStatus(NO_ERROR),
mExitPending(false), mRunning(false)
+#ifdef HAVE_ANDROID_OS
+ , mTid(-1)
+#endif
{
}
@@ -715,6 +718,7 @@
res = androidCreateRawThreadEtc(_threadLoop,
this, name, priority, stack, &mThread);
}
+ // The new thread wakes up at _threadLoop, but immediately blocks on mLock
if (res == false) {
mStatus = UNKNOWN_ERROR; // something happened!
@@ -730,11 +734,19 @@
// here merely indicates successfully starting the thread and does not
// imply successful termination/execution.
return NO_ERROR;
+
+ // Exiting scope of mLock is a memory barrier and allows new thread to run
}
int Thread::_threadLoop(void* user)
{
Thread* const self = static_cast<Thread*>(user);
+
+ // force a memory barrier before reading any fields, in particular mHoldSelf
+ {
+ Mutex::Autolock _l(self->mLock);
+ }
+
sp<Thread> strong(self->mHoldSelf);
wp<Thread> weak(strong);
self->mHoldSelf.clear();
@@ -753,7 +765,7 @@
self->mStatus = self->readyToRun();
result = (self->mStatus == NO_ERROR);
- if (result && !self->mExitPending) {
+ if (result && !self->exitPending()) {
// Binder threads (and maybe others) rely on threadLoop
// running at least once after a successful ::readyToRun()
// (unless, of course, the thread has already been asked to exit
@@ -770,18 +782,21 @@
result = self->threadLoop();
}
+ // establish a scope for mLock
+ {
+ Mutex::Autolock _l(self->mLock);
if (result == false || self->mExitPending) {
self->mExitPending = true;
- self->mLock.lock();
self->mRunning = false;
// clear thread ID so that requestExitAndWait() does not exit if
// called by a new thread using the same thread ID as this one.
self->mThread = thread_id_t(-1);
+ // note that interested observers blocked in requestExitAndWait are
+ // awoken by broadcast, but blocked on mLock until break exits scope
self->mThreadExitedCondition.broadcast();
- self->mThread = thread_id_t(-1); // thread id could be reused
- self->mLock.unlock();
break;
}
+ }
// Release our strong reference, to let a chance to the thread
// to die a peaceful death.
@@ -795,6 +810,7 @@
void Thread::requestExit()
{
+ Mutex::Autolock _l(mLock);
mExitPending = true;
}
@@ -815,6 +831,8 @@
while (mRunning == true) {
mThreadExitedCondition.wait(mLock);
}
+ // This next line is probably not needed any more, but is being left for
+ // historical reference. Note that each interested party will clear flag.
mExitPending = false;
return mStatus;
@@ -822,6 +840,7 @@
bool Thread::exitPending() const
{
+ Mutex::Autolock _l(mLock);
return mExitPending;
}
diff --git a/libs/utils/tests/Looper_test.cpp b/libs/utils/tests/Looper_test.cpp
index cea1313..8bf2ba2 100644
--- a/libs/utils/tests/Looper_test.cpp
+++ b/libs/utils/tests/Looper_test.cpp
@@ -16,6 +16,13 @@
namespace android {
+enum {
+ MSG_TEST1 = 1,
+ MSG_TEST2 = 2,
+ MSG_TEST3 = 3,
+ MSG_TEST4 = 4,
+};
+
class DelayedWake : public DelayedTask {
sp<Looper> mLooper;
@@ -82,6 +89,15 @@
}
};
+class StubMessageHandler : public MessageHandler {
+public:
+ Vector<Message> messages;
+
+ virtual void handleMessage(const Message& message) {
+ messages.push(message);
+ }
+};
+
class LooperTest : public testing::Test {
protected:
sp<Looper> mLooper;
@@ -421,5 +437,257 @@
<< "replacement handler callback should be invoked";
}
+TEST_F(LooperTest, SendMessage_WhenOneMessageIsEnqueue_ShouldInvokeHandlerDuringNextPoll) {
+ sp<StubMessageHandler> handler = new StubMessageHandler();
+ mLooper->sendMessage(handler, Message(MSG_TEST1));
+
+ StopWatch stopWatch("pollOnce");
+ int result = mLooper->pollOnce(100);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should approx. zero because message was already sent";
+ EXPECT_EQ(ALOOPER_POLL_CALLBACK, result)
+ << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent";
+ EXPECT_EQ(size_t(1), handler->messages.size())
+ << "handled message";
+ EXPECT_EQ(MSG_TEST1, handler->messages[0].what)
+ << "handled message";
+}
+
+TEST_F(LooperTest, SendMessage_WhenMultipleMessagesAreEnqueued_ShouldInvokeHandlersInOrderDuringNextPoll) {
+ sp<StubMessageHandler> handler1 = new StubMessageHandler();
+ sp<StubMessageHandler> handler2 = new StubMessageHandler();
+ mLooper->sendMessage(handler1, Message(MSG_TEST1));
+ mLooper->sendMessage(handler2, Message(MSG_TEST2));
+ mLooper->sendMessage(handler1, Message(MSG_TEST3));
+ mLooper->sendMessage(handler1, Message(MSG_TEST4));
+
+ StopWatch stopWatch("pollOnce");
+ int result = mLooper->pollOnce(1000);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should approx. zero because message was already sent";
+ EXPECT_EQ(ALOOPER_POLL_CALLBACK, result)
+ << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent";
+ EXPECT_EQ(size_t(3), handler1->messages.size())
+ << "handled message";
+ EXPECT_EQ(MSG_TEST1, handler1->messages[0].what)
+ << "handled message";
+ EXPECT_EQ(MSG_TEST3, handler1->messages[1].what)
+ << "handled message";
+ EXPECT_EQ(MSG_TEST4, handler1->messages[2].what)
+ << "handled message";
+ EXPECT_EQ(size_t(1), handler2->messages.size())
+ << "handled message";
+ EXPECT_EQ(MSG_TEST2, handler2->messages[0].what)
+ << "handled message";
+}
+
+TEST_F(LooperTest, SendMessageDelayed_WhenSentToTheFuture_ShouldInvokeHandlerAfterDelayTime) {
+ sp<StubMessageHandler> handler = new StubMessageHandler();
+ mLooper->sendMessageDelayed(ms2ns(100), handler, Message(MSG_TEST1));
+
+ StopWatch stopWatch("pollOnce");
+ int result = mLooper->pollOnce(1000);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "first poll should end quickly because next message timeout was computed";
+ EXPECT_EQ(ALOOPER_POLL_WAKE, result)
+ << "pollOnce result should be ALOOPER_POLL_WAKE due to wakeup";
+ EXPECT_EQ(size_t(0), handler->messages.size())
+ << "no message handled yet";
+
+ result = mLooper->pollOnce(1000);
+ elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_EQ(size_t(1), handler->messages.size())
+ << "handled message";
+ EXPECT_EQ(MSG_TEST1, handler->messages[0].what)
+ << "handled message";
+ EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "second poll should end around the time of the delayed message dispatch";
+ EXPECT_EQ(ALOOPER_POLL_CALLBACK, result)
+ << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent";
+
+ result = mLooper->pollOnce(100);
+ elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_NEAR(100 + 100, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "third poll should timeout";
+ EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result)
+ << "pollOnce result should be ALOOPER_POLL_TIMEOUT because there were no messages left";
+}
+
+TEST_F(LooperTest, SendMessageDelayed_WhenSentToThePast_ShouldInvokeHandlerDuringNextPoll) {
+ sp<StubMessageHandler> handler = new StubMessageHandler();
+ mLooper->sendMessageDelayed(ms2ns(-1000), handler, Message(MSG_TEST1));
+
+ StopWatch stopWatch("pollOnce");
+ int result = mLooper->pollOnce(100);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should approx. zero because message was already sent";
+ EXPECT_EQ(ALOOPER_POLL_CALLBACK, result)
+ << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent";
+ EXPECT_EQ(size_t(1), handler->messages.size())
+ << "handled message";
+ EXPECT_EQ(MSG_TEST1, handler->messages[0].what)
+ << "handled message";
+}
+
+TEST_F(LooperTest, SendMessageDelayed_WhenSentToThePresent_ShouldInvokeHandlerDuringNextPoll) {
+ sp<StubMessageHandler> handler = new StubMessageHandler();
+ mLooper->sendMessageDelayed(0, handler, Message(MSG_TEST1));
+
+ StopWatch stopWatch("pollOnce");
+ int result = mLooper->pollOnce(100);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should approx. zero because message was already sent";
+ EXPECT_EQ(ALOOPER_POLL_CALLBACK, result)
+ << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent";
+ EXPECT_EQ(size_t(1), handler->messages.size())
+ << "handled message";
+ EXPECT_EQ(MSG_TEST1, handler->messages[0].what)
+ << "handled message";
+}
+
+TEST_F(LooperTest, SendMessageAtTime_WhenSentToTheFuture_ShouldInvokeHandlerAfterDelayTime) {
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ sp<StubMessageHandler> handler = new StubMessageHandler();
+ mLooper->sendMessageAtTime(now + ms2ns(100), handler, Message(MSG_TEST1));
+
+ StopWatch stopWatch("pollOnce");
+ int result = mLooper->pollOnce(1000);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "first poll should end quickly because next message timeout was computed";
+ EXPECT_EQ(ALOOPER_POLL_WAKE, result)
+ << "pollOnce result should be ALOOPER_POLL_WAKE due to wakeup";
+ EXPECT_EQ(size_t(0), handler->messages.size())
+ << "no message handled yet";
+
+ result = mLooper->pollOnce(1000);
+ elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_EQ(size_t(1), handler->messages.size())
+ << "handled message";
+ EXPECT_EQ(MSG_TEST1, handler->messages[0].what)
+ << "handled message";
+ EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "second poll should end around the time of the delayed message dispatch";
+ EXPECT_EQ(ALOOPER_POLL_CALLBACK, result)
+ << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent";
+
+ result = mLooper->pollOnce(100);
+ elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_NEAR(100 + 100, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "third poll should timeout";
+ EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result)
+ << "pollOnce result should be ALOOPER_POLL_TIMEOUT because there were no messages left";
+}
+
+TEST_F(LooperTest, SendMessageAtTime_WhenSentToThePast_ShouldInvokeHandlerDuringNextPoll) {
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ sp<StubMessageHandler> handler = new StubMessageHandler();
+ mLooper->sendMessageAtTime(now - ms2ns(1000), handler, Message(MSG_TEST1));
+
+ StopWatch stopWatch("pollOnce");
+ int result = mLooper->pollOnce(100);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should approx. zero because message was already sent";
+ EXPECT_EQ(ALOOPER_POLL_CALLBACK, result)
+ << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent";
+ EXPECT_EQ(size_t(1), handler->messages.size())
+ << "handled message";
+ EXPECT_EQ(MSG_TEST1, handler->messages[0].what)
+ << "handled message";
+}
+
+TEST_F(LooperTest, SendMessageAtTime_WhenSentToThePresent_ShouldInvokeHandlerDuringNextPoll) {
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ sp<StubMessageHandler> handler = new StubMessageHandler();
+ mLooper->sendMessageAtTime(now, handler, Message(MSG_TEST1));
+
+ StopWatch stopWatch("pollOnce");
+ int result = mLooper->pollOnce(100);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should approx. zero because message was already sent";
+ EXPECT_EQ(ALOOPER_POLL_CALLBACK, result)
+ << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent";
+ EXPECT_EQ(size_t(1), handler->messages.size())
+ << "handled message";
+ EXPECT_EQ(MSG_TEST1, handler->messages[0].what)
+ << "handled message";
+}
+
+TEST_F(LooperTest, RemoveMessage_WhenRemovingAllMessagesForHandler_ShouldRemoveThoseMessage) {
+ sp<StubMessageHandler> handler = new StubMessageHandler();
+ mLooper->sendMessage(handler, Message(MSG_TEST1));
+ mLooper->sendMessage(handler, Message(MSG_TEST2));
+ mLooper->sendMessage(handler, Message(MSG_TEST3));
+ mLooper->removeMessages(handler);
+
+ StopWatch stopWatch("pollOnce");
+ int result = mLooper->pollOnce(0);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should approx. zero because message was sent so looper was awoken";
+ EXPECT_EQ(ALOOPER_POLL_WAKE, result)
+ << "pollOnce result should be ALOOPER_POLL_WAKE because looper was awoken";
+ EXPECT_EQ(size_t(0), handler->messages.size())
+ << "no messages to handle";
+
+ result = mLooper->pollOnce(0);
+
+ EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result)
+ << "pollOnce result should be ALOOPER_POLL_TIMEOUT because there was nothing to do";
+ EXPECT_EQ(size_t(0), handler->messages.size())
+ << "no messages to handle";
+}
+
+TEST_F(LooperTest, RemoveMessage_WhenRemovingSomeMessagesForHandler_ShouldRemoveThoseMessage) {
+ sp<StubMessageHandler> handler = new StubMessageHandler();
+ mLooper->sendMessage(handler, Message(MSG_TEST1));
+ mLooper->sendMessage(handler, Message(MSG_TEST2));
+ mLooper->sendMessage(handler, Message(MSG_TEST3));
+ mLooper->sendMessage(handler, Message(MSG_TEST4));
+ mLooper->removeMessages(handler, MSG_TEST3);
+ mLooper->removeMessages(handler, MSG_TEST1);
+
+ StopWatch stopWatch("pollOnce");
+ int result = mLooper->pollOnce(0);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should approx. zero because message was sent so looper was awoken";
+ EXPECT_EQ(ALOOPER_POLL_CALLBACK, result)
+ << "pollOnce result should be ALOOPER_POLL_CALLBACK because two messages were sent";
+ EXPECT_EQ(size_t(2), handler->messages.size())
+ << "no messages to handle";
+ EXPECT_EQ(MSG_TEST2, handler->messages[0].what)
+ << "handled message";
+ EXPECT_EQ(MSG_TEST4, handler->messages[1].what)
+ << "handled message";
+
+ result = mLooper->pollOnce(0);
+
+ EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result)
+ << "pollOnce result should be ALOOPER_POLL_TIMEOUT because there was nothing to do";
+ EXPECT_EQ(size_t(2), handler->messages.size())
+ << "no more messages to handle";
+}
} // namespace android
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 0f7d639..57af001 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -341,6 +341,45 @@
drawWithOpenGL(clip, tex);
}
+// As documented in libhardware header, formats in the range
+// 0x100 - 0x1FF are specific to the HAL implementation, and
+// are known to have no alpha channel
+// TODO: move definition for device-specific range into
+// hardware.h, instead of using hard-coded values here.
+#define HARDWARE_IS_DEVICE_FORMAT(f) ((f) >= 0x100 && (f) <= 0x1FF)
+
+bool Layer::needsBlending(const sp<GraphicBuffer>& buffer) const
+{
+ // If buffers where set with eOpaque flag, all buffers are known to
+ // be opaque without having to check their actual format
+ if (mNeedsBlending && buffer != NULL) {
+ PixelFormat format = buffer->getPixelFormat();
+
+ if (HARDWARE_IS_DEVICE_FORMAT(format)) {
+ return false;
+ }
+
+ PixelFormatInfo info;
+ status_t err = getPixelFormatInfo(format, &info);
+ if (!err && info.h_alpha <= info.l_alpha) {
+ return false;
+ }
+ }
+
+ // Return opacity as determined from flags and format options
+ // passed to setBuffers()
+ return mNeedsBlending;
+}
+
+bool Layer::needsBlending() const
+{
+ if (mBufferManager.hasActiveBuffer()) {
+ return needsBlending(mBufferManager.getActiveBuffer());
+ }
+
+ return mNeedsBlending;
+}
+
bool Layer::needsFiltering() const
{
if (!(mFlags & DisplayHardware::SLOW_CONFIG)) {
@@ -588,6 +627,9 @@
// we retired a buffer, which becomes the new front buffer
const bool noActiveBuffer = !mBufferManager.hasActiveBuffer();
+ const bool activeBlending =
+ noActiveBuffer ? true : needsBlending(mBufferManager.getActiveBuffer());
+
if (mBufferManager.setActiveBufferIndex(buf) < NO_ERROR) {
LOGE("retireAndLock() buffer index (%d) out of range", int(buf));
mPostedDirtyRegion.clear();
@@ -602,6 +644,12 @@
sp<GraphicBuffer> newFrontBuffer(getBuffer(buf));
if (newFrontBuffer != NULL) {
+ if (!noActiveBuffer && activeBlending != needsBlending(newFrontBuffer)) {
+ // new buffer has different opacity than previous active buffer, need
+ // to recompute visible regions accordingly
+ recomputeVisibleRegions = true;
+ }
+
// get the dirty region
// compute the posted region
const Region dirty(lcblk->getDirtyRegion(buf));
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 2b38414..bccc900 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -75,7 +75,8 @@
virtual uint32_t doTransaction(uint32_t transactionFlags);
virtual void lockPageFlip(bool& recomputeVisibleRegions);
virtual void unlockPageFlip(const Transform& planeTransform, Region& outDirtyRegion);
- virtual bool needsBlending() const { return mNeedsBlending; }
+ virtual bool needsBlending(const sp<GraphicBuffer>& buffer) const;
+ virtual bool needsBlending() const;
virtual bool needsDithering() const { return mNeedsDithering; }
virtual bool needsFiltering() const;
virtual bool isSecure() const { return mSecure; }
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index fd3f0c2..554fa43 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2150,8 +2150,8 @@
sh = (!sh) ? hw_h : sh;
const size_t size = sw * sh * 4;
- LOGD("screenshot: sw=%d, sh=%d, minZ=%d, maxZ=%d",
- sw, sh, minLayerZ, maxLayerZ);
+ //LOGD("screenshot: sw=%d, sh=%d, minZ=%d, maxZ=%d",
+ // sw, sh, minLayerZ, maxLayerZ);
// make sure to clear all GL error flags
while ( glGetError() != GL_NO_ERROR ) ;
@@ -2236,7 +2236,7 @@
hw.compositionComplete();
- LOGD("screenshot: result = %s", result<0 ? strerror(result) : "OK");
+ // LOGD("screenshot: result = %s", result<0 ? strerror(result) : "OK");
return result;
}
@@ -2470,7 +2470,7 @@
}
break;
}
- if (++name > 31)
+ if (++name >= SharedBufferStack::NUM_LAYERS_MAX)
name = NO_MEMORY;
} while(name >= 0);