diff options
Diffstat (limited to 'services')
18 files changed, 1538 insertions, 367 deletions
diff --git a/services/input/InputDispatcher.cpp b/services/input/InputDispatcher.cpp index 19295e6..ff26fc9 100644 --- a/services/input/InputDispatcher.cpp +++ b/services/input/InputDispatcher.cpp @@ -2135,8 +2135,8 @@ InputDispatcher::splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet if (pointerIds.hasBit(pointerId)) { splitPointerIndexMap[splitPointerCount] = originalPointerIndex; splitPointerIds[splitPointerCount] = pointerId; - splitPointerCoords[splitPointerCount] = - originalMotionEntry->firstSample.pointerCoords[originalPointerIndex]; + splitPointerCoords[splitPointerCount].copyFrom( + originalMotionEntry->firstSample.pointerCoords[originalPointerIndex]); splitPointerCount += 1; } } @@ -2199,8 +2199,8 @@ InputDispatcher::splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet for (uint32_t splitPointerIndex = 0; splitPointerIndex < splitPointerCount; splitPointerIndex++) { uint32_t originalPointerIndex = splitPointerIndexMap[splitPointerIndex]; - splitPointerCoords[splitPointerIndex] = - originalMotionSample->pointerCoords[originalPointerIndex]; + splitPointerCoords[splitPointerIndex].copyFrom( + originalMotionSample->pointerCoords[originalPointerIndex]); } mAllocator.appendMotionSample(splitMotionEntry, originalMotionSample->eventTime, @@ -3453,7 +3453,7 @@ InputDispatcher::MotionEntry* InputDispatcher::Allocator::obtainMotionEntry(nsec entry->lastSample = & entry->firstSample; for (uint32_t i = 0; i < pointerCount; i++) { entry->pointerIds[i] = pointerIds[i]; - entry->firstSample.pointerCoords[i] = pointerCoords[i]; + entry->firstSample.pointerCoords[i].copyFrom(pointerCoords[i]); } return entry; } @@ -3556,7 +3556,7 @@ void InputDispatcher::Allocator::appendMotionSample(MotionEntry* motionEntry, sample->eventTime = eventTime; uint32_t pointerCount = motionEntry->pointerCount; for (uint32_t i = 0; i < pointerCount; i++) { - sample->pointerCoords[i] = pointerCoords[i]; + sample->pointerCoords[i].copyFrom(pointerCoords[i]); } sample->next = NULL; @@ -3693,7 +3693,7 @@ void InputDispatcher::InputState::MotionMemento::setPointers(const MotionEntry* pointerCount = entry->pointerCount; for (uint32_t i = 0; i < entry->pointerCount; i++) { pointerIds[i] = entry->pointerIds[i]; - pointerCoords[i] = entry->lastSample->pointerCoords[i]; + pointerCoords[i].copyFrom(entry->lastSample->pointerCoords[i]); } } diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp index 3029028..592939f 100644 --- a/services/input/InputReader.cpp +++ b/services/input/InputReader.cpp @@ -33,6 +33,9 @@ // Log debug messages about pointer assignment calculations. #define DEBUG_POINTER_ASSIGNMENT 0 +// Log debug messages about gesture detection. +#define DEBUG_GESTURES 0 + #include "InputReader.h" @@ -54,6 +57,38 @@ namespace android { +// --- Constants --- + +// Quiet time between certain gesture transitions. +// Time to allow for all fingers or buttons to settle into a stable state before +// starting a new gesture. +static const nsecs_t QUIET_INTERVAL = 100 * 1000000; // 100 ms + +// The minimum speed that a pointer must travel for us to consider switching the active +// touch pointer to it during a drag. This threshold is set to avoid switching due +// to noise from a finger resting on the touch pad (perhaps just pressing it down). +static const float DRAG_MIN_SWITCH_SPEED = 50.0f; // pixels per second + +// Tap gesture delay time. +// The time between down and up must be less than this to be considered a tap. +static const nsecs_t TAP_INTERVAL = 100 * 1000000; // 100 ms + +// The distance in pixels that the pointer is allowed to move from initial down +// to up and still be called a tap. +static const float TAP_SLOP = 5.0f; // 5 pixels + +// The transition from INDETERMINATE_MULTITOUCH to SWIPE or FREEFORM gesture mode is made when +// all of the pointers have traveled this number of pixels from the start point. +static const float MULTITOUCH_MIN_TRAVEL = 5.0f; + +// The transition from INDETERMINATE_MULTITOUCH to SWIPE gesture mode can only occur when the +// cosine of the angle between the two vectors is greater than or equal to than this value +// which indicates that the vectors are oriented in the same direction. +// When the vectors are oriented in the exactly same direction, the cosine is 1.0. +// (In exactly opposite directions, the cosine is -1.0.) +static const float SWIPE_TRANSITION_ANGLE_COSINE = 0.5f; // cosine of 45 degrees + + // --- Static Functions --- template<typename T> @@ -81,6 +116,12 @@ inline static float pythag(float x, float y) { return sqrtf(x * x + y * y); } +inline static int32_t distanceSquared(int32_t x1, int32_t y1, int32_t x2, int32_t y2) { + int32_t dx = x1 - x2; + int32_t dy = y1 - y2; + return dx * dx + dy * dy; +} + inline static int32_t signExtendNybble(int32_t value) { return value >= 8 ? value - 16 : value; } @@ -207,7 +248,7 @@ void InputReader::loopOnce() { mEventHub->getEvent(& rawEvent); #if DEBUG_RAW_EVENTS - LOGD("Input event: device=%d type=0x%x scancode=%d keycode=%d value=%d", + LOGD("Input event: device=%d type=0x%04x scancode=0x%04x keycode=0x%04x value=0x%04x", rawEvent.deviceId, rawEvent.type, rawEvent.scanCode, rawEvent.keyCode, rawEvent.value); #endif @@ -1540,7 +1581,7 @@ TouchInputMapper::~TouchInputMapper() { } uint32_t TouchInputMapper::getSources() { - return mTouchSource; + return mTouchSource | mPointerSource; } void TouchInputMapper::populateDeviceInfo(InputDeviceInfo* info) { @@ -1579,6 +1620,18 @@ void TouchInputMapper::populateDeviceInfo(InputDeviceInfo* info) { if (mLocked.orientedRanges.haveOrientation) { info->addMotionRange(mLocked.orientedRanges.orientation); } + + if (mPointerController != NULL) { + float minX, minY, maxX, maxY; + if (mPointerController->getBounds(&minX, &minY, &maxX, &maxY)) { + info->addMotionRange(AMOTION_EVENT_AXIS_X, mPointerSource, + minX, maxX, 0.0f, 0.0f); + info->addMotionRange(AMOTION_EVENT_AXIS_Y, mPointerSource, + minY, maxY, 0.0f, 0.0f); + } + info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, mPointerSource, + 0.0f, 1.0f, 0.0f, 0.0f); + } } // release lock } @@ -1608,6 +1661,21 @@ void TouchInputMapper::dump(String8& dump) { dump.appendFormat(INDENT3 "Last Touch:\n"); dump.appendFormat(INDENT4 "Pointer Count: %d\n", mLastTouch.pointerCount); + dump.appendFormat(INDENT4 "Button State: 0x%08x\n", mLastTouch.buttonState); + + if (mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER) { + dump.appendFormat(INDENT3 "Pointer Gesture Detector:\n"); + dump.appendFormat(INDENT4 "XMovementScale: %0.3f\n", + mLocked.pointerGestureXMovementScale); + dump.appendFormat(INDENT4 "YMovementScale: %0.3f\n", + mLocked.pointerGestureYMovementScale); + dump.appendFormat(INDENT4 "XZoomScale: %0.3f\n", + mLocked.pointerGestureXZoomScale); + dump.appendFormat(INDENT4 "YZoomScale: %0.3f\n", + mLocked.pointerGestureYZoomScale); + dump.appendFormat(INDENT4 "MaxSwipeWidthSquared: %d\n", + mLocked.pointerGestureMaxSwipeWidthSquared); + } } // release lock } @@ -1630,6 +1698,8 @@ void TouchInputMapper::initializeLocked() { mLocked.orientedRanges.haveTouchSize = false; mLocked.orientedRanges.haveToolSize = false; mLocked.orientedRanges.haveOrientation = false; + + mPointerGesture.reset(); } void TouchInputMapper::configure() { @@ -1642,9 +1712,15 @@ void TouchInputMapper::configure() { switch (mParameters.deviceType) { case Parameters::DEVICE_TYPE_TOUCH_SCREEN: mTouchSource = AINPUT_SOURCE_TOUCHSCREEN; + mPointerSource = 0; break; case Parameters::DEVICE_TYPE_TOUCH_PAD: mTouchSource = AINPUT_SOURCE_TOUCHPAD; + mPointerSource = 0; + break; + case Parameters::DEVICE_TYPE_POINTER: + mTouchSource = AINPUT_SOURCE_TOUCHPAD; + mPointerSource = AINPUT_SOURCE_MOUSE; break; default: assert(false); @@ -1671,14 +1747,26 @@ void TouchInputMapper::configureParameters() { mParameters.useJumpyTouchFilter = getPolicy()->filterJumpyTouchEvents(); mParameters.virtualKeyQuietTime = getPolicy()->getVirtualKeyQuietTime(); + if (getEventHub()->hasRelativeAxis(getDeviceId(), REL_X) + || getEventHub()->hasRelativeAxis(getDeviceId(), REL_Y)) { + // The device is a cursor device with a touch pad attached. + // By default don't use the touch pad to move the pointer. + mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD; + } else { + // The device is just a touch pad. + // By default use the touch pad to move the pointer and to perform related gestures. + mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER; + } + String8 deviceTypeString; - mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD; if (getDevice()->getConfiguration().tryGetProperty(String8("touch.deviceType"), deviceTypeString)) { if (deviceTypeString == "touchScreen") { mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN; } else if (deviceTypeString == "touchPad") { mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD; + } else if (deviceTypeString == "pointer") { + mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER; } else { LOGW("Invalid value for touch.deviceType: '%s'", deviceTypeString.string()); } @@ -1690,6 +1778,7 @@ void TouchInputMapper::configureParameters() { mParameters.associatedDisplayId = mParameters.orientationAware || mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN + || mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER ? 0 : -1; } @@ -1703,6 +1792,9 @@ void TouchInputMapper::dumpParameters(String8& dump) { case Parameters::DEVICE_TYPE_TOUCH_PAD: dump.append(INDENT4 "DeviceType: touchPad\n"); break; + case Parameters::DEVICE_TYPE_POINTER: + dump.append(INDENT4 "DeviceType: pointer\n"); + break; default: assert(false); } @@ -1776,6 +1868,11 @@ bool TouchInputMapper::configureSurfaceLocked() { } } + if (mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER + && mPointerController == NULL) { + mPointerController = getPolicy()->obtainPointerController(getDeviceId()); + } + bool orientationChanged = mLocked.surfaceOrientation != orientation; if (orientationChanged) { mLocked.surfaceOrientation = orientation; @@ -1997,6 +2094,37 @@ bool TouchInputMapper::configureSurfaceLocked() { mLocked.orientedRanges.y.fuzz = mLocked.yScale; break; } + + // Compute pointer gesture detection parameters. + // TODO: These factors should not be hardcoded. + if (mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER) { + int32_t rawWidth = mRawAxes.x.maxValue - mRawAxes.x.minValue + 1; + int32_t rawHeight = mRawAxes.y.maxValue - mRawAxes.y.minValue + 1; + + // Scale movements such that one whole swipe of the touch pad covers a portion + // of the display along whichever axis of the touch pad is longer. + // Assume that the touch pad has a square aspect ratio such that movements in + // X and Y of the same number of raw units cover the same physical distance. + const float scaleFactor = 0.8f; + + mLocked.pointerGestureXMovementScale = rawWidth > rawHeight + ? scaleFactor * float(mLocked.associatedDisplayWidth) / rawWidth + : scaleFactor * float(mLocked.associatedDisplayHeight) / rawHeight; + mLocked.pointerGestureYMovementScale = mLocked.pointerGestureXMovementScale; + + // Scale zooms to cover a smaller range of the display than movements do. + // This value determines the area around the pointer that is affected by freeform + // pointer gestures. + mLocked.pointerGestureXZoomScale = mLocked.pointerGestureXMovementScale * 0.4f; + mLocked.pointerGestureYZoomScale = mLocked.pointerGestureYMovementScale * 0.4f; + + // Max width between pointers to detect a swipe gesture is 3/4 of the short + // axis of the touch pad. Touches that are wider than this are translated + // into freeform gestures. + mLocked.pointerGestureMaxSwipeWidthSquared = min(rawWidth, rawHeight) * 3 / 4; + mLocked.pointerGestureMaxSwipeWidthSquared *= + mLocked.pointerGestureMaxSwipeWidthSquared; + } } return true; @@ -2476,12 +2604,17 @@ void TouchInputMapper::syncTouch(nsecs_t when, bool havePointerIds) { TouchResult touchResult = consumeOffScreenTouches(when, policyFlags); if (touchResult == DISPATCH_TOUCH) { suppressSwipeOntoVirtualKeys(when); + if (mPointerController != NULL) { + dispatchPointerGestures(when, policyFlags); + } dispatchTouches(when, policyFlags); } // Copy current touch to last touch in preparation for the next cycle. + // Keep the button state so we can track edge-triggered button state changes. if (touchResult == DROP_STROKE) { mLastTouch.clear(); + mLastTouch.buttonState = savedTouch->buttonState; } else { mLastTouch.copyFrom(*savedTouch); } @@ -2629,329 +2762,1097 @@ void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) { return; // nothing to do! } + // Update current touch coordinates. + int32_t edgeFlags; + float xPrecision, yPrecision; + prepareTouches(&edgeFlags, &xPrecision, &yPrecision); + + // Dispatch motions. BitSet32 currentIdBits = mCurrentTouch.idBits; BitSet32 lastIdBits = mLastTouch.idBits; + uint32_t metaState = getContext()->getGlobalMetaState(); if (currentIdBits == lastIdBits) { // No pointer id changes so this is a move event. // The dispatcher takes care of batching moves so we don't have to deal with that here. - int32_t motionEventAction = AMOTION_EVENT_ACTION_MOVE; - dispatchTouch(when, policyFlags, & mCurrentTouch, - currentIdBits, -1, currentPointerCount, motionEventAction); + dispatchMotion(when, policyFlags, mTouchSource, + AMOTION_EVENT_ACTION_MOVE, 0, metaState, AMOTION_EVENT_EDGE_FLAG_NONE, + mCurrentTouchCoords, mCurrentTouch.idToIndex, currentIdBits, -1, + xPrecision, yPrecision, mDownTime); } else { // There may be pointers going up and pointers going down and pointers moving // all at the same time. - BitSet32 upIdBits(lastIdBits.value & ~ currentIdBits.value); - BitSet32 downIdBits(currentIdBits.value & ~ lastIdBits.value); - BitSet32 activeIdBits(lastIdBits.value); - uint32_t pointerCount = lastPointerCount; - - // Produce an intermediate representation of the touch data that consists of the - // old location of pointers that have just gone up and the new location of pointers that - // have just moved but omits the location of pointers that have just gone down. - TouchData interimTouch; - interimTouch.copyFrom(mLastTouch); - + BitSet32 upIdBits(lastIdBits.value & ~currentIdBits.value); + BitSet32 downIdBits(currentIdBits.value & ~lastIdBits.value); BitSet32 moveIdBits(lastIdBits.value & currentIdBits.value); - bool moveNeeded = false; - while (!moveIdBits.isEmpty()) { - uint32_t moveId = moveIdBits.firstMarkedBit(); - moveIdBits.clearBit(moveId); - - int32_t oldIndex = mLastTouch.idToIndex[moveId]; - int32_t newIndex = mCurrentTouch.idToIndex[moveId]; - if (mLastTouch.pointers[oldIndex] != mCurrentTouch.pointers[newIndex]) { - interimTouch.pointers[oldIndex] = mCurrentTouch.pointers[newIndex]; - moveNeeded = true; - } - } + BitSet32 dispatchedIdBits(lastIdBits.value); + + // Update last coordinates of pointers that have moved so that we observe the new + // pointer positions at the same time as other pointers that have just gone up. + bool moveNeeded = updateMovedPointerCoords( + mCurrentTouchCoords, mCurrentTouch.idToIndex, + mLastTouchCoords, mLastTouch.idToIndex, + moveIdBits); - // Dispatch pointer up events using the interim pointer locations. + // Dispatch pointer up events. while (!upIdBits.isEmpty()) { uint32_t upId = upIdBits.firstMarkedBit(); upIdBits.clearBit(upId); - BitSet32 oldActiveIdBits = activeIdBits; - activeIdBits.clearBit(upId); - int32_t motionEventAction; - if (activeIdBits.isEmpty()) { - motionEventAction = AMOTION_EVENT_ACTION_UP; - } else { - motionEventAction = AMOTION_EVENT_ACTION_POINTER_UP; - } - - dispatchTouch(when, policyFlags, &interimTouch, - oldActiveIdBits, upId, pointerCount, motionEventAction); - pointerCount -= 1; + dispatchMotion(when, policyFlags, mTouchSource, + AMOTION_EVENT_ACTION_POINTER_UP, 0, metaState, 0, + mLastTouchCoords, mLastTouch.idToIndex, dispatchedIdBits, upId, + xPrecision, yPrecision, mDownTime); + dispatchedIdBits.clearBit(upId); } // Dispatch move events if any of the remaining pointers moved from their old locations. // Although applications receive new locations as part of individual pointer up // events, they do not generally handle them except when presented in a move event. if (moveNeeded) { - dispatchTouch(when, policyFlags, &mCurrentTouch, - activeIdBits, -1, pointerCount, AMOTION_EVENT_ACTION_MOVE); + assert(moveIdBits.value == dispatchedIdBits.value); + dispatchMotion(when, policyFlags, mTouchSource, + AMOTION_EVENT_ACTION_MOVE, 0, metaState, 0, + mCurrentTouchCoords, mCurrentTouch.idToIndex, dispatchedIdBits, -1, + xPrecision, yPrecision, mDownTime); } // Dispatch pointer down events using the new pointer locations. while (!downIdBits.isEmpty()) { uint32_t downId = downIdBits.firstMarkedBit(); downIdBits.clearBit(downId); - BitSet32 oldActiveIdBits = activeIdBits; - activeIdBits.markBit(downId); + dispatchedIdBits.markBit(downId); - int32_t motionEventAction; - if (oldActiveIdBits.isEmpty()) { - motionEventAction = AMOTION_EVENT_ACTION_DOWN; + if (dispatchedIdBits.count() == 1) { + // First pointer is going down. Set down time. mDownTime = when; } else { - motionEventAction = AMOTION_EVENT_ACTION_POINTER_DOWN; + // Only send edge flags with first pointer down. + edgeFlags = AMOTION_EVENT_EDGE_FLAG_NONE; } - pointerCount += 1; - dispatchTouch(when, policyFlags, &mCurrentTouch, - activeIdBits, downId, pointerCount, motionEventAction); + dispatchMotion(when, policyFlags, mTouchSource, + AMOTION_EVENT_ACTION_POINTER_DOWN, 0, metaState, edgeFlags, + mCurrentTouchCoords, mCurrentTouch.idToIndex, dispatchedIdBits, downId, + xPrecision, yPrecision, mDownTime); } } -} -void TouchInputMapper::dispatchTouch(nsecs_t when, uint32_t policyFlags, - TouchData* touch, BitSet32 idBits, uint32_t changedId, uint32_t pointerCount, - int32_t motionEventAction) { - int32_t pointerIds[MAX_POINTERS]; - PointerCoords pointerCoords[MAX_POINTERS]; - int32_t motionEventEdgeFlags = AMOTION_EVENT_EDGE_FLAG_NONE; - float xPrecision, yPrecision; + // Update state for next time. + for (uint32_t i = 0; i < currentPointerCount; i++) { + mLastTouchCoords[i].copyFrom(mCurrentTouchCoords[i]); + } +} - { // acquire lock - AutoMutex _l(mLock); +void TouchInputMapper::prepareTouches(int32_t* outEdgeFlags, + float* outXPrecision, float* outYPrecision) { + uint32_t currentPointerCount = mCurrentTouch.pointerCount; + uint32_t lastPointerCount = mLastTouch.pointerCount; - // Walk through the the active pointers and map touch screen coordinates (TouchData) into - // display or surface coordinates (PointerCoords) and adjust for display orientation. - for (uint32_t outIndex = 0; ! idBits.isEmpty(); outIndex++) { - uint32_t id = idBits.firstMarkedBit(); - idBits.clearBit(id); - uint32_t inIndex = touch->idToIndex[id]; + AutoMutex _l(mLock); - const PointerData& in = touch->pointers[inIndex]; + // Walk through the the active pointers and map touch screen coordinates (TouchData) into + // display or surface coordinates (PointerCoords) and adjust for display orientation. + for (uint32_t i = 0; i < currentPointerCount; i++) { + const PointerData& in = mCurrentTouch.pointers[i]; - // ToolMajor and ToolMinor - float toolMajor, toolMinor; - switch (mCalibration.toolSizeCalibration) { - case Calibration::TOOL_SIZE_CALIBRATION_GEOMETRIC: - toolMajor = in.toolMajor * mLocked.geometricScale; - if (mRawAxes.toolMinor.valid) { - toolMinor = in.toolMinor * mLocked.geometricScale; - } else { - toolMinor = toolMajor; - } - break; - case Calibration::TOOL_SIZE_CALIBRATION_LINEAR: - toolMajor = in.toolMajor != 0 - ? in.toolMajor * mLocked.toolSizeLinearScale + mLocked.toolSizeLinearBias + // ToolMajor and ToolMinor + float toolMajor, toolMinor; + switch (mCalibration.toolSizeCalibration) { + case Calibration::TOOL_SIZE_CALIBRATION_GEOMETRIC: + toolMajor = in.toolMajor * mLocked.geometricScale; + if (mRawAxes.toolMinor.valid) { + toolMinor = in.toolMinor * mLocked.geometricScale; + } else { + toolMinor = toolMajor; + } + break; + case Calibration::TOOL_SIZE_CALIBRATION_LINEAR: + toolMajor = in.toolMajor != 0 + ? in.toolMajor * mLocked.toolSizeLinearScale + mLocked.toolSizeLinearBias + : 0; + if (mRawAxes.toolMinor.valid) { + toolMinor = in.toolMinor != 0 + ? in.toolMinor * mLocked.toolSizeLinearScale + + mLocked.toolSizeLinearBias : 0; - if (mRawAxes.toolMinor.valid) { - toolMinor = in.toolMinor != 0 - ? in.toolMinor * mLocked.toolSizeLinearScale - + mLocked.toolSizeLinearBias - : 0; - } else { - toolMinor = toolMajor; - } - break; - case Calibration::TOOL_SIZE_CALIBRATION_AREA: - if (in.toolMajor != 0) { - float diameter = sqrtf(in.toolMajor - * mLocked.toolSizeAreaScale + mLocked.toolSizeAreaBias); - toolMajor = diameter * mLocked.toolSizeLinearScale + mLocked.toolSizeLinearBias; - } else { - toolMajor = 0; - } + } else { toolMinor = toolMajor; - break; - default: + } + break; + case Calibration::TOOL_SIZE_CALIBRATION_AREA: + if (in.toolMajor != 0) { + float diameter = sqrtf(in.toolMajor + * mLocked.toolSizeAreaScale + mLocked.toolSizeAreaBias); + toolMajor = diameter * mLocked.toolSizeLinearScale + mLocked.toolSizeLinearBias; + } else { toolMajor = 0; - toolMinor = 0; - break; } + toolMinor = toolMajor; + break; + default: + toolMajor = 0; + toolMinor = 0; + break; + } + + if (mCalibration.haveToolSizeIsSummed && mCalibration.toolSizeIsSummed) { + toolMajor /= currentPointerCount; + toolMinor /= currentPointerCount; + } - if (mCalibration.haveToolSizeIsSummed && mCalibration.toolSizeIsSummed) { - toolMajor /= pointerCount; - toolMinor /= pointerCount; + // Pressure + float rawPressure; + switch (mCalibration.pressureSource) { + case Calibration::PRESSURE_SOURCE_PRESSURE: + rawPressure = in.pressure; + break; + case Calibration::PRESSURE_SOURCE_TOUCH: + rawPressure = in.touchMajor; + break; + default: + rawPressure = 0; + } + + float pressure; + switch (mCalibration.pressureCalibration) { + case Calibration::PRESSURE_CALIBRATION_PHYSICAL: + case Calibration::PRESSURE_CALIBRATION_AMPLITUDE: + pressure = rawPressure * mLocked.pressureScale; + break; + default: + pressure = 1; + break; + } + + // TouchMajor and TouchMinor + float touchMajor, touchMinor; + switch (mCalibration.touchSizeCalibration) { + case Calibration::TOUCH_SIZE_CALIBRATION_GEOMETRIC: + touchMajor = in.touchMajor * mLocked.geometricScale; + if (mRawAxes.touchMinor.valid) { + touchMinor = in.touchMinor * mLocked.geometricScale; + } else { + touchMinor = touchMajor; } + break; + case Calibration::TOUCH_SIZE_CALIBRATION_PRESSURE: + touchMajor = toolMajor * pressure; + touchMinor = toolMinor * pressure; + break; + default: + touchMajor = 0; + touchMinor = 0; + break; + } - // Pressure - float rawPressure; - switch (mCalibration.pressureSource) { - case Calibration::PRESSURE_SOURCE_PRESSURE: - rawPressure = in.pressure; - break; - case Calibration::PRESSURE_SOURCE_TOUCH: - rawPressure = in.touchMajor; - break; - default: - rawPressure = 0; + if (touchMajor > toolMajor) { + touchMajor = toolMajor; + } + if (touchMinor > toolMinor) { + touchMinor = toolMinor; + } + + // Size + float size; + switch (mCalibration.sizeCalibration) { + case Calibration::SIZE_CALIBRATION_NORMALIZED: { + float rawSize = mRawAxes.toolMinor.valid + ? avg(in.toolMajor, in.toolMinor) + : in.toolMajor; + size = rawSize * mLocked.sizeScale; + break; + } + default: + size = 0; + break; + } + + // Orientation + float orientation; + switch (mCalibration.orientationCalibration) { + case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED: + orientation = in.orientation * mLocked.orientationScale; + break; + case Calibration::ORIENTATION_CALIBRATION_VECTOR: { + int32_t c1 = signExtendNybble((in.orientation & 0xf0) >> 4); + int32_t c2 = signExtendNybble(in.orientation & 0x0f); + if (c1 != 0 || c2 != 0) { + orientation = atan2f(c1, c2) * 0.5f; + float scale = 1.0f + pythag(c1, c2) / 16.0f; + touchMajor *= scale; + touchMinor /= scale; + toolMajor *= scale; + toolMinor /= scale; + } else { + orientation = 0; } + break; + } + default: + orientation = 0; + } - float pressure; - switch (mCalibration.pressureCalibration) { - case Calibration::PRESSURE_CALIBRATION_PHYSICAL: - case Calibration::PRESSURE_CALIBRATION_AMPLITUDE: - pressure = rawPressure * mLocked.pressureScale; - break; - default: - pressure = 1; - break; + // X and Y + // Adjust coords for surface orientation. + float x, y; + switch (mLocked.surfaceOrientation) { + case DISPLAY_ORIENTATION_90: + x = float(in.y - mRawAxes.y.minValue) * mLocked.yScale; + y = float(mRawAxes.x.maxValue - in.x) * mLocked.xScale; + orientation -= M_PI_2; + if (orientation < - M_PI_2) { + orientation += M_PI; } + break; + case DISPLAY_ORIENTATION_180: + x = float(mRawAxes.x.maxValue - in.x) * mLocked.xScale; + y = float(mRawAxes.y.maxValue - in.y) * mLocked.yScale; + break; + case DISPLAY_ORIENTATION_270: + x = float(mRawAxes.y.maxValue - in.y) * mLocked.yScale; + y = float(in.x - mRawAxes.x.minValue) * mLocked.xScale; + orientation += M_PI_2; + if (orientation > M_PI_2) { + orientation -= M_PI; + } + break; + default: + x = float(in.x - mRawAxes.x.minValue) * mLocked.xScale; + y = float(in.y - mRawAxes.y.minValue) * mLocked.yScale; + break; + } - // TouchMajor and TouchMinor - float touchMajor, touchMinor; - switch (mCalibration.touchSizeCalibration) { - case Calibration::TOUCH_SIZE_CALIBRATION_GEOMETRIC: - touchMajor = in.touchMajor * mLocked.geometricScale; - if (mRawAxes.touchMinor.valid) { - touchMinor = in.touchMinor * mLocked.geometricScale; - } else { - touchMinor = touchMajor; - } - break; - case Calibration::TOUCH_SIZE_CALIBRATION_PRESSURE: - touchMajor = toolMajor * pressure; - touchMinor = toolMinor * pressure; - break; - default: - touchMajor = 0; - touchMinor = 0; - break; + // Write output coords. + PointerCoords& out = mCurrentTouchCoords[i]; + out.clear(); + 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); + } + + // Check edge flags by looking only at the first pointer since the flags are + // global to the event. + *outEdgeFlags = AMOTION_EVENT_EDGE_FLAG_NONE; + if (lastPointerCount == 0 && currentPointerCount > 0) { + const PointerData& in = mCurrentTouch.pointers[0]; + + if (in.x <= mRawAxes.x.minValue) { + *outEdgeFlags |= rotateEdgeFlag(AMOTION_EVENT_EDGE_FLAG_LEFT, + mLocked.surfaceOrientation); + } else if (in.x >= mRawAxes.x.maxValue) { + *outEdgeFlags |= rotateEdgeFlag(AMOTION_EVENT_EDGE_FLAG_RIGHT, + mLocked.surfaceOrientation); + } + if (in.y <= mRawAxes.y.minValue) { + *outEdgeFlags |= rotateEdgeFlag(AMOTION_EVENT_EDGE_FLAG_TOP, + mLocked.surfaceOrientation); + } else if (in.y >= mRawAxes.y.maxValue) { + *outEdgeFlags |= rotateEdgeFlag(AMOTION_EVENT_EDGE_FLAG_BOTTOM, + mLocked.surfaceOrientation); + } + } + + *outXPrecision = mLocked.orientedXPrecision; + *outYPrecision = mLocked.orientedYPrecision; +} + +void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlags) { + // Update current gesture coordinates. + bool cancelPreviousGesture, finishPreviousGesture; + preparePointerGestures(when, &cancelPreviousGesture, &finishPreviousGesture); + + // Send events! + uint32_t metaState = getContext()->getGlobalMetaState(); + + // Update last coordinates of pointers that have moved so that we observe the new + // pointer positions at the same time as other pointers that have just gone up. + bool down = mPointerGesture.currentGestureMode == PointerGesture::CLICK_OR_DRAG + || mPointerGesture.currentGestureMode == PointerGesture::SWIPE + || mPointerGesture.currentGestureMode == PointerGesture::FREEFORM; + bool moveNeeded = false; + if (down && !cancelPreviousGesture && !finishPreviousGesture + && mPointerGesture.lastGesturePointerCount != 0 + && mPointerGesture.currentGesturePointerCount != 0) { + BitSet32 movedGestureIdBits(mPointerGesture.currentGestureIdBits.value + & mPointerGesture.lastGestureIdBits.value); + moveNeeded = updateMovedPointerCoords( + mPointerGesture.currentGestureCoords, mPointerGesture.currentGestureIdToIndex, + mPointerGesture.lastGestureCoords, mPointerGesture.lastGestureIdToIndex, + movedGestureIdBits); + } + + // Send motion events for all pointers that went up or were canceled. + BitSet32 dispatchedGestureIdBits(mPointerGesture.lastGestureIdBits); + if (!dispatchedGestureIdBits.isEmpty()) { + if (cancelPreviousGesture) { + dispatchMotion(when, policyFlags, mPointerSource, + AMOTION_EVENT_ACTION_CANCEL, 0, metaState, AMOTION_EVENT_EDGE_FLAG_NONE, + mPointerGesture.lastGestureCoords, mPointerGesture.lastGestureIdToIndex, + dispatchedGestureIdBits, -1, + 0, 0, mPointerGesture.downTime); + + dispatchedGestureIdBits.clear(); + } else { + BitSet32 upGestureIdBits; + if (finishPreviousGesture) { + upGestureIdBits = dispatchedGestureIdBits; + } else { + upGestureIdBits.value = dispatchedGestureIdBits.value + & ~mPointerGesture.currentGestureIdBits.value; + } + while (!upGestureIdBits.isEmpty()) { + uint32_t id = upGestureIdBits.firstMarkedBit(); + upGestureIdBits.clearBit(id); + + dispatchMotion(when, policyFlags, mPointerSource, + AMOTION_EVENT_ACTION_POINTER_UP, 0, + metaState, AMOTION_EVENT_EDGE_FLAG_NONE, + mPointerGesture.lastGestureCoords, mPointerGesture.lastGestureIdToIndex, + dispatchedGestureIdBits, id, + 0, 0, mPointerGesture.downTime); + + dispatchedGestureIdBits.clearBit(id); } + } + } - if (touchMajor > toolMajor) { - touchMajor = toolMajor; + // Send motion events for all pointers that moved. + if (moveNeeded) { + dispatchMotion(when, policyFlags, mPointerSource, + AMOTION_EVENT_ACTION_MOVE, 0, metaState, AMOTION_EVENT_EDGE_FLAG_NONE, + mPointerGesture.currentGestureCoords, mPointerGesture.currentGestureIdToIndex, + dispatchedGestureIdBits, -1, + 0, 0, mPointerGesture.downTime); + } + + // Send motion events for all pointers that went down. + if (down) { + BitSet32 downGestureIdBits(mPointerGesture.currentGestureIdBits.value + & ~dispatchedGestureIdBits.value); + while (!downGestureIdBits.isEmpty()) { + uint32_t id = downGestureIdBits.firstMarkedBit(); + downGestureIdBits.clearBit(id); + dispatchedGestureIdBits.markBit(id); + + int32_t edgeFlags = AMOTION_EVENT_EDGE_FLAG_NONE; + if (dispatchedGestureIdBits.count() == 1) { + // First pointer is going down. Calculate edge flags and set down time. + uint32_t index = mPointerGesture.currentGestureIdToIndex[id]; + const PointerCoords& downCoords = mPointerGesture.currentGestureCoords[index]; + edgeFlags = calculateEdgeFlagsUsingPointerBounds(mPointerController, + downCoords.getAxisValue(AMOTION_EVENT_AXIS_X), + downCoords.getAxisValue(AMOTION_EVENT_AXIS_Y)); + mPointerGesture.downTime = when; } - if (touchMinor > toolMinor) { - touchMinor = toolMinor; + + dispatchMotion(when, policyFlags, mPointerSource, + AMOTION_EVENT_ACTION_POINTER_DOWN, 0, metaState, edgeFlags, + mPointerGesture.currentGestureCoords, mPointerGesture.currentGestureIdToIndex, + dispatchedGestureIdBits, id, + 0, 0, mPointerGesture.downTime); + } + } + + // Send down and up for a tap. + if (mPointerGesture.currentGestureMode == PointerGesture::TAP) { + const PointerCoords& coords = mPointerGesture.currentGestureCoords[0]; + int32_t edgeFlags = calculateEdgeFlagsUsingPointerBounds(mPointerController, + coords.getAxisValue(AMOTION_EVENT_AXIS_X), + coords.getAxisValue(AMOTION_EVENT_AXIS_Y)); + nsecs_t downTime = mPointerGesture.downTime = mPointerGesture.tapTime; + mPointerGesture.resetTapTime(); + + dispatchMotion(downTime, policyFlags, mPointerSource, + AMOTION_EVENT_ACTION_DOWN, 0, metaState, edgeFlags, + mPointerGesture.currentGestureCoords, mPointerGesture.currentGestureIdToIndex, + mPointerGesture.currentGestureIdBits, -1, + 0, 0, downTime); + dispatchMotion(when, policyFlags, mPointerSource, + AMOTION_EVENT_ACTION_UP, 0, metaState, edgeFlags, + mPointerGesture.currentGestureCoords, mPointerGesture.currentGestureIdToIndex, + mPointerGesture.currentGestureIdBits, -1, + 0, 0, downTime); + } + + // Send motion events for hover. + if (mPointerGesture.currentGestureMode == PointerGesture::HOVER) { + dispatchMotion(when, policyFlags, mPointerSource, + AMOTION_EVENT_ACTION_HOVER_MOVE, 0, metaState, AMOTION_EVENT_EDGE_FLAG_NONE, + mPointerGesture.currentGestureCoords, mPointerGesture.currentGestureIdToIndex, + mPointerGesture.currentGestureIdBits, -1, + 0, 0, mPointerGesture.downTime); + } + + // Update state. + mPointerGesture.lastGestureMode = mPointerGesture.currentGestureMode; + if (!down) { + mPointerGesture.lastGesturePointerCount = 0; + mPointerGesture.lastGestureIdBits.clear(); + } else { + uint32_t currentGesturePointerCount = mPointerGesture.currentGesturePointerCount; + mPointerGesture.lastGesturePointerCount = currentGesturePointerCount; + mPointerGesture.lastGestureIdBits = mPointerGesture.currentGestureIdBits; + for (BitSet32 idBits(mPointerGesture.currentGestureIdBits); !idBits.isEmpty(); ) { + uint32_t id = idBits.firstMarkedBit(); + idBits.clearBit(id); + uint32_t index = mPointerGesture.currentGestureIdToIndex[id]; + mPointerGesture.lastGestureCoords[index].copyFrom( + mPointerGesture.currentGestureCoords[index]); + mPointerGesture.lastGestureIdToIndex[id] = index; + } + } +} + +void TouchInputMapper::preparePointerGestures(nsecs_t when, + bool* outCancelPreviousGesture, bool* outFinishPreviousGesture) { + *outCancelPreviousGesture = false; + *outFinishPreviousGesture = false; + + AutoMutex _l(mLock); + + // Update the velocity tracker. + { + VelocityTracker::Position positions[MAX_POINTERS]; + uint32_t count = 0; + for (BitSet32 idBits(mCurrentTouch.idBits); !idBits.isEmpty(); count++) { + uint32_t id = idBits.firstMarkedBit(); + idBits.clearBit(id); + uint32_t index = mCurrentTouch.idToIndex[id]; + positions[count].x = mCurrentTouch.pointers[index].x + * mLocked.pointerGestureXMovementScale; + positions[count].y = mCurrentTouch.pointers[index].y + * mLocked.pointerGestureYMovementScale; + } + mPointerGesture.velocityTracker.addMovement(when, mCurrentTouch.idBits, positions); + } + + // Pick a new active touch id if needed. + // Choose an arbitrary pointer that just went down, if there is one. + // Otherwise choose an arbitrary remaining pointer. + // This guarantees we always have an active touch id when there is at least one pointer. + // We always switch to the newest pointer down because that's usually where the user's + // attention is focused. + int32_t activeTouchId; + BitSet32 downTouchIdBits(mCurrentTouch.idBits.value & ~mLastTouch.idBits.value); + if (!downTouchIdBits.isEmpty()) { + activeTouchId = mPointerGesture.activeTouchId = downTouchIdBits.firstMarkedBit(); + } else { + activeTouchId = mPointerGesture.activeTouchId; + if (activeTouchId < 0 || !mCurrentTouch.idBits.hasBit(activeTouchId)) { + if (!mCurrentTouch.idBits.isEmpty()) { + activeTouchId = mPointerGesture.activeTouchId = + mCurrentTouch.idBits.firstMarkedBit(); + } else { + activeTouchId = mPointerGesture.activeTouchId = -1; } + } + } - // Size - float size; - switch (mCalibration.sizeCalibration) { - case Calibration::SIZE_CALIBRATION_NORMALIZED: { - float rawSize = mRawAxes.toolMinor.valid - ? avg(in.toolMajor, in.toolMinor) - : in.toolMajor; - size = rawSize * mLocked.sizeScale; - break; + // Update the touch origin data to track where each finger originally went down. + if (mCurrentTouch.pointerCount == 0 || mPointerGesture.touchOrigin.pointerCount == 0) { + // Fast path when all fingers have gone up or down. + mPointerGesture.touchOrigin.copyFrom(mCurrentTouch); + } else { + // Slow path when only some fingers have gone up or down. + for (BitSet32 idBits(mPointerGesture.touchOrigin.idBits.value + & ~mCurrentTouch.idBits.value); !idBits.isEmpty(); ) { + uint32_t id = idBits.firstMarkedBit(); + idBits.clearBit(id); + mPointerGesture.touchOrigin.idBits.clearBit(id); + uint32_t index = mPointerGesture.touchOrigin.idToIndex[id]; + uint32_t count = --mPointerGesture.touchOrigin.pointerCount; + while (index < count) { + mPointerGesture.touchOrigin.pointers[index] = + mPointerGesture.touchOrigin.pointers[index + 1]; + uint32_t movedId = mPointerGesture.touchOrigin.pointers[index].id; + mPointerGesture.touchOrigin.idToIndex[movedId] = index; + index += 1; } - default: - size = 0; - break; + } + for (BitSet32 idBits(mCurrentTouch.idBits.value + & ~mPointerGesture.touchOrigin.idBits.value); !idBits.isEmpty(); ) { + uint32_t id = idBits.firstMarkedBit(); + idBits.clearBit(id); + mPointerGesture.touchOrigin.idBits.markBit(id); + uint32_t index = mPointerGesture.touchOrigin.pointerCount++; + mPointerGesture.touchOrigin.pointers[index] = + mCurrentTouch.pointers[mCurrentTouch.idToIndex[id]]; + mPointerGesture.touchOrigin.idToIndex[id] = index; + } + } + + // Determine whether we are in quiet time. + bool isQuietTime = when < mPointerGesture.quietTime + QUIET_INTERVAL; + if (!isQuietTime) { + if ((mPointerGesture.lastGestureMode == PointerGesture::SWIPE + || mPointerGesture.lastGestureMode == PointerGesture::FREEFORM) + && mCurrentTouch.pointerCount < 2) { + // Enter quiet time when exiting swipe or freeform state. + // This is to prevent accidentally entering the hover state and flinging the + // pointer when finishing a swipe and there is still one pointer left onscreen. + isQuietTime = true; + } else if (mPointerGesture.lastGestureMode == PointerGesture::CLICK_OR_DRAG + && mCurrentTouch.pointerCount >= 2 + && !isPointerDown(mCurrentTouch.buttonState)) { + // Enter quiet time when releasing the button and there are still two or more + // fingers down. This may indicate that one finger was used to press the button + // but it has not gone up yet. + isQuietTime = true; + } + if (isQuietTime) { + mPointerGesture.quietTime = when; + } + } + + // Switch states based on button and pointer state. + if (isQuietTime) { + // Case 1: Quiet time. (QUIET) +#if DEBUG_GESTURES + LOGD("Gestures: QUIET for next %0.3fms", + (mPointerGesture.quietTime + QUIET_INTERVAL - when) * 0.000001f); +#endif + *outFinishPreviousGesture = true; + + mPointerGesture.activeGestureId = -1; + mPointerGesture.currentGestureMode = PointerGesture::QUIET; + mPointerGesture.currentGesturePointerCount = 0; + mPointerGesture.currentGestureIdBits.clear(); + } else if (isPointerDown(mCurrentTouch.buttonState)) { + // Case 2: Button is pressed. (DRAG) + // The pointer follows the active touch point. + // Emit DOWN, MOVE, UP events at the pointer location. + // + // Only the active touch matters; other fingers are ignored. This policy helps + // to handle the case where the user places a second finger on the touch pad + // to apply the necessary force to depress an integrated button below the surface. + // We don't want the second finger to be delivered to applications. + // + // For this to work well, we need to make sure to track the pointer that is really + // active. If the user first puts one finger down to click then adds another + // finger to drag then the active pointer should switch to the finger that is + // being dragged. +#if DEBUG_GESTURES + LOGD("Gestures: CLICK_OR_DRAG activeTouchId=%d, " + "currentTouchPointerCount=%d", activeTouchId, mCurrentTouch.pointerCount); +#endif + // Reset state when just starting. + if (mPointerGesture.lastGestureMode != PointerGesture::CLICK_OR_DRAG) { + *outFinishPreviousGesture = true; + mPointerGesture.activeGestureId = 0; + } + + // Switch pointers if needed. + // Find the fastest pointer and follow it. + if (activeTouchId >= 0) { + if (mCurrentTouch.pointerCount > 1) { + int32_t bestId = -1; + float bestSpeed = DRAG_MIN_SWITCH_SPEED; + for (uint32_t i = 0; i < mCurrentTouch.pointerCount; i++) { + uint32_t id = mCurrentTouch.pointers[i].id; + float vx, vy; + if (mPointerGesture.velocityTracker.getVelocity(id, &vx, &vy)) { + float speed = pythag(vx, vy); + if (speed > bestSpeed) { + bestId = id; + bestSpeed = speed; + } + } + } + if (bestId >= 0 && bestId != activeTouchId) { + mPointerGesture.activeTouchId = activeTouchId = bestId; +#if DEBUG_GESTURES + LOGD("Gestures: CLICK_OR_DRAG switched pointers, " + "bestId=%d, bestSpeed=%0.3f", bestId, bestSpeed); +#endif + } } - // Orientation - float orientation; - switch (mCalibration.orientationCalibration) { - case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED: - orientation = in.orientation * mLocked.orientationScale; - break; - case Calibration::ORIENTATION_CALIBRATION_VECTOR: { - int32_t c1 = signExtendNybble((in.orientation & 0xf0) >> 4); - int32_t c2 = signExtendNybble(in.orientation & 0x0f); - if (c1 != 0 || c2 != 0) { - orientation = atan2f(c1, c2) * 0.5f; - float scale = 1.0f + pythag(c1, c2) / 16.0f; - touchMajor *= scale; - touchMinor /= scale; - toolMajor *= scale; - toolMinor /= scale; + if (mLastTouch.idBits.hasBit(activeTouchId)) { + const PointerData& currentPointer = + mCurrentTouch.pointers[mCurrentTouch.idToIndex[activeTouchId]]; + const PointerData& lastPointer = + mLastTouch.pointers[mLastTouch.idToIndex[activeTouchId]]; + float deltaX = (currentPointer.x - lastPointer.x) + * mLocked.pointerGestureXMovementScale; + float deltaY = (currentPointer.y - lastPointer.y) + * mLocked.pointerGestureYMovementScale; + mPointerController->move(deltaX, deltaY); + } + } + + float x, y; + mPointerController->getPosition(&x, &y); + + mPointerGesture.currentGestureMode = PointerGesture::CLICK_OR_DRAG; + mPointerGesture.currentGesturePointerCount = 1; + mPointerGesture.currentGestureIdBits.clear(); + mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId); + mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0; + mPointerGesture.currentGestureCoords[0].clear(); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f); + } else if (mCurrentTouch.pointerCount == 0) { + // Case 3. No fingers down and button is not pressed. (NEUTRAL) + *outFinishPreviousGesture = true; + + // Watch for taps coming out of HOVER or INDETERMINATE_MULTITOUCH mode. + bool tapped = false; + if (mPointerGesture.lastGestureMode == PointerGesture::HOVER + || mPointerGesture.lastGestureMode + == PointerGesture::INDETERMINATE_MULTITOUCH) { + if (when <= mPointerGesture.tapTime + TAP_INTERVAL) { + float x, y; + mPointerController->getPosition(&x, &y); + if (fabs(x - mPointerGesture.initialPointerX) <= TAP_SLOP + && fabs(y - mPointerGesture.initialPointerY) <= TAP_SLOP) { +#if DEBUG_GESTURES + LOGD("Gestures: TAP"); +#endif + mPointerGesture.activeGestureId = 0; + mPointerGesture.currentGestureMode = PointerGesture::TAP; + mPointerGesture.currentGesturePointerCount = 1; + mPointerGesture.currentGestureIdBits.clear(); + mPointerGesture.currentGestureIdBits.markBit( + mPointerGesture.activeGestureId); + mPointerGesture.currentGestureIdToIndex[ + mPointerGesture.activeGestureId] = 0; + mPointerGesture.currentGestureCoords[0].clear(); + mPointerGesture.currentGestureCoords[0].setAxisValue( + AMOTION_EVENT_AXIS_X, mPointerGesture.initialPointerX); + mPointerGesture.currentGestureCoords[0].setAxisValue( + AMOTION_EVENT_AXIS_Y, mPointerGesture.initialPointerY); + mPointerGesture.currentGestureCoords[0].setAxisValue( + AMOTION_EVENT_AXIS_PRESSURE, 1.0f); + tapped = true; } else { - orientation = 0; +#if DEBUG_GESTURES + LOGD("Gestures: Not a TAP, deltaX=%f, deltaY=%f", + x - mPointerGesture.initialPointerX, + y - mPointerGesture.initialPointerY); +#endif } - break; + } else { +#if DEBUG_GESTURES + LOGD("Gestures: Not a TAP, delay=%lld", + when - mPointerGesture.tapTime); +#endif } - default: - orientation = 0; + } + if (!tapped) { +#if DEBUG_GESTURES + LOGD("Gestures: NEUTRAL"); +#endif + mPointerGesture.activeGestureId = -1; + mPointerGesture.currentGestureMode = PointerGesture::NEUTRAL; + mPointerGesture.currentGesturePointerCount = 0; + mPointerGesture.currentGestureIdBits.clear(); + } + } else if (mCurrentTouch.pointerCount == 1) { + // Case 4. Exactly one finger down, button is not pressed. (HOVER) + // The pointer follows the active touch point. + // Emit HOVER_MOVE events at the pointer location. + assert(activeTouchId >= 0); + +#if DEBUG_GESTURES + LOGD("Gestures: HOVER"); +#endif + + if (mLastTouch.idBits.hasBit(activeTouchId)) { + const PointerData& currentPointer = + mCurrentTouch.pointers[mCurrentTouch.idToIndex[activeTouchId]]; + const PointerData& lastPointer = + mLastTouch.pointers[mLastTouch.idToIndex[activeTouchId]]; + float deltaX = (currentPointer.x - lastPointer.x) + * mLocked.pointerGestureXMovementScale; + float deltaY = (currentPointer.y - lastPointer.y) + * mLocked.pointerGestureYMovementScale; + mPointerController->move(deltaX, deltaY); + } + + *outFinishPreviousGesture = true; + mPointerGesture.activeGestureId = 0; + + float x, y; + mPointerController->getPosition(&x, &y); + + mPointerGesture.currentGestureMode = PointerGesture::HOVER; + mPointerGesture.currentGesturePointerCount = 1; + mPointerGesture.currentGestureIdBits.clear(); + mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId); + mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0; + mPointerGesture.currentGestureCoords[0].clear(); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.0f); + + if (mLastTouch.pointerCount == 0 && mCurrentTouch.pointerCount != 0) { + mPointerGesture.tapTime = when; + mPointerGesture.initialPointerX = x; + mPointerGesture.initialPointerY = y; + } + } else { + // Case 5. At least two fingers down, button is not pressed. (SWIPE or FREEFORM + // or INDETERMINATE_MULTITOUCH) + // Initially we watch and wait for something interesting to happen so as to + // avoid making a spurious guess as to the nature of the gesture. For example, + // the fingers may be in transition to some other state such as pressing or + // releasing the button or we may be performing a two finger tap. + // + // Fix the centroid of the figure when the gesture actually starts. + // We do not recalculate the centroid at any other time during the gesture because + // it would affect the relationship of the touch points relative to the pointer location. + assert(activeTouchId >= 0); + + uint32_t currentTouchPointerCount = mCurrentTouch.pointerCount; + if (currentTouchPointerCount > MAX_POINTERS) { + currentTouchPointerCount = MAX_POINTERS; + } + + if (mPointerGesture.lastGestureMode != PointerGesture::INDETERMINATE_MULTITOUCH + && mPointerGesture.lastGestureMode != PointerGesture::SWIPE + && mPointerGesture.lastGestureMode != PointerGesture::FREEFORM) { + mPointerGesture.currentGestureMode = PointerGesture::INDETERMINATE_MULTITOUCH; + + *outFinishPreviousGesture = true; + mPointerGesture.activeGestureId = -1; + + // Remember the initial pointer location. + // Everything we do will be relative to this location. + mPointerController->getPosition(&mPointerGesture.initialPointerX, + &mPointerGesture.initialPointerY); + + // Track taps. + if (mLastTouch.pointerCount == 0 && mCurrentTouch.pointerCount != 0) { + mPointerGesture.tapTime = when; } - // X and Y - // Adjust coords for surface orientation. - float x, y; - switch (mLocked.surfaceOrientation) { - case DISPLAY_ORIENTATION_90: - x = float(in.y - mRawAxes.y.minValue) * mLocked.yScale; - y = float(mRawAxes.x.maxValue - in.x) * mLocked.xScale; - orientation -= M_PI_2; - if (orientation < - M_PI_2) { - orientation += M_PI; - } - break; - case DISPLAY_ORIENTATION_180: - x = float(mRawAxes.x.maxValue - in.x) * mLocked.xScale; - y = float(mRawAxes.y.maxValue - in.y) * mLocked.yScale; - break; - case DISPLAY_ORIENTATION_270: - x = float(mRawAxes.y.maxValue - in.y) * mLocked.yScale; - y = float(in.x - mRawAxes.x.minValue) * mLocked.xScale; - orientation += M_PI_2; - if (orientation > M_PI_2) { - orientation -= M_PI; + // Reset the touch origin to be relative to exactly where the fingers are now + // in case they have moved some distance away as part of a previous gesture. + // We want to know how far the fingers have traveled since we started considering + // a multitouch gesture. + mPointerGesture.touchOrigin.copyFrom(mCurrentTouch); + } else { + mPointerGesture.currentGestureMode = mPointerGesture.lastGestureMode; + } + + if (mPointerGesture.currentGestureMode == PointerGesture::INDETERMINATE_MULTITOUCH) { + // Wait for the pointers to start moving before doing anything. + bool decideNow = true; + for (uint32_t i = 0; i < currentTouchPointerCount; i++) { + const PointerData& current = mCurrentTouch.pointers[i]; + const PointerData& origin = mPointerGesture.touchOrigin.pointers[ + mPointerGesture.touchOrigin.idToIndex[current.id]]; + float distance = pythag( + (current.x - origin.x) * mLocked.pointerGestureXZoomScale, + (current.y - origin.y) * mLocked.pointerGestureYZoomScale); + if (distance < MULTITOUCH_MIN_TRAVEL) { + decideNow = false; + break; } - break; - default: - x = float(in.x - mRawAxes.x.minValue) * mLocked.xScale; - y = float(in.y - mRawAxes.y.minValue) * mLocked.yScale; - break; } - // Write output coords. - PointerCoords& out = pointerCoords[outIndex]; - out.clear(); - 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); - - if (id == changedId) { - motionEventAction |= outIndex << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; + if (decideNow) { + mPointerGesture.currentGestureMode = PointerGesture::FREEFORM; + if (currentTouchPointerCount == 2 + && distanceSquared( + mCurrentTouch.pointers[0].x, mCurrentTouch.pointers[0].y, + mCurrentTouch.pointers[1].x, mCurrentTouch.pointers[1].y) + <= mLocked.pointerGestureMaxSwipeWidthSquared) { + const PointerData& current1 = mCurrentTouch.pointers[0]; + const PointerData& current2 = mCurrentTouch.pointers[1]; + const PointerData& origin1 = mPointerGesture.touchOrigin.pointers[ + mPointerGesture.touchOrigin.idToIndex[current1.id]]; + const PointerData& origin2 = mPointerGesture.touchOrigin.pointers[ + mPointerGesture.touchOrigin.idToIndex[current2.id]]; + + float x1 = (current1.x - origin1.x) * mLocked.pointerGestureXZoomScale; + float y1 = (current1.y - origin1.y) * mLocked.pointerGestureYZoomScale; + float x2 = (current2.x - origin2.x) * mLocked.pointerGestureXZoomScale; + float y2 = (current2.y - origin2.y) * mLocked.pointerGestureYZoomScale; + float magnitude1 = pythag(x1, y1); + float magnitude2 = pythag(x2, y2); + + // Calculate the dot product of the vectors. + // When the vectors are oriented in approximately the same direction, + // the angle betweeen them is near zero and the cosine of the angle + // approches 1.0. Recall that dot(v1, v2) = cos(angle) * mag(v1) * mag(v2). + // We know that the magnitude is at least MULTITOUCH_MIN_TRAVEL because + // we checked it above. + float dot = x1 * x2 + y1 * y2; + float cosine = dot / (magnitude1 * magnitude2); // denominator always > 0 + if (cosine > SWIPE_TRANSITION_ANGLE_COSINE) { + mPointerGesture.currentGestureMode = PointerGesture::SWIPE; + } + } + + // Remember the initial centroid for the duration of the gesture. + mPointerGesture.initialCentroidX = 0; + mPointerGesture.initialCentroidY = 0; + for (uint32_t i = 0; i < currentTouchPointerCount; i++) { + const PointerData& touch = mCurrentTouch.pointers[i]; + mPointerGesture.initialCentroidX += touch.x; + mPointerGesture.initialCentroidY += touch.y; + } + mPointerGesture.initialCentroidX /= int32_t(currentTouchPointerCount); + mPointerGesture.initialCentroidY /= int32_t(currentTouchPointerCount); + + mPointerGesture.activeGestureId = 0; + } + } else if (mPointerGesture.currentGestureMode == PointerGesture::SWIPE) { + // Switch to FREEFORM if additional pointers go down. + if (currentTouchPointerCount > 2) { + *outCancelPreviousGesture = true; + mPointerGesture.currentGestureMode = PointerGesture::FREEFORM; } } - // Check edge flags by looking only at the first pointer since the flags are - // global to the event. - if (motionEventAction == AMOTION_EVENT_ACTION_DOWN) { - uint32_t inIndex = touch->idToIndex[pointerIds[0]]; - const PointerData& in = touch->pointers[inIndex]; + if (mPointerGesture.currentGestureMode == PointerGesture::SWIPE) { + // SWIPE mode. +#if DEBUG_GESTURES + LOGD("Gestures: SWIPE activeTouchId=%d," + "activeGestureId=%d, currentTouchPointerCount=%d", + activeTouchId, mPointerGesture.activeGestureId, currentTouchPointerCount); +#endif + assert(mPointerGesture.activeGestureId >= 0); + + float x = (mCurrentTouch.pointers[0].x + mCurrentTouch.pointers[1].x + - mPointerGesture.initialCentroidX * 2) * 0.5f + * mLocked.pointerGestureXMovementScale + mPointerGesture.initialPointerX; + float y = (mCurrentTouch.pointers[0].y + mCurrentTouch.pointers[1].y + - mPointerGesture.initialCentroidY * 2) * 0.5f + * mLocked.pointerGestureYMovementScale + mPointerGesture.initialPointerY; + + mPointerGesture.currentGesturePointerCount = 1; + mPointerGesture.currentGestureIdBits.clear(); + mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId); + mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0; + mPointerGesture.currentGestureCoords[0].clear(); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f); + } else if (mPointerGesture.currentGestureMode == PointerGesture::FREEFORM) { + // FREEFORM mode. +#if DEBUG_GESTURES + LOGD("Gestures: FREEFORM activeTouchId=%d," + "activeGestureId=%d, currentTouchPointerCount=%d", + activeTouchId, mPointerGesture.activeGestureId, currentTouchPointerCount); +#endif + assert(mPointerGesture.activeGestureId >= 0); + + mPointerGesture.currentGesturePointerCount = currentTouchPointerCount; + mPointerGesture.currentGestureIdBits.clear(); + + BitSet32 mappedTouchIdBits; + BitSet32 usedGestureIdBits; + if (mPointerGesture.lastGestureMode != PointerGesture::FREEFORM) { + // Initially, assign the active gesture id to the active touch point + // if there is one. No other touch id bits are mapped yet. + if (!*outCancelPreviousGesture) { + mappedTouchIdBits.markBit(activeTouchId); + usedGestureIdBits.markBit(mPointerGesture.activeGestureId); + mPointerGesture.freeformTouchToGestureIdMap[activeTouchId] = + mPointerGesture.activeGestureId; + } else { + mPointerGesture.activeGestureId = -1; + } + } else { + // Otherwise, assume we mapped all touches from the previous frame. + // Reuse all mappings that are still applicable. + mappedTouchIdBits.value = mLastTouch.idBits.value & mCurrentTouch.idBits.value; + usedGestureIdBits = mPointerGesture.lastGestureIdBits; + + // Check whether we need to choose a new active gesture id because the + // current went went up. + for (BitSet32 upTouchIdBits(mLastTouch.idBits.value & ~mCurrentTouch.idBits.value); + !upTouchIdBits.isEmpty(); ) { + uint32_t upTouchId = upTouchIdBits.firstMarkedBit(); + upTouchIdBits.clearBit(upTouchId); + uint32_t upGestureId = mPointerGesture.freeformTouchToGestureIdMap[upTouchId]; + if (upGestureId == uint32_t(mPointerGesture.activeGestureId)) { + mPointerGesture.activeGestureId = -1; + break; + } + } + } + +#if DEBUG_GESTURES + LOGD("Gestures: FREEFORM follow up " + "mappedTouchIdBits=0x%08x, usedGestureIdBits=0x%08x, " + "activeGestureId=%d", + mappedTouchIdBits.value, usedGestureIdBits.value, + mPointerGesture.activeGestureId); +#endif - if (in.x <= mRawAxes.x.minValue) { - motionEventEdgeFlags |= rotateEdgeFlag(AMOTION_EVENT_EDGE_FLAG_LEFT, - mLocked.surfaceOrientation); - } else if (in.x >= mRawAxes.x.maxValue) { - motionEventEdgeFlags |= rotateEdgeFlag(AMOTION_EVENT_EDGE_FLAG_RIGHT, - mLocked.surfaceOrientation); + for (uint32_t i = 0; i < currentTouchPointerCount; i++) { + uint32_t touchId = mCurrentTouch.pointers[i].id; + uint32_t gestureId; + if (!mappedTouchIdBits.hasBit(touchId)) { + gestureId = usedGestureIdBits.firstUnmarkedBit(); + usedGestureIdBits.markBit(gestureId); + mPointerGesture.freeformTouchToGestureIdMap[touchId] = gestureId; +#if DEBUG_GESTURES + LOGD("Gestures: FREEFORM " + "new mapping for touch id %d -> gesture id %d", + touchId, gestureId); +#endif + } else { + gestureId = mPointerGesture.freeformTouchToGestureIdMap[touchId]; +#if DEBUG_GESTURES + LOGD("Gestures: FREEFORM " + "existing mapping for touch id %d -> gesture id %d", + touchId, gestureId); +#endif + } + mPointerGesture.currentGestureIdBits.markBit(gestureId); + mPointerGesture.currentGestureIdToIndex[gestureId] = i; + + float x = (mCurrentTouch.pointers[i].x - mPointerGesture.initialCentroidX) + * mLocked.pointerGestureXZoomScale + mPointerGesture.initialPointerX; + float y = (mCurrentTouch.pointers[i].y - mPointerGesture.initialCentroidY) + * mLocked.pointerGestureYZoomScale + mPointerGesture.initialPointerY; + + mPointerGesture.currentGestureCoords[i].clear(); + mPointerGesture.currentGestureCoords[i].setAxisValue( + AMOTION_EVENT_AXIS_X, x); + mPointerGesture.currentGestureCoords[i].setAxisValue( + AMOTION_EVENT_AXIS_Y, y); + mPointerGesture.currentGestureCoords[i].setAxisValue( + AMOTION_EVENT_AXIS_PRESSURE, 1.0f); } - if (in.y <= mRawAxes.y.minValue) { - motionEventEdgeFlags |= rotateEdgeFlag(AMOTION_EVENT_EDGE_FLAG_TOP, - mLocked.surfaceOrientation); - } else if (in.y >= mRawAxes.y.maxValue) { - motionEventEdgeFlags |= rotateEdgeFlag(AMOTION_EVENT_EDGE_FLAG_BOTTOM, - mLocked.surfaceOrientation); + + if (mPointerGesture.activeGestureId < 0) { + mPointerGesture.activeGestureId = + mPointerGesture.currentGestureIdBits.firstMarkedBit(); +#if DEBUG_GESTURES + LOGD("Gestures: FREEFORM new " + "activeGestureId=%d", mPointerGesture.activeGestureId); +#endif } + } else { + // INDETERMINATE_MULTITOUCH mode. + // Do nothing. +#if DEBUG_GESTURES + LOGD("Gestures: INDETERMINATE_MULTITOUCH"); +#endif } + } - xPrecision = mLocked.orientedXPrecision; - yPrecision = mLocked.orientedYPrecision; - } // release lock + // Unfade the pointer if the user is doing anything with the touch pad. + mPointerController->setButtonState(mCurrentTouch.buttonState); + if (mCurrentTouch.buttonState || mCurrentTouch.pointerCount != 0) { + mPointerController->unfade(); + } + +#if DEBUG_GESTURES + LOGD("Gestures: finishPreviousGesture=%s, cancelPreviousGesture=%s, " + "currentGestureMode=%d, currentGesturePointerCount=%d, currentGestureIdBits=0x%08x, " + "lastGestureMode=%d, lastGesturePointerCount=%d, lastGestureIdBits=0x%08x", + toString(*outFinishPreviousGesture), toString(*outCancelPreviousGesture), + mPointerGesture.currentGestureMode, mPointerGesture.currentGesturePointerCount, + mPointerGesture.currentGestureIdBits.value, + mPointerGesture.lastGestureMode, mPointerGesture.lastGesturePointerCount, + mPointerGesture.lastGestureIdBits.value); + for (BitSet32 idBits = mPointerGesture.currentGestureIdBits; !idBits.isEmpty(); ) { + uint32_t id = idBits.firstMarkedBit(); + idBits.clearBit(id); + uint32_t index = mPointerGesture.currentGestureIdToIndex[id]; + const PointerCoords& coords = mPointerGesture.currentGestureCoords[index]; + LOGD(" currentGesture[%d]: index=%d, x=%0.3f, y=%0.3f, pressure=%0.3f", + id, index, coords.getAxisValue(AMOTION_EVENT_AXIS_X), + coords.getAxisValue(AMOTION_EVENT_AXIS_Y), + coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE)); + } + for (BitSet32 idBits = mPointerGesture.lastGestureIdBits; !idBits.isEmpty(); ) { + uint32_t id = idBits.firstMarkedBit(); + idBits.clearBit(id); + uint32_t index = mPointerGesture.lastGestureIdToIndex[id]; + const PointerCoords& coords = mPointerGesture.lastGestureCoords[index]; + LOGD(" lastGesture[%d]: index=%d, x=%0.3f, y=%0.3f, pressure=%0.3f", + id, index, coords.getAxisValue(AMOTION_EVENT_AXIS_X), + coords.getAxisValue(AMOTION_EVENT_AXIS_Y), + coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE)); + } +#endif +} + +void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source, + int32_t action, int32_t flags, uint32_t metaState, int32_t edgeFlags, + const PointerCoords* coords, const uint32_t* idToIndex, BitSet32 idBits, + int32_t changedId, float xPrecision, float yPrecision, nsecs_t downTime) { + PointerCoords pointerCoords[MAX_POINTERS]; + int32_t pointerIds[MAX_POINTERS]; + uint32_t pointerCount = 0; + while (!idBits.isEmpty()) { + uint32_t id = idBits.firstMarkedBit(); + idBits.clearBit(id); + uint32_t index = idToIndex[id]; + pointerIds[pointerCount] = id; + pointerCoords[pointerCount].copyFrom(coords[index]); + + if (changedId >= 0 && id == uint32_t(changedId)) { + action |= pointerCount << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; + } + + pointerCount += 1; + } + + assert(pointerCount != 0); + + if (changedId >= 0 && pointerCount == 1) { + // Replace initial down and final up action. + // We can compare the action without masking off the changed pointer index + // because we know the index is 0. + if (action == AMOTION_EVENT_ACTION_POINTER_DOWN) { + action = AMOTION_EVENT_ACTION_DOWN; + } else if (action == AMOTION_EVENT_ACTION_POINTER_UP) { + action = AMOTION_EVENT_ACTION_UP; + } else { + // Can't happen. + assert(false); + } + } + + getDispatcher()->notifyMotion(when, getDeviceId(), source, policyFlags, + action, flags, metaState, edgeFlags, + pointerCount, pointerIds, pointerCoords, xPrecision, yPrecision, downTime); +} - getDispatcher()->notifyMotion(when, getDeviceId(), mTouchSource, policyFlags, - motionEventAction, 0, getContext()->getGlobalMetaState(), motionEventEdgeFlags, - pointerCount, pointerIds, pointerCoords, - xPrecision, yPrecision, mDownTime); +bool TouchInputMapper::updateMovedPointerCoords( + const PointerCoords* inCoords, const uint32_t* inIdToIndex, + PointerCoords* outCoords, const uint32_t* outIdToIndex, BitSet32 idBits) const { + bool changed = false; + while (!idBits.isEmpty()) { + uint32_t id = idBits.firstMarkedBit(); + idBits.clearBit(id); + + uint32_t inIndex = inIdToIndex[id]; + uint32_t outIndex = outIdToIndex[id]; + const PointerCoords& curInCoords = inCoords[inIndex]; + PointerCoords& curOutCoords = outCoords[outIndex]; + + if (curInCoords != curOutCoords) { + curOutCoords.copyFrom(curInCoords); + changed = true; + } + } + return changed; +} + +void TouchInputMapper::fadePointer() { + { // acquire lock + AutoMutex _l(mLock); + if (mPointerController != NULL) { + mPointerController->fade(); + } + } // release lock } bool TouchInputMapper::isPointInsideSurfaceLocked(int32_t x, int32_t y) { @@ -3592,6 +4493,7 @@ void SingleTouchInputMapper::initialize() { mY = 0; mPressure = 0; // default to 0 for devices that don't report pressure mToolWidth = 0; // default to 0 for devices that don't report tool width + mButtonState = 0; } void SingleTouchInputMapper::reset() { @@ -3611,6 +4513,19 @@ void SingleTouchInputMapper::process(const RawEvent* rawEvent) { // not have received valid position information yet. This logic assumes that // BTN_TOUCH is always followed by SYN_REPORT as part of a complete packet. break; + default: + if (mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER) { + uint32_t buttonState = getButtonStateForScanCode(rawEvent->scanCode); + if (buttonState) { + if (rawEvent->value) { + mAccumulator.buttonDown |= buttonState; + } else { + mAccumulator.buttonUp |= buttonState; + } + mAccumulator.fields |= Accumulator::FIELD_BUTTONS; + } + } + break; } break; @@ -3671,6 +4586,10 @@ void SingleTouchInputMapper::sync(nsecs_t when) { mToolWidth = mAccumulator.absToolWidth; } + if (fields & Accumulator::FIELD_BUTTONS) { + mButtonState = (mButtonState | mAccumulator.buttonDown) & ~mAccumulator.buttonUp; + } + mCurrentTouch.clear(); if (mDown) { @@ -3686,6 +4605,7 @@ void SingleTouchInputMapper::sync(nsecs_t when) { mCurrentTouch.pointers[0].orientation = 0; mCurrentTouch.idToIndex[0] = 0; mCurrentTouch.idBits.markBit(0); + mCurrentTouch.buttonState = mButtonState; } syncTouch(when, true); @@ -3715,6 +4635,7 @@ MultiTouchInputMapper::~MultiTouchInputMapper() { void MultiTouchInputMapper::initialize() { mAccumulator.clear(); + mButtonState = 0; } void MultiTouchInputMapper::reset() { @@ -3725,6 +4646,20 @@ void MultiTouchInputMapper::reset() { void MultiTouchInputMapper::process(const RawEvent* rawEvent) { switch (rawEvent->type) { + case EV_KEY: { + if (mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER) { + uint32_t buttonState = getButtonStateForScanCode(rawEvent->scanCode); + if (buttonState) { + if (rawEvent->value) { + mAccumulator.buttonDown |= buttonState; + } else { + mAccumulator.buttonUp |= buttonState; + } + } + } + break; + } + case EV_ABS: { uint32_t pointerIndex = mAccumulator.pointerCount; Accumulator::Pointer* pointer = & mAccumulator.pointers[pointerIndex]; @@ -3902,6 +4837,9 @@ void MultiTouchInputMapper::sync(nsecs_t when) { mCurrentTouch.pointerCount = outCount; + mButtonState = (mButtonState | mAccumulator.buttonDown) & ~mAccumulator.buttonUp; + mCurrentTouch.buttonState = mButtonState; + syncTouch(when, havePointerIds); mAccumulator.clear(); diff --git a/services/input/InputReader.h b/services/input/InputReader.h index 68002ca..55ab479 100644 --- a/services/input/InputReader.h +++ b/services/input/InputReader.h @@ -558,6 +558,8 @@ public: virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags); + virtual void fadePointer(); + protected: Mutex mLock; @@ -611,10 +613,12 @@ protected: PointerData pointers[MAX_POINTERS]; BitSet32 idBits; uint32_t idToIndex[MAX_POINTER_ID + 1]; + uint32_t buttonState; void copyFrom(const TouchData& other) { pointerCount = other.pointerCount; idBits = other.idBits; + buttonState = other.buttonState; for (uint32_t i = 0; i < pointerCount; i++) { pointers[i] = other.pointers[i]; @@ -627,17 +631,20 @@ protected: inline void clear() { pointerCount = 0; idBits.clear(); + buttonState = 0; } }; // Input sources supported by the device. uint32_t mTouchSource; // sources when reporting touch data + uint32_t mPointerSource; // sources when reporting pointer gestures // Immutable configuration parameters. struct Parameters { enum DeviceType { DEVICE_TYPE_TOUCH_SCREEN, DEVICE_TYPE_TOUCH_PAD, + DEVICE_TYPE_POINTER, }; DeviceType deviceType; @@ -735,11 +742,17 @@ protected: // Current and previous touch sample data. TouchData mCurrentTouch; + PointerCoords mCurrentTouchCoords[MAX_POINTERS]; + TouchData mLastTouch; + PointerCoords mLastTouchCoords[MAX_POINTERS]; // The time the primary pointer last went down. nsecs_t mDownTime; + // The pointer controller, or null if the device is not a pointer. + sp<PointerControllerInterface> mPointerController; + struct LockedState { Vector<VirtualKey> virtualKeys; @@ -804,6 +817,17 @@ protected: int32_t keyCode; int32_t scanCode; } currentVirtualKey; + + // Scale factor for gesture based pointer movements. + float pointerGestureXMovementScale; + float pointerGestureYMovementScale; + + // Scale factor for gesture based zooming and other freeform motions. + float pointerGestureXZoomScale; + float pointerGestureYZoomScale; + + // The maximum swipe width squared. + int32_t pointerGestureMaxSwipeWidthSquared; } mLocked; virtual void configureParameters(); @@ -869,13 +893,148 @@ private: uint64_t distance : 48; // squared distance }; + struct PointerGesture { + enum Mode { + // No fingers, button is not pressed. + // Nothing happening. + NEUTRAL, + + // No fingers, button is not pressed. + // Tap detected. + // Emits DOWN and UP events at the pointer location. + TAP, + + // Button is pressed. + // Pointer follows the active finger if there is one. Other fingers are ignored. + // Emits DOWN, MOVE and UP events at the pointer location. + CLICK_OR_DRAG, + + // Exactly one finger, button is not pressed. + // Pointer follows the active finger. + // Emits HOVER_MOVE events at the pointer location. + HOVER, + + // More than two fingers involved but they haven't moved enough for us + // to figure out what is intended. + INDETERMINATE_MULTITOUCH, + + // Exactly two fingers moving in the same direction, button is not pressed. + // Pointer does not move. + // Emits DOWN, MOVE and UP events with a single pointer coordinate that + // follows the midpoint between both fingers. + // The centroid is fixed when entering this state. + SWIPE, + + // Two or more fingers moving in arbitrary directions, button is not pressed. + // Pointer does not move. + // Emits DOWN, POINTER_DOWN, MOVE, POINTER_UP and UP events that follow + // each finger individually relative to the initial centroid of the finger. + // The centroid is fixed when entering this state. + FREEFORM, + + // Waiting for quiet time to end before starting the next gesture. + QUIET, + }; + + // The active pointer id from the raw touch data. + int32_t activeTouchId; // -1 if none + + // The active pointer id from the gesture last delivered to the application. + int32_t activeGestureId; // -1 if none + + // Pointer coords and ids for the current and previous pointer gesture. + Mode currentGestureMode; + uint32_t currentGesturePointerCount; + BitSet32 currentGestureIdBits; + uint32_t currentGestureIdToIndex[MAX_POINTER_ID + 1]; + PointerCoords currentGestureCoords[MAX_POINTERS]; + + Mode lastGestureMode; + uint32_t lastGesturePointerCount; + BitSet32 lastGestureIdBits; + uint32_t lastGestureIdToIndex[MAX_POINTER_ID + 1]; + PointerCoords lastGestureCoords[MAX_POINTERS]; + + // Tracks for all pointers originally went down. + TouchData touchOrigin; + + // Describes how touch ids are mapped to gesture ids for freeform gestures. + uint32_t freeformTouchToGestureIdMap[MAX_POINTER_ID + 1]; + + // Initial centroid of the movement. + // Used to calculate how far the touch pointers have moved since the gesture started. + int32_t initialCentroidX; + int32_t initialCentroidY; + + // Initial pointer location. + // Used to track where the pointer was when the gesture started. + float initialPointerX; + float initialPointerY; + + // Time the pointer gesture last went down. + nsecs_t downTime; + + // Time we started waiting for a tap gesture. + nsecs_t tapTime; + + // Time we started waiting for quiescence. + nsecs_t quietTime; + + // A velocity tracker for determining whether to switch active pointers during drags. + VelocityTracker velocityTracker; + + void reset() { + activeTouchId = -1; + activeGestureId = -1; + currentGestureMode = NEUTRAL; + currentGesturePointerCount = 0; + currentGestureIdBits.clear(); + lastGestureMode = NEUTRAL; + lastGesturePointerCount = 0; + lastGestureIdBits.clear(); + touchOrigin.clear(); + initialCentroidX = 0; + initialCentroidY = 0; + initialPointerX = 0; + initialPointerY = 0; + downTime = 0; + velocityTracker.clear(); + resetTapTime(); + resetQuietTime(); + } + + void resetTapTime() { + tapTime = LLONG_MIN; + } + + void resetQuietTime() { + quietTime = LLONG_MIN; + } + } mPointerGesture; + void initializeLocked(); TouchResult consumeOffScreenTouches(nsecs_t when, uint32_t policyFlags); void dispatchTouches(nsecs_t when, uint32_t policyFlags); - void dispatchTouch(nsecs_t when, uint32_t policyFlags, TouchData* touch, - BitSet32 idBits, uint32_t changedId, uint32_t pointerCount, - int32_t motionEventAction); + void prepareTouches(int32_t* outEdgeFlags, float* outXPrecision, float* outYPrecision); + void dispatchPointerGestures(nsecs_t when, uint32_t policyFlags); + void preparePointerGestures(nsecs_t when, + bool* outCancelPreviousGesture, bool* outFinishPreviousGesture); + + // Dispatches a motion event. + // If the changedId is >= 0 and the action is POINTER_DOWN or POINTER_UP, the + // method will take care of setting the index and transmuting the action to DOWN or UP + // it is the first / last pointer to go down / up. + void dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source, + int32_t action, int32_t flags, uint32_t metaState, int32_t edgeFlags, + const PointerCoords* coords, const uint32_t* idToIndex, BitSet32 idBits, + int32_t changedId, float xPrecision, float yPrecision, nsecs_t downTime); + + // Updates pointer coords for pointers with specified ids that have moved. + // Returns true if any of them changed. + bool updateMovedPointerCoords(const PointerCoords* inCoords, const uint32_t* inIdToIndex, + PointerCoords* outCoords, const uint32_t* outIdToIndex, BitSet32 idBits) const; + void suppressSwipeOntoVirtualKeys(nsecs_t when); bool isPointInsideSurfaceLocked(int32_t x, int32_t y); @@ -907,6 +1066,7 @@ private: FIELD_ABS_Y = 4, FIELD_ABS_PRESSURE = 8, FIELD_ABS_TOOL_WIDTH = 16, + FIELD_BUTTONS = 32, }; uint32_t fields; @@ -917,8 +1077,13 @@ private: int32_t absPressure; int32_t absToolWidth; + uint32_t buttonDown; + uint32_t buttonUp; + inline void clear() { fields = 0; + buttonDown = 0; + buttonUp = 0; } } mAccumulator; @@ -927,6 +1092,7 @@ private: int32_t mY; int32_t mPressure; int32_t mToolWidth; + uint32_t mButtonState; void initialize(); @@ -978,12 +1144,20 @@ private: } } pointers[MAX_POINTERS + 1]; // + 1 to remove the need for extra range checks + // Bitfield of buttons that went down or up. + uint32_t buttonDown; + uint32_t buttonUp; + inline void clear() { pointerCount = 0; pointers[0].clear(); + buttonDown = 0; + buttonUp = 0; } } mAccumulator; + uint32_t mButtonState; + void initialize(); void sync(nsecs_t when); diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp index 67a2e21..075eff3 100644 --- a/services/input/tests/InputReader_test.cpp +++ b/services/input/tests/InputReader_test.cpp @@ -2448,11 +2448,21 @@ void SingleTouchInputMapperTest::processSync(SingleTouchInputMapper* mapper) { } -TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsNotSpecified_ReturnsTouchPad) { +TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsNotSpecifiedAndNotACursor_ReturnsPointer) { SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice); prepareAxes(POSITION); addMapperAndConfigure(mapper); + ASSERT_EQ(AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, mapper->getSources()); +} + +TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsNotSpecifiedAndIsACursor_ReturnsTouchPad) { + SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice); + mFakeEventHub->addRelativeAxis(DEVICE_ID, REL_X); + mFakeEventHub->addRelativeAxis(DEVICE_ID, REL_Y); + prepareAxes(POSITION); + addMapperAndConfigure(mapper); + ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper->getSources()); } diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java index b7d0a8f..1ab22c0 100644 --- a/services/java/com/android/server/ConnectivityService.java +++ b/services/java/com/android/server/ConnectivityService.java @@ -24,6 +24,7 @@ import android.content.pm.PackageManager; import android.database.ContentObserver; import android.net.ConnectivityManager; import android.net.DummyDataStateTracker; +import android.net.EthernetDataTracker; import android.net.IConnectivityManager; import android.net.LinkProperties; import android.net.MobileDataStateTracker; @@ -420,6 +421,10 @@ public class ConnectivityService extends IConnectivityManager.Stub { mNetTrackers[netType] = BluetoothTetheringDataTracker.getInstance(); mNetTrackers[netType].startMonitoring(context, mHandler); break; + case ConnectivityManager.TYPE_ETHERNET: + mNetTrackers[netType] = EthernetDataTracker.getInstance(); + mNetTrackers[netType].startMonitoring(context, mHandler); + break; default: loge("Trying to create a DataStateTracker for an unknown radio type " + mNetAttributes[netType].mRadio); diff --git a/services/java/com/android/server/DeviceStorageMonitorService.java b/services/java/com/android/server/DeviceStorageMonitorService.java index 0fba7c3..b0d2158 100644 --- a/services/java/com/android/server/DeviceStorageMonitorService.java +++ b/services/java/com/android/server/DeviceStorageMonitorService.java @@ -398,4 +398,24 @@ class DeviceStorageMonitorService extends Binder { // force an early check postCheckMemoryMsg(true, 0); } + + /** + * Callable from other things in the system service to obtain the low memory + * threshold. + * + * @return low memory threshold in bytes + */ + public long getMemoryLowThreshold() { + return mMemLowThreshold; + } + + /** + * Callable from other things in the system process to check whether memory + * is low. + * + * @return true is memory is low + */ + public boolean isMemoryLow() { + return mLowMemFlag; + } } diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java index 04f271d..3aebb5c 100644 --- a/services/java/com/android/server/PackageManagerService.java +++ b/services/java/com/android/server/PackageManagerService.java @@ -4919,12 +4919,24 @@ class PackageManagerService extends IPackageManager.Stub { Slog.w(TAG, "Cannot install fwd locked apps on sdcard"); ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION; } else { + final long lowThreshold; + + final DeviceStorageMonitorService dsm = (DeviceStorageMonitorService) ServiceManager + .getService(DeviceStorageMonitorService.SERVICE); + if (dsm == null) { + Log.w(TAG, "Couldn't get low memory threshold; no free limit imposed"); + lowThreshold = 0L; + } else { + lowThreshold = dsm.getMemoryLowThreshold(); + } + // Remote call to find out default install location final PackageInfoLite pkgLite; try { mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION); - pkgLite = mContainerService.getMinimalPackageInfo(packageURI, flags); + pkgLite = mContainerService.getMinimalPackageInfo(packageURI, flags, + lowThreshold); } finally { mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION); } @@ -5144,10 +5156,26 @@ class PackageManagerService extends IPackageManager.Stub { } boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException { + final long lowThreshold; + + final DeviceStorageMonitorService dsm = (DeviceStorageMonitorService) ServiceManager + .getService(DeviceStorageMonitorService.SERVICE); + if (dsm == null) { + Log.w(TAG, "Couldn't get low memory threshold; no free limit imposed"); + lowThreshold = 0L; + } else { + if (dsm.isMemoryLow()) { + Log.w(TAG, "Memory is reported as being too low; aborting package install"); + return false; + } + + lowThreshold = dsm.getMemoryLowThreshold(); + } + try { mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION); - return imcs.checkFreeStorage(false, packageURI); + return imcs.checkInternalFreeStorage(packageURI, lowThreshold); } finally { mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION); } @@ -5373,7 +5401,7 @@ class PackageManagerService extends IPackageManager.Stub { try { mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION); - return imcs.checkFreeStorage(true, packageURI); + return imcs.checkExternalFreeStorage(packageURI); } finally { mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION); } diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java index 0000237..915b679 100644 --- a/services/java/com/android/server/WifiService.java +++ b/services/java/com/android/server/WifiService.java @@ -235,6 +235,15 @@ public class WifiService extends IWifiManager.Stub { } break; } + case AsyncChannel.CMD_CHANNEL_DISCONNECTED: { + if (msg.arg1 == AsyncChannel.STATUS_SEND_UNSUCCESSFUL) { + Slog.d(TAG, "Send failed, client connection lost"); + } else { + Slog.d(TAG, "Client connection lost with reason: " + msg.arg1); + } + mClients.remove((AsyncChannel) msg.obj); + break; + } case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: { AsyncChannel ac = new AsyncChannel(); ac.connect(mContext, this, msg.replyTo); diff --git a/services/jni/com_android_server_InputApplication.cpp b/services/jni/com_android_server_InputApplication.cpp index e64ec4e..1f80242 100644 --- a/services/jni/com_android_server_InputApplication.cpp +++ b/services/jni/com_android_server_InputApplication.cpp @@ -26,8 +26,6 @@ namespace android { static struct { - jclass clazz; - jfieldID inputApplicationHandle; jfieldID name; jfieldID dispatchingTimeoutNanos; @@ -69,25 +67,25 @@ void android_server_InputApplication_toNative( #define FIND_CLASS(var, className) \ var = env->FindClass(className); \ - LOG_FATAL_IF(! var, "Unable to find class " className); \ - var = jclass(env->NewGlobalRef(var)); + LOG_FATAL_IF(! var, "Unable to find class " className); #define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \ var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \ LOG_FATAL_IF(! var, "Unable to find field " fieldName); int register_android_server_InputApplication(JNIEnv* env) { - FIND_CLASS(gInputApplicationClassInfo.clazz, "com/android/server/wm/InputApplication"); + jclass clazz; + FIND_CLASS(clazz, "com/android/server/wm/InputApplication"); GET_FIELD_ID(gInputApplicationClassInfo.inputApplicationHandle, - gInputApplicationClassInfo.clazz, + clazz, "inputApplicationHandle", "Lcom/android/server/wm/InputApplicationHandle;"); - GET_FIELD_ID(gInputApplicationClassInfo.name, gInputApplicationClassInfo.clazz, + GET_FIELD_ID(gInputApplicationClassInfo.name, clazz, "name", "Ljava/lang/String;"); GET_FIELD_ID(gInputApplicationClassInfo.dispatchingTimeoutNanos, - gInputApplicationClassInfo.clazz, + clazz, "dispatchingTimeoutNanos", "J"); return 0; } diff --git a/services/jni/com_android_server_InputApplicationHandle.cpp b/services/jni/com_android_server_InputApplicationHandle.cpp index 3a1214f..9516964 100644 --- a/services/jni/com_android_server_InputApplicationHandle.cpp +++ b/services/jni/com_android_server_InputApplicationHandle.cpp @@ -26,8 +26,6 @@ namespace android { static struct { - jclass clazz; - jfieldID ptr; } gInputApplicationHandleClassInfo; @@ -98,8 +96,7 @@ static JNINativeMethod gInputApplicationHandleMethods[] = { #define FIND_CLASS(var, className) \ var = env->FindClass(className); \ - LOG_FATAL_IF(! var, "Unable to find class " className); \ - var = jclass(env->NewGlobalRef(var)); + LOG_FATAL_IF(! var, "Unable to find class " className); #define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \ var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \ @@ -110,9 +107,10 @@ int register_android_server_InputApplicationHandle(JNIEnv* env) { gInputApplicationHandleMethods, NELEM(gInputApplicationHandleMethods)); LOG_FATAL_IF(res < 0, "Unable to register native methods."); - FIND_CLASS(gInputApplicationHandleClassInfo.clazz, "com/android/server/wm/InputApplicationHandle"); + jclass clazz; + FIND_CLASS(clazz, "com/android/server/wm/InputApplicationHandle"); - GET_FIELD_ID(gInputApplicationHandleClassInfo.ptr, gInputApplicationHandleClassInfo.clazz, + GET_FIELD_ID(gInputApplicationHandleClassInfo.ptr, clazz, "ptr", "I"); return 0; diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp index 80dddc2..e7e1fa2 100644 --- a/services/jni/com_android_server_InputManager.cpp +++ b/services/jni/com_android_server_InputManager.cpp @@ -52,8 +52,6 @@ namespace android { static struct { - jclass clazz; - jmethodID notifyConfigurationChanged; jmethodID notifyLidSwitchChanged; jmethodID notifyInputChannelBroken; @@ -95,16 +93,12 @@ static struct { } gInputDeviceClassInfo; static struct { - jclass clazz; - jfieldID touchscreen; jfieldID keyboard; jfieldID navigation; } gConfigurationClassInfo; static struct { - jclass clazz; - jfieldID bitmap; jfieldID hotSpotX; jfieldID hotSpotY; @@ -1024,15 +1018,14 @@ static jint android_server_InputManager_nativeInjectInputEvent(JNIEnv* env, jcla return gNativeInputManager->getInputManager()->getDispatcher()->injectInputEvent( & keyEvent, injectorPid, injectorUid, syncMode, timeoutMillis); } else if (env->IsInstanceOf(inputEventObj, gMotionEventClassInfo.clazz)) { - MotionEvent motionEvent; - status_t status = android_view_MotionEvent_toNative(env, inputEventObj, & motionEvent); - if (status) { + const MotionEvent* motionEvent = android_view_MotionEvent_getNativePtr(env, inputEventObj); + if (!motionEvent) { jniThrowRuntimeException(env, "Could not read contents of MotionEvent object."); return INPUT_EVENT_INJECTION_FAILED; } return gNativeInputManager->getInputManager()->getDispatcher()->injectInputEvent( - & motionEvent, injectorPid, injectorUid, syncMode, timeoutMillis); + motionEvent, injectorPid, injectorUid, syncMode, timeoutMillis); } else { jniThrowRuntimeException(env, "Invalid input event type."); return INPUT_EVENT_INJECTION_FAILED; @@ -1226,8 +1219,7 @@ static JNINativeMethod gInputManagerMethods[] = { #define FIND_CLASS(var, className) \ var = env->FindClass(className); \ - LOG_FATAL_IF(! var, "Unable to find class " className); \ - var = jclass(env->NewGlobalRef(var)); + LOG_FATAL_IF(! var, "Unable to find class " className); #define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \ var = env->GetMethodID(clazz, methodName, methodDescriptor); \ @@ -1244,77 +1236,82 @@ int register_android_server_InputManager(JNIEnv* env) { // Callbacks - FIND_CLASS(gCallbacksClassInfo.clazz, "com/android/server/wm/InputManager$Callbacks"); + jclass clazz; + FIND_CLASS(clazz, "com/android/server/wm/InputManager$Callbacks"); - GET_METHOD_ID(gCallbacksClassInfo.notifyConfigurationChanged, gCallbacksClassInfo.clazz, + GET_METHOD_ID(gCallbacksClassInfo.notifyConfigurationChanged, clazz, "notifyConfigurationChanged", "(J)V"); - GET_METHOD_ID(gCallbacksClassInfo.notifyLidSwitchChanged, gCallbacksClassInfo.clazz, + GET_METHOD_ID(gCallbacksClassInfo.notifyLidSwitchChanged, clazz, "notifyLidSwitchChanged", "(JZ)V"); - GET_METHOD_ID(gCallbacksClassInfo.notifyInputChannelBroken, gCallbacksClassInfo.clazz, + GET_METHOD_ID(gCallbacksClassInfo.notifyInputChannelBroken, clazz, "notifyInputChannelBroken", "(Lcom/android/server/wm/InputWindowHandle;)V"); - GET_METHOD_ID(gCallbacksClassInfo.notifyANR, gCallbacksClassInfo.clazz, + GET_METHOD_ID(gCallbacksClassInfo.notifyANR, clazz, "notifyANR", "(Lcom/android/server/wm/InputApplicationHandle;Lcom/android/server/wm/InputWindowHandle;)J"); - GET_METHOD_ID(gCallbacksClassInfo.interceptKeyBeforeQueueing, gCallbacksClassInfo.clazz, + GET_METHOD_ID(gCallbacksClassInfo.interceptKeyBeforeQueueing, clazz, "interceptKeyBeforeQueueing", "(Landroid/view/KeyEvent;IZ)I"); GET_METHOD_ID(gCallbacksClassInfo.interceptMotionBeforeQueueingWhenScreenOff, - gCallbacksClassInfo.clazz, + clazz, "interceptMotionBeforeQueueingWhenScreenOff", "(I)I"); - GET_METHOD_ID(gCallbacksClassInfo.interceptKeyBeforeDispatching, gCallbacksClassInfo.clazz, + GET_METHOD_ID(gCallbacksClassInfo.interceptKeyBeforeDispatching, clazz, "interceptKeyBeforeDispatching", "(Lcom/android/server/wm/InputWindowHandle;Landroid/view/KeyEvent;I)Z"); - GET_METHOD_ID(gCallbacksClassInfo.dispatchUnhandledKey, gCallbacksClassInfo.clazz, + GET_METHOD_ID(gCallbacksClassInfo.dispatchUnhandledKey, clazz, "dispatchUnhandledKey", "(Lcom/android/server/wm/InputWindowHandle;Landroid/view/KeyEvent;I)Landroid/view/KeyEvent;"); - GET_METHOD_ID(gCallbacksClassInfo.checkInjectEventsPermission, gCallbacksClassInfo.clazz, + GET_METHOD_ID(gCallbacksClassInfo.checkInjectEventsPermission, clazz, "checkInjectEventsPermission", "(II)Z"); - GET_METHOD_ID(gCallbacksClassInfo.filterTouchEvents, gCallbacksClassInfo.clazz, + GET_METHOD_ID(gCallbacksClassInfo.filterTouchEvents, clazz, "filterTouchEvents", "()Z"); - GET_METHOD_ID(gCallbacksClassInfo.filterJumpyTouchEvents, gCallbacksClassInfo.clazz, + GET_METHOD_ID(gCallbacksClassInfo.filterJumpyTouchEvents, clazz, "filterJumpyTouchEvents", "()Z"); - GET_METHOD_ID(gCallbacksClassInfo.getVirtualKeyQuietTimeMillis, gCallbacksClassInfo.clazz, + GET_METHOD_ID(gCallbacksClassInfo.getVirtualKeyQuietTimeMillis, clazz, "getVirtualKeyQuietTimeMillis", "()I"); - GET_METHOD_ID(gCallbacksClassInfo.getExcludedDeviceNames, gCallbacksClassInfo.clazz, + GET_METHOD_ID(gCallbacksClassInfo.getExcludedDeviceNames, clazz, "getExcludedDeviceNames", "()[Ljava/lang/String;"); - GET_METHOD_ID(gCallbacksClassInfo.getKeyRepeatTimeout, gCallbacksClassInfo.clazz, + GET_METHOD_ID(gCallbacksClassInfo.getKeyRepeatTimeout, clazz, "getKeyRepeatTimeout", "()I"); - GET_METHOD_ID(gCallbacksClassInfo.getKeyRepeatDelay, gCallbacksClassInfo.clazz, + GET_METHOD_ID(gCallbacksClassInfo.getKeyRepeatDelay, clazz, "getKeyRepeatDelay", "()I"); - GET_METHOD_ID(gCallbacksClassInfo.getMaxEventsPerSecond, gCallbacksClassInfo.clazz, + GET_METHOD_ID(gCallbacksClassInfo.getMaxEventsPerSecond, clazz, "getMaxEventsPerSecond", "()I"); - GET_METHOD_ID(gCallbacksClassInfo.getPointerLayer, gCallbacksClassInfo.clazz, + GET_METHOD_ID(gCallbacksClassInfo.getPointerLayer, clazz, "getPointerLayer", "()I"); - GET_METHOD_ID(gCallbacksClassInfo.getPointerIcon, gCallbacksClassInfo.clazz, + GET_METHOD_ID(gCallbacksClassInfo.getPointerIcon, clazz, "getPointerIcon", "()Lcom/android/server/wm/InputManager$PointerIcon;"); // KeyEvent FIND_CLASS(gKeyEventClassInfo.clazz, "android/view/KeyEvent"); + gKeyEventClassInfo.clazz = jclass(env->NewGlobalRef(gKeyEventClassInfo.clazz)); + // MotionEvent FIND_CLASS(gMotionEventClassInfo.clazz, "android/view/MotionEvent"); + gMotionEventClassInfo.clazz = jclass(env->NewGlobalRef(gMotionEventClassInfo.clazz)); // InputDevice FIND_CLASS(gInputDeviceClassInfo.clazz, "android/view/InputDevice"); + gInputDeviceClassInfo.clazz = jclass(env->NewGlobalRef(gInputDeviceClassInfo.clazz)); GET_METHOD_ID(gInputDeviceClassInfo.ctor, gInputDeviceClassInfo.clazz, "<init>", "()V"); @@ -1336,28 +1333,28 @@ int register_android_server_InputManager(JNIEnv* env) { // Configuration - FIND_CLASS(gConfigurationClassInfo.clazz, "android/content/res/Configuration"); + FIND_CLASS(clazz, "android/content/res/Configuration"); - GET_FIELD_ID(gConfigurationClassInfo.touchscreen, gConfigurationClassInfo.clazz, + GET_FIELD_ID(gConfigurationClassInfo.touchscreen, clazz, "touchscreen", "I"); - GET_FIELD_ID(gConfigurationClassInfo.keyboard, gConfigurationClassInfo.clazz, + GET_FIELD_ID(gConfigurationClassInfo.keyboard, clazz, "keyboard", "I"); - GET_FIELD_ID(gConfigurationClassInfo.navigation, gConfigurationClassInfo.clazz, + GET_FIELD_ID(gConfigurationClassInfo.navigation, clazz, "navigation", "I"); // PointerIcon - FIND_CLASS(gPointerIconClassInfo.clazz, "com/android/server/wm/InputManager$PointerIcon"); + FIND_CLASS(clazz, "com/android/server/wm/InputManager$PointerIcon"); - GET_FIELD_ID(gPointerIconClassInfo.bitmap, gPointerIconClassInfo.clazz, + GET_FIELD_ID(gPointerIconClassInfo.bitmap, clazz, "bitmap", "Landroid/graphics/Bitmap;"); - GET_FIELD_ID(gPointerIconClassInfo.hotSpotX, gPointerIconClassInfo.clazz, + GET_FIELD_ID(gPointerIconClassInfo.hotSpotX, clazz, "hotSpotX", "F"); - GET_FIELD_ID(gPointerIconClassInfo.hotSpotY, gPointerIconClassInfo.clazz, + GET_FIELD_ID(gPointerIconClassInfo.hotSpotY, clazz, "hotSpotY", "F"); return 0; diff --git a/services/jni/com_android_server_InputWindow.cpp b/services/jni/com_android_server_InputWindow.cpp index 8548b47..99f625c 100644 --- a/services/jni/com_android_server_InputWindow.cpp +++ b/services/jni/com_android_server_InputWindow.cpp @@ -28,8 +28,6 @@ namespace android { static struct { - jclass clazz; - jfieldID inputWindowHandle; jfieldID inputChannel; jfieldID name; @@ -136,71 +134,71 @@ void android_server_InputWindow_toNative( #define FIND_CLASS(var, className) \ var = env->FindClass(className); \ - LOG_FATAL_IF(! var, "Unable to find class " className); \ - var = jclass(env->NewGlobalRef(var)); + LOG_FATAL_IF(! var, "Unable to find class " className); #define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \ var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \ LOG_FATAL_IF(! var, "Unable to find field " fieldName); int register_android_server_InputWindow(JNIEnv* env) { - FIND_CLASS(gInputWindowClassInfo.clazz, "com/android/server/wm/InputWindow"); + jclass clazz; + FIND_CLASS(clazz, "com/android/server/wm/InputWindow"); - GET_FIELD_ID(gInputWindowClassInfo.inputWindowHandle, gInputWindowClassInfo.clazz, + GET_FIELD_ID(gInputWindowClassInfo.inputWindowHandle, clazz, "inputWindowHandle", "Lcom/android/server/wm/InputWindowHandle;"); - GET_FIELD_ID(gInputWindowClassInfo.inputChannel, gInputWindowClassInfo.clazz, + GET_FIELD_ID(gInputWindowClassInfo.inputChannel, clazz, "inputChannel", "Landroid/view/InputChannel;"); - GET_FIELD_ID(gInputWindowClassInfo.name, gInputWindowClassInfo.clazz, + GET_FIELD_ID(gInputWindowClassInfo.name, clazz, "name", "Ljava/lang/String;"); - GET_FIELD_ID(gInputWindowClassInfo.layoutParamsFlags, gInputWindowClassInfo.clazz, + GET_FIELD_ID(gInputWindowClassInfo.layoutParamsFlags, clazz, "layoutParamsFlags", "I"); - GET_FIELD_ID(gInputWindowClassInfo.layoutParamsType, gInputWindowClassInfo.clazz, + GET_FIELD_ID(gInputWindowClassInfo.layoutParamsType, clazz, "layoutParamsType", "I"); - GET_FIELD_ID(gInputWindowClassInfo.dispatchingTimeoutNanos, gInputWindowClassInfo.clazz, + GET_FIELD_ID(gInputWindowClassInfo.dispatchingTimeoutNanos, clazz, "dispatchingTimeoutNanos", "J"); - GET_FIELD_ID(gInputWindowClassInfo.frameLeft, gInputWindowClassInfo.clazz, + GET_FIELD_ID(gInputWindowClassInfo.frameLeft, clazz, "frameLeft", "I"); - GET_FIELD_ID(gInputWindowClassInfo.frameTop, gInputWindowClassInfo.clazz, + GET_FIELD_ID(gInputWindowClassInfo.frameTop, clazz, "frameTop", "I"); - GET_FIELD_ID(gInputWindowClassInfo.frameRight, gInputWindowClassInfo.clazz, + GET_FIELD_ID(gInputWindowClassInfo.frameRight, clazz, "frameRight", "I"); - GET_FIELD_ID(gInputWindowClassInfo.frameBottom, gInputWindowClassInfo.clazz, + GET_FIELD_ID(gInputWindowClassInfo.frameBottom, clazz, "frameBottom", "I"); - GET_FIELD_ID(gInputWindowClassInfo.touchableRegion, gInputWindowClassInfo.clazz, + GET_FIELD_ID(gInputWindowClassInfo.touchableRegion, clazz, "touchableRegion", "Landroid/graphics/Region;"); - GET_FIELD_ID(gInputWindowClassInfo.visible, gInputWindowClassInfo.clazz, + GET_FIELD_ID(gInputWindowClassInfo.visible, clazz, "visible", "Z"); - GET_FIELD_ID(gInputWindowClassInfo.canReceiveKeys, gInputWindowClassInfo.clazz, + GET_FIELD_ID(gInputWindowClassInfo.canReceiveKeys, clazz, "canReceiveKeys", "Z"); - GET_FIELD_ID(gInputWindowClassInfo.hasFocus, gInputWindowClassInfo.clazz, + GET_FIELD_ID(gInputWindowClassInfo.hasFocus, clazz, "hasFocus", "Z"); - GET_FIELD_ID(gInputWindowClassInfo.hasWallpaper, gInputWindowClassInfo.clazz, + GET_FIELD_ID(gInputWindowClassInfo.hasWallpaper, clazz, "hasWallpaper", "Z"); - GET_FIELD_ID(gInputWindowClassInfo.paused, gInputWindowClassInfo.clazz, + GET_FIELD_ID(gInputWindowClassInfo.paused, clazz, "paused", "Z"); - GET_FIELD_ID(gInputWindowClassInfo.layer, gInputWindowClassInfo.clazz, + GET_FIELD_ID(gInputWindowClassInfo.layer, clazz, "layer", "I"); - GET_FIELD_ID(gInputWindowClassInfo.ownerPid, gInputWindowClassInfo.clazz, + GET_FIELD_ID(gInputWindowClassInfo.ownerPid, clazz, "ownerPid", "I"); - GET_FIELD_ID(gInputWindowClassInfo.ownerUid, gInputWindowClassInfo.clazz, + GET_FIELD_ID(gInputWindowClassInfo.ownerUid, clazz, "ownerUid", "I"); return 0; } diff --git a/services/jni/com_android_server_InputWindowHandle.cpp b/services/jni/com_android_server_InputWindowHandle.cpp index 5b74e43..aaf679c 100644 --- a/services/jni/com_android_server_InputWindowHandle.cpp +++ b/services/jni/com_android_server_InputWindowHandle.cpp @@ -27,8 +27,6 @@ namespace android { static struct { - jclass clazz; - jfieldID ptr; jfieldID inputApplicationHandle; } gInputWindowHandleClassInfo; @@ -108,8 +106,7 @@ static JNINativeMethod gInputWindowHandleMethods[] = { #define FIND_CLASS(var, className) \ var = env->FindClass(className); \ - LOG_FATAL_IF(! var, "Unable to find class " className); \ - var = jclass(env->NewGlobalRef(var)); + LOG_FATAL_IF(! var, "Unable to find class " className); #define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \ var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \ @@ -120,13 +117,14 @@ int register_android_server_InputWindowHandle(JNIEnv* env) { gInputWindowHandleMethods, NELEM(gInputWindowHandleMethods)); LOG_FATAL_IF(res < 0, "Unable to register native methods."); - FIND_CLASS(gInputWindowHandleClassInfo.clazz, "com/android/server/wm/InputWindowHandle"); + jclass clazz; + FIND_CLASS(clazz, "com/android/server/wm/InputWindowHandle"); - GET_FIELD_ID(gInputWindowHandleClassInfo.ptr, gInputWindowHandleClassInfo.clazz, + GET_FIELD_ID(gInputWindowHandleClassInfo.ptr, clazz, "ptr", "I"); GET_FIELD_ID(gInputWindowHandleClassInfo.inputApplicationHandle, - gInputWindowHandleClassInfo.clazz, + clazz, "inputApplicationHandle", "Lcom/android/server/wm/InputApplicationHandle;"); return 0; diff --git a/services/jni/com_android_server_PowerManagerService.cpp b/services/jni/com_android_server_PowerManagerService.cpp index 705be60..a389c11 100644 --- a/services/jni/com_android_server_PowerManagerService.cpp +++ b/services/jni/com_android_server_PowerManagerService.cpp @@ -35,8 +35,6 @@ namespace android { // ---------------------------------------------------------------------------- static struct { - jclass clazz; - jmethodID goToSleep; jmethodID userActivity; } gPowerManagerServiceClassInfo; @@ -144,8 +142,7 @@ static JNINativeMethod gPowerManagerServiceMethods[] = { #define FIND_CLASS(var, className) \ var = env->FindClass(className); \ - LOG_FATAL_IF(! var, "Unable to find class " className); \ - var = jclass(env->NewGlobalRef(var)); + LOG_FATAL_IF(! var, "Unable to find class " className); #define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \ var = env->GetMethodID(clazz, methodName, methodDescriptor); \ @@ -162,12 +159,13 @@ int register_android_server_PowerManagerService(JNIEnv* env) { // Callbacks - FIND_CLASS(gPowerManagerServiceClassInfo.clazz, "com/android/server/PowerManagerService"); + jclass clazz; + FIND_CLASS(clazz, "com/android/server/PowerManagerService"); - GET_METHOD_ID(gPowerManagerServiceClassInfo.goToSleep, gPowerManagerServiceClassInfo.clazz, + GET_METHOD_ID(gPowerManagerServiceClassInfo.goToSleep, clazz, "goToSleep", "(J)V"); - GET_METHOD_ID(gPowerManagerServiceClassInfo.userActivity, gPowerManagerServiceClassInfo.clazz, + GET_METHOD_ID(gPowerManagerServiceClassInfo.userActivity, clazz, "userActivity", "(JZIZ)V"); // Initialize diff --git a/services/sensorservice/Android.mk b/services/sensorservice/Android.mk index 7e17fdd..c50e4a1 100644 --- a/services/sensorservice/Android.mk +++ b/services/sensorservice/Android.mk @@ -27,7 +27,7 @@ LOCAL_SHARED_LIBRARIES := \ libui \ libgui -LOCAL_PRELINK_MODULE := false + LOCAL_MODULE:= libsensorservice diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 1297363..7a69097 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -52,6 +52,7 @@ template <typename T> inline T min(T a, T b) { Layer::Layer(SurfaceFlinger* flinger, DisplayID display, const sp<Client>& client) : LayerBaseClient(flinger, display, client), + mFormat(PIXEL_FORMAT_NONE), mGLExtensions(GLExtensions::getInstance()), mNeedsBlending(true), mNeedsDithering(false), @@ -59,7 +60,8 @@ Layer::Layer(SurfaceFlinger* flinger, mProtectedByApp(false), mTextureManager(), mBufferManager(mTextureManager), - mWidth(0), mHeight(0), mNeedsScaling(false), mFixedSize(false) + mWidth(0), mHeight(0), + mNeedsScaling(false), mFixedSize(false) { } diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index a9fa1ef..ea283c6 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -600,7 +600,7 @@ sp<FreezeLock> SurfaceFlinger::getFreezeLock() const } void SurfaceFlinger::computeVisibleRegions( - LayerVector& currentLayers, Region& dirtyRegion, Region& opaqueRegion) + const LayerVector& currentLayers, Region& dirtyRegion, Region& opaqueRegion) { const GraphicPlane& plane(graphicPlane(0)); const Transform& planeTransform(plane.transform()); @@ -735,8 +735,7 @@ void SurfaceFlinger::commitTransaction() void SurfaceFlinger::handlePageFlip() { bool visibleRegions = mVisibleRegionsDirty; - LayerVector& currentLayers( - const_cast<LayerVector&>(mDrawingState.layersSortedByZ)); + const LayerVector& currentLayers(mDrawingState.layersSortedByZ); visibleRegions |= lockPageFlip(currentLayers); const DisplayHardware& hw = graphicPlane(0).displayHardware(); @@ -748,9 +747,8 @@ void SurfaceFlinger::handlePageFlip() /* * rebuild the visible layer list */ + const size_t count = currentLayers.size(); mVisibleLayersSortedByZ.clear(); - const LayerVector& currentLayers(mDrawingState.layersSortedByZ); - size_t count = currentLayers.size(); mVisibleLayersSortedByZ.setCapacity(count); for (size_t i=0 ; i<count ; i++) { if (!currentLayers[i]->visibleRegionScreen.isEmpty()) @@ -2515,7 +2513,7 @@ ssize_t UserClient::getTokenForSurface(const sp<ISurface>& sur) const } break; } - if (++name >= SharedBufferStack::NUM_LAYERS_MAX) + if (++name >= int32_t(SharedBufferStack::NUM_LAYERS_MAX)) name = NO_MEMORY; } while(name >= 0); @@ -2562,7 +2560,7 @@ sp<GraphicBuffer> GraphicBufferAlloc::createGraphicBuffer(uint32_t w, uint32_t h void GraphicBufferAlloc::freeAllGraphicBuffersExcept(int bufIdx) { Mutex::Autolock _l(mLock); - if (0 <= bufIdx && bufIdx < mBuffers.size()) { + if (bufIdx >= 0 && size_t(bufIdx) < mBuffers.size()) { sp<GraphicBuffer> b(mBuffers[bufIdx]); mBuffers.clear(); mBuffers.add(b); diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 9566819..0964848 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -304,7 +304,7 @@ private: Vector< sp<LayerBase> >& ditchedLayers); void computeVisibleRegions( - LayerVector& currentLayers, + const LayerVector& currentLayers, Region& dirtyRegion, Region& wormholeRegion); @@ -371,7 +371,6 @@ private: // access must be protected by mStateLock mutable Mutex mStateLock; State mCurrentState; - State mDrawingState; volatile int32_t mTransactionFlags; volatile int32_t mTransactionCount; Condition mTransactionCV; @@ -395,6 +394,7 @@ private: // Can only accessed from the main thread, these members // don't need synchronization + State mDrawingState; Region mDirtyRegion; Region mDirtyRegionRemovedLayer; Region mInvalidRegion; |