1/*
2 ** Copyright 2011, The Android Open Source Project
3 **
4 ** Licensed under the Apache License, Version 2.0 (the "License");
5 ** you may not use this file except in compliance with the License.
6 ** You may obtain a copy of the License at
7 **
8 **     http://www.apache.org/licenses/LICENSE-2.0
9 **
10 ** Unless required by applicable law or agreed to in writing, software
11 ** distributed under the License is distributed on an "AS IS" BASIS,
12 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 ** See the License for the specific language governing permissions and
14 ** limitations under the License.
15 */
16
17package com.android.server.accessibility;
18
19import android.content.Context;
20import android.graphics.Point;
21import android.os.Handler;
22import android.util.Slog;
23import android.view.InputDevice;
24import android.view.MotionEvent;
25import android.view.MotionEvent.PointerCoords;
26import android.view.MotionEvent.PointerProperties;
27import android.view.ViewConfiguration;
28import android.view.accessibility.AccessibilityEvent;
29import android.view.accessibility.AccessibilityManager;
30import android.view.accessibility.AccessibilityNodeInfo;
31
32import com.android.server.policy.WindowManagerPolicy;
33
34import java.util.ArrayList;
35import java.util.Arrays;
36import java.util.List;
37
38/**
39 * This class is a strategy for performing touch exploration. It
40 * transforms the motion event stream by modifying, adding, replacing,
41 * and consuming certain events. The interaction model is:
42 *
43 * <ol>
44 *   <li>1. One finger moving slow around performs touch exploration.</li>
45 *   <li>2. One finger moving fast around performs gestures.</li>
46 *   <li>3. Two close fingers moving in the same direction perform a drag.</li>
47 *   <li>4. Multi-finger gestures are delivered to view hierarchy.</li>
48 *   <li>5. Two fingers moving in different directions are considered a multi-finger gesture.</li>
49 *   <li>7. Double tapping clicks on the on the last touch explored location if it was in
50 *          a window that does not take focus, otherwise the click is within the accessibility
51 *          focused rectangle.</li>
52 *   <li>7. Tapping and holding for a while performs a long press in a similar fashion
53 *          as the click above.</li>
54 * <ol>
55 *
56 * @hide
57 */
58class TouchExplorer extends BaseEventStreamTransformation
59        implements AccessibilityGestureDetector.Listener {
60
61    private static final boolean DEBUG = false;
62
63    // Tag for logging received events.
64    private static final String LOG_TAG = "TouchExplorer";
65
66    // States this explorer can be in.
67    private static final int STATE_TOUCH_EXPLORING = 0x00000001;
68    private static final int STATE_DRAGGING = 0x00000002;
69    private static final int STATE_DELEGATING = 0x00000004;
70    private static final int STATE_GESTURE_DETECTING = 0x00000005;
71
72    private static final int CLICK_LOCATION_NONE = 0;
73    private static final int CLICK_LOCATION_ACCESSIBILITY_FOCUS = 1;
74    private static final int CLICK_LOCATION_LAST_TOUCH_EXPLORED = 2;
75
76    // The maximum of the cosine between the vectors of two moving
77    // pointers so they can be considered moving in the same direction.
78    private static final float MAX_DRAGGING_ANGLE_COS = 0.525321989f; // cos(pi/4)
79
80    // Constant referring to the ids bits of all pointers.
81    private static final int ALL_POINTER_ID_BITS = 0xFFFFFFFF;
82
83    // This constant captures the current implementation detail that
84    // pointer IDs are between 0 and 31 inclusive (subject to change).
85    // (See MAX_POINTER_ID in frameworks/base/include/ui/Input.h)
86    private static final int MAX_POINTER_COUNT = 32;
87
88    // Invalid pointer ID.
89    private static final int INVALID_POINTER_ID = -1;
90
91    // The minimal distance before we take the middle of the distance between
92    // the two dragging pointers as opposed to use the location of the primary one.
93    private static final int MIN_POINTER_DISTANCE_TO_USE_MIDDLE_LOCATION_DIP = 200;
94
95    // The timeout after which we are no longer trying to detect a gesture.
96    private static final int EXIT_GESTURE_DETECTION_TIMEOUT = 2000;
97
98    // Timeout before trying to decide what the user is trying to do.
99    private final int mDetermineUserIntentTimeout;
100
101    // Slop between the first and second tap to be a double tap.
102    private final int mDoubleTapSlop;
103
104    // The current state of the touch explorer.
105    private int mCurrentState = STATE_TOUCH_EXPLORING;
106
107    // The ID of the pointer used for dragging.
108    private int mDraggingPointerId;
109
110    // Handler for performing asynchronous operations.
111    private final Handler mHandler;
112
113    // Command for delayed sending of a hover enter and move event.
114    private final SendHoverEnterAndMoveDelayed mSendHoverEnterAndMoveDelayed;
115
116    // Command for delayed sending of a hover exit event.
117    private final SendHoverExitDelayed mSendHoverExitDelayed;
118
119    // Command for delayed sending of touch exploration end events.
120    private final SendAccessibilityEventDelayed mSendTouchExplorationEndDelayed;
121
122    // Command for delayed sending of touch interaction end events.
123    private final SendAccessibilityEventDelayed mSendTouchInteractionEndDelayed;
124
125    // Command for exiting gesture detection mode after a timeout.
126    private final ExitGestureDetectionModeDelayed mExitGestureDetectionModeDelayed;
127
128    // Helper to detect gestures.
129    private final AccessibilityGestureDetector mGestureDetector;
130
131    // The scaled minimal distance before we take the middle of the distance between
132    // the two dragging pointers as opposed to use the location of the primary one.
133    private final int mScaledMinPointerDistanceToUseMiddleLocation;
134
135    // Helper class to track received pointers.
136    private final ReceivedPointerTracker mReceivedPointerTracker;
137
138    // Helper class to track injected pointers.
139    private final InjectedPointerTracker mInjectedPointerTracker;
140
141    // Handle to the accessibility manager service.
142    private final AccessibilityManagerService mAms;
143
144    // Temporary point to avoid instantiation.
145    private final Point mTempPoint = new Point();
146
147    // Context in which this explorer operates.
148    private final Context mContext;
149
150    // The long pressing pointer id if coordinate remapping is needed.
151    private int mLongPressingPointerId = -1;
152
153    // The long pressing pointer X if coordinate remapping is needed.
154    private int mLongPressingPointerDeltaX;
155
156    // The long pressing pointer Y if coordinate remapping is needed.
157    private int mLongPressingPointerDeltaY;
158
159    // The id of the last touch explored window.
160    private int mLastTouchedWindowId;
161
162    // Whether touch exploration is in progress.
163    private boolean mTouchExplorationInProgress;
164
165    /**
166     * Creates a new instance.
167     *
168     * @param inputFilter The input filter associated with this explorer.
169     * @param context A context handle for accessing resources.
170     */
171    public TouchExplorer(Context context, AccessibilityManagerService service) {
172        mContext = context;
173        mAms = service;
174        mReceivedPointerTracker = new ReceivedPointerTracker();
175        mInjectedPointerTracker = new InjectedPointerTracker();
176        mDetermineUserIntentTimeout = ViewConfiguration.getDoubleTapTimeout();
177        mDoubleTapSlop = ViewConfiguration.get(context).getScaledDoubleTapSlop();
178        mHandler = new Handler(context.getMainLooper());
179        mExitGestureDetectionModeDelayed = new ExitGestureDetectionModeDelayed();
180        mSendHoverEnterAndMoveDelayed = new SendHoverEnterAndMoveDelayed();
181        mSendHoverExitDelayed = new SendHoverExitDelayed();
182        mSendTouchExplorationEndDelayed = new SendAccessibilityEventDelayed(
183                AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END,
184                mDetermineUserIntentTimeout);
185        mSendTouchInteractionEndDelayed = new SendAccessibilityEventDelayed(
186                AccessibilityEvent.TYPE_TOUCH_INTERACTION_END,
187                mDetermineUserIntentTimeout);
188        mGestureDetector = new AccessibilityGestureDetector(context, this);
189        final float density = context.getResources().getDisplayMetrics().density;
190        mScaledMinPointerDistanceToUseMiddleLocation =
191            (int) (MIN_POINTER_DISTANCE_TO_USE_MIDDLE_LOCATION_DIP * density);
192    }
193
194    @Override
195    public void clearEvents(int inputSource) {
196        if (inputSource == InputDevice.SOURCE_TOUCHSCREEN) {
197            clear();
198        }
199        super.clearEvents(inputSource);
200    }
201
202    @Override
203    public void onDestroy() {
204        clear();
205    }
206
207    private void clear() {
208        // If we have not received an event then we are in initial
209        // state. Therefore, there is not need to clean anything.
210        MotionEvent event = mReceivedPointerTracker.getLastReceivedEvent();
211        if (event != null) {
212            clear(mReceivedPointerTracker.getLastReceivedEvent(), WindowManagerPolicy.FLAG_TRUSTED);
213        }
214    }
215
216    private void clear(MotionEvent event, int policyFlags) {
217        switch (mCurrentState) {
218            case STATE_TOUCH_EXPLORING: {
219                // If a touch exploration gesture is in progress send events for its end.
220                sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
221            } break;
222            case STATE_DRAGGING: {
223                mDraggingPointerId = INVALID_POINTER_ID;
224                // Send exit to all pointers that we have delivered.
225                sendUpForInjectedDownPointers(event, policyFlags);
226            } break;
227            case STATE_DELEGATING: {
228                // Send exit to all pointers that we have delivered.
229                sendUpForInjectedDownPointers(event, policyFlags);
230            } break;
231            case STATE_GESTURE_DETECTING: {
232                // No state specific cleanup required.
233            } break;
234        }
235        // Remove all pending callbacks.
236        mSendHoverEnterAndMoveDelayed.cancel();
237        mSendHoverExitDelayed.cancel();
238        mExitGestureDetectionModeDelayed.cancel();
239        mSendTouchExplorationEndDelayed.cancel();
240        mSendTouchInteractionEndDelayed.cancel();
241        // Reset the pointer trackers.
242        mReceivedPointerTracker.clear();
243        mInjectedPointerTracker.clear();
244        // Clear the gesture detector
245        mGestureDetector.clear();
246        // Go to initial state.
247        // Clear the long pressing pointer remap data.
248        mLongPressingPointerId = -1;
249        mLongPressingPointerDeltaX = 0;
250        mLongPressingPointerDeltaY = 0;
251        mCurrentState = STATE_TOUCH_EXPLORING;
252        mTouchExplorationInProgress = false;
253        mAms.onTouchInteractionEnd();
254    }
255
256    @Override
257    public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
258        if (!event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)) {
259            super.onMotionEvent(event, rawEvent, policyFlags);
260            return;
261        }
262
263        if (DEBUG) {
264            Slog.d(LOG_TAG, "Received event: " + event + ", policyFlags=0x"
265                    + Integer.toHexString(policyFlags));
266            Slog.d(LOG_TAG, getStateSymbolicName(mCurrentState));
267        }
268
269        mReceivedPointerTracker.onMotionEvent(rawEvent);
270
271        // The motion detector is interested in the movements in physical space,
272        // so it uses the rawEvent to ignore magnification and other
273        // transformations.
274        if (mGestureDetector.onMotionEvent(rawEvent, policyFlags)) {
275            // Event was handled by the gesture detector.
276            return;
277        }
278
279        if (event.getActionMasked() == MotionEvent.ACTION_CANCEL) {
280            clear(event, policyFlags);
281            return;
282        }
283
284        switch(mCurrentState) {
285            case STATE_TOUCH_EXPLORING: {
286                handleMotionEventStateTouchExploring(event, rawEvent, policyFlags);
287            } break;
288            case STATE_DRAGGING: {
289                handleMotionEventStateDragging(event, policyFlags);
290            } break;
291            case STATE_DELEGATING: {
292                handleMotionEventStateDelegating(event, policyFlags);
293            } break;
294            case STATE_GESTURE_DETECTING: {
295                // Already handled.
296            } break;
297            default:
298                Slog.e(LOG_TAG, "Illegal state: " + mCurrentState);
299                clear(event, policyFlags);
300        }
301    }
302
303    @Override
304    public void onAccessibilityEvent(AccessibilityEvent event) {
305        final int eventType = event.getEventType();
306
307        // The event for gesture end should be strictly after the
308        // last hover exit event.
309        if (mSendTouchExplorationEndDelayed.isPending()
310                && eventType == AccessibilityEvent.TYPE_VIEW_HOVER_EXIT) {
311                    mSendTouchExplorationEndDelayed.cancel();
312            sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END);
313        }
314
315        // The event for touch interaction end should be strictly after the
316        // last hover exit and the touch exploration gesture end events.
317        if (mSendTouchInteractionEndDelayed.isPending()
318                && eventType == AccessibilityEvent.TYPE_VIEW_HOVER_EXIT) {
319            mSendTouchInteractionEndDelayed.cancel();
320            sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
321        }
322
323        // If a new window opens or the accessibility focus moves we no longer
324        // want to click/long press on the last touch explored location.
325        switch (eventType) {
326            case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
327            case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED: {
328                if (mInjectedPointerTracker.mLastInjectedHoverEventForClick != null) {
329                    mInjectedPointerTracker.mLastInjectedHoverEventForClick.recycle();
330                    mInjectedPointerTracker.mLastInjectedHoverEventForClick = null;
331                }
332                mLastTouchedWindowId = -1;
333            } break;
334            case AccessibilityEvent.TYPE_VIEW_HOVER_ENTER:
335            case AccessibilityEvent.TYPE_VIEW_HOVER_EXIT: {
336                mLastTouchedWindowId = event.getWindowId();
337            } break;
338        }
339        super.onAccessibilityEvent(event);
340    }
341
342    @Override
343    public void onDoubleTapAndHold(MotionEvent event, int policyFlags) {
344        // Ignore the event if we aren't touch exploring.
345        if (mCurrentState != STATE_TOUCH_EXPLORING) {
346            return;
347        }
348
349        // Pointers should not be zero when running this command.
350        if (mReceivedPointerTracker.getLastReceivedEvent().getPointerCount() == 0) {
351            return;
352        }
353
354        final int pointerIndex = event.getActionIndex();
355        final int pointerId = event.getPointerId(pointerIndex);
356
357        Point clickLocation = mTempPoint;
358        final int result = computeClickLocation(clickLocation);
359
360        if (result == CLICK_LOCATION_NONE) {
361            return;
362        }
363
364        mLongPressingPointerId = pointerId;
365        mLongPressingPointerDeltaX = (int) event.getX(pointerIndex) - clickLocation.x;
366        mLongPressingPointerDeltaY = (int) event.getY(pointerIndex) - clickLocation.y;
367
368        sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
369
370        mCurrentState = STATE_DELEGATING;
371        sendDownForAllNotInjectedPointers(event, policyFlags);
372    }
373
374    @Override
375    public boolean onDoubleTap(MotionEvent event, int policyFlags) {
376        // Ignore the event if we aren't touch exploring.
377        if (mCurrentState != STATE_TOUCH_EXPLORING) {
378            return false;
379        }
380
381        // Remove pending event deliveries.
382        mSendHoverEnterAndMoveDelayed.cancel();
383        mSendHoverExitDelayed.cancel();
384
385        if (mSendTouchExplorationEndDelayed.isPending()) {
386            mSendTouchExplorationEndDelayed.forceSendAndRemove();
387        }
388        if (mSendTouchInteractionEndDelayed.isPending()) {
389            mSendTouchInteractionEndDelayed.forceSendAndRemove();
390        }
391
392        // Try to use the standard accessibility API to click
393        if (mAms.performActionOnAccessibilityFocusedItem(
394                AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK)) {
395            return true;
396        }
397        Slog.e(LOG_TAG, "ACTION_CLICK failed. Dispatching motion events to simulate click.");
398
399        final int pointerIndex = event.getActionIndex();
400        final int pointerId = event.getPointerId(pointerIndex);
401
402        Point clickLocation = mTempPoint;
403        final int result = computeClickLocation(clickLocation);
404        if (result == CLICK_LOCATION_NONE) {
405            // We can't send a click to no location, but the gesture was still
406            // consumed.
407            return true;
408        }
409
410        // Do the click.
411        PointerProperties[] properties = new PointerProperties[1];
412        properties[0] = new PointerProperties();
413        event.getPointerProperties(pointerIndex, properties[0]);
414        PointerCoords[] coords = new PointerCoords[1];
415        coords[0] = new PointerCoords();
416        coords[0].x = clickLocation.x;
417        coords[0].y = clickLocation.y;
418        MotionEvent click_event = MotionEvent.obtain(event.getDownTime(),
419                event.getEventTime(), MotionEvent.ACTION_DOWN, 1, properties,
420                coords, 0, 0, 1.0f, 1.0f, event.getDeviceId(), 0,
421                event.getSource(), event.getFlags());
422        final boolean targetAccessibilityFocus = (result == CLICK_LOCATION_ACCESSIBILITY_FOCUS);
423        sendActionDownAndUp(click_event, policyFlags, targetAccessibilityFocus);
424        click_event.recycle();
425        return true;
426    }
427
428    @Override
429    public boolean onGestureStarted() {
430      // We have to perform gesture detection, so
431      // clear the current state and try to detect.
432      mCurrentState = STATE_GESTURE_DETECTING;
433      mSendHoverEnterAndMoveDelayed.cancel();
434      mSendHoverExitDelayed.cancel();
435      mExitGestureDetectionModeDelayed.post();
436      // Send accessibility event to announce the start
437      // of gesture recognition.
438      sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_START);
439      return false;
440    }
441
442    @Override
443    public boolean onGestureCompleted(int gestureId) {
444        if (mCurrentState != STATE_GESTURE_DETECTING) {
445            return false;
446        }
447
448        endGestureDetection();
449
450        mAms.onGesture(gestureId);
451
452        return true;
453    }
454
455    @Override
456    public boolean onGestureCancelled(MotionEvent event, int policyFlags) {
457        if (mCurrentState == STATE_GESTURE_DETECTING) {
458            endGestureDetection();
459            return true;
460        } else if (mCurrentState == STATE_TOUCH_EXPLORING) {
461            // If the finger is still moving, pass the event on.
462            if (event.getActionMasked() == MotionEvent.ACTION_MOVE) {
463                final int pointerId = mReceivedPointerTracker.getPrimaryPointerId();
464                final int pointerIdBits = (1 << pointerId);
465
466                // We have just decided that the user is touch,
467                // exploring so start sending events.
468                mSendHoverEnterAndMoveDelayed.addEvent(event);
469                mSendHoverEnterAndMoveDelayed.forceSendAndRemove();
470                mSendHoverExitDelayed.cancel();
471                sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE, pointerIdBits, policyFlags);
472                return true;
473            }
474        }
475        return false;
476    }
477
478    /**
479     * Handles a motion event in touch exploring state.
480     *
481     * @param event The event to be handled.
482     * @param rawEvent The raw (unmodified) motion event.
483     * @param policyFlags The policy flags associated with the event.
484     */
485    private void handleMotionEventStateTouchExploring(MotionEvent event, MotionEvent rawEvent,
486            int policyFlags) {
487        ReceivedPointerTracker receivedTracker = mReceivedPointerTracker;
488
489        switch (event.getActionMasked()) {
490            case MotionEvent.ACTION_DOWN: {
491                mAms.onTouchInteractionStart();
492
493                sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_START);
494
495                // If we still have not notified the user for the last
496                // touch, we figure out what to do. If were waiting
497                // we resent the delayed callback and wait again.
498                mSendHoverEnterAndMoveDelayed.cancel();
499                mSendHoverExitDelayed.cancel();
500
501                if (mSendTouchExplorationEndDelayed.isPending()) {
502                    mSendTouchExplorationEndDelayed.forceSendAndRemove();
503                }
504
505                if (mSendTouchInteractionEndDelayed.isPending()) {
506                    mSendTouchInteractionEndDelayed.forceSendAndRemove();
507                }
508
509                if (!mGestureDetector.firstTapDetected() && !mTouchExplorationInProgress) {
510                    if (!mSendHoverEnterAndMoveDelayed.isPending()) {
511                        // Deliver hover enter with a delay to have a chance
512                        // to detect what the user is trying to do.
513                        final int pointerId = receivedTracker.getPrimaryPointerId();
514                        final int pointerIdBits = (1 << pointerId);
515                        mSendHoverEnterAndMoveDelayed.post(event, true, pointerIdBits,
516                                policyFlags);
517                    } else {
518                        // Cache the event until we discern exploration from gesturing.
519                        mSendHoverEnterAndMoveDelayed.addEvent(event);
520                    }
521                }
522            } break;
523            case MotionEvent.ACTION_POINTER_DOWN: {
524                // Another finger down means that if we have not started to deliver
525                // hover events, we will not have to. The code for ACTION_MOVE will
526                // decide what we will actually do next.
527                mSendHoverEnterAndMoveDelayed.cancel();
528                mSendHoverExitDelayed.cancel();
529            } break;
530            case MotionEvent.ACTION_MOVE: {
531                final int pointerId = receivedTracker.getPrimaryPointerId();
532                final int pointerIndex = event.findPointerIndex(pointerId);
533                final int pointerIdBits = (1 << pointerId);
534                switch (event.getPointerCount()) {
535                    case 1: {
536                        // We have not started sending events since we try to
537                        // figure out what the user is doing.
538                        if (mSendHoverEnterAndMoveDelayed.isPending()) {
539                            // Cache the event until we discern exploration from gesturing.
540                            mSendHoverEnterAndMoveDelayed.addEvent(event);
541                        } else {
542                            if (mTouchExplorationInProgress) {
543                                sendTouchExplorationGestureStartAndHoverEnterIfNeeded(policyFlags);
544                                sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE, pointerIdBits,
545                                        policyFlags);
546                            }
547                        }
548                    } break;
549                    case 2: {
550                        // More than one pointer so the user is not touch exploring
551                        // and now we have to decide whether to delegate or drag.
552                        if (mSendHoverEnterAndMoveDelayed.isPending()) {
553                            // We have not started sending events so cancel
554                            // scheduled sending events.
555                            mSendHoverEnterAndMoveDelayed.cancel();
556                            mSendHoverExitDelayed.cancel();
557                        } else {
558                            if (mTouchExplorationInProgress) {
559                                // If the user is touch exploring the second pointer may be
560                                // performing a double tap to activate an item without need
561                                // for the user to lift his exploring finger.
562                                // It is *important* to use the distance traveled by the pointers
563                                // on the screen which may or may not be magnified.
564                                final float deltaX = receivedTracker.getReceivedPointerDownX(
565                                        pointerId) - rawEvent.getX(pointerIndex);
566                                final float deltaY = receivedTracker.getReceivedPointerDownY(
567                                        pointerId) - rawEvent.getY(pointerIndex);
568                                final double moveDelta = Math.hypot(deltaX, deltaY);
569                                if (moveDelta < mDoubleTapSlop) {
570                                    break;
571                                }
572                                // We are sending events so send exit and gesture
573                                // end since we transition to another state.
574                                sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
575                            }
576                        }
577
578                        if (isDraggingGesture(event)) {
579                            // Two pointers moving in the same direction within
580                            // a given distance perform a drag.
581                            mCurrentState = STATE_DRAGGING;
582                            mDraggingPointerId = pointerId;
583                            event.setEdgeFlags(receivedTracker.getLastReceivedDownEdgeFlags());
584                            sendMotionEvent(event, MotionEvent.ACTION_DOWN, pointerIdBits,
585                                    policyFlags);
586                        } else {
587                            // Two pointers moving arbitrary are delegated to the view hierarchy.
588                            mCurrentState = STATE_DELEGATING;
589                            sendDownForAllNotInjectedPointers(event, policyFlags);
590                        }
591                    } break;
592                    default: {
593                        // More than one pointer so the user is not touch exploring
594                        // and now we have to decide whether to delegate or drag.
595                        if (mSendHoverEnterAndMoveDelayed.isPending()) {
596                            // We have not started sending events so cancel
597                            // scheduled sending events.
598                            mSendHoverEnterAndMoveDelayed.cancel();
599                            mSendHoverExitDelayed.cancel();
600                        } else {
601                            // We are sending events so send exit and gesture
602                            // end since we transition to another state.
603                            sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
604                        }
605
606                        // More than two pointers are delegated to the view hierarchy.
607                        mCurrentState = STATE_DELEGATING;
608                        sendDownForAllNotInjectedPointers(event, policyFlags);
609                    }
610                }
611            } break;
612            case MotionEvent.ACTION_UP: {
613                mAms.onTouchInteractionEnd();
614                final int pointerId = event.getPointerId(event.getActionIndex());
615                final int pointerIdBits = (1 << pointerId);
616
617                if (mSendHoverEnterAndMoveDelayed.isPending()) {
618                    // If we have not delivered the enter schedule an exit.
619                    mSendHoverExitDelayed.post(event, pointerIdBits, policyFlags);
620                } else {
621                    // The user is touch exploring so we send events for end.
622                    sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
623                }
624
625                if (!mSendTouchInteractionEndDelayed.isPending()) {
626                    mSendTouchInteractionEndDelayed.post();
627                }
628
629            } break;
630        }
631    }
632
633    /**
634     * Handles a motion event in dragging state.
635     *
636     * @param event The event to be handled.
637     * @param policyFlags The policy flags associated with the event.
638     */
639    private void handleMotionEventStateDragging(MotionEvent event, int policyFlags) {
640        int pointerIdBits = 0;
641        // Clear the dragging pointer id if it's no longer valid.
642        if (event.findPointerIndex(mDraggingPointerId) == -1) {
643            Slog.e(LOG_TAG, "mDraggingPointerId doesn't match any pointers on current event. " +
644                    "mDraggingPointerId: " + Integer.toString(mDraggingPointerId) +
645                    ", Event: " + event);
646            mDraggingPointerId = INVALID_POINTER_ID;
647        } else {
648            pointerIdBits = (1 << mDraggingPointerId);
649        }
650        switch (event.getActionMasked()) {
651            case MotionEvent.ACTION_DOWN: {
652                Slog.e(LOG_TAG, "Dragging state can be reached only if two "
653                        + "pointers are already down");
654                clear(event, policyFlags);
655                return;
656            }
657            case MotionEvent.ACTION_POINTER_DOWN: {
658                // We are in dragging state so we have two pointers and another one
659                // goes down => delegate the three pointers to the view hierarchy
660                mCurrentState = STATE_DELEGATING;
661                if (mDraggingPointerId != INVALID_POINTER_ID) {
662                    sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags);
663                }
664                sendDownForAllNotInjectedPointers(event, policyFlags);
665            } break;
666            case MotionEvent.ACTION_MOVE: {
667                if (mDraggingPointerId == INVALID_POINTER_ID) {
668                    break;
669                }
670                switch (event.getPointerCount()) {
671                    case 1: {
672                        // do nothing
673                    } break;
674                    case 2: {
675                        if (isDraggingGesture(event)) {
676                            final float firstPtrX = event.getX(0);
677                            final float firstPtrY = event.getY(0);
678                            final float secondPtrX = event.getX(1);
679                            final float secondPtrY = event.getY(1);
680
681                            final float deltaX = firstPtrX - secondPtrX;
682                            final float deltaY = firstPtrY - secondPtrY;
683                            final double distance = Math.hypot(deltaX, deltaY);
684
685                            if (distance > mScaledMinPointerDistanceToUseMiddleLocation) {
686                                event.setLocation(deltaX / 2, deltaY / 2);
687                            }
688
689                            // If still dragging send a drag event.
690                            sendMotionEvent(event, MotionEvent.ACTION_MOVE, pointerIdBits,
691                                    policyFlags);
692                        } else {
693                            // The two pointers are moving either in different directions or
694                            // no close enough => delegate the gesture to the view hierarchy.
695                            mCurrentState = STATE_DELEGATING;
696                            // Send an event to the end of the drag gesture.
697                            sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits,
698                                    policyFlags);
699                            // Deliver all pointers to the view hierarchy.
700                            sendDownForAllNotInjectedPointers(event, policyFlags);
701                        }
702                    } break;
703                    default: {
704                        mCurrentState = STATE_DELEGATING;
705                        // Send an event to the end of the drag gesture.
706                        sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits,
707                                policyFlags);
708                        // Deliver all pointers to the view hierarchy.
709                        sendDownForAllNotInjectedPointers(event, policyFlags);
710                    }
711                }
712            } break;
713            case MotionEvent.ACTION_POINTER_UP: {
714                 final int pointerId = event.getPointerId(event.getActionIndex());
715                 if (pointerId == mDraggingPointerId) {
716                     mDraggingPointerId = INVALID_POINTER_ID;
717                     // Send an event to the end of the drag gesture.
718                     sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags);
719                 }
720            } break;
721            case MotionEvent.ACTION_UP: {
722                mAms.onTouchInteractionEnd();
723                // Announce the end of a new touch interaction.
724                sendAccessibilityEvent(
725                        AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
726                final int pointerId = event.getPointerId(event.getActionIndex());
727                if (pointerId == mDraggingPointerId) {
728                    mDraggingPointerId = INVALID_POINTER_ID;
729                    // Send an event to the end of the drag gesture.
730                    sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags);
731                }
732                mCurrentState = STATE_TOUCH_EXPLORING;
733            } break;
734        }
735    }
736
737    /**
738     * Handles a motion event in delegating state.
739     *
740     * @param event The event to be handled.
741     * @param policyFlags The policy flags associated with the event.
742     */
743    private void handleMotionEventStateDelegating(MotionEvent event, int policyFlags) {
744        switch (event.getActionMasked()) {
745            case MotionEvent.ACTION_DOWN: {
746                Slog.e(LOG_TAG, "Delegating state can only be reached if "
747                        + "there is at least one pointer down!");
748                clear(event, policyFlags);
749                return;
750            }
751            case MotionEvent.ACTION_UP: {
752                // Offset the event if we are doing a long press as the
753                // target is not necessarily under the user's finger.
754                if (mLongPressingPointerId >= 0) {
755                    event = offsetEvent(event, - mLongPressingPointerDeltaX,
756                            - mLongPressingPointerDeltaY);
757                    // Clear the long press state.
758                    mLongPressingPointerId = -1;
759                    mLongPressingPointerDeltaX = 0;
760                    mLongPressingPointerDeltaY = 0;
761                }
762
763                // Deliver the event.
764                sendMotionEvent(event, event.getAction(), ALL_POINTER_ID_BITS, policyFlags);
765
766                // Announce the end of a the touch interaction.
767                mAms.onTouchInteractionEnd();
768                sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
769
770                mCurrentState = STATE_TOUCH_EXPLORING;
771            } break;
772            default: {
773                // Deliver the event.
774                sendMotionEvent(event, event.getAction(), ALL_POINTER_ID_BITS, policyFlags);
775            }
776        }
777    }
778
779    private void endGestureDetection() {
780        mAms.onTouchInteractionEnd();
781
782        // Announce the end of the gesture recognition.
783        sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_END);
784        // Announce the end of a the touch interaction.
785        sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
786
787        mExitGestureDetectionModeDelayed.cancel();
788        mCurrentState = STATE_TOUCH_EXPLORING;
789    }
790
791    /**
792     * Sends an accessibility event of the given type.
793     *
794     * @param type The event type.
795     */
796    private void sendAccessibilityEvent(int type) {
797        AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(mContext);
798        if (accessibilityManager.isEnabled()) {
799            AccessibilityEvent event = AccessibilityEvent.obtain(type);
800            event.setWindowId(mAms.getActiveWindowId());
801            accessibilityManager.sendAccessibilityEvent(event);
802            switch (type) {
803                case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START: {
804                    mTouchExplorationInProgress = true;
805                } break;
806                case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END: {
807                    mTouchExplorationInProgress = false;
808                } break;
809            }
810        }
811    }
812
813    /**
814     * Sends down events to the view hierarchy for all pointers which are
815     * not already being delivered i.e. pointers that are not yet injected.
816     *
817     * @param prototype The prototype from which to create the injected events.
818     * @param policyFlags The policy flags associated with the event.
819     */
820    private void sendDownForAllNotInjectedPointers(MotionEvent prototype, int policyFlags) {
821        InjectedPointerTracker injectedPointers = mInjectedPointerTracker;
822
823        // Inject the injected pointers.
824        int pointerIdBits = 0;
825        final int pointerCount = prototype.getPointerCount();
826        for (int i = 0; i < pointerCount; i++) {
827            final int pointerId = prototype.getPointerId(i);
828            // Do not send event for already delivered pointers.
829            if (!injectedPointers.isInjectedPointerDown(pointerId)) {
830                pointerIdBits |= (1 << pointerId);
831                final int action = computeInjectionAction(MotionEvent.ACTION_DOWN, i);
832                sendMotionEvent(prototype, action, pointerIdBits, policyFlags);
833            }
834        }
835    }
836
837    /**
838     * Sends the exit events if needed. Such events are hover exit and touch explore
839     * gesture end.
840     *
841     * @param policyFlags The policy flags associated with the event.
842     */
843    private void sendHoverExitAndTouchExplorationGestureEndIfNeeded(int policyFlags) {
844        MotionEvent event = mInjectedPointerTracker.getLastInjectedHoverEvent();
845        if (event != null && event.getActionMasked() != MotionEvent.ACTION_HOVER_EXIT) {
846            final int pointerIdBits = event.getPointerIdBits();
847            if (!mSendTouchExplorationEndDelayed.isPending()) {
848                mSendTouchExplorationEndDelayed.post();
849            }
850            sendMotionEvent(event, MotionEvent.ACTION_HOVER_EXIT, pointerIdBits, policyFlags);
851        }
852    }
853
854    /**
855     * Sends the enter events if needed. Such events are hover enter and touch explore
856     * gesture start.
857     *
858     * @param policyFlags The policy flags associated with the event.
859     */
860    private void sendTouchExplorationGestureStartAndHoverEnterIfNeeded(int policyFlags) {
861        MotionEvent event = mInjectedPointerTracker.getLastInjectedHoverEvent();
862        if (event != null && event.getActionMasked() == MotionEvent.ACTION_HOVER_EXIT) {
863            final int pointerIdBits = event.getPointerIdBits();
864            sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START);
865            sendMotionEvent(event, MotionEvent.ACTION_HOVER_ENTER, pointerIdBits, policyFlags);
866        }
867    }
868
869    /**
870     * Sends up events to the view hierarchy for all pointers which are
871     * already being delivered i.e. pointers that are injected.
872     *
873     * @param prototype The prototype from which to create the injected events.
874     * @param policyFlags The policy flags associated with the event.
875     */
876    private void sendUpForInjectedDownPointers(MotionEvent prototype, int policyFlags) {
877        final InjectedPointerTracker injectedTracked = mInjectedPointerTracker;
878        int pointerIdBits = 0;
879        final int pointerCount = prototype.getPointerCount();
880        for (int i = 0; i < pointerCount; i++) {
881            final int pointerId = prototype.getPointerId(i);
882            // Skip non injected down pointers.
883            if (!injectedTracked.isInjectedPointerDown(pointerId)) {
884                continue;
885            }
886            pointerIdBits |= (1 << pointerId);
887            final int action = computeInjectionAction(MotionEvent.ACTION_UP, i);
888            sendMotionEvent(prototype, action, pointerIdBits, policyFlags);
889        }
890    }
891
892    /**
893     * Sends an up and down events.
894     *
895     * @param prototype The prototype from which to create the injected events.
896     * @param policyFlags The policy flags associated with the event.
897     * @param targetAccessibilityFocus Whether the event targets the accessibility focus.
898     */
899    private void sendActionDownAndUp(MotionEvent prototype, int policyFlags,
900            boolean targetAccessibilityFocus) {
901        // Tap with the pointer that last explored.
902        final int pointerId = prototype.getPointerId(prototype.getActionIndex());
903        final int pointerIdBits = (1 << pointerId);
904        prototype.setTargetAccessibilityFocus(targetAccessibilityFocus);
905        sendMotionEvent(prototype, MotionEvent.ACTION_DOWN, pointerIdBits, policyFlags);
906        prototype.setTargetAccessibilityFocus(targetAccessibilityFocus);
907        sendMotionEvent(prototype, MotionEvent.ACTION_UP, pointerIdBits, policyFlags);
908    }
909
910    /**
911     * Sends an event.
912     *
913     * @param prototype The prototype from which to create the injected events.
914     * @param action The action of the event.
915     * @param pointerIdBits The bits of the pointers to send.
916     * @param policyFlags The policy flags associated with the event.
917     */
918    private void sendMotionEvent(MotionEvent prototype, int action, int pointerIdBits,
919            int policyFlags) {
920        prototype.setAction(action);
921
922        MotionEvent event = null;
923        if (pointerIdBits == ALL_POINTER_ID_BITS) {
924            event = prototype;
925        } else {
926            try {
927                event = prototype.split(pointerIdBits);
928            } catch (IllegalArgumentException e) {
929                Slog.e(LOG_TAG, "sendMotionEvent: Failed to split motion event: " + e);
930                return;
931            }
932        }
933        if (action == MotionEvent.ACTION_DOWN) {
934            event.setDownTime(event.getEventTime());
935        } else {
936            event.setDownTime(mInjectedPointerTracker.getLastInjectedDownEventTime());
937        }
938
939        // If the user is long pressing but the long pressing pointer
940        // was not exactly over the accessibility focused item we need
941        // to remap the location of that pointer so the user does not
942        // have to explicitly touch explore something to be able to
943        // long press it, or even worse to avoid the user long pressing
944        // on the wrong item since click and long press behave differently.
945        if (mLongPressingPointerId >= 0) {
946            event = offsetEvent(event, - mLongPressingPointerDeltaX,
947                    - mLongPressingPointerDeltaY);
948        }
949
950        if (DEBUG) {
951            Slog.d(LOG_TAG, "Injecting event: " + event + ", policyFlags=0x"
952                    + Integer.toHexString(policyFlags));
953        }
954
955        // Make sure that the user will see the event.
956        policyFlags |= WindowManagerPolicy.FLAG_PASS_TO_USER;
957        // TODO: For now pass null for the raw event since the touch
958        //       explorer is the last event transformation and it does
959        //       not care about the raw event.
960        super.onMotionEvent(event, null, policyFlags);
961
962        mInjectedPointerTracker.onMotionEvent(event);
963
964        if (event != prototype) {
965            event.recycle();
966        }
967    }
968
969    /**
970     * Offsets all pointers in the given event by adding the specified X and Y
971     * offsets.
972     *
973     * @param event The event to offset.
974     * @param offsetX The X offset.
975     * @param offsetY The Y offset.
976     * @return An event with the offset pointers or the original event if both
977     *         offsets are zero.
978     */
979    private MotionEvent offsetEvent(MotionEvent event, int offsetX, int offsetY) {
980        if (offsetX == 0 && offsetY == 0) {
981            return event;
982        }
983        final int remappedIndex = event.findPointerIndex(mLongPressingPointerId);
984        final int pointerCount = event.getPointerCount();
985        PointerProperties[] props = PointerProperties.createArray(pointerCount);
986        PointerCoords[] coords = PointerCoords.createArray(pointerCount);
987        for (int i = 0; i < pointerCount; i++) {
988            event.getPointerProperties(i, props[i]);
989            event.getPointerCoords(i, coords[i]);
990            if (i == remappedIndex) {
991                coords[i].x += offsetX;
992                coords[i].y += offsetY;
993            }
994        }
995        return MotionEvent.obtain(event.getDownTime(),
996                event.getEventTime(), event.getAction(), event.getPointerCount(),
997                props, coords, event.getMetaState(), event.getButtonState(),
998                1.0f, 1.0f, event.getDeviceId(), event.getEdgeFlags(),
999                event.getSource(), event.getFlags());
1000    }
1001
1002    /**
1003     * Computes the action for an injected event based on a masked action
1004     * and a pointer index.
1005     *
1006     * @param actionMasked The masked action.
1007     * @param pointerIndex The index of the pointer which has changed.
1008     * @return The action to be used for injection.
1009     */
1010    private int computeInjectionAction(int actionMasked, int pointerIndex) {
1011        switch (actionMasked) {
1012            case MotionEvent.ACTION_DOWN:
1013            case MotionEvent.ACTION_POINTER_DOWN: {
1014                InjectedPointerTracker injectedTracker = mInjectedPointerTracker;
1015                // Compute the action based on how many down pointers are injected.
1016                if (injectedTracker.getInjectedPointerDownCount() == 0) {
1017                    return MotionEvent.ACTION_DOWN;
1018                } else {
1019                    return (pointerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT)
1020                        | MotionEvent.ACTION_POINTER_DOWN;
1021                }
1022            }
1023            case MotionEvent.ACTION_POINTER_UP: {
1024                InjectedPointerTracker injectedTracker = mInjectedPointerTracker;
1025                // Compute the action based on how many down pointers are injected.
1026                if (injectedTracker.getInjectedPointerDownCount() == 1) {
1027                    return MotionEvent.ACTION_UP;
1028                } else {
1029                    return (pointerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT)
1030                        | MotionEvent.ACTION_POINTER_UP;
1031                }
1032            }
1033            default:
1034                return actionMasked;
1035        }
1036    }
1037
1038    /**
1039     * Determines whether a two pointer gesture is a dragging one.
1040     *
1041     * @param event The event with the pointer data.
1042     * @return True if the gesture is a dragging one.
1043     */
1044    private boolean isDraggingGesture(MotionEvent event) {
1045        ReceivedPointerTracker receivedTracker = mReceivedPointerTracker;
1046
1047        final float firstPtrX = event.getX(0);
1048        final float firstPtrY = event.getY(0);
1049        final float secondPtrX = event.getX(1);
1050        final float secondPtrY = event.getY(1);
1051
1052        final float firstPtrDownX = receivedTracker.getReceivedPointerDownX(0);
1053        final float firstPtrDownY = receivedTracker.getReceivedPointerDownY(0);
1054        final float secondPtrDownX = receivedTracker.getReceivedPointerDownX(1);
1055        final float secondPtrDownY = receivedTracker.getReceivedPointerDownY(1);
1056
1057        return GestureUtils.isDraggingGesture(firstPtrDownX, firstPtrDownY, secondPtrDownX,
1058                secondPtrDownY, firstPtrX, firstPtrY, secondPtrX, secondPtrY,
1059                MAX_DRAGGING_ANGLE_COS);
1060    }
1061
1062    private int computeClickLocation(Point outLocation) {
1063        MotionEvent lastExploreEvent = mInjectedPointerTracker.getLastInjectedHoverEventForClick();
1064        if (lastExploreEvent != null) {
1065            final int lastExplorePointerIndex = lastExploreEvent.getActionIndex();
1066            outLocation.x = (int) lastExploreEvent.getX(lastExplorePointerIndex);
1067            outLocation.y = (int) lastExploreEvent.getY(lastExplorePointerIndex);
1068            if (!mAms.accessibilityFocusOnlyInActiveWindow()
1069                    || mLastTouchedWindowId == mAms.getActiveWindowId()) {
1070                if (mAms.getAccessibilityFocusClickPointInScreen(outLocation)) {
1071                    return CLICK_LOCATION_ACCESSIBILITY_FOCUS;
1072                } else {
1073                    return CLICK_LOCATION_LAST_TOUCH_EXPLORED;
1074                }
1075            }
1076        }
1077        if (mAms.getAccessibilityFocusClickPointInScreen(outLocation)) {
1078            return CLICK_LOCATION_ACCESSIBILITY_FOCUS;
1079        }
1080        return CLICK_LOCATION_NONE;
1081    }
1082
1083    /**
1084     * Gets the symbolic name of a state.
1085     *
1086     * @param state A state.
1087     * @return The state symbolic name.
1088     */
1089    private static String getStateSymbolicName(int state) {
1090        switch (state) {
1091            case STATE_TOUCH_EXPLORING:
1092                return "STATE_TOUCH_EXPLORING";
1093            case STATE_DRAGGING:
1094                return "STATE_DRAGGING";
1095            case STATE_DELEGATING:
1096                return "STATE_DELEGATING";
1097            case STATE_GESTURE_DETECTING:
1098                return "STATE_GESTURE_DETECTING";
1099            default:
1100                return "Unknown state: " + state;
1101        }
1102    }
1103
1104    /**
1105     * Class for delayed exiting from gesture detecting mode.
1106     */
1107    private final class ExitGestureDetectionModeDelayed implements Runnable {
1108
1109        public void post() {
1110            mHandler.postDelayed(this, EXIT_GESTURE_DETECTION_TIMEOUT);
1111        }
1112
1113        public void cancel() {
1114            mHandler.removeCallbacks(this);
1115        }
1116
1117        @Override
1118        public void run() {
1119            // Announce the end of gesture recognition.
1120            sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_END);
1121            // Clearing puts is in touch exploration state with a finger already
1122            // down, so announce the transition to exploration state.
1123            sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START);
1124            clear();
1125        }
1126    }
1127
1128    /**
1129     * Class for delayed sending of hover enter and move events.
1130     */
1131    class SendHoverEnterAndMoveDelayed implements Runnable {
1132        private final String LOG_TAG_SEND_HOVER_DELAYED = "SendHoverEnterAndMoveDelayed";
1133
1134        private final List<MotionEvent> mEvents = new ArrayList<MotionEvent>();
1135
1136        private int mPointerIdBits;
1137        private int mPolicyFlags;
1138
1139        public void post(MotionEvent event, boolean touchExplorationInProgress,
1140                int pointerIdBits, int policyFlags) {
1141            cancel();
1142            addEvent(event);
1143            mPointerIdBits = pointerIdBits;
1144            mPolicyFlags = policyFlags;
1145            mHandler.postDelayed(this, mDetermineUserIntentTimeout);
1146        }
1147
1148        public void addEvent(MotionEvent event) {
1149            mEvents.add(MotionEvent.obtain(event));
1150        }
1151
1152        public void cancel() {
1153            if (isPending()) {
1154                mHandler.removeCallbacks(this);
1155                clear();
1156            }
1157        }
1158
1159        private boolean isPending() {
1160            return mHandler.hasCallbacks(this);
1161        }
1162
1163        private void clear() {
1164            mPointerIdBits = -1;
1165            mPolicyFlags = 0;
1166            final int eventCount = mEvents.size();
1167            for (int i = eventCount - 1; i >= 0; i--) {
1168                mEvents.remove(i).recycle();
1169            }
1170        }
1171
1172        public void forceSendAndRemove() {
1173            if (isPending()) {
1174                run();
1175                cancel();
1176            }
1177        }
1178
1179        public void run() {
1180            // Send an accessibility event to announce the touch exploration start.
1181            sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START);
1182
1183            if (!mEvents.isEmpty()) {
1184                // Deliver a down event.
1185                sendMotionEvent(mEvents.get(0), MotionEvent.ACTION_HOVER_ENTER,
1186                        mPointerIdBits, mPolicyFlags);
1187                if (DEBUG) {
1188                    Slog.d(LOG_TAG_SEND_HOVER_DELAYED,
1189                            "Injecting motion event: ACTION_HOVER_ENTER");
1190                }
1191
1192                // Deliver move events.
1193                final int eventCount = mEvents.size();
1194                for (int i = 1; i < eventCount; i++) {
1195                    sendMotionEvent(mEvents.get(i), MotionEvent.ACTION_HOVER_MOVE,
1196                            mPointerIdBits, mPolicyFlags);
1197                    if (DEBUG) {
1198                        Slog.d(LOG_TAG_SEND_HOVER_DELAYED,
1199                                "Injecting motion event: ACTION_HOVER_MOVE");
1200                    }
1201                }
1202            }
1203            clear();
1204        }
1205    }
1206
1207    /**
1208     * Class for delayed sending of hover exit events.
1209     */
1210    class SendHoverExitDelayed implements Runnable {
1211        private final String LOG_TAG_SEND_HOVER_DELAYED = "SendHoverExitDelayed";
1212
1213        private MotionEvent mPrototype;
1214        private int mPointerIdBits;
1215        private int mPolicyFlags;
1216
1217        public void post(MotionEvent prototype, int pointerIdBits, int policyFlags) {
1218            cancel();
1219            mPrototype = MotionEvent.obtain(prototype);
1220            mPointerIdBits = pointerIdBits;
1221            mPolicyFlags = policyFlags;
1222            mHandler.postDelayed(this, mDetermineUserIntentTimeout);
1223        }
1224
1225        public void cancel() {
1226            if (isPending()) {
1227                mHandler.removeCallbacks(this);
1228                clear();
1229            }
1230        }
1231
1232        private boolean isPending() {
1233            return mHandler.hasCallbacks(this);
1234        }
1235
1236        private void clear() {
1237            mPrototype.recycle();
1238            mPrototype = null;
1239            mPointerIdBits = -1;
1240            mPolicyFlags = 0;
1241        }
1242
1243        public void forceSendAndRemove() {
1244            if (isPending()) {
1245                run();
1246                cancel();
1247            }
1248        }
1249
1250        public void run() {
1251            if (DEBUG) {
1252                Slog.d(LOG_TAG_SEND_HOVER_DELAYED, "Injecting motion event:"
1253                        + " ACTION_HOVER_EXIT");
1254            }
1255            sendMotionEvent(mPrototype, MotionEvent.ACTION_HOVER_EXIT,
1256                    mPointerIdBits, mPolicyFlags);
1257            if (!mSendTouchExplorationEndDelayed.isPending()) {
1258                mSendTouchExplorationEndDelayed.cancel();
1259                mSendTouchExplorationEndDelayed.post();
1260            }
1261            if (mSendTouchInteractionEndDelayed.isPending()) {
1262                  mSendTouchInteractionEndDelayed.cancel();
1263                mSendTouchInteractionEndDelayed.post();
1264            }
1265            clear();
1266        }
1267    }
1268
1269    private class SendAccessibilityEventDelayed implements Runnable {
1270        private final int mEventType;
1271        private final int mDelay;
1272
1273        public SendAccessibilityEventDelayed(int eventType, int delay) {
1274            mEventType = eventType;
1275            mDelay = delay;
1276        }
1277
1278        public void cancel() {
1279            mHandler.removeCallbacks(this);
1280        }
1281
1282        public void post() {
1283            mHandler.postDelayed(this, mDelay);
1284        }
1285
1286        public boolean isPending() {
1287            return mHandler.hasCallbacks(this);
1288        }
1289
1290        public void forceSendAndRemove() {
1291            if (isPending()) {
1292                run();
1293                cancel();
1294            }
1295        }
1296
1297        @Override
1298        public void run() {
1299            sendAccessibilityEvent(mEventType);
1300        }
1301    }
1302
1303    @Override
1304    public String toString() {
1305        return LOG_TAG;
1306    }
1307
1308    class InjectedPointerTracker {
1309        private static final String LOG_TAG_INJECTED_POINTER_TRACKER = "InjectedPointerTracker";
1310
1311        // Keep track of which pointers sent to the system are down.
1312        private int mInjectedPointersDown;
1313
1314        // The time of the last injected down.
1315        private long mLastInjectedDownEventTime;
1316
1317        // The last injected hover event.
1318        private MotionEvent mLastInjectedHoverEvent;
1319
1320        // The last injected hover event used for performing clicks.
1321        private MotionEvent mLastInjectedHoverEventForClick;
1322
1323        /**
1324         * Processes an injected {@link MotionEvent} event.
1325         *
1326         * @param event The event to process.
1327         */
1328        public void onMotionEvent(MotionEvent event) {
1329            final int action = event.getActionMasked();
1330            switch (action) {
1331                case MotionEvent.ACTION_DOWN:
1332                case MotionEvent.ACTION_POINTER_DOWN: {
1333                    final int pointerId = event.getPointerId(event.getActionIndex());
1334                    final int pointerFlag = (1 << pointerId);
1335                    mInjectedPointersDown |= pointerFlag;
1336                    mLastInjectedDownEventTime = event.getDownTime();
1337                } break;
1338                case MotionEvent.ACTION_UP:
1339                case MotionEvent.ACTION_POINTER_UP: {
1340                    final int pointerId = event.getPointerId(event.getActionIndex());
1341                    final int pointerFlag = (1 << pointerId);
1342                    mInjectedPointersDown &= ~pointerFlag;
1343                    if (mInjectedPointersDown == 0) {
1344                        mLastInjectedDownEventTime = 0;
1345                    }
1346                } break;
1347                case MotionEvent.ACTION_HOVER_ENTER:
1348                case MotionEvent.ACTION_HOVER_MOVE:
1349                case MotionEvent.ACTION_HOVER_EXIT: {
1350                    if (mLastInjectedHoverEvent != null) {
1351                        mLastInjectedHoverEvent.recycle();
1352                    }
1353                    mLastInjectedHoverEvent = MotionEvent.obtain(event);
1354                    if (mLastInjectedHoverEventForClick != null) {
1355                        mLastInjectedHoverEventForClick.recycle();
1356                    }
1357                    mLastInjectedHoverEventForClick = MotionEvent.obtain(event);
1358                } break;
1359            }
1360            if (DEBUG) {
1361                Slog.i(LOG_TAG_INJECTED_POINTER_TRACKER, "Injected pointer:\n" + toString());
1362            }
1363        }
1364
1365        /**
1366         * Clears the internals state.
1367         */
1368        public void clear() {
1369            mInjectedPointersDown = 0;
1370        }
1371
1372        /**
1373         * @return The time of the last injected down event.
1374         */
1375        public long getLastInjectedDownEventTime() {
1376            return mLastInjectedDownEventTime;
1377        }
1378
1379        /**
1380         * @return The number of down pointers injected to the view hierarchy.
1381         */
1382        public int getInjectedPointerDownCount() {
1383            return Integer.bitCount(mInjectedPointersDown);
1384        }
1385
1386        /**
1387         * @return The bits of the injected pointers that are down.
1388         */
1389        public int getInjectedPointersDown() {
1390            return mInjectedPointersDown;
1391        }
1392
1393        /**
1394         * Whether an injected pointer is down.
1395         *
1396         * @param pointerId The unique pointer id.
1397         * @return True if the pointer is down.
1398         */
1399        public boolean isInjectedPointerDown(int pointerId) {
1400            final int pointerFlag = (1 << pointerId);
1401            return (mInjectedPointersDown & pointerFlag) != 0;
1402        }
1403
1404        /**
1405         * @return The the last injected hover event.
1406         */
1407        public MotionEvent getLastInjectedHoverEvent() {
1408            return mLastInjectedHoverEvent;
1409        }
1410
1411        /**
1412         * @return The the last injected hover event.
1413         */
1414        public MotionEvent getLastInjectedHoverEventForClick() {
1415            return mLastInjectedHoverEventForClick;
1416        }
1417
1418        @Override
1419        public String toString() {
1420            StringBuilder builder = new StringBuilder();
1421            builder.append("=========================");
1422            builder.append("\nDown pointers #");
1423            builder.append(Integer.bitCount(mInjectedPointersDown));
1424            builder.append(" [ ");
1425            for (int i = 0; i < MAX_POINTER_COUNT; i++) {
1426                if ((mInjectedPointersDown & i) != 0) {
1427                    builder.append(i);
1428                    builder.append(" ");
1429                }
1430            }
1431            builder.append("]");
1432            builder.append("\n=========================");
1433            return builder.toString();
1434        }
1435    }
1436
1437    class ReceivedPointerTracker {
1438        private static final String LOG_TAG_RECEIVED_POINTER_TRACKER = "ReceivedPointerTracker";
1439
1440        // Keep track of where and when a pointer went down.
1441        private final float[] mReceivedPointerDownX = new float[MAX_POINTER_COUNT];
1442        private final float[] mReceivedPointerDownY = new float[MAX_POINTER_COUNT];
1443        private final long[] mReceivedPointerDownTime = new long[MAX_POINTER_COUNT];
1444
1445        // Which pointers are down.
1446        private int mReceivedPointersDown;
1447
1448        // The edge flags of the last received down event.
1449        private int mLastReceivedDownEdgeFlags;
1450
1451        // Primary pointer which is either the first that went down
1452        // or if it goes up the next one that most recently went down.
1453        private int mPrimaryPointerId;
1454
1455        // Keep track of the last up pointer data.
1456        private long mLastReceivedUpPointerDownTime;
1457        private float mLastReceivedUpPointerDownX;
1458        private float mLastReceivedUpPointerDownY;
1459
1460        private MotionEvent mLastReceivedEvent;
1461
1462        /**
1463         * Clears the internals state.
1464         */
1465        public void clear() {
1466            Arrays.fill(mReceivedPointerDownX, 0);
1467            Arrays.fill(mReceivedPointerDownY, 0);
1468            Arrays.fill(mReceivedPointerDownTime, 0);
1469            mReceivedPointersDown = 0;
1470            mPrimaryPointerId = 0;
1471            mLastReceivedUpPointerDownTime = 0;
1472            mLastReceivedUpPointerDownX = 0;
1473            mLastReceivedUpPointerDownY = 0;
1474        }
1475
1476        /**
1477         * Processes a received {@link MotionEvent} event.
1478         *
1479         * @param event The event to process.
1480         */
1481        public void onMotionEvent(MotionEvent event) {
1482            if (mLastReceivedEvent != null) {
1483                mLastReceivedEvent.recycle();
1484            }
1485            mLastReceivedEvent = MotionEvent.obtain(event);
1486
1487            final int action = event.getActionMasked();
1488            switch (action) {
1489                case MotionEvent.ACTION_DOWN: {
1490                    handleReceivedPointerDown(event.getActionIndex(), event);
1491                } break;
1492                case MotionEvent.ACTION_POINTER_DOWN: {
1493                    handleReceivedPointerDown(event.getActionIndex(), event);
1494                } break;
1495                case MotionEvent.ACTION_UP: {
1496                    handleReceivedPointerUp(event.getActionIndex(), event);
1497                } break;
1498                case MotionEvent.ACTION_POINTER_UP: {
1499                    handleReceivedPointerUp(event.getActionIndex(), event);
1500                } break;
1501            }
1502            if (DEBUG) {
1503                Slog.i(LOG_TAG_RECEIVED_POINTER_TRACKER, "Received pointer:\n" + toString());
1504            }
1505        }
1506
1507        /**
1508         * @return The last received event.
1509         */
1510        public MotionEvent getLastReceivedEvent() {
1511            return mLastReceivedEvent;
1512        }
1513
1514        /**
1515         * @return The number of received pointers that are down.
1516         */
1517        public int getReceivedPointerDownCount() {
1518            return Integer.bitCount(mReceivedPointersDown);
1519        }
1520
1521        /**
1522         * Whether an received pointer is down.
1523         *
1524         * @param pointerId The unique pointer id.
1525         * @return True if the pointer is down.
1526         */
1527        public boolean isReceivedPointerDown(int pointerId) {
1528            final int pointerFlag = (1 << pointerId);
1529            return (mReceivedPointersDown & pointerFlag) != 0;
1530        }
1531
1532        /**
1533         * @param pointerId The unique pointer id.
1534         * @return The X coordinate where the pointer went down.
1535         */
1536        public float getReceivedPointerDownX(int pointerId) {
1537            return mReceivedPointerDownX[pointerId];
1538        }
1539
1540        /**
1541         * @param pointerId The unique pointer id.
1542         * @return The Y coordinate where the pointer went down.
1543         */
1544        public float getReceivedPointerDownY(int pointerId) {
1545            return mReceivedPointerDownY[pointerId];
1546        }
1547
1548        /**
1549         * @param pointerId The unique pointer id.
1550         * @return The time when the pointer went down.
1551         */
1552        public long getReceivedPointerDownTime(int pointerId) {
1553            return mReceivedPointerDownTime[pointerId];
1554        }
1555
1556        /**
1557         * @return The id of the primary pointer.
1558         */
1559        public int getPrimaryPointerId() {
1560            if (mPrimaryPointerId == INVALID_POINTER_ID) {
1561                mPrimaryPointerId = findPrimaryPointerId();
1562            }
1563            return mPrimaryPointerId;
1564        }
1565
1566        /**
1567         * @return The time when the last up received pointer went down.
1568         */
1569        public long getLastReceivedUpPointerDownTime() {
1570            return mLastReceivedUpPointerDownTime;
1571        }
1572
1573        /**
1574         * @return The down X of the last received pointer that went up.
1575         */
1576        public float getLastReceivedUpPointerDownX() {
1577            return mLastReceivedUpPointerDownX;
1578        }
1579
1580        /**
1581         * @return The down Y of the last received pointer that went up.
1582         */
1583        public float getLastReceivedUpPointerDownY() {
1584            return mLastReceivedUpPointerDownY;
1585        }
1586
1587        /**
1588         * @return The edge flags of the last received down event.
1589         */
1590        public int getLastReceivedDownEdgeFlags() {
1591            return mLastReceivedDownEdgeFlags;
1592        }
1593
1594        /**
1595         * Handles a received pointer down event.
1596         *
1597         * @param pointerIndex The index of the pointer that has changed.
1598         * @param event The event to be handled.
1599         */
1600        private void handleReceivedPointerDown(int pointerIndex, MotionEvent event) {
1601            final int pointerId = event.getPointerId(pointerIndex);
1602            final int pointerFlag = (1 << pointerId);
1603
1604            mLastReceivedUpPointerDownTime = 0;
1605            mLastReceivedUpPointerDownX = 0;
1606            mLastReceivedUpPointerDownX = 0;
1607
1608            mLastReceivedDownEdgeFlags = event.getEdgeFlags();
1609
1610            mReceivedPointersDown |= pointerFlag;
1611            mReceivedPointerDownX[pointerId] = event.getX(pointerIndex);
1612            mReceivedPointerDownY[pointerId] = event.getY(pointerIndex);
1613            mReceivedPointerDownTime[pointerId] = event.getEventTime();
1614
1615            mPrimaryPointerId = pointerId;
1616        }
1617
1618        /**
1619         * Handles a received pointer up event.
1620         *
1621         * @param pointerIndex The index of the pointer that has changed.
1622         * @param event The event to be handled.
1623         */
1624        private void handleReceivedPointerUp(int pointerIndex, MotionEvent event) {
1625            final int pointerId = event.getPointerId(pointerIndex);
1626            final int pointerFlag = (1 << pointerId);
1627
1628            mLastReceivedUpPointerDownTime = getReceivedPointerDownTime(pointerId);
1629            mLastReceivedUpPointerDownX = mReceivedPointerDownX[pointerId];
1630            mLastReceivedUpPointerDownY = mReceivedPointerDownY[pointerId];
1631
1632            mReceivedPointersDown &= ~pointerFlag;
1633            mReceivedPointerDownX[pointerId] = 0;
1634            mReceivedPointerDownY[pointerId] = 0;
1635            mReceivedPointerDownTime[pointerId] = 0;
1636
1637            if (mPrimaryPointerId == pointerId) {
1638                mPrimaryPointerId = INVALID_POINTER_ID;
1639            }
1640        }
1641
1642        /**
1643         * @return The primary pointer id.
1644         */
1645        private int findPrimaryPointerId() {
1646            int primaryPointerId = INVALID_POINTER_ID;
1647            long minDownTime = Long.MAX_VALUE;
1648
1649            // Find the pointer that went down first.
1650            int pointerIdBits = mReceivedPointersDown;
1651            while (pointerIdBits > 0) {
1652                final int pointerId = Integer.numberOfTrailingZeros(pointerIdBits);
1653                pointerIdBits &= ~(1 << pointerId);
1654                final long downPointerTime = mReceivedPointerDownTime[pointerId];
1655                if (downPointerTime < minDownTime) {
1656                    minDownTime = downPointerTime;
1657                    primaryPointerId = pointerId;
1658                }
1659            }
1660            return primaryPointerId;
1661        }
1662
1663        @Override
1664        public String toString() {
1665            StringBuilder builder = new StringBuilder();
1666            builder.append("=========================");
1667            builder.append("\nDown pointers #");
1668            builder.append(getReceivedPointerDownCount());
1669            builder.append(" [ ");
1670            for (int i = 0; i < MAX_POINTER_COUNT; i++) {
1671                if (isReceivedPointerDown(i)) {
1672                    builder.append(i);
1673                    builder.append(" ");
1674                }
1675            }
1676            builder.append("]");
1677            builder.append("\nPrimary pointer id [ ");
1678            builder.append(getPrimaryPointerId());
1679            builder.append(" ]");
1680            builder.append("\n=========================");
1681            return builder.toString();
1682        }
1683    }
1684}
1685