From f37f06bb155e173d36d3f8aa591b8a8ef01919e9 Mon Sep 17 00:00:00 2001 From: Paul Westbrook Date: Thu, 17 Feb 2011 10:59:56 -0800 Subject: [PATCH 01/14] Revert "turn off sqlite WAL until bugs bug:3024421 and bug:3353077 are fixed" This reverts commit d67dbeeb322eac08681fab5be09d30f9663cc128. This reenables WAL in the framework. We are tracking down the cause of the problems with WAL --- .../database/sqlite/SQLiteDatabase.java | 91 +++++++++---------- 1 file changed, 44 insertions(+), 47 deletions(-) diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java index 891a5d9d46019..2e43eef3c770a 100644 --- a/core/java/android/database/sqlite/SQLiteDatabase.java +++ b/core/java/android/database/sqlite/SQLiteDatabase.java @@ -2354,39 +2354,37 @@ public class SQLiteDatabase extends SQLiteClosable { * @return true if write-ahead-logging is set. false otherwise */ public boolean enableWriteAheadLogging() { - // turn off WAL until lockingprotocolerror bug and diskIO bug are fixed - return false; -// // make sure the database is not READONLY. WAL doesn't make sense for readonly-databases. -// if (isReadOnly()) { -// return false; -// } -// // acquire lock - no that no other thread is enabling WAL at the same time -// lock(); -// try { -// if (mConnectionPool != null) { -// // already enabled -// return true; -// } -// if (mPath.equalsIgnoreCase(MEMORY_DB_PATH)) { -// Log.i(TAG, "can't enable WAL for memory databases."); -// return false; -// } -// -// // make sure this database has NO attached databases because sqlite's write-ahead-logging -// // doesn't work for databases with attached databases -// if (mHasAttachedDbs) { -// if (Log.isLoggable(TAG, Log.DEBUG)) { -// Log.d(TAG, -// "this database: " + mPath + " has attached databases. can't enable WAL."); -// } -// return false; -// } -// mConnectionPool = new DatabaseConnectionPool(this); -// setJournalMode(mPath, "WAL"); -// return true; -// } finally { -// unlock(); -// } + // make sure the database is not READONLY. WAL doesn't make sense for readonly-databases. + if (isReadOnly()) { + return false; + } + // acquire lock - no that no other thread is enabling WAL at the same time + lock(); + try { + if (mConnectionPool != null) { + // already enabled + return true; + } + if (mPath.equalsIgnoreCase(MEMORY_DB_PATH)) { + Log.i(TAG, "can't enable WAL for memory databases."); + return false; + } + + // make sure this database has NO attached databases because sqlite's write-ahead-logging + // doesn't work for databases with attached databases + if (mHasAttachedDbs) { + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, + "this database: " + mPath + " has attached databases. can't enable WAL."); + } + return false; + } + mConnectionPool = new DatabaseConnectionPool(this); + setJournalMode(mPath, "WAL"); + return true; + } finally { + unlock(); + } } /** @@ -2394,20 +2392,19 @@ public class SQLiteDatabase extends SQLiteClosable { * @hide */ public void disableWriteAheadLogging() { - return; -// // grab database lock so that writeAheadLogging is not disabled from 2 different threads -// // at the same time -// lock(); -// try { -// if (mConnectionPool == null) { -// return; // already disabled -// } -// mConnectionPool.close(); -// setJournalMode(mPath, "TRUNCATE"); -// mConnectionPool = null; -// } finally { -// unlock(); -// } + // grab database lock so that writeAheadLogging is not disabled from 2 different threads + // at the same time + lock(); + try { + if (mConnectionPool == null) { + return; // already disabled + } + mConnectionPool.close(); + setJournalMode(mPath, "TRUNCATE"); + mConnectionPool = null; + } finally { + unlock(); + } } /* package */ SQLiteDatabase getDatabaseHandle(String sql) { From 2643990cee361a78008dc47e23b496a251fe851e Mon Sep 17 00:00:00 2001 From: Adam Powell Date: Thu, 17 Feb 2011 12:03:09 -0800 Subject: [PATCH 02/14] Fix bug 3465210 - Anchored popups don't appear above anchor when there isn't enough room. Oops. Fix a regression introduced by recent changes. Change-Id: I413998dc2413763f2ea3f31a7ca68f098be19b36 --- core/java/android/widget/PopupWindow.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java index 53932af654042..4b858d0dd1544 100644 --- a/core/java/android/widget/PopupWindow.java +++ b/core/java/android/widget/PopupWindow.java @@ -1056,16 +1056,17 @@ public class PopupWindow { anchor.getWindowVisibleDisplayFrame(displayFrame); final View root = anchor.getRootView(); - if (mAllowScrollingAnchorParent && (p.y + mPopupHeight > displayFrame.bottom || - p.x + mPopupWidth - root.getWidth() > 0)) { + if (p.y + mPopupHeight > displayFrame.bottom || p.x + mPopupWidth - root.getWidth() > 0) { // if the drop down disappears at the bottom of the screen. we try to // scroll a parent scrollview or move the drop down back up on top of // the edit box - int scrollX = anchor.getScrollX(); - int scrollY = anchor.getScrollY(); - Rect r = new Rect(scrollX, scrollY, scrollX + mPopupWidth + xoff, - scrollY + mPopupHeight + anchor.getHeight() + yoff); - anchor.requestRectangleOnScreen(r, true); + if (mAllowScrollingAnchorParent) { + int scrollX = anchor.getScrollX(); + int scrollY = anchor.getScrollY(); + Rect r = new Rect(scrollX, scrollY, scrollX + mPopupWidth + xoff, + scrollY + mPopupHeight + anchor.getHeight() + yoff); + anchor.requestRectangleOnScreen(r, true); + } // now we re-evaluate the space available, and decide from that // whether the pop-up will go above or below the anchor. From 9fdc8d5ebe808fe88188ee9dbc35fb93bef0cc6c Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Thu, 17 Feb 2011 13:01:34 -0800 Subject: [PATCH 03/14] Fix a regression with MotionEvent parceling. Also added some more unit tests. Change-Id: I413654294d1a998eec056884e6df5eaa50f3daf4 --- core/jni/android_view_MotionEvent.cpp | 58 +-- include/ui/Input.h | 51 +-- libs/ui/Input.cpp | 30 +- libs/ui/tests/Android.mk | 7 +- libs/ui/tests/InputChannel_test.cpp | 18 +- libs/ui/tests/InputEvent_test.cpp | 417 ++++++++++++++++++ .../tests/InputPublisherAndConsumer_test.cpp | 80 ++-- native/include/android/input.h | 50 +-- services/input/InputDispatcher.cpp | 44 +- services/input/InputReader.cpp | 36 +- services/input/tests/InputReader_test.cpp | 38 +- 11 files changed, 637 insertions(+), 192 deletions(-) create mode 100644 libs/ui/tests/InputEvent_test.cpp diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp index 99fbbe9792980..069e40b50e461 100644 --- a/core/jni/android_view_MotionEvent.cpp +++ b/core/jni/android_view_MotionEvent.cpp @@ -188,23 +188,23 @@ static bool validatePointerCoords(JNIEnv* env, jobject pointerCoordsObj) { static void pointerCoordsToNative(JNIEnv* env, jobject pointerCoordsObj, float xOffset, float yOffset, PointerCoords* outRawPointerCoords) { outRawPointerCoords->clear(); - outRawPointerCoords->setAxisValue(AINPUT_MOTION_AXIS_X, + outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_X, env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.x) - xOffset); - outRawPointerCoords->setAxisValue(AINPUT_MOTION_AXIS_Y, + outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_Y, env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.y) - yOffset); - outRawPointerCoords->setAxisValue(AINPUT_MOTION_AXIS_PRESSURE, + outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.pressure)); - outRawPointerCoords->setAxisValue(AINPUT_MOTION_AXIS_SIZE, + outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_SIZE, env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.size)); - outRawPointerCoords->setAxisValue(AINPUT_MOTION_AXIS_TOUCH_MAJOR, + outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.touchMajor)); - outRawPointerCoords->setAxisValue(AINPUT_MOTION_AXIS_TOUCH_MINOR, + outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.touchMinor)); - outRawPointerCoords->setAxisValue(AINPUT_MOTION_AXIS_TOOL_MAJOR, + outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.toolMajor)); - outRawPointerCoords->setAxisValue(AINPUT_MOTION_AXIS_TOOL_MINOR, + outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.toolMinor)); - outRawPointerCoords->setAxisValue(AINPUT_MOTION_AXIS_ORIENTATION, + outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.orientation)); uint32_t bits = env->GetIntField(pointerCoordsObj, @@ -254,34 +254,34 @@ static jfloatArray obtainPackedAxisValuesArray(JNIEnv* env, uint32_t minSize, static void pointerCoordsFromNative(JNIEnv* env, const PointerCoords* rawPointerCoords, float xOffset, float yOffset, jobject outPointerCoordsObj) { env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.x, - rawPointerCoords->getAxisValue(AINPUT_MOTION_AXIS_X) + xOffset); + rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_X) + xOffset); env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.y, - rawPointerCoords->getAxisValue(AINPUT_MOTION_AXIS_Y) + yOffset); + rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_Y) + yOffset); env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.pressure, - rawPointerCoords->getAxisValue(AINPUT_MOTION_AXIS_PRESSURE)); + rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_PRESSURE)); env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.size, - rawPointerCoords->getAxisValue(AINPUT_MOTION_AXIS_SIZE)); + rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_SIZE)); env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.touchMajor, - rawPointerCoords->getAxisValue(AINPUT_MOTION_AXIS_TOUCH_MAJOR)); + rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR)); env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.touchMinor, - rawPointerCoords->getAxisValue(AINPUT_MOTION_AXIS_TOUCH_MINOR)); + rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR)); env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.toolMajor, - rawPointerCoords->getAxisValue(AINPUT_MOTION_AXIS_TOOL_MAJOR)); + rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR)); env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.toolMinor, - rawPointerCoords->getAxisValue(AINPUT_MOTION_AXIS_TOOL_MINOR)); + rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR)); env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.orientation, - rawPointerCoords->getAxisValue(AINPUT_MOTION_AXIS_ORIENTATION)); + rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION)); const uint32_t unpackedAxisBits = 0 - | (1 << AINPUT_MOTION_AXIS_X) - | (1 << AINPUT_MOTION_AXIS_Y) - | (1 << AINPUT_MOTION_AXIS_PRESSURE) - | (1 << AINPUT_MOTION_AXIS_SIZE) - | (1 << AINPUT_MOTION_AXIS_TOUCH_MAJOR) - | (1 << AINPUT_MOTION_AXIS_TOUCH_MINOR) - | (1 << AINPUT_MOTION_AXIS_TOOL_MAJOR) - | (1 << AINPUT_MOTION_AXIS_TOOL_MINOR) - | (1 << AINPUT_MOTION_AXIS_ORIENTATION); + | (1 << AMOTION_EVENT_AXIS_X) + | (1 << AMOTION_EVENT_AXIS_Y) + | (1 << AMOTION_EVENT_AXIS_PRESSURE) + | (1 << AMOTION_EVENT_AXIS_SIZE) + | (1 << AMOTION_EVENT_AXIS_TOUCH_MAJOR) + | (1 << AMOTION_EVENT_AXIS_TOUCH_MINOR) + | (1 << AMOTION_EVENT_AXIS_TOOL_MAJOR) + | (1 << AMOTION_EVENT_AXIS_TOOL_MINOR) + | (1 << AMOTION_EVENT_AXIS_ORIENTATION); uint32_t outBits = 0; uint32_t remainingBits = rawPointerCoords->bits & ~unpackedAxisBits; @@ -610,7 +610,7 @@ static jint android_view_MotionEvent_nativeReadFromParcel(JNIEnv* env, jclass cl Parcel* parcel = parcelForJavaObject(env, parcelObj); status_t status = event->readFromParcel(parcel); - if (!status) { + if (status) { if (!nativePtr) { delete event; } @@ -626,7 +626,7 @@ static void android_view_MotionEvent_nativeWriteToParcel(JNIEnv* env, jclass cla Parcel* parcel = parcelForJavaObject(env, parcelObj); status_t status = event->writeToParcel(parcel); - if (!status) { + if (status) { jniThrowRuntimeException(env, "Failed to write MotionEvent parcel."); } } diff --git a/include/ui/Input.h b/include/ui/Input.h index b7b5a8d931139..cb9327e1210cb 100644 --- a/include/ui/Input.h +++ b/include/ui/Input.h @@ -170,7 +170,7 @@ struct InputConfiguration { * Pointer coordinate data. */ struct PointerCoords { - static const size_t MAX_AXES = 15; // 15 so that sizeof(PointerCoords) == 16 * 4 == 64 + enum { MAX_AXES = 15 }; // 15 so that sizeof(PointerCoords) == 16 * 4 == 64 // Bitfield of axes that are present in this structure. uint32_t bits; // 32bits are enough for now, can raise to 64bit when needed @@ -192,14 +192,14 @@ struct PointerCoords { return values[index]; } - inline void setAxisValue(int32_t axis, float value) { + 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; + return NO_MEMORY; } bits |= axisBit; for (uint32_t i = count; i > index; i--) { @@ -207,6 +207,7 @@ struct PointerCoords { } } values[index] = value; + return OK; } inline float* editAxisValue(int32_t axis) { @@ -351,49 +352,49 @@ public: float getRawAxisValue(int32_t axis, size_t pointerIndex) const; inline float getRawX(size_t pointerIndex) const { - return getRawAxisValue(AINPUT_MOTION_AXIS_X, pointerIndex); + return getRawAxisValue(AMOTION_EVENT_AXIS_X, pointerIndex); } inline float getRawY(size_t pointerIndex) const { - return getRawAxisValue(AINPUT_MOTION_AXIS_Y, pointerIndex); + return getRawAxisValue(AMOTION_EVENT_AXIS_Y, pointerIndex); } float getAxisValue(int32_t axis, size_t pointerIndex) const; inline float getX(size_t pointerIndex) const { - return getAxisValue(AINPUT_MOTION_AXIS_X, pointerIndex); + return getAxisValue(AMOTION_EVENT_AXIS_X, pointerIndex); } inline float getY(size_t pointerIndex) const { - return getAxisValue(AINPUT_MOTION_AXIS_Y, pointerIndex); + return getAxisValue(AMOTION_EVENT_AXIS_Y, pointerIndex); } inline float getPressure(size_t pointerIndex) const { - return getAxisValue(AINPUT_MOTION_AXIS_PRESSURE, pointerIndex); + return getAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pointerIndex); } inline float getSize(size_t pointerIndex) const { - return getAxisValue(AINPUT_MOTION_AXIS_SIZE, pointerIndex); + return getAxisValue(AMOTION_EVENT_AXIS_SIZE, pointerIndex); } inline float getTouchMajor(size_t pointerIndex) const { - return getAxisValue(AINPUT_MOTION_AXIS_TOUCH_MAJOR, pointerIndex); + return getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, pointerIndex); } inline float getTouchMinor(size_t pointerIndex) const { - return getAxisValue(AINPUT_MOTION_AXIS_TOUCH_MINOR, pointerIndex); + return getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, pointerIndex); } inline float getToolMajor(size_t pointerIndex) const { - return getAxisValue(AINPUT_MOTION_AXIS_TOOL_MAJOR, pointerIndex); + return getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, pointerIndex); } inline float getToolMinor(size_t pointerIndex) const { - return getAxisValue(AINPUT_MOTION_AXIS_TOOL_MINOR, pointerIndex); + return getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, pointerIndex); } inline float getOrientation(size_t pointerIndex) const { - return getAxisValue(AINPUT_MOTION_AXIS_ORIENTATION, pointerIndex); + return getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, pointerIndex); } inline size_t getHistorySize() const { return mSampleEventTimes.size() - 1; } @@ -410,59 +411,59 @@ public: inline float getHistoricalRawX(size_t pointerIndex, size_t historicalIndex) const { return getHistoricalRawAxisValue( - AINPUT_MOTION_AXIS_X, pointerIndex, historicalIndex); + AMOTION_EVENT_AXIS_X, pointerIndex, historicalIndex); } inline float getHistoricalRawY(size_t pointerIndex, size_t historicalIndex) const { return getHistoricalRawAxisValue( - AINPUT_MOTION_AXIS_Y, pointerIndex, historicalIndex); + AMOTION_EVENT_AXIS_Y, pointerIndex, historicalIndex); } float getHistoricalAxisValue(int32_t axis, size_t pointerIndex, size_t historicalIndex) const; inline float getHistoricalX(size_t pointerIndex, size_t historicalIndex) const { return getHistoricalAxisValue( - AINPUT_MOTION_AXIS_X, pointerIndex, historicalIndex); + AMOTION_EVENT_AXIS_X, pointerIndex, historicalIndex); } inline float getHistoricalY(size_t pointerIndex, size_t historicalIndex) const { return getHistoricalAxisValue( - AINPUT_MOTION_AXIS_Y, pointerIndex, historicalIndex); + AMOTION_EVENT_AXIS_Y, pointerIndex, historicalIndex); } inline float getHistoricalPressure(size_t pointerIndex, size_t historicalIndex) const { return getHistoricalAxisValue( - AINPUT_MOTION_AXIS_PRESSURE, pointerIndex, historicalIndex); + AMOTION_EVENT_AXIS_PRESSURE, pointerIndex, historicalIndex); } inline float getHistoricalSize(size_t pointerIndex, size_t historicalIndex) const { return getHistoricalAxisValue( - AINPUT_MOTION_AXIS_SIZE, pointerIndex, historicalIndex); + AMOTION_EVENT_AXIS_SIZE, pointerIndex, historicalIndex); } inline float getHistoricalTouchMajor(size_t pointerIndex, size_t historicalIndex) const { return getHistoricalAxisValue( - AINPUT_MOTION_AXIS_TOUCH_MAJOR, pointerIndex, historicalIndex); + AMOTION_EVENT_AXIS_TOUCH_MAJOR, pointerIndex, historicalIndex); } inline float getHistoricalTouchMinor(size_t pointerIndex, size_t historicalIndex) const { return getHistoricalAxisValue( - AINPUT_MOTION_AXIS_TOUCH_MINOR, pointerIndex, historicalIndex); + AMOTION_EVENT_AXIS_TOUCH_MINOR, pointerIndex, historicalIndex); } inline float getHistoricalToolMajor(size_t pointerIndex, size_t historicalIndex) const { return getHistoricalAxisValue( - AINPUT_MOTION_AXIS_TOOL_MAJOR, pointerIndex, historicalIndex); + AMOTION_EVENT_AXIS_TOOL_MAJOR, pointerIndex, historicalIndex); } inline float getHistoricalToolMinor(size_t pointerIndex, size_t historicalIndex) const { return getHistoricalAxisValue( - AINPUT_MOTION_AXIS_TOOL_MINOR, pointerIndex, historicalIndex); + AMOTION_EVENT_AXIS_TOOL_MINOR, pointerIndex, historicalIndex); } inline float getHistoricalOrientation(size_t pointerIndex, size_t historicalIndex) const { return getHistoricalAxisValue( - AINPUT_MOTION_AXIS_ORIENTATION, pointerIndex, historicalIndex); + AMOTION_EVENT_AXIS_ORIENTATION, pointerIndex, historicalIndex); } void initialize( diff --git a/libs/ui/Input.cpp b/libs/ui/Input.cpp index 90b954e037622..e3107d50fd8b5 100644 --- a/libs/ui/Input.cpp +++ b/libs/ui/Input.cpp @@ -362,10 +362,10 @@ float MotionEvent::getRawAxisValue(int32_t axis, size_t pointerIndex) const { float MotionEvent::getAxisValue(int32_t axis, size_t pointerIndex) const { float value = getRawPointerCoords(pointerIndex)->getAxisValue(axis); switch (axis) { - case AINPUT_MOTION_AXIS_X: + case AMOTION_EVENT_AXIS_X: value += mXOffset; break; - case AINPUT_MOTION_AXIS_Y: + case AMOTION_EVENT_AXIS_Y: value += mYOffset; break; } @@ -386,10 +386,10 @@ float MotionEvent::getHistoricalAxisValue(int32_t axis, size_t pointerIndex, size_t historicalIndex) const { float value = getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis); switch (axis) { - case AINPUT_MOTION_AXIS_X: + case AMOTION_EVENT_AXIS_X: value += mXOffset; break; - case AINPUT_MOTION_AXIS_Y: + case AMOTION_EVENT_AXIS_Y: value += mYOffset; break; } @@ -419,12 +419,12 @@ void MotionEvent::scale(float scaleFactor) { PointerCoords& c = mSamplePointerCoords.editItemAt(i); // No need to scale pressure or size since they are normalized. // No need to scale orientation since it is meaningless to do so. - scaleAxisValue(c, AINPUT_MOTION_AXIS_X, scaleFactor); - scaleAxisValue(c, AINPUT_MOTION_AXIS_Y, scaleFactor); - scaleAxisValue(c, AINPUT_MOTION_AXIS_TOUCH_MAJOR, scaleFactor); - scaleAxisValue(c, AINPUT_MOTION_AXIS_TOUCH_MINOR, scaleFactor); - scaleAxisValue(c, AINPUT_MOTION_AXIS_TOOL_MAJOR, scaleFactor); - scaleAxisValue(c, AINPUT_MOTION_AXIS_TOOL_MINOR, scaleFactor); + scaleAxisValue(c, AMOTION_EVENT_AXIS_X, scaleFactor); + scaleAxisValue(c, AMOTION_EVENT_AXIS_Y, scaleFactor); + scaleAxisValue(c, AMOTION_EVENT_AXIS_TOUCH_MAJOR, scaleFactor); + scaleAxisValue(c, AMOTION_EVENT_AXIS_TOUCH_MINOR, scaleFactor); + scaleAxisValue(c, AMOTION_EVENT_AXIS_TOOL_MAJOR, scaleFactor); + scaleAxisValue(c, AMOTION_EVENT_AXIS_TOOL_MINOR, scaleFactor); } } @@ -471,8 +471,8 @@ void MotionEvent::transform(const SkMatrix* matrix) { size_t numSamples = mSamplePointerCoords.size(); for (size_t i = 0; i < numSamples; i++) { PointerCoords& c = mSamplePointerCoords.editItemAt(i); - float* xPtr = c.editAxisValue(AINPUT_MOTION_AXIS_X); - float* yPtr = c.editAxisValue(AINPUT_MOTION_AXIS_Y); + float* xPtr = c.editAxisValue(AMOTION_EVENT_AXIS_X); + float* yPtr = c.editAxisValue(AMOTION_EVENT_AXIS_Y); if (xPtr && yPtr) { float x = *xPtr + oldXOffset; float y = *yPtr + oldYOffset; @@ -481,7 +481,7 @@ void MotionEvent::transform(const SkMatrix* matrix) { *yPtr = SkScalarToFloat(point.fY) - newYOffset; } - float* orientationPtr = c.editAxisValue(AINPUT_MOTION_AXIS_ORIENTATION); + float* orientationPtr = c.editAxisValue(AMOTION_EVENT_AXIS_ORIENTATION); if (orientationPtr) { *orientationPtr = transformAngle(matrix, *orientationPtr); } @@ -523,7 +523,7 @@ status_t MotionEvent::readFromParcel(Parcel* parcel) { for (size_t i = 0; i < pointerCount; i++) { mSamplePointerCoords.push(); status_t status = mSamplePointerCoords.editTop().readFromParcel(parcel); - if (!status) { + if (status) { return status; } } @@ -559,7 +559,7 @@ status_t MotionEvent::writeToParcel(Parcel* parcel) const { parcel->writeInt64(mSampleEventTimes.itemAt(h)); for (size_t i = 0; i < pointerCount; i++) { status_t status = (pc++)->writeToParcel(parcel); - if (!status) { + if (status) { return status; } } diff --git a/libs/ui/tests/Android.mk b/libs/ui/tests/Android.mk index 580d73cf384c0..e23197185c850 100644 --- a/libs/ui/tests/Android.mk +++ b/libs/ui/tests/Android.mk @@ -7,6 +7,7 @@ ifneq ($(TARGET_SIMULATOR),true) # Build the unit tests. test_src_files := \ InputChannel_test.cpp \ + InputEvent_test.cpp \ InputPublisherAndConsumer_test.cpp shared_libraries := \ @@ -18,7 +19,8 @@ shared_libraries := \ libhardware \ libhardware_legacy \ libui \ - libstlport + libstlport \ + libskia static_libraries := \ libgtest \ @@ -28,7 +30,8 @@ c_includes := \ bionic \ bionic/libstdc++/include \ external/gtest/include \ - external/stlport/stlport + external/stlport/stlport \ + external/skia/include/core module_tags := eng tests diff --git a/libs/ui/tests/InputChannel_test.cpp b/libs/ui/tests/InputChannel_test.cpp index 6cec1c02ea95b..eff22ee5f0eb4 100644 --- a/libs/ui/tests/InputChannel_test.cpp +++ b/libs/ui/tests/InputChannel_test.cpp @@ -1,6 +1,18 @@ -// -// Copyright 2010 The Android Open Source Project -// +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include #include diff --git a/libs/ui/tests/InputEvent_test.cpp b/libs/ui/tests/InputEvent_test.cpp new file mode 100644 index 0000000000000..7b15c381e63fb --- /dev/null +++ b/libs/ui/tests/InputEvent_test.cpp @@ -0,0 +1,417 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +namespace android { + +class BaseTest : public testing::Test { +protected: + virtual void SetUp() { } + virtual void TearDown() { } +}; + +// --- PointerCoordsTest --- + +class PointerCoordsTest : public BaseTest { +}; + +TEST_F(PointerCoordsTest, ClearSetsBitsToZero) { + PointerCoords coords; + coords.clear(); + + ASSERT_EQ(0U, coords.bits); +} + +TEST_F(PointerCoordsTest, AxisValues) { + float* valuePtr; + PointerCoords coords; + coords.clear(); + + // Check invariants when no axes are present. + ASSERT_EQ(0, coords.getAxisValue(0)) + << "getAxisValue should return zero because axis is not present"; + ASSERT_EQ(0, coords.getAxisValue(1)) + << "getAxisValue should return zero because axis is not present"; + + ASSERT_EQ(NULL, coords.editAxisValue(0)) + << "editAxisValue should return null because axis is not present"; + + // Set first axis. + ASSERT_EQ(OK, coords.setAxisValue(1, 5)); + ASSERT_EQ(0x00000002U, coords.bits); + ASSERT_EQ(5, coords.values[0]); + + ASSERT_EQ(0, coords.getAxisValue(0)) + << "getAxisValue should return zero because axis is not present"; + ASSERT_EQ(5, coords.getAxisValue(1)) + << "getAxisValue should return value of axis"; + + // 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(5, coords.values[0]); + ASSERT_EQ(2, coords.values[1]); + + ASSERT_EQ(0, coords.getAxisValue(0)) + << "getAxisValue should return zero because axis is not present"; + ASSERT_EQ(5, coords.getAxisValue(1)) + << "getAxisValue should return value of axis"; + ASSERT_EQ(0, coords.getAxisValue(2)) + << "getAxisValue should return zero because axis is not present"; + ASSERT_EQ(2, coords.getAxisValue(3)) + << "getAxisValue should return value of axis"; + + // 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(4, coords.values[0]); + ASSERT_EQ(5, coords.values[1]); + ASSERT_EQ(2, coords.values[2]); + + ASSERT_EQ(4, coords.getAxisValue(0)) + << "getAxisValue should return value of axis"; + ASSERT_EQ(5, coords.getAxisValue(1)) + << "getAxisValue should return value of axis"; + ASSERT_EQ(0, coords.getAxisValue(2)) + << "getAxisValue should return zero because axis is not present"; + ASSERT_EQ(2, coords.getAxisValue(3)) + << "getAxisValue should return value of axis"; + + // Edit an existing axis value in place. + valuePtr = coords.editAxisValue(1); + ASSERT_EQ(5, *valuePtr) + << "editAxisValue should return pointer to axis value"; + + *valuePtr = 7; + ASSERT_EQ(7, coords.getAxisValue(1)) + << "getAxisValue should return value of axis"; + + // 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(4, coords.values[0]); + ASSERT_EQ(7, coords.values[1]); + ASSERT_EQ(1, coords.values[2]); + ASSERT_EQ(2, coords.values[3]); + + ASSERT_EQ(4, coords.getAxisValue(0)) + << "getAxisValue should return value of axis"; + ASSERT_EQ(7, coords.getAxisValue(1)) + << "getAxisValue should return value of axis"; + ASSERT_EQ(1, coords.getAxisValue(2)) + << "getAxisValue should return value of axis"; + ASSERT_EQ(2, coords.getAxisValue(3)) + << "getAxisValue should return value of axis"; + + // Set an existing axis value in place. + ASSERT_EQ(OK, coords.setAxisValue(1, 6)); + ASSERT_EQ(0x0000000fU, coords.bits); + ASSERT_EQ(4, coords.values[0]); + ASSERT_EQ(6, coords.values[1]); + ASSERT_EQ(1, coords.values[2]); + ASSERT_EQ(2, coords.values[3]); + + ASSERT_EQ(4, coords.getAxisValue(0)) + << "getAxisValue should return value of axis"; + ASSERT_EQ(6, coords.getAxisValue(1)) + << "getAxisValue should return value of axis"; + ASSERT_EQ(1, coords.getAxisValue(2)) + << "getAxisValue should return value of axis"; + ASSERT_EQ(2, coords.getAxisValue(3)) + << "getAxisValue should return value of axis"; + + // Set maximum number of axes. + 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)); + + // 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)); +} + +TEST_F(PointerCoordsTest, ReadAndWriteParcel) { + Parcel parcel; + + PointerCoords inCoords; + inCoords.clear(); + PointerCoords outCoords; + + // Round trip with empty coords. + inCoords.writeToParcel(&parcel); + parcel.setDataPosition(0); + outCoords.readFromParcel(&parcel); + + ASSERT_EQ(0U, outCoords.bits); + + // Round trip with some values. + parcel.freeData(); + inCoords.setAxisValue(2, 5); + inCoords.setAxisValue(5, 8); + + inCoords.writeToParcel(&parcel); + parcel.setDataPosition(0); + outCoords.readFromParcel(&parcel); + + ASSERT_EQ(outCoords.bits, inCoords.bits); + ASSERT_EQ(outCoords.values[0], inCoords.values[0]); + ASSERT_EQ(outCoords.values[1], inCoords.values[1]); +} + + +// --- KeyEventTest --- + +class KeyEventTest : public BaseTest { +}; + +TEST_F(KeyEventTest, Properties) { + KeyEvent event; + + // Initialize and get properties. + const nsecs_t ARBITRARY_DOWN_TIME = 1; + const nsecs_t ARBITRARY_EVENT_TIME = 2; + event.initialize(2, AINPUT_SOURCE_GAMEPAD, AKEY_EVENT_ACTION_DOWN, + AKEY_EVENT_FLAG_FROM_SYSTEM, AKEYCODE_BUTTON_X, 121, + AMETA_ALT_ON, 1, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME); + + ASSERT_EQ(AINPUT_EVENT_TYPE_KEY, event.getType()); + ASSERT_EQ(2, event.getDeviceId()); + ASSERT_EQ(AINPUT_SOURCE_GAMEPAD, event.getSource()); + ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, event.getAction()); + ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, event.getFlags()); + ASSERT_EQ(AKEYCODE_BUTTON_X, event.getKeyCode()); + ASSERT_EQ(121, event.getScanCode()); + ASSERT_EQ(AMETA_ALT_ON, event.getMetaState()); + ASSERT_EQ(1, event.getRepeatCount()); + ASSERT_EQ(ARBITRARY_DOWN_TIME, event.getDownTime()); + ASSERT_EQ(ARBITRARY_EVENT_TIME, event.getEventTime()); + + // Set source. + event.setSource(AINPUT_SOURCE_JOYSTICK); + ASSERT_EQ(AINPUT_SOURCE_JOYSTICK, event.getSource()); +} + + +// --- MotionEventTest --- + +class MotionEventTest : public BaseTest { +}; + +TEST_F(MotionEventTest, Properties) { + MotionEvent event; + + // 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; + int32_t pointerIds[] = { 1, 2 }; + PointerCoords pointerCoords[2]; + pointerCoords[0].clear(); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 10); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 11); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 12); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 13); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 14); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 15); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 16); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 17); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 18); + pointerCoords[1].clear(); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 20); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 21); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 22); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 23); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 24); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 25); + 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, + AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, + AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, + X_OFFSET, Y_OFFSET, 2.0f, 2.1f, + ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, + 2, pointerIds, pointerCoords); + + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 110); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 111); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 112); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 113); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 114); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 115); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 116); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 117); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 118); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 120); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 121); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 122); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 123); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 124); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 125); + 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); + + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 210); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 211); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 212); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 213); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 214); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 215); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 216); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 217); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 218); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 220); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 221); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 222); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 223); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 224); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 225); + 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); + + 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.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()); + + ASSERT_EQ(11, event.getHistoricalRawPointerCoords(0, 0)-> + getAxisValue(AMOTION_EVENT_AXIS_Y)); + ASSERT_EQ(21, event.getHistoricalRawPointerCoords(1, 0)-> + getAxisValue(AMOTION_EVENT_AXIS_Y)); + ASSERT_EQ(111, event.getHistoricalRawPointerCoords(0, 1)-> + getAxisValue(AMOTION_EVENT_AXIS_Y)); + ASSERT_EQ(121, event.getHistoricalRawPointerCoords(1, 1)-> + getAxisValue(AMOTION_EVENT_AXIS_Y)); + ASSERT_EQ(211, event.getRawPointerCoords(0)-> + getAxisValue(AMOTION_EVENT_AXIS_Y)); + 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(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(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(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)); +} + +} // namespace android diff --git a/libs/ui/tests/InputPublisherAndConsumer_test.cpp b/libs/ui/tests/InputPublisherAndConsumer_test.cpp index 1819a8be1698d..6e18a4f1abb6d 100644 --- a/libs/ui/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/ui/tests/InputPublisherAndConsumer_test.cpp @@ -1,6 +1,18 @@ -// -// Copyright 2010 The Android Open Source Project -// +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include #include @@ -161,15 +173,15 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent( samplePointerCoords.push(); PointerCoords& pc = samplePointerCoords.editTop(); pc.clear(); - pc.setAxisValue(AINPUT_MOTION_AXIS_X, 100 * i + j); - pc.setAxisValue(AINPUT_MOTION_AXIS_Y, 200 * i + j); - pc.setAxisValue(AINPUT_MOTION_AXIS_PRESSURE, 0.5 * i + j); - pc.setAxisValue(AINPUT_MOTION_AXIS_SIZE, 0.7 * i + j); - pc.setAxisValue(AINPUT_MOTION_AXIS_TOUCH_MAJOR, 1.5 * i + j); - pc.setAxisValue(AINPUT_MOTION_AXIS_TOUCH_MINOR, 1.7 * i + j); - pc.setAxisValue(AINPUT_MOTION_AXIS_TOOL_MAJOR, 2.5 * i + j); - pc.setAxisValue(AINPUT_MOTION_AXIS_TOOL_MAJOR, 2.7 * i + j); - pc.setAxisValue(AINPUT_MOTION_AXIS_ORIENTATION, 3.5 * i + j); + pc.setAxisValue(AMOTION_EVENT_AXIS_X, 100 * i + j); + pc.setAxisValue(AMOTION_EVENT_AXIS_Y, 200 * i + j); + pc.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.5 * i + j); + pc.setAxisValue(AMOTION_EVENT_AXIS_SIZE, 0.7 * i + j); + pc.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 1.5 * i + j); + pc.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 1.7 * i + j); + pc.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 2.5 * i + j); + pc.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 2.7 * i + j); + pc.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 3.5 * i + j); } } @@ -241,27 +253,27 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent( for (size_t i = 0; i < pointerCount; i++) { SCOPED_TRACE(i); size_t offset = sampleIndex * pointerCount + i; - EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_X), + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_X), motionEvent->getHistoricalRawX(i, sampleIndex)); - EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_Y), + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_Y), motionEvent->getHistoricalRawY(i, sampleIndex)); - EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_X) + xOffset, + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_X) + xOffset, motionEvent->getHistoricalX(i, sampleIndex)); - EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_Y) + yOffset, + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_Y) + yOffset, motionEvent->getHistoricalY(i, sampleIndex)); - EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_PRESSURE), + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), motionEvent->getHistoricalPressure(i, sampleIndex)); - EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_SIZE), + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_SIZE), motionEvent->getHistoricalSize(i, sampleIndex)); - EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_TOUCH_MAJOR), + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), motionEvent->getHistoricalTouchMajor(i, sampleIndex)); - EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_TOUCH_MINOR), + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), motionEvent->getHistoricalTouchMinor(i, sampleIndex)); - EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_TOOL_MAJOR), + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), motionEvent->getHistoricalToolMajor(i, sampleIndex)); - EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_TOOL_MINOR), + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), motionEvent->getHistoricalToolMinor(i, sampleIndex)); - EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_ORIENTATION), + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION), motionEvent->getHistoricalOrientation(i, sampleIndex)); } } @@ -271,27 +283,27 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent( for (size_t i = 0; i < pointerCount; i++) { SCOPED_TRACE(i); size_t offset = lastSampleIndex * pointerCount + i; - EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_X), + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_X), motionEvent->getRawX(i)); - EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_Y), + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_Y), motionEvent->getRawY(i)); - EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_X) + xOffset, + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_X) + xOffset, motionEvent->getX(i)); - EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_Y) + yOffset, + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_Y) + yOffset, motionEvent->getY(i)); - EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_PRESSURE), + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), motionEvent->getPressure(i)); - EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_SIZE), + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_SIZE), motionEvent->getSize(i)); - EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_TOUCH_MAJOR), + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), motionEvent->getTouchMajor(i)); - EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_TOUCH_MINOR), + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), motionEvent->getTouchMinor(i)); - EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_TOOL_MAJOR), + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), motionEvent->getToolMajor(i)); - EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_TOOL_MINOR), + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), motionEvent->getToolMinor(i)); - EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AINPUT_MOTION_AXIS_ORIENTATION), + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION), motionEvent->getOrientation(i)); } diff --git a/native/include/android/input.h b/native/include/android/input.h index 0ffb8b5fb1ef7..ee05020913b87 100644 --- a/native/include/android/input.h +++ b/native/include/android/input.h @@ -320,6 +320,21 @@ enum { AMOTION_EVENT_EDGE_FLAG_RIGHT = 0x08 }; +/* + * Constants that identify each individual axis of a motion event. + */ +enum { + AMOTION_EVENT_AXIS_X = 0, + AMOTION_EVENT_AXIS_Y = 1, + AMOTION_EVENT_AXIS_PRESSURE = 2, + AMOTION_EVENT_AXIS_SIZE = 3, + AMOTION_EVENT_AXIS_TOUCH_MAJOR = 4, + AMOTION_EVENT_AXIS_TOUCH_MINOR = 5, + AMOTION_EVENT_AXIS_TOOL_MAJOR = 6, + AMOTION_EVENT_AXIS_TOOL_MINOR = 7, + AMOTION_EVENT_AXIS_ORIENTATION = 8, +}; + /* * Input sources. * @@ -362,21 +377,6 @@ enum { AINPUT_KEYBOARD_TYPE_ALPHABETIC = 2, }; -/* - * Constants that identify each individual axis of a motion event. - */ -enum { - AINPUT_MOTION_AXIS_X = 0, - AINPUT_MOTION_AXIS_Y = 1, - AINPUT_MOTION_AXIS_PRESSURE = 2, - AINPUT_MOTION_AXIS_SIZE = 3, - AINPUT_MOTION_AXIS_TOUCH_MAJOR = 4, - AINPUT_MOTION_AXIS_TOUCH_MINOR = 5, - AINPUT_MOTION_AXIS_TOOL_MAJOR = 6, - AINPUT_MOTION_AXIS_TOOL_MINOR = 7, - AINPUT_MOTION_AXIS_ORIENTATION = 8, -}; - /* * Constants used to retrieve information about the range of motion for a particular * coordinate of a motion event. @@ -384,18 +384,18 @@ enum { * Refer to the documentation on android.view.InputDevice for more details about input sources * and their correct interpretation. * - * DEPRECATION NOTICE: These constants are deprecated. Use AINPUT_MOTION_AXIS_* constants instead. + * DEPRECATION NOTICE: These constants are deprecated. Use AMOTION_EVENT_AXIS_* constants instead. */ enum { - AINPUT_MOTION_RANGE_X = AINPUT_MOTION_AXIS_X, - AINPUT_MOTION_RANGE_Y = AINPUT_MOTION_AXIS_Y, - AINPUT_MOTION_RANGE_PRESSURE = AINPUT_MOTION_AXIS_PRESSURE, - AINPUT_MOTION_RANGE_SIZE = AINPUT_MOTION_AXIS_SIZE, - AINPUT_MOTION_RANGE_TOUCH_MAJOR = AINPUT_MOTION_AXIS_TOUCH_MAJOR, - AINPUT_MOTION_RANGE_TOUCH_MINOR = AINPUT_MOTION_AXIS_TOUCH_MINOR, - AINPUT_MOTION_RANGE_TOOL_MAJOR = AINPUT_MOTION_AXIS_TOOL_MAJOR, - AINPUT_MOTION_RANGE_TOOL_MINOR = AINPUT_MOTION_AXIS_TOOL_MINOR, - AINPUT_MOTION_RANGE_ORIENTATION = AINPUT_MOTION_AXIS_ORIENTATION, + AINPUT_MOTION_RANGE_X = AMOTION_EVENT_AXIS_X, + AINPUT_MOTION_RANGE_Y = AMOTION_EVENT_AXIS_Y, + AINPUT_MOTION_RANGE_PRESSURE = AMOTION_EVENT_AXIS_PRESSURE, + AINPUT_MOTION_RANGE_SIZE = AMOTION_EVENT_AXIS_SIZE, + AINPUT_MOTION_RANGE_TOUCH_MAJOR = AMOTION_EVENT_AXIS_TOUCH_MAJOR, + AINPUT_MOTION_RANGE_TOUCH_MINOR = AMOTION_EVENT_AXIS_TOUCH_MINOR, + AINPUT_MOTION_RANGE_TOOL_MAJOR = AMOTION_EVENT_AXIS_TOOL_MAJOR, + AINPUT_MOTION_RANGE_TOOL_MINOR = AMOTION_EVENT_AXIS_TOOL_MINOR, + AINPUT_MOTION_RANGE_ORIENTATION = AMOTION_EVENT_AXIS_ORIENTATION, } __attribute__ ((deprecated)); diff --git a/services/input/InputDispatcher.cpp b/services/input/InputDispatcher.cpp index 466a9b364063b..ae11fb1e025ef 100644 --- a/services/input/InputDispatcher.cpp +++ b/services/input/InputDispatcher.cpp @@ -483,9 +483,9 @@ bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) { && mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY && mInputTargetWaitApplication != NULL) { int32_t x = int32_t(motionEntry->firstSample.pointerCoords[0]. - getAxisValue(AINPUT_MOTION_AXIS_X)); + getAxisValue(AMOTION_EVENT_AXIS_X)); int32_t y = int32_t(motionEntry->firstSample.pointerCoords[0]. - getAxisValue(AINPUT_MOTION_AXIS_Y)); + getAxisValue(AMOTION_EVENT_AXIS_Y)); const InputWindow* touchedWindow = findTouchedWindowAtLocked(x, y); if (touchedWindow && touchedWindow->inputWindowHandle != NULL @@ -890,15 +890,15 @@ void InputDispatcher::logOutboundMotionDetailsLocked(const char* prefix, const M "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, " "orientation=%f", i, entry->pointerIds[i], - sample->pointerCoords[i].getAxisValue(AINPUT_MOTION_AXIS_X), - sample->pointerCoords[i].getAxisValue(AINPUT_MOTION_AXIS_Y), - sample->pointerCoords[i].getAxisValue(AINPUT_MOTION_AXIS_PRESSURE), - sample->pointerCoords[i].getAxisValue(AINPUT_MOTION_AXIS_SIZE), - sample->pointerCoords[i].getAxisValue(AINPUT_MOTION_AXIS_TOUCH_MAJOR), - sample->pointerCoords[i].getAxisValue(AINPUT_MOTION_AXIS_TOUCH_MINOR), - sample->pointerCoords[i].getAxisValue(AINPUT_MOTION_AXIS_TOOL_MAJOR), - sample->pointerCoords[i].getAxisValue(AINPUT_MOTION_AXIS_TOOL_MINOR), - sample->pointerCoords[i].getAxisValue(AINPUT_MOTION_AXIS_ORIENTATION)); + sample->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X), + sample->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y), + sample->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), + sample->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE), + sample->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), + sample->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), + sample->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), + sample->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), + sample->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION)); } // Keep in mind that due to batching, it is possible for the number of samples actually @@ -1195,9 +1195,9 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, int32_t pointerIndex = getMotionEventActionPointerIndex(action); int32_t x = int32_t(entry->firstSample.pointerCoords[pointerIndex]. - getAxisValue(AINPUT_MOTION_AXIS_X)); + getAxisValue(AMOTION_EVENT_AXIS_X)); int32_t y = int32_t(entry->firstSample.pointerCoords[pointerIndex]. - getAxisValue(AINPUT_MOTION_AXIS_Y)); + getAxisValue(AMOTION_EVENT_AXIS_Y)); const InputWindow* newTouchedWindow = NULL; const InputWindow* topErrorWindow = NULL; @@ -2284,15 +2284,15 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, uint32_t "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, " "orientation=%f", i, pointerIds[i], - pointerCoords[i].getAxisValue(AINPUT_MOTION_AXIS_X), - pointerCoords[i].getAxisValue(AINPUT_MOTION_AXIS_Y), - pointerCoords[i].getAxisValue(AINPUT_MOTION_AXIS_PRESSURE), - pointerCoords[i].getAxisValue(AINPUT_MOTION_AXIS_SIZE), - pointerCoords[i].getAxisValue(AINPUT_MOTION_AXIS_TOUCH_MAJOR), - pointerCoords[i].getAxisValue(AINPUT_MOTION_AXIS_TOUCH_MINOR), - pointerCoords[i].getAxisValue(AINPUT_MOTION_AXIS_TOOL_MAJOR), - pointerCoords[i].getAxisValue(AINPUT_MOTION_AXIS_TOOL_MINOR), - pointerCoords[i].getAxisValue(AINPUT_MOTION_AXIS_ORIENTATION)); + pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X), + pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y), + pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), + pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE), + pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), + pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), + pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), + pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), + pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION)); } #endif if (! validateMotionEvent(action, pointerCount, pointerIds)) { diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp index 577da01527ad2..8f38cb259fefb 100644 --- a/services/input/InputReader.cpp +++ b/services/input/InputReader.cpp @@ -1294,14 +1294,14 @@ void CursorInputMapper::sync(nsecs_t when) { } float x, y; mPointerController->getPosition(&x, &y); - pointerCoords.setAxisValue(AINPUT_MOTION_AXIS_X, x); - pointerCoords.setAxisValue(AINPUT_MOTION_AXIS_Y, y); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y); } else { - pointerCoords.setAxisValue(AINPUT_MOTION_AXIS_X, deltaX); - pointerCoords.setAxisValue(AINPUT_MOTION_AXIS_Y, deltaY); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, deltaX); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, deltaY); } - pointerCoords.setAxisValue(AINPUT_MOTION_AXIS_PRESSURE, mLocked.down ? 1.0f : 0.0f); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, mLocked.down ? 1.0f : 0.0f); } // release lock int32_t metaState = mContext->getGlobalMetaState(); @@ -2686,15 +2686,15 @@ void TouchInputMapper::dispatchTouch(nsecs_t when, uint32_t policyFlags, // Write output coords. PointerCoords& out = pointerCoords[outIndex]; out.clear(); - out.setAxisValue(AINPUT_MOTION_AXIS_X, x); - out.setAxisValue(AINPUT_MOTION_AXIS_Y, y); - out.setAxisValue(AINPUT_MOTION_AXIS_PRESSURE, pressure); - out.setAxisValue(AINPUT_MOTION_AXIS_SIZE, size); - out.setAxisValue(AINPUT_MOTION_AXIS_TOUCH_MAJOR, touchMajor); - out.setAxisValue(AINPUT_MOTION_AXIS_TOUCH_MINOR, touchMinor); - out.setAxisValue(AINPUT_MOTION_AXIS_TOOL_MAJOR, toolMajor); - out.setAxisValue(AINPUT_MOTION_AXIS_TOOL_MINOR, toolMinor); - out.setAxisValue(AINPUT_MOTION_AXIS_ORIENTATION, orientation); + out.setAxisValue(AMOTION_EVENT_AXIS_X, x); + out.setAxisValue(AMOTION_EVENT_AXIS_Y, y); + out.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure); + out.setAxisValue(AMOTION_EVENT_AXIS_SIZE, size); + out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, touchMajor); + out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, touchMinor); + out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, toolMajor); + out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, toolMinor); + out.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, orientation); pointerIds[outIndex] = int32_t(id); @@ -2706,8 +2706,8 @@ void TouchInputMapper::dispatchTouch(nsecs_t when, uint32_t policyFlags, // Check edge flags by looking only at the first pointer since the flags are // global to the event. if (motionEventAction == AMOTION_EVENT_ACTION_DOWN) { - float x = pointerCoords[0].getAxisValue(AINPUT_MOTION_AXIS_X); - float y = pointerCoords[0].getAxisValue(AINPUT_MOTION_AXIS_Y); + float x = pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X); + float y = pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y); if (x <= 0) { motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_LEFT; @@ -3857,8 +3857,8 @@ void JoystickInputMapper::sync(nsecs_t when) { if (motionAxisChanged) { PointerCoords pointerCoords; pointerCoords.clear(); - pointerCoords.setAxisValue(AINPUT_MOTION_AXIS_X, mAxes.x.value); - pointerCoords.setAxisValue(AINPUT_MOTION_AXIS_Y, mAxes.y.value); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, mAxes.x.value); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, mAxes.y.value); int32_t pointerId = 0; getDispatcher()->notifyMotion(when, getDeviceId(), AINPUT_SOURCE_JOYSTICK, 0, diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp index 34d613aa80e91..41d67ed27d4fc 100644 --- a/services/input/tests/InputReader_test.cpp +++ b/services/input/tests/InputReader_test.cpp @@ -1473,15 +1473,15 @@ protected: float x, float y, float pressure, float size, float touchMajor, float touchMinor, float toolMajor, float toolMinor, float orientation) { - ASSERT_NEAR(x, coords.getAxisValue(AINPUT_MOTION_AXIS_X), 1); - ASSERT_NEAR(y, coords.getAxisValue(AINPUT_MOTION_AXIS_Y), 1); - ASSERT_NEAR(pressure, coords.getAxisValue(AINPUT_MOTION_AXIS_PRESSURE), EPSILON); - ASSERT_NEAR(size, coords.getAxisValue(AINPUT_MOTION_AXIS_SIZE), EPSILON); - ASSERT_NEAR(touchMajor, coords.getAxisValue(AINPUT_MOTION_AXIS_TOUCH_MAJOR), 1); - ASSERT_NEAR(touchMinor, coords.getAxisValue(AINPUT_MOTION_AXIS_TOUCH_MINOR), 1); - ASSERT_NEAR(toolMajor, coords.getAxisValue(AINPUT_MOTION_AXIS_TOOL_MAJOR), 1); - ASSERT_NEAR(toolMinor, coords.getAxisValue(AINPUT_MOTION_AXIS_TOOL_MINOR), 1); - ASSERT_NEAR(orientation, coords.getAxisValue(AINPUT_MOTION_AXIS_ORIENTATION), EPSILON); + ASSERT_NEAR(x, coords.getAxisValue(AMOTION_EVENT_AXIS_X), 1); + ASSERT_NEAR(y, coords.getAxisValue(AMOTION_EVENT_AXIS_Y), 1); + ASSERT_NEAR(pressure, coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), EPSILON); + ASSERT_NEAR(size, coords.getAxisValue(AMOTION_EVENT_AXIS_SIZE), EPSILON); + ASSERT_NEAR(touchMajor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), 1); + ASSERT_NEAR(touchMinor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), 1); + ASSERT_NEAR(toolMajor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), 1); + ASSERT_NEAR(toolMinor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), 1); + ASSERT_NEAR(orientation, coords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION), EPSILON); } }; @@ -2892,8 +2892,8 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenNotOrientationAware_DoesNotRotate processSync(mapper); ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_NEAR(50, args.pointerCoords[0].getAxisValue(AINPUT_MOTION_AXIS_X), 1); - ASSERT_NEAR(75, args.pointerCoords[0].getAxisValue(AINPUT_MOTION_AXIS_Y), 1); + ASSERT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1); + ASSERT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1); processUp(mapper); processSync(mapper); @@ -2914,8 +2914,8 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenOrientationAware_RotatesMotions) processSync(mapper); ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_NEAR(50, args.pointerCoords[0].getAxisValue(AINPUT_MOTION_AXIS_X), 1); - ASSERT_NEAR(75, args.pointerCoords[0].getAxisValue(AINPUT_MOTION_AXIS_Y), 1); + ASSERT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1); + ASSERT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1); processUp(mapper); processSync(mapper); @@ -2927,8 +2927,8 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenOrientationAware_RotatesMotions) processSync(mapper); ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_NEAR(75, args.pointerCoords[0].getAxisValue(AINPUT_MOTION_AXIS_X), 1); - ASSERT_NEAR(DISPLAY_WIDTH - 50, args.pointerCoords[0].getAxisValue(AINPUT_MOTION_AXIS_Y), 1); + ASSERT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1); + ASSERT_NEAR(DISPLAY_WIDTH - 50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1); processUp(mapper); processSync(mapper); @@ -2940,8 +2940,8 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenOrientationAware_RotatesMotions) processSync(mapper); ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_NEAR(DISPLAY_WIDTH - 50, args.pointerCoords[0].getAxisValue(AINPUT_MOTION_AXIS_X), 1); - ASSERT_NEAR(DISPLAY_HEIGHT - 75, args.pointerCoords[0].getAxisValue(AINPUT_MOTION_AXIS_Y), 1); + ASSERT_NEAR(DISPLAY_WIDTH - 50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1); + ASSERT_NEAR(DISPLAY_HEIGHT - 75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1); processUp(mapper); processSync(mapper); @@ -2953,8 +2953,8 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenOrientationAware_RotatesMotions) processSync(mapper); ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_NEAR(DISPLAY_HEIGHT - 75, args.pointerCoords[0].getAxisValue(AINPUT_MOTION_AXIS_X), 1); - ASSERT_NEAR(50, args.pointerCoords[0].getAxisValue(AINPUT_MOTION_AXIS_Y), 1); + ASSERT_NEAR(DISPLAY_HEIGHT - 75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1); + ASSERT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1); processUp(mapper); processSync(mapper); From 0af94442aa509de93b3216ac60a95e7163895cbb Mon Sep 17 00:00:00 2001 From: Christopher Tate Date: Fri, 25 Feb 2011 14:37:32 -0800 Subject: [PATCH 04/14] Revert "Binder linkage no longer depends on JNI objrefs as persistent tokens" This reverts commit c2d55dd89743c8a38deb809f3cdf1ad2d1dbac2b. --- core/jni/android_util_Binder.cpp | 196 ++++++++----------------------- include/binder/IBinder.h | 2 +- 2 files changed, 52 insertions(+), 146 deletions(-) diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp index bd5305d767593..7a53874c69835 100644 --- a/core/jni/android_util_Binder.cpp +++ b/core/jni/android_util_Binder.cpp @@ -31,8 +31,6 @@ #include #include #include -#include -#include #include #include #include @@ -324,15 +322,25 @@ private: class JavaBBinderHolder : public RefBase { public: - sp get(JNIEnv* env, jobject obj) + JavaBBinderHolder(JNIEnv* env, jobject object) + : mObject(object) + { + LOGV("Creating JavaBBinderHolder for Object %p\n", object); + } + ~JavaBBinderHolder() + { + LOGV("Destroying JavaBBinderHolder for Object %p\n", mObject); + } + + sp get(JNIEnv* env) { AutoMutex _l(mLock); sp b = mBinder.promote(); if (b == NULL) { - b = new JavaBBinder(env, obj); + b = new JavaBBinder(env, mObject); mBinder = b; LOGV("Creating JavaBinder %p (refs %p) for Object %p, weakCount=%d\n", - b.get(), b->getWeakRefs(), obj, b->getWeakRefs()->getWeakCount()); + b.get(), b->getWeakRefs(), mObject, b->getWeakRefs()->getWeakCount()); } return b; @@ -346,41 +354,20 @@ public: private: Mutex mLock; + jobject mObject; wp mBinder; }; // ---------------------------------------------------------------------------- -// Per-IBinder death recipient bookkeeping. This is how we reconcile local jobject -// death recipient references passed in through JNI with the permanent corresponding -// JavaDeathRecipient objects. - -class JavaDeathRecipient; - -class DeathRecipientList : public RefBase { - List< sp > mList; - Mutex mLock; - -public: - ~DeathRecipientList(); - - void add(const sp& recipient); - void remove(const sp& recipient); - sp find(jobject recipient); -}; - -// ---------------------------------------------------------------------------- - class JavaDeathRecipient : public IBinder::DeathRecipient { public: - JavaDeathRecipient(JNIEnv* env, jobject object, sp& list) - : mVM(jnienv_to_javavm(env)), mObject(env->NewGlobalRef(object)), mList(list) + JavaDeathRecipient(JNIEnv* env, jobject object) + : mVM(jnienv_to_javavm(env)), mObject(env->NewGlobalRef(object)), + mHoldsRef(true) { - // These objects manage their own lifetimes so are responsible for final bookkeeping. - // The list holds a strong reference to this object. - mList->add(this); - + incStrong(this); android_atomic_inc(&gNumDeathRefs); incRefsCreated(env); } @@ -404,12 +391,16 @@ public: void clearReference() { - mList->remove(this); - } - - bool matches(jobject obj) { - JNIEnv* env = javavm_to_jnienv(mVM); - return env->IsSameObject(obj, mObject); + bool release = false; + mLock.lock(); + if (mHoldsRef) { + mHoldsRef = false; + release = true; + } + mLock.unlock(); + if (release) { + decStrong(this); + } } protected: @@ -424,57 +415,12 @@ protected: private: JavaVM* const mVM; jobject const mObject; - sp mList; + Mutex mLock; + bool mHoldsRef; }; // ---------------------------------------------------------------------------- -DeathRecipientList::~DeathRecipientList() { - AutoMutex _l(mLock); - - // Should never happen -- the JavaDeathRecipient objects that have added themselves - // to the list are holding references on the list object. Only when they are torn - // down can the list header be destroyed. - if (mList.size() > 0) { - LOGE("Retiring binder %p with extant death recipients\n", this); - } -} - -void DeathRecipientList::add(const sp& recipient) { - AutoMutex _l(mLock); - - mList.push_back(recipient); -} - -void DeathRecipientList::remove(const sp& recipient) { - AutoMutex _l(mLock); - - List< sp >::iterator iter; - for (iter = mList.begin(); iter != mList.end(); iter++) { - if (*iter == recipient) { - mList.erase(iter); - return; - } - } -} - -sp DeathRecipientList::find(jobject recipient) { - AutoMutex _l(mLock); - - List< sp >::iterator iter; - for (iter = mList.begin(); iter != mList.end(); iter++) { - if ((*iter)->matches(recipient)) { - return *iter; - } - } - return NULL; -} - -static KeyedVector > gDeathRecipientsByIBinder; -static Mutex gDeathRecipientMapLock; - -// ---------------------------------------------------------------------------- - namespace android { static void proxy_cleanup(const void* id, void* obj, void* cleanupCookie) @@ -544,7 +490,7 @@ sp ibinderForJavaObject(JNIEnv* env, jobject obj) if (env->IsInstanceOf(obj, gBinderOffsets.mClass)) { JavaBBinderHolder* jbh = (JavaBBinderHolder*) env->GetIntField(obj, gBinderOffsets.mObject); - return jbh != NULL ? jbh->get(env, obj) : NULL; + return jbh != NULL ? jbh->get(env) : NULL; } if (env->IsInstanceOf(obj, gBinderProxyOffsets.mClass)) { @@ -675,26 +621,26 @@ static void android_os_Binder_flushPendingCommands(JNIEnv* env, jobject clazz) IPCThreadState::self()->flushCommands(); } -static void android_os_Binder_init(JNIEnv* env, jobject obj) +static void android_os_Binder_init(JNIEnv* env, jobject clazz) { - JavaBBinderHolder* jbh = new JavaBBinderHolder(); + JavaBBinderHolder* jbh = new JavaBBinderHolder(env, clazz); if (jbh == NULL) { jniThrowException(env, "java/lang/OutOfMemoryError", NULL); return; } - LOGV("Java Binder %p: acquiring first ref on holder %p", obj, jbh); - jbh->incStrong((void*)android_os_Binder_init); - env->SetIntField(obj, gBinderOffsets.mObject, (int)jbh); + LOGV("Java Binder %p: acquiring first ref on holder %p", clazz, jbh); + jbh->incStrong(clazz); + env->SetIntField(clazz, gBinderOffsets.mObject, (int)jbh); } -static void android_os_Binder_destroy(JNIEnv* env, jobject obj) +static void android_os_Binder_destroy(JNIEnv* env, jobject clazz) { JavaBBinderHolder* jbh = (JavaBBinderHolder*) - env->GetIntField(obj, gBinderOffsets.mObject); + env->GetIntField(clazz, gBinderOffsets.mObject); if (jbh != NULL) { - env->SetIntField(obj, gBinderOffsets.mObject, 0); - LOGV("Java Binder %p: removing ref on holder %p", obj, jbh); - jbh->decStrong((void*)android_os_Binder_init); + env->SetIntField(clazz, gBinderOffsets.mObject, 0); + LOGV("Java Binder %p: removing ref on holder %p", clazz, jbh); + jbh->decStrong(clazz); } else { // Encountering an uninitialized binder is harmless. All it means is that // the Binder was only partially initialized when its finalizer ran and called @@ -702,7 +648,7 @@ static void android_os_Binder_destroy(JNIEnv* env, jobject obj) // For example, a Binder subclass constructor might have thrown an exception before // it could delegate to its superclass's constructor. Consequently init() would // not have been called and the holder pointer would remain NULL. - LOGV("Java Binder %p: ignoring uninitialized binder", obj); + LOGV("Java Binder %p: ignoring uninitialized binder", clazz); } } @@ -1027,25 +973,8 @@ static void android_os_BinderProxy_linkToDeath(JNIEnv* env, jobject obj, LOGV("linkToDeath: binder=%p recipient=%p\n", target, recipient); if (!target->localBinder()) { - sp jdr; - - { - sp list; - AutoMutex _maplocker(gDeathRecipientMapLock); - - ssize_t listIndex = gDeathRecipientsByIBinder.indexOfKey(target); - if (listIndex < 0) { - // Set up the death notice bookkeeping for this binder lazily - list = new DeathRecipientList; - gDeathRecipientsByIBinder.add(target, list); - } else { - list = gDeathRecipientsByIBinder.valueAt(listIndex); - } - - jdr = new JavaDeathRecipient(env, recipient, list); - } - - status_t err = target->linkToDeath(jdr, NULL, flags); + sp jdr = new JavaDeathRecipient(env, recipient); + status_t err = target->linkToDeath(jdr, recipient, flags); if (err != NO_ERROR) { // Failure adding the death recipient, so clear its reference // now. @@ -1074,29 +1003,15 @@ static jboolean android_os_BinderProxy_unlinkToDeath(JNIEnv* env, jobject obj, LOGV("unlinkToDeath: binder=%p recipient=%p\n", target, recipient); if (!target->localBinder()) { - status_t err = NAME_NOT_FOUND; - sp origJDR; - { - AutoMutex _maplocker(gDeathRecipientMapLock); - ssize_t listIndex = gDeathRecipientsByIBinder.indexOfKey(target); - if (listIndex >= 0) { - sp list = gDeathRecipientsByIBinder.valueAt(listIndex); - origJDR = list->find(recipient); + wp dr; + status_t err = target->unlinkToDeath(NULL, recipient, flags, &dr); + if (err == NO_ERROR && dr != NULL) { + sp sdr = dr.promote(); + JavaDeathRecipient* jdr = static_cast(sdr.get()); + if (jdr != NULL) { + jdr->clearReference(); } } - // If we found the matching recipient, proceed to unlink using that - if (origJDR != NULL) { - wp dr; - err = target->unlinkToDeath(origJDR, NULL, flags, &dr); - if (err == NO_ERROR && dr != NULL) { - sp sdr = dr.promote(); - JavaDeathRecipient* jdr = static_cast(sdr.get()); - if (jdr != NULL) { - jdr->clearReference(); - } - } - } - if (err == NO_ERROR || err == DEAD_OBJECT) { res = JNI_TRUE; } else { @@ -1116,15 +1031,6 @@ static void android_os_BinderProxy_destroy(JNIEnv* env, jobject obj) env->SetIntField(obj, gBinderProxyOffsets.mObject, 0); b->decStrong(obj); IPCThreadState::self()->flushCommands(); - - // tear down the death recipient bookkeeping - { - AutoMutex _maplocker(gDeathRecipientMapLock); - ssize_t listIndex = gDeathRecipientsByIBinder.indexOfKey(b); - if (listIndex >= 0) { - gDeathRecipientsByIBinder.removeItemsAt((size_t)listIndex); - } - } } // ---------------------------------------------------------------------------- diff --git a/include/binder/IBinder.h b/include/binder/IBinder.h index 81b56c2b2b3b1..749a977b83b5b 100644 --- a/include/binder/IBinder.h +++ b/include/binder/IBinder.h @@ -98,7 +98,7 @@ public: * 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 reference + * then DeathRecipient::binderDied() will be called with a referene * to this. * * The @a cookie is optional -- if non-NULL, it should be a From 6f60c5dc2895ca1a1ff4cadada078d01152fe3fb Mon Sep 17 00:00:00 2001 From: Fred Quintana Date: Thu, 24 Feb 2011 18:27:50 -0800 Subject: [PATCH 05/14] Fix a deadlock in AccountManagerService cause by different paths of code getting the mCacheLock and DB locks in different orders. The philosophy I followed for this was to ensure that the DatabaseHelper is only ever accessed from within a synchronized(mCacheLock) block. I also renamed a bunch of methods to make it easier to know if a given method should be called from within this synchronized block. Bug: 3404506 Change-Id: Ia48f95e77b77647d0717f70f1d8364da3719cc13 --- .../accounts/AccountManagerService.java | 662 +++++++++--------- .../accounts/AccountManagerServiceTest.java | 2 +- 2 files changed, 348 insertions(+), 316 deletions(-) diff --git a/core/java/android/accounts/AccountManagerService.java b/core/java/android/accounts/AccountManagerService.java index a1c28672f819c..894e19604abb9 100644 --- a/core/java/android/accounts/AccountManagerService.java +++ b/core/java/android/accounts/AccountManagerService.java @@ -52,7 +52,6 @@ import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; -import android.os.SystemProperties; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Log; @@ -81,8 +80,6 @@ import java.util.concurrent.atomic.AtomicReference; public class AccountManagerService extends IAccountManager.Stub implements RegisteredServicesCacheListener { - private static final String GOOGLE_ACCOUNT_TYPE = "com.google"; - private static final String TAG = "AccountManagerService"; private static final int TIMEOUT_DELAY_MS = 1000 * 60; @@ -198,7 +195,9 @@ public class AccountManagerService mContext = context; mPackageManager = packageManager; - mOpenHelper = new DatabaseHelper(mContext); + synchronized (mCacheLock) { + mOpenHelper = new DatabaseHelper(mContext); + } mMessageThread = new HandlerThread("AccountManagerService"); mMessageThread.start(); @@ -248,13 +247,13 @@ public class AccountManagerService } private void validateAccountsAndPopulateCache() { - boolean accountDeleted = false; - SQLiteDatabase db = mOpenHelper.getWritableDatabase(); - Cursor cursor = db.query(TABLE_ACCOUNTS, - new String[]{ACCOUNTS_ID, ACCOUNTS_TYPE, ACCOUNTS_NAME}, - null, null, null, null, null); - try { - synchronized (mCacheLock) { + synchronized (mCacheLock) { + final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); + boolean accountDeleted = false; + Cursor cursor = db.query(TABLE_ACCOUNTS, + new String[]{ACCOUNTS_ID, ACCOUNTS_TYPE, ACCOUNTS_NAME}, + null, null, null, null, null); + try { mAccountCache.clear(); final HashMap> accountNamesByType = new HashMap>(); @@ -280,7 +279,8 @@ public class AccountManagerService accountNames.add(accountName); } } - for (HashMap.Entry> cur : accountNamesByType.entrySet()) { + for (HashMap.Entry> cur + : accountNamesByType.entrySet()) { final String accountType = cur.getKey(); final ArrayList accountNames = cur.getValue(); final Account[] accountsForType = new Account[accountNames.size()]; @@ -291,11 +291,11 @@ public class AccountManagerService } mAccountCache.put(accountType, accountsForType); } - } - } finally { - cursor.close(); - if (accountDeleted) { - sendAccountsChangedBroadcast(); + } finally { + cursor.close(); + if (accountDeleted) { + sendAccountsChangedBroadcast(); + } } } } @@ -315,28 +315,30 @@ public class AccountManagerService long identityToken = clearCallingIdentity(); try { - return readPasswordFromDatabase(account); + return readPasswordInternal(account); } finally { restoreCallingIdentity(identityToken); } } - private String readPasswordFromDatabase(Account account) { + private String readPasswordInternal(Account account) { if (account == null) { return null; } - SQLiteDatabase db = mOpenHelper.getReadableDatabase(); - Cursor cursor = db.query(TABLE_ACCOUNTS, new String[]{ACCOUNTS_PASSWORD}, - ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?", - new String[]{account.name, account.type}, null, null, null); - try { - if (cursor.moveToNext()) { - return cursor.getString(0); + synchronized (mCacheLock) { + final SQLiteDatabase db = mOpenHelper.getReadableDatabase(); + Cursor cursor = db.query(TABLE_ACCOUNTS, new String[]{ACCOUNTS_PASSWORD}, + ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?", + new String[]{account.name, account.type}, null, null, null); + try { + if (cursor.moveToNext()) { + return cursor.getString(0); + } + return null; + } finally { + cursor.close(); } - return null; - } finally { - cursor.close(); } } @@ -352,7 +354,7 @@ public class AccountManagerService checkAuthenticateAccountsPermission(account); long identityToken = clearCallingIdentity(); try { - return readUserDataFromCache(account, key); + return readUserDataInternal(account, key); } finally { restoreCallingIdentity(identityToken); } @@ -394,58 +396,60 @@ public class AccountManagerService // fails if the account already exists long identityToken = clearCallingIdentity(); try { - return insertAccountIntoDatabase(account, password, extras); + return addAccountInternal(account, password, extras); } finally { restoreCallingIdentity(identityToken); } } - private boolean insertAccountIntoDatabase(Account account, String password, Bundle extras) { - SQLiteDatabase db = mOpenHelper.getWritableDatabase(); + private boolean addAccountInternal(Account account, String password, Bundle extras) { if (account == null) { return false; } - db.beginTransaction(); - try { - long numMatches = DatabaseUtils.longForQuery(db, - "select count(*) from " + TABLE_ACCOUNTS - + " WHERE " + ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?", - new String[]{account.name, account.type}); - if (numMatches > 0) { - Log.w(TAG, "insertAccountIntoDatabase: " + account - + ", skipping since the account already exists"); - return false; - } - ContentValues values = new ContentValues(); - values.put(ACCOUNTS_NAME, account.name); - values.put(ACCOUNTS_TYPE, account.type); - values.put(ACCOUNTS_PASSWORD, password); - long accountId = db.insert(TABLE_ACCOUNTS, ACCOUNTS_NAME, values); - if (accountId < 0) { - Log.w(TAG, "insertAccountIntoDatabase: " + account - + ", skipping the DB insert failed"); - return false; - } - if (extras != null) { - for (String key : extras.keySet()) { - final String value = extras.getString(key); - if (insertExtra(db, accountId, key, value) < 0) { - Log.w(TAG, "insertAccountIntoDatabase: " + account - + ", skipping since insertExtra failed for key " + key); - return false; + synchronized (mCacheLock) { + final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); + db.beginTransaction(); + try { + long numMatches = DatabaseUtils.longForQuery(db, + "select count(*) from " + TABLE_ACCOUNTS + + " WHERE " + ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?", + new String[]{account.name, account.type}); + if (numMatches > 0) { + Log.w(TAG, "insertAccountIntoDatabase: " + account + + ", skipping since the account already exists"); + return false; + } + ContentValues values = new ContentValues(); + values.put(ACCOUNTS_NAME, account.name); + values.put(ACCOUNTS_TYPE, account.type); + values.put(ACCOUNTS_PASSWORD, password); + long accountId = db.insert(TABLE_ACCOUNTS, ACCOUNTS_NAME, values); + if (accountId < 0) { + Log.w(TAG, "insertAccountIntoDatabase: " + account + + ", skipping the DB insert failed"); + return false; + } + if (extras != null) { + for (String key : extras.keySet()) { + final String value = extras.getString(key); + if (insertExtraLocked(db, accountId, key, value) < 0) { + Log.w(TAG, "insertAccountIntoDatabase: " + account + + ", skipping since insertExtra failed for key " + key); + return false; + } } } + db.setTransactionSuccessful(); + insertAccountIntoCacheLocked(account); + } finally { + db.endTransaction(); } - db.setTransactionSuccessful(); - insertAccountIntoCache(account); - } finally { - db.endTransaction(); + sendAccountsChangedBroadcast(); + return true; } - sendAccountsChangedBroadcast(); - return true; } - private long insertExtra(SQLiteDatabase db, long accountId, String key, String value) { + private long insertExtraLocked(SQLiteDatabase db, long accountId, String key, String value) { ContentValues values = new ContentValues(); values.put(EXTRAS_KEY, key); values.put(EXTRAS_ACCOUNTS_ID, accountId); @@ -578,7 +582,7 @@ public class AccountManagerService && !result.containsKey(AccountManager.KEY_INTENT)) { final boolean removalAllowed = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT); if (removalAllowed) { - removeAccount(mAccount); + removeAccountInternal(mAccount); } IAccountManagerResponse response = getResponseAndClose(); if (response != null) { @@ -599,12 +603,14 @@ public class AccountManagerService } } - protected void removeAccount(Account account) { - final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); - db.delete(TABLE_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?", - new String[]{account.name, account.type}); - removeAccountFromCache(account); - sendAccountsChangedBroadcast(); + protected void removeAccountInternal(Account account) { + synchronized (mCacheLock) { + final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); + db.delete(TABLE_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?", + new String[]{account.name, account.type}); + removeAccountFromCacheLocked(account); + sendAccountsChangedBroadcast(); + } } public void invalidateAuthToken(String accountType, String authToken) { @@ -618,20 +624,22 @@ public class AccountManagerService checkManageAccountsOrUseCredentialsPermissions(); long identityToken = clearCallingIdentity(); try { - SQLiteDatabase db = mOpenHelper.getWritableDatabase(); - db.beginTransaction(); - try { - invalidateAuthToken(db, accountType, authToken); - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); + synchronized (mCacheLock) { + final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); + db.beginTransaction(); + try { + invalidateAuthTokenLocked(db, accountType, authToken); + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } } } finally { restoreCallingIdentity(identityToken); } } - private void invalidateAuthToken(SQLiteDatabase db, String accountType, String authToken) { + private void invalidateAuthTokenLocked(SQLiteDatabase db, String accountType, String authToken) { if (authToken == null || accountType == null) { return; } @@ -652,7 +660,8 @@ public class AccountManagerService String accountName = cursor.getString(1); String authTokenType = cursor.getString(2); db.delete(TABLE_AUTHTOKENS, AUTHTOKENS_ID + "=" + authTokenId, null); - writeAuthTokenIntoCache(new Account(accountName, accountType), authTokenType, null); + writeAuthTokenIntoCacheLocked(db, new Account(accountName, accountType), + authTokenType, null); } } finally { cursor.close(); @@ -664,28 +673,30 @@ public class AccountManagerService return false; } cancelNotification(getSigninRequiredNotificationId(account)); - SQLiteDatabase db = mOpenHelper.getWritableDatabase(); - db.beginTransaction(); - try { - long accountId = getAccountId(db, account); - if (accountId < 0) { + synchronized (mCacheLock) { + final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); + db.beginTransaction(); + try { + long accountId = getAccountIdLocked(db, account); + if (accountId < 0) { + return false; + } + db.delete(TABLE_AUTHTOKENS, + AUTHTOKENS_ACCOUNTS_ID + "=" + accountId + " AND " + AUTHTOKENS_TYPE + "=?", + new String[]{type}); + ContentValues values = new ContentValues(); + values.put(AUTHTOKENS_ACCOUNTS_ID, accountId); + values.put(AUTHTOKENS_TYPE, type); + values.put(AUTHTOKENS_AUTHTOKEN, authToken); + if (db.insert(TABLE_AUTHTOKENS, AUTHTOKENS_AUTHTOKEN, values) >= 0) { + db.setTransactionSuccessful(); + writeAuthTokenIntoCacheLocked(db, account, type, authToken); + return true; + } return false; + } finally { + db.endTransaction(); } - db.delete(TABLE_AUTHTOKENS, - AUTHTOKENS_ACCOUNTS_ID + "=" + accountId + " AND " + AUTHTOKENS_TYPE + "=?", - new String[]{type}); - ContentValues values = new ContentValues(); - values.put(AUTHTOKENS_ACCOUNTS_ID, accountId); - values.put(AUTHTOKENS_TYPE, type); - values.put(AUTHTOKENS_AUTHTOKEN, authToken); - if (db.insert(TABLE_AUTHTOKENS, AUTHTOKENS_AUTHTOKEN, values) >= 0) { - db.setTransactionSuccessful(); - writeAuthTokenIntoCache(account, type, authToken); - return true; - } - return false; - } finally { - db.endTransaction(); } } @@ -701,7 +712,7 @@ public class AccountManagerService checkAuthenticateAccountsPermission(account); long identityToken = clearCallingIdentity(); try { - return readAuthTokenFromCache(account, authTokenType); + return readAuthTokenInternal(account, authTokenType); } finally { restoreCallingIdentity(identityToken); } @@ -735,35 +746,35 @@ public class AccountManagerService checkAuthenticateAccountsPermission(account); long identityToken = clearCallingIdentity(); try { - setPasswordInDB(account, password); + setPasswordInternal(account, password); } finally { restoreCallingIdentity(identityToken); } } - private void setPasswordInDB(Account account, String password) { + private void setPasswordInternal(Account account, String password) { if (account == null) { return; } - SQLiteDatabase db = mOpenHelper.getWritableDatabase(); - db.beginTransaction(); - try { - final ContentValues values = new ContentValues(); - values.put(ACCOUNTS_PASSWORD, password); - final long accountId = getAccountId(db, account); - if (accountId >= 0) { - final String[] argsAccountId = {String.valueOf(accountId)}; - db.update(TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId); - db.delete(TABLE_AUTHTOKENS, AUTHTOKENS_ACCOUNTS_ID + "=?", argsAccountId); - synchronized (mCacheLock) { + synchronized (mCacheLock) { + final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); + db.beginTransaction(); + try { + final ContentValues values = new ContentValues(); + values.put(ACCOUNTS_PASSWORD, password); + final long accountId = getAccountIdLocked(db, account); + if (accountId >= 0) { + final String[] argsAccountId = {String.valueOf(accountId)}; + db.update(TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId); + db.delete(TABLE_AUTHTOKENS, AUTHTOKENS_ACCOUNTS_ID + "=?", argsAccountId); mAuthTokenCache.remove(account); + db.setTransactionSuccessful(); } - db.setTransactionSuccessful(); + } finally { + db.endTransaction(); } - } finally { - db.endTransaction(); + sendAccountsChangedBroadcast(); } - sendAccountsChangedBroadcast(); } private void sendAccountsChangedBroadcast() { @@ -782,7 +793,7 @@ public class AccountManagerService checkManageAccountsPermission(); long identityToken = clearCallingIdentity(); try { - setPasswordInDB(account, null); + setPasswordInternal(account, null); } finally { restoreCallingIdentity(identityToken); } @@ -800,41 +811,43 @@ public class AccountManagerService checkAuthenticateAccountsPermission(account); long identityToken = clearCallingIdentity(); try { - writeUserdataIntoDatabase(account, key, value); + setUserdataInternal(account, key, value); } finally { restoreCallingIdentity(identityToken); } } - private void writeUserdataIntoDatabase(Account account, String key, String value) { + private void setUserdataInternal(Account account, String key, String value) { if (account == null || key == null) { return; } - SQLiteDatabase db = mOpenHelper.getWritableDatabase(); - db.beginTransaction(); - try { - long accountId = getAccountId(db, account); - if (accountId < 0) { - return; - } - long extrasId = getExtrasId(db, accountId, key); - if (extrasId < 0 ) { - extrasId = insertExtra(db, accountId, key, value); - if (extrasId < 0) { - return; - } - } else { - ContentValues values = new ContentValues(); - values.put(EXTRAS_VALUE, value); - if (1 != db.update(TABLE_EXTRAS, values, EXTRAS_ID + "=" + extrasId, null)) { + synchronized (mCacheLock) { + final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); + db.beginTransaction(); + try { + long accountId = getAccountIdLocked(db, account); + if (accountId < 0) { return; } + long extrasId = getExtrasIdLocked(db, accountId, key); + if (extrasId < 0 ) { + extrasId = insertExtraLocked(db, accountId, key, value); + if (extrasId < 0) { + return; + } + } else { + ContentValues values = new ContentValues(); + values.put(EXTRAS_VALUE, value); + if (1 != db.update(TABLE_EXTRAS, values, EXTRAS_ID + "=" + extrasId, null)) { + return; + } + } + writeUserDataIntoCacheLocked(db, account, key, value); + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); } - db.setTransactionSuccessful(); - writeUserDataIntoCache(account, key, value); - } finally { - db.endTransaction(); } } @@ -940,7 +953,7 @@ public class AccountManagerService // if the caller has permission, do the peek. otherwise go the more expensive // route of starting a Session if (!customTokens && permissionGranted) { - String authToken = readAuthTokenFromCache(account, authTokenType); + String authToken = readAuthTokenInternal(account, authTokenType); if (authToken != null) { Bundle result = new Bundle(); result.putString(AccountManager.KEY_AUTHTOKEN, authToken); @@ -1246,7 +1259,9 @@ public class AccountManagerService } public void run() throws RemoteException { - mAccountsOfType = getAccountsByTypeFromCache(mAccountType); + synchronized (mCacheLock) { + mAccountsOfType = getAccountsFromCacheLocked(mAccountType); + } // check whether each account matches the requested features mAccountsWithFeatures = new ArrayList(mAccountsOfType.length); mCurrentAccount = 0; @@ -1332,7 +1347,9 @@ public class AccountManagerService checkReadAccountsPermission(); long identityToken = clearCallingIdentity(); try { - return getAccountsByTypeFromCache(type); + synchronized (mCacheLock) { + return getAccountsFromCacheLocked(type); + } } finally { restoreCallingIdentity(identityToken); } @@ -1353,7 +1370,10 @@ public class AccountManagerService long identityToken = clearCallingIdentity(); try { if (features == null || features.length == 0) { - Account[] accounts = getAccountsByTypeFromCache(type); + Account[] accounts; + synchronized (mCacheLock) { + accounts = getAccountsFromCacheLocked(type); + } Bundle result = new Bundle(); result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts); onResult(response, result); @@ -1365,7 +1385,7 @@ public class AccountManagerService } } - private long getAccountId(SQLiteDatabase db, Account account) { + private long getAccountIdLocked(SQLiteDatabase db, Account account) { Cursor cursor = db.query(TABLE_ACCOUNTS, new String[]{ACCOUNTS_ID}, "name=? AND type=?", new String[]{account.name, account.type}, null, null, null); try { @@ -1378,7 +1398,7 @@ public class AccountManagerService } } - private long getExtrasId(SQLiteDatabase db, long accountId, String key) { + private long getExtrasIdLocked(SQLiteDatabase db, long accountId, String key) { Cursor cursor = db.query(TABLE_EXTRAS, new String[]{EXTRAS_ID}, EXTRAS_ACCOUNTS_ID + "=" + accountId + " AND " + EXTRAS_KEY + "=?", new String[]{key}, null, null, null); @@ -1668,6 +1688,11 @@ public class AccountManagerService super(context, AccountManagerService.getDatabaseName(), null, DATABASE_VERSION); } + /** + * This call needs to be made while the mCacheLock is held. The way to + * ensure this is to get the lock any time a method is called ont the DatabaseHelper + * @param db The database. + */ @Override public void onCreate(SQLiteDatabase db) { db.execSQL("CREATE TABLE " + TABLE_ACCOUNTS + " ( " @@ -1756,19 +1781,24 @@ public class AccountManagerService ContentValues values = new ContentValues(); values.put(META_KEY, key); values.put(META_VALUE, value); - mOpenHelper.getWritableDatabase().replace(TABLE_META, META_KEY, values); + synchronized (mCacheLock) { + mOpenHelper.getWritableDatabase().replace(TABLE_META, META_KEY, values); + } } private String getMetaValue(String key) { - Cursor c = mOpenHelper.getReadableDatabase().query(TABLE_META, - new String[]{META_VALUE}, META_KEY + "=?", new String[]{key}, null, null, null); - try { - if (c.moveToNext()) { - return c.getString(0); + synchronized (mCacheLock) { + final SQLiteDatabase db = mOpenHelper.getReadableDatabase(); + Cursor c = db.query(TABLE_META, + new String[]{META_VALUE}, META_KEY + "=?", new String[]{key}, null, null, null); + try { + if (c.moveToNext()) { + return c.getString(0); + } + return null; + } finally { + c.close(); } - return null; - } finally { - c.close(); } } @@ -1794,42 +1824,44 @@ public class AccountManagerService } protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) { - final boolean isCheckinRequest = scanArgs(args, "--checkin") || scanArgs(args, "-c"); + synchronized (mCacheLock) { + final SQLiteDatabase db = mOpenHelper.getReadableDatabase(); - if (isCheckinRequest) { - // This is a checkin request. *Only* upload the account types and the count of each. - SQLiteDatabase db = mOpenHelper.getReadableDatabase(); + final boolean isCheckinRequest = scanArgs(args, "--checkin") || scanArgs(args, "-c"); - Cursor cursor = db.query(TABLE_ACCOUNTS, ACCOUNT_TYPE_COUNT_PROJECTION, - null, null, ACCOUNTS_TYPE, null, null); - try { - while (cursor.moveToNext()) { - // print type,count - fout.println(cursor.getString(0) + "," + cursor.getString(1)); + if (isCheckinRequest) { + // This is a checkin request. *Only* upload the account types and the count of each. + Cursor cursor = db.query(TABLE_ACCOUNTS, ACCOUNT_TYPE_COUNT_PROJECTION, + null, null, ACCOUNTS_TYPE, null, null); + try { + while (cursor.moveToNext()) { + // print type,count + fout.println(cursor.getString(0) + "," + cursor.getString(1)); + } + } finally { + if (cursor != null) { + cursor.close(); + } } - } finally { - if (cursor != null) { - cursor.close(); + } else { + Account[] accounts = getAccountsFromCacheLocked(null /* type */); + fout.println("Accounts: " + accounts.length); + for (Account account : accounts) { + fout.println(" " + account); } - } - } else { - Account[] accounts = getAccountsByTypeFromCache(null /* type */); - fout.println("Accounts: " + accounts.length); - for (Account account : accounts) { - fout.println(" " + account); - } - fout.println(); - synchronized (mSessions) { - final long now = SystemClock.elapsedRealtime(); - fout.println("Active Sessions: " + mSessions.size()); - for (Session session : mSessions.values()) { - fout.println(" " + session.toDebugString(now)); + fout.println(); + synchronized (mSessions) { + final long now = SystemClock.elapsedRealtime(); + fout.println("Active Sessions: " + mSessions.size()); + for (Session session : mSessions.values()) { + fout.println(" " + session.toDebugString(now)); + } } - } - fout.println(); - mAuthenticatorCache.dump(fd, fout, args); + fout.println(); + mAuthenticatorCache.dump(fd, fout, args); + } } } @@ -1942,20 +1974,22 @@ public class AccountManagerService if (Binder.getCallingUid() == android.os.Process.SYSTEM_UID) { return true; } - SQLiteDatabase db = mOpenHelper.getReadableDatabase(); - String[] args = {String.valueOf(Binder.getCallingUid()), authTokenType, - account.name, account.type}; - final boolean permissionGranted = - DatabaseUtils.longForQuery(db, COUNT_OF_MATCHING_GRANTS, args) != 0; - if (!permissionGranted && ActivityManager.isRunningInTestHarness()) { - // TODO: Skip this check when running automated tests. Replace this - // with a more general solution. - Log.d(TAG, "no credentials permission for usage of " + account + ", " - + authTokenType + " by uid " + Binder.getCallingUid() - + " but ignoring since device is in test harness."); - return true; + synchronized (mCacheLock) { + final SQLiteDatabase db = mOpenHelper.getReadableDatabase(); + String[] args = {String.valueOf(Binder.getCallingUid()), authTokenType, + account.name, account.type}; + final boolean permissionGranted = + DatabaseUtils.longForQuery(db, COUNT_OF_MATCHING_GRANTS, args) != 0; + if (!permissionGranted && ActivityManager.isRunningInTestHarness()) { + // TODO: Skip this check when running automated tests. Replace this + // with a more general solution. + Log.d(TAG, "no credentials permission for usage of " + account + ", " + + authTokenType + " by uid " + Binder.getCallingUid() + + " but ignoring since device is in test harness."); + return true; + } + return permissionGranted; } - return permissionGranted; } private void checkCallingUidAgainstAuthenticator(Account account) { @@ -2000,22 +2034,24 @@ public class AccountManagerService Log.e(TAG, "grantAppPermission: called with invalid arguments", new Exception()); return; } - SQLiteDatabase db = mOpenHelper.getWritableDatabase(); - db.beginTransaction(); - try { - long accountId = getAccountId(db, account); - if (accountId >= 0) { - ContentValues values = new ContentValues(); - values.put(GRANTS_ACCOUNTS_ID, accountId); - values.put(GRANTS_AUTH_TOKEN_TYPE, authTokenType); - values.put(GRANTS_GRANTEE_UID, uid); - db.insert(TABLE_GRANTS, GRANTS_ACCOUNTS_ID, values); - db.setTransactionSuccessful(); + synchronized (mCacheLock) { + final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); + db.beginTransaction(); + try { + long accountId = getAccountIdLocked(db, account); + if (accountId >= 0) { + ContentValues values = new ContentValues(); + values.put(GRANTS_ACCOUNTS_ID, accountId); + values.put(GRANTS_AUTH_TOKEN_TYPE, authTokenType); + values.put(GRANTS_GRANTEE_UID, uid); + db.insert(TABLE_GRANTS, GRANTS_ACCOUNTS_ID, values); + db.setTransactionSuccessful(); + } + } finally { + db.endTransaction(); } - } finally { - db.endTransaction(); + cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid)); } - cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid)); } /** @@ -2031,153 +2067,149 @@ public class AccountManagerService Log.e(TAG, "revokeAppPermission: called with invalid arguments", new Exception()); return; } - SQLiteDatabase db = mOpenHelper.getWritableDatabase(); - db.beginTransaction(); - try { - long accountId = getAccountId(db, account); - if (accountId >= 0) { - db.delete(TABLE_GRANTS, - GRANTS_ACCOUNTS_ID + "=? AND " + GRANTS_AUTH_TOKEN_TYPE + "=? AND " - + GRANTS_GRANTEE_UID + "=?", - new String[]{String.valueOf(accountId), authTokenType, - String.valueOf(uid)}); - db.setTransactionSuccessful(); + synchronized (mCacheLock) { + final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); + db.beginTransaction(); + try { + long accountId = getAccountIdLocked(db, account); + if (accountId >= 0) { + db.delete(TABLE_GRANTS, + GRANTS_ACCOUNTS_ID + "=? AND " + GRANTS_AUTH_TOKEN_TYPE + "=? AND " + + GRANTS_GRANTEE_UID + "=?", + new String[]{String.valueOf(accountId), authTokenType, + String.valueOf(uid)}); + db.setTransactionSuccessful(); + } + } finally { + db.endTransaction(); } - } finally { - db.endTransaction(); + cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid)); } - cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid)); } static final private String stringArrayToString(String[] value) { return value != null ? ("[" + TextUtils.join(",", value) + "]") : null; } - private void removeAccountFromCache(Account account) { - synchronized (mCacheLock) { - final Account[] oldAccountsForType = mAccountCache.get(account.type); - if (oldAccountsForType != null) { - ArrayList newAccountsList = new ArrayList(); - for (Account curAccount : oldAccountsForType) { - if (!curAccount.equals(account)) { - newAccountsList.add(curAccount); - } - } - if (newAccountsList.isEmpty()) { - mAccountCache.remove(account.type); - } else { - Account[] newAccountsForType = new Account[newAccountsList.size()]; - newAccountsForType = newAccountsList.toArray(newAccountsForType); - mAccountCache.put(account.type, newAccountsForType); + private void removeAccountFromCacheLocked(Account account) { + final Account[] oldAccountsForType = mAccountCache.get(account.type); + if (oldAccountsForType != null) { + ArrayList newAccountsList = new ArrayList(); + for (Account curAccount : oldAccountsForType) { + if (!curAccount.equals(account)) { + newAccountsList.add(curAccount); } } - mUserDataCache.remove(account); - mAuthTokenCache.remove(account); + if (newAccountsList.isEmpty()) { + mAccountCache.remove(account.type); + } else { + Account[] newAccountsForType = new Account[newAccountsList.size()]; + newAccountsForType = newAccountsList.toArray(newAccountsForType); + mAccountCache.put(account.type, newAccountsForType); + } } + mUserDataCache.remove(account); + mAuthTokenCache.remove(account); } /** * This assumes that the caller has already checked that the account is not already present. */ - private void insertAccountIntoCache(Account account) { - synchronized (mCacheLock) { - Account[] accountsForType = mAccountCache.get(account.type); - int oldLength = (accountsForType != null) ? accountsForType.length : 0; - Account[] newAccountsForType = new Account[oldLength + 1]; - if (accountsForType != null) { - System.arraycopy(accountsForType, 0, newAccountsForType, 0, oldLength); - } - newAccountsForType[oldLength] = account; - mAccountCache.put(account.type, newAccountsForType); + private void insertAccountIntoCacheLocked(Account account) { + Account[] accountsForType = mAccountCache.get(account.type); + int oldLength = (accountsForType != null) ? accountsForType.length : 0; + Account[] newAccountsForType = new Account[oldLength + 1]; + if (accountsForType != null) { + System.arraycopy(accountsForType, 0, newAccountsForType, 0, oldLength); } + newAccountsForType[oldLength] = account; + mAccountCache.put(account.type, newAccountsForType); } - protected Account[] getAccountsByTypeFromCache(String accountType) { - synchronized (mCacheLock) { - if (accountType != null) { - final Account[] accounts = mAccountCache.get(accountType); - if (accounts == null) { - return EMPTY_ACCOUNT_ARRAY; - } else { - return Arrays.copyOf(accounts, accounts.length); - } + protected Account[] getAccountsFromCacheLocked(String accountType) { + if (accountType != null) { + final Account[] accounts = mAccountCache.get(accountType); + if (accounts == null) { + return EMPTY_ACCOUNT_ARRAY; } else { - int totalLength = 0; - for (Account[] accounts : mAccountCache.values()) { - totalLength += accounts.length; - } - if (totalLength == 0) { - return EMPTY_ACCOUNT_ARRAY; - } - Account[] accounts = new Account[totalLength]; - totalLength = 0; - for (Account[] accountsOfType : mAccountCache.values()) { - System.arraycopy(accountsOfType, 0, accounts, totalLength, - accountsOfType.length); - totalLength += accountsOfType.length; - } - return accounts; + return Arrays.copyOf(accounts, accounts.length); } + } else { + int totalLength = 0; + for (Account[] accounts : mAccountCache.values()) { + totalLength += accounts.length; + } + if (totalLength == 0) { + return EMPTY_ACCOUNT_ARRAY; + } + Account[] accounts = new Account[totalLength]; + totalLength = 0; + for (Account[] accountsOfType : mAccountCache.values()) { + System.arraycopy(accountsOfType, 0, accounts, totalLength, + accountsOfType.length); + totalLength += accountsOfType.length; + } + return accounts; } } - protected void writeUserDataIntoCache(Account account, String key, String value) { - synchronized (mCacheLock) { - HashMap userDataForAccount = mUserDataCache.get(account); - if (userDataForAccount == null) { - userDataForAccount = readUserDataForAccountFromDatabase(account); - mUserDataCache.put(account, userDataForAccount); - } - if (value == null) { - userDataForAccount.remove(key); - } else { - userDataForAccount.put(key, value); - } + protected void writeUserDataIntoCacheLocked(final SQLiteDatabase db, Account account, + String key, String value) { + HashMap userDataForAccount = mUserDataCache.get(account); + if (userDataForAccount == null) { + userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account); + mUserDataCache.put(account, userDataForAccount); + } + if (value == null) { + userDataForAccount.remove(key); + } else { + userDataForAccount.put(key, value); } } - protected void writeAuthTokenIntoCache(Account account, String key, String value) { - synchronized (mCacheLock) { - HashMap authTokensForAccount = mAuthTokenCache.get(account); - if (authTokensForAccount == null) { - authTokensForAccount = readAuthTokensForAccountFromDatabase(account); - mAuthTokenCache.put(account, authTokensForAccount); - } - if (value == null) { - authTokensForAccount.remove(key); - } else { - authTokensForAccount.put(key, value); - } + protected void writeAuthTokenIntoCacheLocked(final SQLiteDatabase db, Account account, + String key, String value) { + HashMap authTokensForAccount = mAuthTokenCache.get(account); + if (authTokensForAccount == null) { + authTokensForAccount = readAuthTokensForAccountFromDatabaseLocked(db, account); + mAuthTokenCache.put(account, authTokensForAccount); + } + if (value == null) { + authTokensForAccount.remove(key); + } else { + authTokensForAccount.put(key, value); } } - protected String readAuthTokenFromCache(Account account, String authTokenType) { + protected String readAuthTokenInternal(Account account, String authTokenType) { synchronized (mCacheLock) { HashMap authTokensForAccount = mAuthTokenCache.get(account); if (authTokensForAccount == null) { // need to populate the cache for this account - authTokensForAccount = readAuthTokensForAccountFromDatabase(account); + final SQLiteDatabase db = mOpenHelper.getReadableDatabase(); + authTokensForAccount = readAuthTokensForAccountFromDatabaseLocked(db, account); mAuthTokenCache.put(account, authTokensForAccount); } return authTokensForAccount.get(authTokenType); } } - protected String readUserDataFromCache(Account account, String key) { + protected String readUserDataInternal(Account account, String key) { synchronized (mCacheLock) { HashMap userDataForAccount = mUserDataCache.get(account); if (userDataForAccount == null) { // need to populate the cache for this account - userDataForAccount = readUserDataForAccountFromDatabase(account); + final SQLiteDatabase db = mOpenHelper.getReadableDatabase(); + userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account); mUserDataCache.put(account, userDataForAccount); } return userDataForAccount.get(key); } } - protected HashMap readUserDataForAccountFromDatabase(Account account) { + protected HashMap readUserDataForAccountFromDatabaseLocked( + final SQLiteDatabase db, Account account) { HashMap userDataForAccount = new HashMap(); - SQLiteDatabase db = mOpenHelper.getReadableDatabase(); Cursor cursor = db.query(TABLE_EXTRAS, COLUMNS_EXTRAS_KEY_AND_VALUE, SELECTION_USERDATA_BY_ACCOUNT, @@ -2195,9 +2227,9 @@ public class AccountManagerService return userDataForAccount; } - protected HashMap readAuthTokensForAccountFromDatabase(Account account) { + protected HashMap readAuthTokensForAccountFromDatabaseLocked( + final SQLiteDatabase db, Account account) { HashMap authTokensForAccount = new HashMap(); - SQLiteDatabase db = mOpenHelper.getReadableDatabase(); Cursor cursor = db.query(TABLE_AUTHTOKENS, COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN, SELECTION_AUTHTOKENS_BY_ACCOUNT, diff --git a/core/tests/coretests/src/android/accounts/AccountManagerServiceTest.java b/core/tests/coretests/src/android/accounts/AccountManagerServiceTest.java index 887b03289f006..6efc61a01b556 100644 --- a/core/tests/coretests/src/android/accounts/AccountManagerServiceTest.java +++ b/core/tests/coretests/src/android/accounts/AccountManagerServiceTest.java @@ -96,7 +96,7 @@ public class AccountManagerServiceTest extends AndroidTestCase { assertEquals(a21, accounts[1]); assertEquals(a31, accounts[2]); - mAms.removeAccount(a21); + mAms.removeAccountInternal(a21); accounts = mAms.getAccounts("type1" ); Arrays.sort(accounts, new AccountSorter()); From a59af53599807c8558c58ef91e2cf8daebcc2c29 Mon Sep 17 00:00:00 2001 From: Christopher Tate Date: Tue, 1 Mar 2011 11:55:27 -0800 Subject: [PATCH 06/14] Fix binder proxy death notice tracking There was an issue with stale recipient tracking when BinderProxy weak references had been purged and a new proxy object allocated for a still-live underlying IBinder. The death recipient bookkeeping has now been reworked so that it's fundmentally tied to the BinderProxy instances, not maintained as global state, to prevent this sort of confusion entirely. Bug 3499939 Change-Id: I75c5216b6d53b90868ac969e32c9725201e51be3 --- core/java/android/os/Binder.java | 1 + core/jni/android_util_Binder.cpp | 90 +++++++++++++------------------- 2 files changed, 37 insertions(+), 54 deletions(-) diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java index a402c91baa914..7dc36f99ca657 100644 --- a/core/java/android/os/Binder.java +++ b/core/java/android/os/Binder.java @@ -392,4 +392,5 @@ final class BinderProxy implements IBinder { final private WeakReference mSelf; private int mObject; + private int mOrgue; } diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp index 15362eb40e660..5deed1ec26d3f 100644 --- a/core/jni/android_util_Binder.cpp +++ b/core/jni/android_util_Binder.cpp @@ -105,6 +105,7 @@ static struct binderproxy_offsets_t // Object state. jfieldID mObject; jfieldID mSelf; + jfieldID mOrgue; } gBinderProxyOffsets; @@ -374,12 +375,12 @@ public: class JavaDeathRecipient : public IBinder::DeathRecipient { public: - JavaDeathRecipient(JNIEnv* env, jobject object, sp& list) + JavaDeathRecipient(JNIEnv* env, jobject object, const sp& list) : mVM(jnienv_to_javavm(env)), mObject(env->NewGlobalRef(object)), mList(list) { // These objects manage their own lifetimes so are responsible for final bookkeeping. // The list holds a strong reference to this object. - mList->add(this); + list->add(this); android_atomic_inc(&gNumDeathRefs); incRefsCreated(env); @@ -404,7 +405,10 @@ public: void clearReference() { - mList->remove(this); + sp list = mList.promote(); + if (list != NULL) { + list->remove(this); + } } bool matches(jobject obj) { @@ -424,7 +428,7 @@ protected: private: JavaVM* const mVM; jobject const mObject; - sp mList; + wp mList; }; // ---------------------------------------------------------------------------- @@ -436,7 +440,7 @@ DeathRecipientList::~DeathRecipientList() { // to the list are holding references on the list object. Only when they are torn // down can the list header be destroyed. if (mList.size() > 0) { - LOGE("Retiring binder %p with extant death recipients\n", this); + LOGE("Retiring DRL %p with extant death recipients\n", this); } } @@ -470,9 +474,6 @@ sp DeathRecipientList::find(jobject recipient) { return NULL; } -static KeyedVector > gDeathRecipientsByIBinder; -static Mutex gDeathRecipientMapLock; - // ---------------------------------------------------------------------------- namespace android { @@ -517,7 +518,7 @@ jobject javaObjectForIBinder(JNIEnv* env, const sp& val) object = env->NewObject(gBinderProxyOffsets.mClass, gBinderProxyOffsets.mConstructor); if (object != NULL) { - LOGV("objectForBinder %p: created new %p!\n", val.get(), object); + LOGV("objectForBinder %p: created new proxy %p !\n", val.get(), object); // The proxy holds a reference to the native object. env->SetIntField(object, gBinderProxyOffsets.mObject, (int)val.get()); val->incStrong(object); @@ -529,6 +530,11 @@ jobject javaObjectForIBinder(JNIEnv* env, const sp& val) val->attachObject(&gBinderProxyOffsets, refObject, jnienv_to_javavm(env), proxy_cleanup); + // Also remember the death recipients registered on this proxy + sp drl = new DeathRecipientList; + drl->incStrong((void*)javaObjectForIBinder); + env->SetIntField(object, gBinderProxyOffsets.mOrgue, reinterpret_cast(drl.get())); + // Note that a new object reference has been created. android_atomic_inc(&gNumProxyRefs); incRefsCreated(env); @@ -1027,24 +1033,9 @@ static void android_os_BinderProxy_linkToDeath(JNIEnv* env, jobject obj, LOGV("linkToDeath: binder=%p recipient=%p\n", target, recipient); if (!target->localBinder()) { - sp jdr; - - { - sp list; - AutoMutex _maplocker(gDeathRecipientMapLock); - - ssize_t listIndex = gDeathRecipientsByIBinder.indexOfKey(target); - if (listIndex < 0) { - // Set up the death notice bookkeeping for this binder lazily - list = new DeathRecipientList; - gDeathRecipientsByIBinder.add(target, list); - } else { - list = gDeathRecipientsByIBinder.valueAt(listIndex); - } - - jdr = new JavaDeathRecipient(env, recipient, list); - } - + DeathRecipientList* list = (DeathRecipientList*) + env->GetIntField(obj, gBinderProxyOffsets.mOrgue); + sp jdr = new JavaDeathRecipient(env, recipient, list); status_t err = target->linkToDeath(jdr, NULL, flags); if (err != NO_ERROR) { // Failure adding the death recipient, so clear its reference @@ -1075,20 +1066,11 @@ static jboolean android_os_BinderProxy_unlinkToDeath(JNIEnv* env, jobject obj, if (!target->localBinder()) { status_t err = NAME_NOT_FOUND; - sp origJDR; - { - AutoMutex _maplocker(gDeathRecipientMapLock); - ssize_t listIndex = gDeathRecipientsByIBinder.indexOfKey(target); - if (listIndex >= 0) { - sp list = gDeathRecipientsByIBinder.valueAt(listIndex); - origJDR = list->find(recipient); - } else { - // If there is no DeathRecipientList for this binder, it means the binder - // is dead and in the process of being cleaned up. - err = DEAD_OBJECT; - } - } - // If we found the matching recipient, proceed to unlink using that + + // If we find the matching recipient, proceed to unlink using that + DeathRecipientList* list = (DeathRecipientList*) + env->GetIntField(obj, gBinderProxyOffsets.mOrgue); + sp origJDR = list->find(recipient); if (origJDR != NULL) { wp dr; err = target->unlinkToDeath(origJDR, NULL, flags, &dr); @@ -1115,20 +1097,17 @@ static jboolean android_os_BinderProxy_unlinkToDeath(JNIEnv* env, jobject obj, static void android_os_BinderProxy_destroy(JNIEnv* env, jobject obj) { IBinder* b = (IBinder*) - env->GetIntField(obj, gBinderProxyOffsets.mObject); - LOGV("Destroying BinderProxy %p: binder=%p\n", obj, b); - env->SetIntField(obj, gBinderProxyOffsets.mObject, 0); - b->decStrong(obj); - IPCThreadState::self()->flushCommands(); + env->GetIntField(obj, gBinderProxyOffsets.mObject); + DeathRecipientList* drl = (DeathRecipientList*) + env->GetIntField(obj, gBinderProxyOffsets.mOrgue); - // tear down the death recipient bookkeeping - { - AutoMutex _maplocker(gDeathRecipientMapLock); - ssize_t listIndex = gDeathRecipientsByIBinder.indexOfKey(b); - if (listIndex >= 0) { - gDeathRecipientsByIBinder.removeItemsAt((size_t)listIndex); - } - } + LOGV("Destroying BinderProxy %p: binder=%p drl=%p\n", obj, b, drl); + env->SetIntField(obj, gBinderProxyOffsets.mObject, 0); + env->SetIntField(obj, gBinderProxyOffsets.mOrgue, 0); + drl->decStrong((void*)javaObjectForIBinder); + b->decStrong(obj); + + IPCThreadState::self()->flushCommands(); } // ---------------------------------------------------------------------------- @@ -1178,6 +1157,9 @@ static int int_register_android_os_BinderProxy(JNIEnv* env) gBinderProxyOffsets.mSelf = env->GetFieldID(clazz, "mSelf", "Ljava/lang/ref/WeakReference;"); assert(gBinderProxyOffsets.mSelf); + gBinderProxyOffsets.mOrgue + = env->GetFieldID(clazz, "mOrgue", "I"); + assert(gBinderProxyOffsets.mOrgue); return AndroidRuntime::registerNativeMethods( env, kBinderProxyPathName, From 330dd4c2397e27517242ddfff7050c104849ac2a Mon Sep 17 00:00:00 2001 From: Svetoslav Ganov Date: Thu, 3 Mar 2011 12:55:52 -0800 Subject: [PATCH 07/14] NPE in in AppGlobals#getIntCoreSetting bug:3508658 It ActivityThread#currentActivityThread() is called when the ActivityThread is not attached it returns null and AppGlobals#getIntCoreSetting was not checking for that. Change-Id: I5e00d1947a161ad1e52ecfaa12cbbac3b534a0db --- core/java/android/app/AppGlobals.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/core/java/android/app/AppGlobals.java b/core/java/android/app/AppGlobals.java index 55515b8ae4d04..2b6db8b590f8c 100644 --- a/core/java/android/app/AppGlobals.java +++ b/core/java/android/app/AppGlobals.java @@ -55,6 +55,11 @@ public class AppGlobals { * @return The core settings. */ public static int getIntCoreSetting(String key, int defaultValue) { - return ActivityThread.currentActivityThread().getIntCoreSetting(key, defaultValue); + ActivityThread currentActivityThread = ActivityThread.currentActivityThread(); + if (currentActivityThread != null) { + return currentActivityThread.getIntCoreSetting(key, defaultValue); + } else { + return defaultValue; + } } } From 64e754faabc7015db7f213460cfbae451070a055 Mon Sep 17 00:00:00 2001 From: James Dong Date: Fri, 4 Mar 2011 11:25:17 -0800 Subject: [PATCH 08/14] Do not enforce the rule in MP3Extractor that all audio frames in an mp3 file must have the same mode For some mp3 files, the mode can be changed from one audio frame to another. Without the capability to switch modes, the decoded audio may have lots of artifacts. bug - 3253310 Change-Id: Id57bb1f204642d15e38a800c72214f6794cfaa37 --- media/libstagefright/MP3Extractor.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/media/libstagefright/MP3Extractor.cpp b/media/libstagefright/MP3Extractor.cpp index eb4c68d722d6e..03ce202ea5169 100644 --- a/media/libstagefright/MP3Extractor.cpp +++ b/media/libstagefright/MP3Extractor.cpp @@ -39,10 +39,10 @@ namespace android { // Everything must match except for -// protection, bitrate, padding, private bits, mode extension, +// protection, bitrate, padding, private bits, mode, mode extension, // copyright bit, original bit and emphasis. // Yes ... there are things that must indeed match... -static const uint32_t kMask = 0xfffe0cc0; +static const uint32_t kMask = 0xfffe0c00; // static bool MP3Extractor::get_mp3_frame_size( From 68107c4fa5b5c9e7e5a3f3995fb0d749c795c307 Mon Sep 17 00:00:00 2001 From: Adam Powell Date: Thu, 3 Mar 2011 17:54:55 -0800 Subject: [PATCH 09/14] Fix bug 3509417 - Serialize touch event handling in webview Make sure WebView doesn't process touch events out of order. Change-Id: Id046139af99d0d3f32cc481ca1d85d32ced67f75 --- core/java/android/webkit/WebView.java | 442 ++++++++++++++++------ core/java/android/webkit/WebViewCore.java | 10 +- 2 files changed, 323 insertions(+), 129 deletions(-) diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 2d3157e9d1301..ef7105548617d 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -847,6 +847,9 @@ public class WebView extends AbsoluteLayout private Rect mScrollingLayerBounds = new Rect(); private boolean mSentAutoScrollMessage = false; + // used for serializing asynchronously handled touch events. + private final TouchEventQueue mTouchEventQueue = new TouchEventQueue(); + // Used to notify listeners of a new picture. private PictureListener mPictureListener; /** @@ -5081,6 +5084,8 @@ public class WebView extends AbsoluteLayout } addAccessibilityApisToJavaScript(); + + mTouchEventQueue.reset(); } @Override @@ -5432,34 +5437,17 @@ public class WebView extends AbsoluteLayout + " numPointers=" + ev.getPointerCount()); } - int action = ev.getActionMasked(); - if (ev.getPointerCount() > 1) { // Multi-touch - mIsHandlingMultiTouch = true; - - // If WebKit already showed no interests in this sequence of events, - // WebView handles them directly. - if (mPreventDefault == PREVENT_DEFAULT_NO) { - handleMultiTouchInWebView(ev); - } else { - passMultiTouchToWebKit(ev); - } - return true; + // If WebKit wasn't interested in this multitouch gesture, enqueue + // the event for handling directly rather than making the round trip + // to WebKit and back. + if (ev.getPointerCount() > 1 && mPreventDefault != PREVENT_DEFAULT_NO) { + passMultiTouchToWebKit(ev, mTouchEventQueue.nextTouchSequence()); } else { - final ScaleGestureDetector detector = mZoomManager.getMultiTouchGestureDetector(); - if (detector != null) { - // ScaleGestureDetector needs a consistent event stream to operate properly. - // It won't take any action with fewer than two pointers, but it needs to - // update internal bookkeeping state. - detector.onTouchEvent(ev); - } + mTouchEventQueue.enqueueTouchEvent(ev); } - // Skip ACTION_MOVE for single touch if it's still handling multi-touch. - if (mIsHandlingMultiTouch && action == MotionEvent.ACTION_MOVE) { - return false; - } - - return handleTouchEventCommon(ev, action, Math.round(ev.getX()), Math.round(ev.getY())); + // Since all events are handled asynchronously, we always want the gesture stream. + return true; } /* @@ -5579,6 +5567,7 @@ public class WebView extends AbsoluteLayout ted.mReprocess = mDeferTouchProcess; ted.mNativeLayer = nativeScrollableLayer( contentX, contentY, ted.mNativeLayerRect, null); + ted.mDontEnqueueResult = true; mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted); if (mDeferTouchProcess) { // still needs to set them for compute deltaX/Y @@ -5625,6 +5614,7 @@ public class WebView extends AbsoluteLayout ted.mReprocess = mDeferTouchProcess; ted.mNativeLayer = mScrollingLayer; ted.mNativeLayerRect.set(mScrollingLayerRect); + ted.mDontEnqueueResult = true; mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted); mLastSentTouchTime = eventTime; if (mDeferTouchProcess) { @@ -5806,6 +5796,7 @@ public class WebView extends AbsoluteLayout ted.mReprocess = mDeferTouchProcess; ted.mNativeLayer = mScrollingLayer; ted.mNativeLayerRect.set(mScrollingLayerRect); + ted.mDontEnqueueResult = true; mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted); } mLastTouchUpTime = eventTime; @@ -5828,6 +5819,7 @@ public class WebView extends AbsoluteLayout ted.mNativeLayer = nativeScrollableLayer( contentX, contentY, ted.mNativeLayerRect, null); + ted.mDontEnqueueResult = true; mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted); } else if (mPreventDefault != PREVENT_DEFAULT_YES){ mZoomManager.handleDoubleTap(mLastTouchX, mLastTouchY); @@ -5952,7 +5944,7 @@ public class WebView extends AbsoluteLayout return true; } - private void passMultiTouchToWebKit(MotionEvent ev) { + private void passMultiTouchToWebKit(MotionEvent ev, long sequence) { TouchEventData ted = new TouchEventData(); ted.mAction = ev.getActionMasked(); final int count = ev.getPointerCount(); @@ -5967,6 +5959,7 @@ public class WebView extends AbsoluteLayout ted.mMetaState = ev.getMetaState(); ted.mReprocess = true; ted.mMotionEvent = MotionEvent.obtain(ev); + ted.mSequence = sequence; mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted); cancelLongPress(); mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS); @@ -6016,7 +6009,7 @@ public class WebView extends AbsoluteLayout if (action == MotionEvent.ACTION_POINTER_DOWN) { cancelTouch(); action = MotionEvent.ACTION_DOWN; - } else if (action == MotionEvent.ACTION_POINTER_UP) { + } else if (action == MotionEvent.ACTION_POINTER_UP && ev.getPointerCount() == 2) { // set mLastTouchX/Y to the remaining point mLastTouchX = Math.round(x); mLastTouchY = Math.round(y); @@ -6044,6 +6037,7 @@ public class WebView extends AbsoluteLayout ted.mAction = MotionEvent.ACTION_CANCEL; ted.mNativeLayer = nativeScrollableLayer( x, y, ted.mNativeLayerRect, null); + ted.mDontEnqueueResult = true; mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted); mPreventDefault = PREVENT_DEFAULT_IGNORE; } @@ -7121,6 +7115,297 @@ public class WebView extends AbsoluteLayout return mWebViewCore; } + /** + * Used only by TouchEventQueue to store pending touch events. + */ + private static class QueuedTouch { + long mSequence; + MotionEvent mEvent; // Optional + TouchEventData mTed; // Optional + + QueuedTouch mNext; + + public QueuedTouch set(TouchEventData ted) { + mSequence = ted.mSequence; + mTed = ted; + mEvent = null; + mNext = null; + return this; + } + + public QueuedTouch set(MotionEvent ev, long sequence) { + mEvent = MotionEvent.obtain(ev); + mSequence = sequence; + mTed = null; + mNext = null; + return this; + } + + public QueuedTouch add(QueuedTouch other) { + if (other.mSequence < mSequence) { + other.mNext = this; + return other; + } + + QueuedTouch insertAt = this; + while (insertAt.mNext != null && insertAt.mNext.mSequence < other.mSequence) { + insertAt = insertAt.mNext; + } + other.mNext = insertAt.mNext; + insertAt.mNext = other; + return this; + } + } + + /** + * WebView handles touch events asynchronously since some events must be passed to WebKit + * for potentially slower processing. TouchEventQueue serializes touch events regardless + * of which path they take to ensure that no events are ever processed out of order + * by WebView. + */ + private class TouchEventQueue { + private long mNextTouchSequence = Long.MIN_VALUE + 1; + private long mLastHandledTouchSequence = Long.MIN_VALUE; + private QueuedTouch mTouchEventQueue; + private QueuedTouch mQueuedTouchRecycleBin; + private int mQueuedTouchRecycleCount; + private static final int MAX_RECYCLED_QUEUED_TOUCH = 15; + + private QueuedTouch obtainQueuedTouch() { + if (mQueuedTouchRecycleBin != null) { + QueuedTouch result = mQueuedTouchRecycleBin; + mQueuedTouchRecycleBin = result.mNext; + mQueuedTouchRecycleCount--; + return result; + } + return new QueuedTouch(); + } + + private void recycleQueuedTouch(QueuedTouch qd) { + if (mQueuedTouchRecycleCount < MAX_RECYCLED_QUEUED_TOUCH) { + qd.mNext = mQueuedTouchRecycleBin; + mQueuedTouchRecycleBin = qd; + mQueuedTouchRecycleCount++; + } + } + + /** + * Reset the touch event queue. This will dump any pending events + * and reset the sequence numbering. + */ + public void reset() { + mNextTouchSequence = Long.MIN_VALUE + 1; + mLastHandledTouchSequence = Long.MIN_VALUE; + while (mTouchEventQueue != null) { + QueuedTouch recycleMe = mTouchEventQueue; + mTouchEventQueue = mTouchEventQueue.mNext; + recycleQueuedTouch(recycleMe); + } + } + + /** + * Return the next valid sequence number for tagging incoming touch events. + * @return The next touch event sequence number + */ + public long nextTouchSequence() { + return mNextTouchSequence++; + } + + /** + * Enqueue a touch event in the form of TouchEventData. + * The sequence number will be read from the mSequence field of the argument. + * + * If the touch event's sequence number is the next in line to be processed, it will + * be handled before this method returns. Any subsequent events that have already + * been queued will also be processed in their proper order. + * + * @param ted Touch data to be processed in order. + */ + public void enqueueTouchEvent(TouchEventData ted) { + if (mLastHandledTouchSequence + 1 == ted.mSequence) { + handleQueuedTouchEventData(ted); + + mLastHandledTouchSequence++; + + // Do we have any more? Run them if so. + QueuedTouch qd = mTouchEventQueue; + while (qd != null && qd.mSequence == mLastHandledTouchSequence + 1) { + handleQueuedTouch(qd); + QueuedTouch recycleMe = qd; + qd = qd.mNext; + recycleQueuedTouch(recycleMe); + mLastHandledTouchSequence++; + } + mTouchEventQueue = qd; + } else { + QueuedTouch qd = obtainQueuedTouch().set(ted); + mTouchEventQueue = mTouchEventQueue == null ? qd : mTouchEventQueue.add(qd); + } + } + + /** + * Enqueue a touch event in the form of a MotionEvent from the framework. + * + * If the touch event's sequence number is the next in line to be processed, it will + * be handled before this method returns. Any subsequent events that have already + * been queued will also be processed in their proper order. + * + * @param ev MotionEvent to be processed in order + */ + public void enqueueTouchEvent(MotionEvent ev) { + final long sequence = nextTouchSequence(); + if (mLastHandledTouchSequence + 1 == sequence) { + handleQueuedMotionEvent(ev); + + mLastHandledTouchSequence++; + + // Do we have any more? Run them if so. + QueuedTouch qd = mTouchEventQueue; + while (qd != null && qd.mSequence == mLastHandledTouchSequence + 1) { + handleQueuedTouch(qd); + QueuedTouch recycleMe = qd; + qd = qd.mNext; + recycleQueuedTouch(recycleMe); + mLastHandledTouchSequence++; + } + mTouchEventQueue = qd; + } else { + QueuedTouch qd = obtainQueuedTouch().set(ev, sequence); + mTouchEventQueue = mTouchEventQueue == null ? qd : mTouchEventQueue.add(qd); + } + } + + private void handleQueuedTouch(QueuedTouch qt) { + if (qt.mTed != null) { + handleQueuedTouchEventData(qt.mTed); + } else { + handleQueuedMotionEvent(qt.mEvent); + qt.mEvent.recycle(); + } + } + + private void handleQueuedMotionEvent(MotionEvent ev) { + int action = ev.getActionMasked(); + if (ev.getPointerCount() > 1) { // Multi-touch + mIsHandlingMultiTouch = true; + + handleMultiTouchInWebView(ev); + } else { + final ScaleGestureDetector detector = mZoomManager.getMultiTouchGestureDetector(); + if (detector != null) { + // ScaleGestureDetector needs a consistent event stream to operate properly. + // It won't take any action with fewer than two pointers, but it needs to + // update internal bookkeeping state. + detector.onTouchEvent(ev); + } + + handleTouchEventCommon(ev, action, Math.round(ev.getX()), Math.round(ev.getY())); + } + } + + private void handleQueuedTouchEventData(TouchEventData ted) { + if (!ted.mReprocess) { + if (ted.mAction == MotionEvent.ACTION_DOWN + && mPreventDefault == PREVENT_DEFAULT_MAYBE_YES) { + // if prevent default is called from WebCore, UI + // will not handle the rest of the touch events any + // more. + mPreventDefault = ted.mNativeResult ? PREVENT_DEFAULT_YES + : PREVENT_DEFAULT_NO_FROM_TOUCH_DOWN; + } else if (ted.mAction == MotionEvent.ACTION_MOVE + && mPreventDefault == PREVENT_DEFAULT_NO_FROM_TOUCH_DOWN) { + // the return for the first ACTION_MOVE will decide + // whether UI will handle touch or not. Currently no + // support for alternating prevent default + mPreventDefault = ted.mNativeResult ? PREVENT_DEFAULT_YES + : PREVENT_DEFAULT_NO; + } + if (mPreventDefault == PREVENT_DEFAULT_YES) { + mTouchHighlightRegion.setEmpty(); + } + } else { + if (ted.mPoints.length > 1) { // multi-touch + if (ted.mAction == MotionEvent.ACTION_POINTER_UP && + ted.mMotionEvent.getPointerCount() == 2) { + mIsHandlingMultiTouch = false; + } + if (!ted.mNativeResult) { + mPreventDefault = PREVENT_DEFAULT_NO; + handleMultiTouchInWebView(ted.mMotionEvent); + } else { + mPreventDefault = PREVENT_DEFAULT_YES; + } + return; + } + + // prevent default is not called in WebCore, so the + // message needs to be reprocessed in UI + if (!ted.mNativeResult) { + // Following is for single touch. + switch (ted.mAction) { + case MotionEvent.ACTION_DOWN: + mLastDeferTouchX = contentToViewX(ted.mPoints[0].x) + - mScrollX; + mLastDeferTouchY = contentToViewY(ted.mPoints[0].y) + - mScrollY; + mDeferTouchMode = TOUCH_INIT_MODE; + break; + case MotionEvent.ACTION_MOVE: { + // no snapping in defer process + int x = contentToViewX(ted.mPoints[0].x) - mScrollX; + int y = contentToViewY(ted.mPoints[0].y) - mScrollY; + if (mDeferTouchMode != TOUCH_DRAG_MODE) { + mDeferTouchMode = TOUCH_DRAG_MODE; + mLastDeferTouchX = x; + mLastDeferTouchY = y; + startScrollingLayer(x, y); + startDrag(); + } + int deltaX = pinLocX((int) (mScrollX + + mLastDeferTouchX - x)) + - mScrollX; + int deltaY = pinLocY((int) (mScrollY + + mLastDeferTouchY - y)) + - mScrollY; + doDrag(deltaX, deltaY); + if (deltaX != 0) mLastDeferTouchX = x; + if (deltaY != 0) mLastDeferTouchY = y; + break; + } + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + if (mDeferTouchMode == TOUCH_DRAG_MODE) { + // no fling in defer process + mScroller.springBack(mScrollX, mScrollY, 0, + computeMaxScrollX(), 0, + computeMaxScrollY()); + invalidate(); + WebViewCore.resumePriority(); + WebViewCore.resumeUpdatePicture(mWebViewCore); + } + mDeferTouchMode = TOUCH_DONE_MODE; + break; + case WebViewCore.ACTION_DOUBLETAP: + // doDoubleTap() needs mLastTouchX/Y as anchor + mLastTouchX = contentToViewX(ted.mPoints[0].x) - mScrollX; + mLastTouchY = contentToViewY(ted.mPoints[0].y) - mScrollY; + mZoomManager.handleDoubleTap(mLastTouchX, mLastTouchY); + mDeferTouchMode = TOUCH_DONE_MODE; + break; + case WebViewCore.ACTION_LONGPRESS: + HitTestResult hitTest = getHitTestResult(); + if (hitTest != null && hitTest.mType + != HitTestResult.UNKNOWN_TYPE) { + performLongClick(); + } + mDeferTouchMode = TOUCH_DONE_MODE; + break; + } + } + } + } + } + //------------------------------------------------------------------------- // Methods can be called from a separate thread, like WebViewCore // If it needs to call the View system, it has to send message. @@ -7236,6 +7521,7 @@ public class WebView extends AbsoluteLayout ted.mNativeLayer = nativeScrollableLayer( ted.mPoints[0].x, ted.mPoints[0].y, ted.mNativeLayerRect, null); + ted.mDontEnqueueResult = true; mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted); } else if (mPreventDefault != PREVENT_DEFAULT_YES) { mTouchMode = TOUCH_DONE_MODE; @@ -7450,105 +7736,9 @@ public class WebView extends AbsoluteLayout if (inFullScreenMode()) { break; } - if (msg.obj == null) { - if (msg.arg1 == MotionEvent.ACTION_DOWN - && mPreventDefault == PREVENT_DEFAULT_MAYBE_YES) { - // if prevent default is called from WebCore, UI - // will not handle the rest of the touch events any - // more. - mPreventDefault = msg.arg2 == 1 ? PREVENT_DEFAULT_YES - : PREVENT_DEFAULT_NO_FROM_TOUCH_DOWN; - } else if (msg.arg1 == MotionEvent.ACTION_MOVE - && mPreventDefault == PREVENT_DEFAULT_NO_FROM_TOUCH_DOWN) { - // the return for the first ACTION_MOVE will decide - // whether UI will handle touch or not. Currently no - // support for alternating prevent default - mPreventDefault = msg.arg2 == 1 ? PREVENT_DEFAULT_YES - : PREVENT_DEFAULT_NO; - } - if (mPreventDefault == PREVENT_DEFAULT_YES) { - mTouchHighlightRegion.setEmpty(); - } - } else { - TouchEventData ted = (TouchEventData) msg.obj; - - if (ted.mPoints.length > 1) { // multi-touch - if (ted.mAction == MotionEvent.ACTION_POINTER_UP) { - mIsHandlingMultiTouch = false; - } - if (msg.arg2 == 0) { - mPreventDefault = PREVENT_DEFAULT_NO; - handleMultiTouchInWebView(ted.mMotionEvent); - } else { - mPreventDefault = PREVENT_DEFAULT_YES; - } - break; - } - - // prevent default is not called in WebCore, so the - // message needs to be reprocessed in UI - if (msg.arg2 == 0) { - // Following is for single touch. - switch (ted.mAction) { - case MotionEvent.ACTION_DOWN: - mLastDeferTouchX = contentToViewX(ted.mPoints[0].x) - - mScrollX; - mLastDeferTouchY = contentToViewY(ted.mPoints[0].y) - - mScrollY; - mDeferTouchMode = TOUCH_INIT_MODE; - break; - case MotionEvent.ACTION_MOVE: { - // no snapping in defer process - int x = contentToViewX(ted.mPoints[0].x) - mScrollX; - int y = contentToViewY(ted.mPoints[0].y) - mScrollY; - if (mDeferTouchMode != TOUCH_DRAG_MODE) { - mDeferTouchMode = TOUCH_DRAG_MODE; - mLastDeferTouchX = x; - mLastDeferTouchY = y; - startScrollingLayer(x, y); - startDrag(); - } - int deltaX = pinLocX((int) (mScrollX - + mLastDeferTouchX - x)) - - mScrollX; - int deltaY = pinLocY((int) (mScrollY - + mLastDeferTouchY - y)) - - mScrollY; - doDrag(deltaX, deltaY); - if (deltaX != 0) mLastDeferTouchX = x; - if (deltaY != 0) mLastDeferTouchY = y; - break; - } - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: - if (mDeferTouchMode == TOUCH_DRAG_MODE) { - // no fling in defer process - mScroller.springBack(mScrollX, mScrollY, 0, - computeMaxScrollX(), 0, - computeMaxScrollY()); - invalidate(); - WebViewCore.resumePriority(); - WebViewCore.resumeUpdatePicture(mWebViewCore); - } - mDeferTouchMode = TOUCH_DONE_MODE; - break; - case WebViewCore.ACTION_DOUBLETAP: - // doDoubleTap() needs mLastTouchX/Y as anchor - mLastTouchX = contentToViewX(ted.mPoints[0].x) - mScrollX; - mLastTouchY = contentToViewY(ted.mPoints[0].y) - mScrollY; - mZoomManager.handleDoubleTap(mLastTouchX, mLastTouchY); - mDeferTouchMode = TOUCH_DONE_MODE; - break; - case WebViewCore.ACTION_LONGPRESS: - HitTestResult hitTest = getHitTestResult(); - if (hitTest != null && hitTest.mType - != HitTestResult.UNKNOWN_TYPE) { - performLongClick(); - } - mDeferTouchMode = TOUCH_DONE_MODE; - break; - } - } + TouchEventData ted = (TouchEventData) msg.obj; + if (!ted.mDontEnqueueResult) { + mTouchEventQueue.enqueueTouchEvent(ted); } break; diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index c18c9e498db0e..f367b93820de1 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -825,6 +825,9 @@ final class WebViewCore { MotionEvent mMotionEvent; int mNativeLayer; Rect mNativeLayerRect = new Rect(); + long mSequence; + boolean mNativeResult; + boolean mDontEnqueueResult; } static class GeolocationPermissionsData { @@ -1332,13 +1335,14 @@ final class WebViewCore { nativeScrollLayer(ted.mNativeLayer, ted.mNativeLayerRect); } + ted.mNativeResult = nativeHandleTouchEvent(ted.mAction, ted.mIds, + xArray, yArray, count, ted.mMetaState); Message.obtain( mWebView.mPrivateHandler, WebView.PREVENT_TOUCH_ID, ted.mAction, - nativeHandleTouchEvent(ted.mAction, ted.mIds, - xArray, yArray, count, ted.mMetaState) ? 1 : 0, - ted.mReprocess ? ted : null).sendToTarget(); + ted.mNativeResult ? 1 : 0, + ted).sendToTarget(); break; } From 136b4e5b5389d7e3c93ea13251827d4f4dc4c88a Mon Sep 17 00:00:00 2001 From: Patrick Dubroy Date: Fri, 4 Mar 2011 11:24:07 -0800 Subject: [PATCH 10/14] Remove guard preventing onDetachedFromWindow() from being called This was the cause of 3510699: Launcher has leaked IntentReceiver. Change-Id: I7d0e4662314d657f6ac06a9a9d37861f8d8ee975 --- policy/src/com/android/internal/policy/impl/PhoneWindow.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java index ec89db371278a..64857edb3668c 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java @@ -2187,7 +2187,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { super.onDetachedFromWindow(); final Callback cb = getCallback(); - if (cb != null && !isDestroyed() && mFeatureId < 0) { + if (cb != null && mFeatureId < 0) { cb.onDetachedFromWindow(); } From 477c9ba1278495ac37b5804eef1ef2658d719e2c Mon Sep 17 00:00:00 2001 From: Adam Powell Date: Mon, 7 Mar 2011 15:36:33 -0800 Subject: [PATCH 11/14] Fix bug 4021346 - crash while navigating Change-Id: Iff60c5f4214c56597f2a1b393982d023866e490e --- core/java/android/view/ViewGroup.java | 5 ++--- core/java/android/widget/ListView.java | 5 +++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 3153ac5aaaddc..9f1eef92d33a7 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -4275,11 +4275,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * does not exist within the group */ public View getChildAt(int index) { - try { - return mChildren[index]; - } catch (IndexOutOfBoundsException ex) { + if (index < 0 || index >= mChildrenCount) { return null; } + return mChildren[index]; } /** diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java index 427126b0e68e4..af954c9022e64 100644 --- a/core/java/android/widget/ListView.java +++ b/core/java/android/widget/ListView.java @@ -3019,12 +3019,13 @@ public class ListView extends AbsListView { hasOpaqueScrollbars()) || super.isOpaque(); if (retValue) { // only return true if the list items cover the entire area of the view - final int listTop = mListPadding.top; + final int listTop = mListPadding != null ? mListPadding.top : mPaddingTop; View first = getChildAt(0); if (first == null || first.getTop() > listTop) { return false; } - final int listBottom = getHeight() - mListPadding.bottom; + final int listBottom = getHeight() - + (mListPadding != null ? mListPadding.bottom : mPaddingBottom); View last = getChildAt(getChildCount() - 1); if (last == null || last.getBottom() < listBottom) { return false; From cfb92cdbcafebdea08c6996a17f00fa80beac9c0 Mon Sep 17 00:00:00 2001 From: Adam Powell Date: Thu, 10 Mar 2011 15:53:24 -0800 Subject: [PATCH 12/14] Fix bug 4080407 - Browser stuck after scrolling- page stops responding Account for gaps in touch queue sequence numbering when we flush out canceled events from webcore's queue. Add a 2-second timeout to auto-flush old events when a new gesture begins. Change-Id: I764a7fcd673b3afb629055b001e9346f5b959dba --- core/java/android/webkit/WebView.java | 59 +++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 705eefc2d8a38..a6b02fcae6a19 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -6016,6 +6016,7 @@ public class WebView extends AbsoluteLayout if (shouldForwardTouchEvent()) { if (removeEvents) { mWebViewCore.removeMessages(EventHub.TOUCH_EVENT); + mTouchEventQueue.ignoreCurrentlyMissingEvents(); } TouchEventData ted = new TouchEventData(); ted.mIds = new int[1]; @@ -7154,11 +7155,15 @@ public class WebView extends AbsoluteLayout private class TouchEventQueue { private long mNextTouchSequence = Long.MIN_VALUE + 1; private long mLastHandledTouchSequence = Long.MIN_VALUE; + private long mIgnoreUntilSequence = Long.MIN_VALUE; private QueuedTouch mTouchEventQueue; private QueuedTouch mQueuedTouchRecycleBin; private int mQueuedTouchRecycleCount; private static final int MAX_RECYCLED_QUEUED_TOUCH = 15; + // milliseconds until we abandon hope of getting all of a previous gesture + private static final int QUEUED_GESTURE_TIMEOUT = 2000; + private QueuedTouch obtainQueuedTouch() { if (mQueuedTouchRecycleBin != null) { QueuedTouch result = mQueuedTouchRecycleBin; @@ -7169,6 +7174,13 @@ public class WebView extends AbsoluteLayout return new QueuedTouch(); } + /** + * Allow events with any currently missing sequence numbers to be skipped in processing. + */ + public void ignoreCurrentlyMissingEvents() { + mIgnoreUntilSequence = mNextTouchSequence; + } + private void recycleQueuedTouch(QueuedTouch qd) { if (mQueuedTouchRecycleCount < MAX_RECYCLED_QUEUED_TOUCH) { qd.mNext = mQueuedTouchRecycleBin; @@ -7184,6 +7196,7 @@ public class WebView extends AbsoluteLayout public void reset() { mNextTouchSequence = Long.MIN_VALUE + 1; mLastHandledTouchSequence = Long.MIN_VALUE; + mIgnoreUntilSequence = Long.MIN_VALUE; while (mTouchEventQueue != null) { QueuedTouch recycleMe = mTouchEventQueue; mTouchEventQueue = mTouchEventQueue.mNext; @@ -7210,6 +7223,15 @@ public class WebView extends AbsoluteLayout * @param ted Touch data to be processed in order. */ public void enqueueTouchEvent(TouchEventData ted) { + if (ted.mSequence < mLastHandledTouchSequence) { + // Stale event and we already moved on; drop it. (Should not be common.) + Log.w(LOGTAG, "Stale touch event " + MotionEvent.actionToString(ted.mAction) + + " received from webcore; ignoring"); + return; + } + + dropStaleGestures(ted.mMotionEvent, ted.mSequence); + if (mLastHandledTouchSequence + 1 == ted.mSequence) { handleQueuedTouchEventData(ted); @@ -7242,6 +7264,9 @@ public class WebView extends AbsoluteLayout */ public void enqueueTouchEvent(MotionEvent ev) { final long sequence = nextTouchSequence(); + + dropStaleGestures(ev, sequence); + if (mLastHandledTouchSequence + 1 == sequence) { handleQueuedMotionEvent(ev); @@ -7263,6 +7288,40 @@ public class WebView extends AbsoluteLayout } } + private void dropStaleGestures(MotionEvent ev, long sequence) { + if (ev != null && ev.getAction() == MotionEvent.ACTION_DOWN && + mTouchEventQueue != null) { + long eventTime = ev.getEventTime(); + long nextQueueTime = mTouchEventQueue.mTed != null ? + mTouchEventQueue.mTed.mMotionEvent.getEventTime() : + mTouchEventQueue.mEvent.getEventTime(); + if (eventTime > nextQueueTime + QUEUED_GESTURE_TIMEOUT) { + Log.w(LOGTAG, "Got ACTION_DOWN but still waiting on stale event. " + + "Ignoring previous queued events."); + QueuedTouch qd = mTouchEventQueue; + while (qd != null && qd.mSequence < sequence) { + QueuedTouch recycleMe = qd; + qd = qd.mNext; + recycleQueuedTouch(recycleMe); + } + mTouchEventQueue = qd; + mLastHandledTouchSequence = sequence - 1; + } + } + + if (mIgnoreUntilSequence > mLastHandledTouchSequence) { + QueuedTouch qd = mTouchEventQueue; + while (qd != null && qd.mSequence < mIgnoreUntilSequence && + qd.mSequence < sequence) { + mLastHandledTouchSequence = qd.mSequence; + QueuedTouch recycleMe = qd; + qd = qd.mNext; + recycleQueuedTouch(recycleMe); + } + mTouchEventQueue = qd; + } + } + private void handleQueuedTouch(QueuedTouch qt) { if (qt.mTed != null) { handleQueuedTouchEventData(qt.mTed); From d506e16f472cf16c755c9f3ed7450c9988e9447b Mon Sep 17 00:00:00 2001 From: Romain Guy Date: Tue, 15 Mar 2011 11:12:25 -0700 Subject: [PATCH 13/14] Fix disappearing edges in lists/scrollviews/etc. Bug #4093871 This bug was introduced by a change that modified the way fade areas are cleared. The previous change relied on Rect::intersect() to empty the intersected rect when the two rects don't intersect. Unfortunately this is not what intersect() does. The fix is rather simple and sets the layer's bounds to empty when they don't intersect with the clip or the viewport. This has the side effect of ignoring the layer which is the expected result. Change-Id: Icf0038b9a476c53f8eff7084136aba3033d093e6 --- libs/hwui/OpenGLRenderer.cpp | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index 5d9522e29fa0e..b8bd7d6758d27 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -407,15 +407,19 @@ bool OpenGLRenderer::createLayer(sp snapshot, float left, float top, mSnapshot->transform->mapRect(bounds); // Layers only make sense if they are in the framebuffer's bounds - bounds.intersect(*snapshot->clipRect); + if (bounds.intersect(*snapshot->clipRect)) { + // We cannot work with sub-pixels in this case + bounds.snapToPixelBoundaries(); - // We cannot work with sub-pixels in this case - bounds.snapToPixelBoundaries(); - - // When the layer is not an FBO, we may use glCopyTexImage so we - // need to make sure the layer does not extend outside the bounds - // of the framebuffer - bounds.intersect(snapshot->previous->viewport); + // When the layer is not an FBO, we may use glCopyTexImage so we + // need to make sure the layer does not extend outside the bounds + // of the framebuffer + if (!bounds.intersect(snapshot->previous->viewport)) { + bounds.setEmpty(); + } + } else { + bounds.setEmpty(); + } } if (bounds.isEmpty() || bounds.getWidth() > mCaches.maxTextureSize || From 5b3040964b94d04c6a7285664f4331326e271734 Mon Sep 17 00:00:00 2001 From: Adam Powell Date: Fri, 25 Mar 2011 13:58:19 -0700 Subject: [PATCH 14/14] Fix bug 4170809 - Pages with flash lose touch events Fix WebView touch cancel behavior when we time out waiting for a response from webkit. Change-Id: I0aeeea64b156e204dbe2dda913a42df28704ed16 --- core/java/android/webkit/WebView.java | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 96623b0b5a81d..a3ced9e1f2c13 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -6056,7 +6056,6 @@ public class WebView extends AbsoluteLayout if (shouldForwardTouchEvent()) { if (removeEvents) { mWebViewCore.removeMessages(EventHub.TOUCH_EVENT); - mTouchEventQueue.ignoreCurrentlyMissingEvents(); } TouchEventData ted = new TouchEventData(); ted.mIds = new int[1]; @@ -6073,6 +6072,12 @@ public class WebView extends AbsoluteLayout ted.mSequence = mTouchEventQueue.nextTouchSequence(); mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted); mPreventDefault = PREVENT_DEFAULT_IGNORE; + + if (removeEvents) { + // Mark this after sending the message above; we should + // be willing to ignore the cancel event that we just sent. + mTouchEventQueue.ignoreCurrentlyMissingEvents(); + } } } @@ -7266,17 +7271,18 @@ public class WebView extends AbsoluteLayout * been queued will also be processed in their proper order. * * @param ted Touch data to be processed in order. + * @return true if the event was processed before returning, false if it was just enqueued. */ - public void enqueueTouchEvent(TouchEventData ted) { + public boolean enqueueTouchEvent(TouchEventData ted) { if (ted.mSequence < mLastHandledTouchSequence) { // Stale event and we already moved on; drop it. (Should not be common.) Log.w(LOGTAG, "Stale touch event " + MotionEvent.actionToString(ted.mAction) + " received from webcore; ignoring"); - return; + return false; } if (dropStaleGestures(ted.mMotionEvent, ted.mSequence)) { - return; + return false; } if (mLastHandledTouchSequence + 1 == ted.mSequence) { @@ -7298,6 +7304,7 @@ public class WebView extends AbsoluteLayout QueuedTouch qd = obtainQueuedTouch().set(ted); mTouchEventQueue = mTouchEventQueue == null ? qd : mTouchEventQueue.add(qd); } + return true; } /** @@ -7851,11 +7858,11 @@ public class WebView extends AbsoluteLayout } TouchEventData ted = (TouchEventData) msg.obj; - // WebCore is responding to us; remove pending timeout. - // It will be re-posted when needed. - removeMessages(PREVENT_DEFAULT_TIMEOUT); - - mTouchEventQueue.enqueueTouchEvent(ted); + if (mTouchEventQueue.enqueueTouchEvent(ted)) { + // WebCore is responding to us; remove pending timeout. + // It will be re-posted when needed. + removeMessages(PREVENT_DEFAULT_TIMEOUT); + } break; case REQUEST_KEYBOARD: