1/*
2 * Copyright (C) 2007 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;
18
19import android.util.Slog;
20import android.view.Display;
21import android.view.MotionEvent;
22import android.view.Surface;
23import android.view.WindowManagerPolicy;
24
25import java.io.PrintWriter;
26
27public class InputDevice {
28    static final boolean DEBUG_POINTERS = false;
29    static final boolean DEBUG_HACKS = false;
30
31    /** Amount that trackball needs to move in order to generate a key event. */
32    static final int TRACKBALL_MOVEMENT_THRESHOLD = 6;
33
34    /** Maximum number of pointers we will track and report. */
35    static final int MAX_POINTERS = 10;
36
37    /**
38     * Slop distance for jumpy pointer detection.
39     * The vertical range of the screen divided by this is our epsilon value.
40     */
41    private static final int JUMPY_EPSILON_DIVISOR = 212;
42
43    /** Number of jumpy points to drop for touchscreens that need it. */
44    private static final int JUMPY_TRANSITION_DROPS = 3;
45    private static final int JUMPY_DROP_LIMIT = 3;
46
47    final int id;
48    final int classes;
49    final String name;
50    final AbsoluteInfo absX;
51    final AbsoluteInfo absY;
52    final AbsoluteInfo absPressure;
53    final AbsoluteInfo absSize;
54
55    long mKeyDownTime = 0;
56    int mMetaKeysState = 0;
57
58    // For use by KeyInputQueue for keeping track of the current touch
59    // data in the old non-multi-touch protocol.
60    final int[] curTouchVals = new int[MotionEvent.NUM_SAMPLE_DATA * 2];
61
62    final MotionState mAbs = new MotionState(0, 0);
63    final MotionState mRel = new MotionState(TRACKBALL_MOVEMENT_THRESHOLD,
64            TRACKBALL_MOVEMENT_THRESHOLD);
65
66    static class MotionState {
67        int xPrecision;
68        int yPrecision;
69        float xMoveScale;
70        float yMoveScale;
71        MotionEvent currentMove = null;
72        boolean changed = false;
73        boolean everChanged = false;
74        long mDownTime = 0;
75
76        // The currently assigned pointer IDs, corresponding to the last data.
77        int[] mPointerIds = new int[MAX_POINTERS];
78
79        // This is the last generated pointer data, ordered to match
80        // mPointerIds.
81        boolean mSkipLastPointers;
82        int mLastNumPointers = 0;
83        final int[] mLastData = new int[MotionEvent.NUM_SAMPLE_DATA * MAX_POINTERS];
84
85        // This is the next set of pointer data being generated.  It is not
86        // in any known order, and will be propagated in to mLastData
87        // as part of mapping it to the appropriate pointer IDs.
88        // Note that we have one extra sample of data here, to help clients
89        // avoid doing bounds checking.
90        int mNextNumPointers = 0;
91        final int[] mNextData = new int[(MotionEvent.NUM_SAMPLE_DATA * MAX_POINTERS)
92                                        + MotionEvent.NUM_SAMPLE_DATA];
93
94        // Used to determine whether we dropped bad data, to avoid doing
95        // it repeatedly.
96        final boolean[] mDroppedBadPoint = new boolean[MAX_POINTERS];
97
98        // Used to count the number of jumpy points dropped.
99        private int mJumpyPointsDropped = 0;
100
101        // Used to perform averaging of reported coordinates, to smooth
102        // the data and filter out transients during a release.
103        static final int HISTORY_SIZE = 5;
104        int[] mHistoryDataStart = new int[MAX_POINTERS];
105        int[] mHistoryDataEnd = new int[MAX_POINTERS];
106        final int[] mHistoryData = new int[(MotionEvent.NUM_SAMPLE_DATA * MAX_POINTERS)
107                                        * HISTORY_SIZE];
108        final int[] mAveragedData = new int[MotionEvent.NUM_SAMPLE_DATA * MAX_POINTERS];
109
110        // Temporary data structures for doing the pointer ID mapping.
111        final int[] mLast2Next = new int[MAX_POINTERS];
112        final int[] mNext2Last = new int[MAX_POINTERS];
113        final long[] mNext2LastDistance = new long[MAX_POINTERS];
114
115        // Temporary data structure for generating the final motion data.
116        final float[] mReportData = new float[MotionEvent.NUM_SAMPLE_DATA * MAX_POINTERS];
117
118        // This is not used here, but can be used by callers for state tracking.
119        int mAddingPointerOffset = 0;
120        final boolean[] mDown = new boolean[MAX_POINTERS];
121
122        void dumpIntArray(PrintWriter pw, int[] array) {
123            pw.print("[");
124            for (int i=0; i<array.length; i++) {
125                if (i > 0) pw.print(", ");
126                pw.print(array[i]);
127            }
128            pw.print("]");
129        }
130
131        void dumpBooleanArray(PrintWriter pw, boolean[] array) {
132            pw.print("[");
133            for (int i=0; i<array.length; i++) {
134                if (i > 0) pw.print(", ");
135                pw.print(array[i] ? "true" : "false");
136            }
137            pw.print("]");
138        }
139
140        void dump(PrintWriter pw, String prefix) {
141            pw.print(prefix); pw.print("xPrecision="); pw.print(xPrecision);
142                    pw.print(" yPrecision="); pw.println(yPrecision);
143            pw.print(prefix); pw.print("xMoveScale="); pw.print(xMoveScale);
144                    pw.print(" yMoveScale="); pw.println(yMoveScale);
145            if (currentMove != null) {
146                pw.print(prefix); pw.print("currentMove="); pw.println(currentMove);
147            }
148            if (changed || mDownTime != 0) {
149                pw.print(prefix); pw.print("changed="); pw.print(changed);
150                        pw.print(" mDownTime="); pw.println(mDownTime);
151            }
152            pw.print(prefix); pw.print("mPointerIds="); dumpIntArray(pw, mPointerIds);
153                    pw.println("");
154            if (mSkipLastPointers || mLastNumPointers != 0) {
155                pw.print(prefix); pw.print("mSkipLastPointers="); pw.print(mSkipLastPointers);
156                        pw.print(" mLastNumPointers="); pw.println(mLastNumPointers);
157                pw.print(prefix); pw.print("mLastData="); dumpIntArray(pw, mLastData);
158                        pw.println("");
159            }
160            if (mNextNumPointers != 0) {
161                pw.print(prefix); pw.print("mNextNumPointers="); pw.println(mNextNumPointers);
162                pw.print(prefix); pw.print("mNextData="); dumpIntArray(pw, mNextData);
163                        pw.println("");
164            }
165            pw.print(prefix); pw.print("mDroppedBadPoint=");
166                    dumpBooleanArray(pw, mDroppedBadPoint); pw.println("");
167            pw.print(prefix); pw.print("mAddingPointerOffset="); pw.println(mAddingPointerOffset);
168            pw.print(prefix); pw.print("mDown=");
169                    dumpBooleanArray(pw, mDown); pw.println("");
170        }
171
172        MotionState(int mx, int my) {
173            xPrecision = mx;
174            yPrecision = my;
175            xMoveScale = mx != 0 ? (1.0f/mx) : 1.0f;
176            yMoveScale = my != 0 ? (1.0f/my) : 1.0f;
177            for (int i=0; i<MAX_POINTERS; i++) {
178                mPointerIds[i] = i;
179            }
180        }
181
182        /**
183         * Special hack for devices that have bad screen data: if one of the
184         * points has moved more than a screen height from the last position,
185         * then drop it.
186         */
187        void dropBadPoint(InputDevice dev) {
188            // We should always have absY, but let's be paranoid.
189            if (dev.absY == null) {
190                return;
191            }
192            // Don't do anything if a finger is going down or up.  We run
193            // here before assigning pointer IDs, so there isn't a good
194            // way to do per-finger matching.
195            if (mNextNumPointers != mLastNumPointers) {
196                return;
197            }
198
199            // We consider a single movement across more than a 7/16 of
200            // the long size of the screen to be bad.  This was a magic value
201            // determined by looking at the maximum distance it is feasible
202            // to actually move in one sample.
203            final int maxDy = ((dev.absY.maxValue-dev.absY.minValue)*7)/16;
204
205            // Look through all new points and see if any are farther than
206            // acceptable from all previous points.
207            for (int i=mNextNumPointers-1; i>=0; i--) {
208                final int ioff = i * MotionEvent.NUM_SAMPLE_DATA;
209                //final int x = mNextData[ioff + MotionEvent.SAMPLE_X];
210                final int y = mNextData[ioff + MotionEvent.SAMPLE_Y];
211                if (DEBUG_HACKS) Slog.v("InputDevice", "Looking at next point #" + i + ": y=" + y);
212                boolean dropped = false;
213                if (!mDroppedBadPoint[i] && mLastNumPointers > 0) {
214                    dropped = true;
215                    int closestDy = -1;
216                    int closestY = -1;
217                    // We will drop this new point if it is sufficiently
218                    // far away from -all- last points.
219                    for (int j=mLastNumPointers-1; j>=0; j--) {
220                        final int joff = j * MotionEvent.NUM_SAMPLE_DATA;
221                        //int dx = x - mLastData[joff + MotionEvent.SAMPLE_X];
222                        int dy = y - mLastData[joff + MotionEvent.SAMPLE_Y];
223                        //if (dx < 0) dx = -dx;
224                        if (dy < 0) dy = -dy;
225                        if (DEBUG_HACKS) Slog.v("InputDevice", "Comparing with last point #" + j
226                                + ": y=" + mLastData[joff] + " dy=" + dy);
227                        if (dy < maxDy) {
228                            dropped = false;
229                            break;
230                        } else if (closestDy < 0 || dy < closestDy) {
231                            closestDy = dy;
232                            closestY = mLastData[joff + MotionEvent.SAMPLE_Y];
233                        }
234                    }
235                    if (dropped) {
236                        dropped = true;
237                        Slog.i("InputDevice", "Dropping bad point #" + i
238                                + ": newY=" + y + " closestDy=" + closestDy
239                                + " maxDy=" + maxDy);
240                        mNextData[ioff + MotionEvent.SAMPLE_Y] = closestY;
241                        break;
242                    }
243                }
244                mDroppedBadPoint[i] = dropped;
245            }
246        }
247
248        void dropJumpyPoint(InputDevice dev) {
249            // We should always have absY, but let's be paranoid.
250            if (dev.absY == null) {
251                return;
252            }
253            final int jumpyEpsilon = dev.absY.range / JUMPY_EPSILON_DIVISOR;
254
255            final int nextNumPointers = mNextNumPointers;
256            final int lastNumPointers = mLastNumPointers;
257            final int[] nextData = mNextData;
258            final int[] lastData = mLastData;
259
260            if (nextNumPointers != mLastNumPointers) {
261                if (DEBUG_HACKS) {
262                    Slog.d("InputDevice", "Different pointer count " + lastNumPointers +
263                            " -> " + nextNumPointers);
264                    for (int i = 0; i < nextNumPointers; i++) {
265                        int ioff = i * MotionEvent.NUM_SAMPLE_DATA;
266                        Slog.d("InputDevice", "Pointer " + i + " (" +
267                                mNextData[ioff + MotionEvent.SAMPLE_X] + ", " +
268                                mNextData[ioff + MotionEvent.SAMPLE_Y] + ")");
269                    }
270                }
271
272                // Just drop the first few events going from 1 to 2 pointers.
273                // They're bad often enough that they're not worth considering.
274                if (lastNumPointers == 1 && nextNumPointers == 2
275                        && mJumpyPointsDropped < JUMPY_TRANSITION_DROPS) {
276                    mNextNumPointers = 1;
277                    mJumpyPointsDropped++;
278                } else if (lastNumPointers == 2 && nextNumPointers == 1
279                        && mJumpyPointsDropped < JUMPY_TRANSITION_DROPS) {
280                    // The event when we go from 2 -> 1 tends to be messed up too
281                    System.arraycopy(lastData, 0, nextData, 0,
282                            lastNumPointers * MotionEvent.NUM_SAMPLE_DATA);
283                    mNextNumPointers = lastNumPointers;
284                    mJumpyPointsDropped++;
285
286                    if (DEBUG_HACKS) {
287                        for (int i = 0; i < mNextNumPointers; i++) {
288                            int ioff = i * MotionEvent.NUM_SAMPLE_DATA;
289                            Slog.d("InputDevice", "Pointer " + i + " replaced (" +
290                                    mNextData[ioff + MotionEvent.SAMPLE_X] + ", " +
291                                    mNextData[ioff + MotionEvent.SAMPLE_Y] + ")");
292                        }
293                    }
294                } else {
295                    mJumpyPointsDropped = 0;
296
297                    if (DEBUG_HACKS) {
298                        Slog.d("InputDevice", "Transition - drop limit reset");
299                    }
300                }
301                return;
302            }
303
304            // A 'jumpy' point is one where the coordinate value for one axis
305            // has jumped to the other pointer's location. No need to do anything
306            // else if we only have one pointer.
307            if (nextNumPointers < 2) {
308                return;
309            }
310
311            int badPointerIndex = -1;
312            int badPointerReplaceXWith = 0;
313            int badPointerReplaceYWith = 0;
314            int badPointerDistance = Integer.MIN_VALUE;
315            for (int i = nextNumPointers - 1; i >= 0; i--) {
316                boolean dropx = false;
317                boolean dropy = false;
318
319                // Limit how many times a jumpy point can get dropped.
320                if (mJumpyPointsDropped < JUMPY_DROP_LIMIT) {
321                    final int ioff = i * MotionEvent.NUM_SAMPLE_DATA;
322                    final int x = nextData[ioff + MotionEvent.SAMPLE_X];
323                    final int y = nextData[ioff + MotionEvent.SAMPLE_Y];
324
325                    if (DEBUG_HACKS) {
326                        Slog.d("InputDevice", "Point " + i + " (" + x + ", " + y + ")");
327                    }
328
329                    // Check if a touch point is too close to another's coordinates
330                    for (int j = 0; j < nextNumPointers && !dropx && !dropy; j++) {
331                        if (j == i) {
332                            continue;
333                        }
334
335                        final int joff = j * MotionEvent.NUM_SAMPLE_DATA;
336                        final int xOther = nextData[joff + MotionEvent.SAMPLE_X];
337                        final int yOther = nextData[joff + MotionEvent.SAMPLE_Y];
338
339                        dropx = Math.abs(x - xOther) <= jumpyEpsilon;
340                        dropy = Math.abs(y - yOther) <= jumpyEpsilon;
341                    }
342
343                    if (dropx) {
344                        int xreplace = lastData[MotionEvent.SAMPLE_X];
345                        int yreplace = lastData[MotionEvent.SAMPLE_Y];
346                        int distance = Math.abs(yreplace - y);
347                        for (int j = 1; j < lastNumPointers; j++) {
348                            final int joff = j * MotionEvent.NUM_SAMPLE_DATA;
349                            int lasty = lastData[joff + MotionEvent.SAMPLE_Y];
350                            int currDist = Math.abs(lasty - y);
351                            if (currDist < distance) {
352                                xreplace = lastData[joff + MotionEvent.SAMPLE_X];
353                                yreplace = lasty;
354                                distance = currDist;
355                            }
356                        }
357
358                        int badXDelta = Math.abs(xreplace - x);
359                        if (badXDelta > badPointerDistance) {
360                            badPointerDistance = badXDelta;
361                            badPointerIndex = i;
362                            badPointerReplaceXWith = xreplace;
363                            badPointerReplaceYWith = yreplace;
364                        }
365                    } else if (dropy) {
366                        int xreplace = lastData[MotionEvent.SAMPLE_X];
367                        int yreplace = lastData[MotionEvent.SAMPLE_Y];
368                        int distance = Math.abs(xreplace - x);
369                        for (int j = 1; j < lastNumPointers; j++) {
370                            final int joff = j * MotionEvent.NUM_SAMPLE_DATA;
371                            int lastx = lastData[joff + MotionEvent.SAMPLE_X];
372                            int currDist = Math.abs(lastx - x);
373                            if (currDist < distance) {
374                                xreplace = lastx;
375                                yreplace = lastData[joff + MotionEvent.SAMPLE_Y];
376                                distance = currDist;
377                            }
378                        }
379
380                        int badYDelta = Math.abs(yreplace - y);
381                        if (badYDelta > badPointerDistance) {
382                            badPointerDistance = badYDelta;
383                            badPointerIndex = i;
384                            badPointerReplaceXWith = xreplace;
385                            badPointerReplaceYWith = yreplace;
386                        }
387                    }
388                }
389            }
390            if (badPointerIndex >= 0) {
391                if (DEBUG_HACKS) {
392                    Slog.d("InputDevice", "Replacing bad pointer " + badPointerIndex +
393                            " with (" + badPointerReplaceXWith + ", " + badPointerReplaceYWith +
394                            ")");
395                }
396
397                final int offset = badPointerIndex * MotionEvent.NUM_SAMPLE_DATA;
398                nextData[offset + MotionEvent.SAMPLE_X] = badPointerReplaceXWith;
399                nextData[offset + MotionEvent.SAMPLE_Y] = badPointerReplaceYWith;
400                mJumpyPointsDropped++;
401            } else {
402                mJumpyPointsDropped = 0;
403            }
404        }
405
406        /**
407         * Special hack for devices that have bad screen data: aggregate and
408         * compute averages of the coordinate data, to reduce the amount of
409         * jitter seen by applications.
410         */
411        int[] generateAveragedData(int upOrDownPointer, int lastNumPointers,
412                int nextNumPointers) {
413            final int numPointers = mLastNumPointers;
414            final int[] rawData = mLastData;
415            if (DEBUG_HACKS) Slog.v("InputDevice", "lastNumPointers=" + lastNumPointers
416                    + " nextNumPointers=" + nextNumPointers
417                    + " numPointers=" + numPointers);
418            for (int i=0; i<numPointers; i++) {
419                final int ioff = i * MotionEvent.NUM_SAMPLE_DATA;
420                // We keep the average data in offsets based on the pointer
421                // ID, so we don't need to move it around as fingers are
422                // pressed and released.
423                final int p = mPointerIds[i];
424                final int poff = p * MotionEvent.NUM_SAMPLE_DATA * HISTORY_SIZE;
425                if (i == upOrDownPointer && lastNumPointers != nextNumPointers) {
426                    if (lastNumPointers < nextNumPointers) {
427                        // This pointer is going down.  Clear its history
428                        // and start fresh.
429                        if (DEBUG_HACKS) Slog.v("InputDevice", "Pointer down @ index "
430                                + upOrDownPointer + " id " + mPointerIds[i]);
431                        mHistoryDataStart[i] = 0;
432                        mHistoryDataEnd[i] = 0;
433                        System.arraycopy(rawData, ioff, mHistoryData, poff,
434                                MotionEvent.NUM_SAMPLE_DATA);
435                        System.arraycopy(rawData, ioff, mAveragedData, ioff,
436                                MotionEvent.NUM_SAMPLE_DATA);
437                        continue;
438                    } else {
439                        // The pointer is going up.  Just fall through to
440                        // recompute the last averaged point (and don't add
441                        // it as a new point to include in the average).
442                        if (DEBUG_HACKS) Slog.v("InputDevice", "Pointer up @ index "
443                                + upOrDownPointer + " id " + mPointerIds[i]);
444                    }
445                } else {
446                    int end = mHistoryDataEnd[i];
447                    int eoff = poff + (end*MotionEvent.NUM_SAMPLE_DATA);
448                    int oldX = mHistoryData[eoff + MotionEvent.SAMPLE_X];
449                    int oldY = mHistoryData[eoff + MotionEvent.SAMPLE_Y];
450                    int newX = rawData[ioff + MotionEvent.SAMPLE_X];
451                    int newY = rawData[ioff + MotionEvent.SAMPLE_Y];
452                    int dx = newX-oldX;
453                    int dy = newY-oldY;
454                    int delta = dx*dx + dy*dy;
455                    if (DEBUG_HACKS) Slog.v("InputDevice", "Delta from last: " + delta);
456                    if (delta >= (75*75)) {
457                        // Magic number, if moving farther than this, turn
458                        // off filtering to avoid lag in response.
459                        mHistoryDataStart[i] = 0;
460                        mHistoryDataEnd[i] = 0;
461                        System.arraycopy(rawData, ioff, mHistoryData, poff,
462                                MotionEvent.NUM_SAMPLE_DATA);
463                        System.arraycopy(rawData, ioff, mAveragedData, ioff,
464                                MotionEvent.NUM_SAMPLE_DATA);
465                        continue;
466                    } else {
467                        end++;
468                        if (end >= HISTORY_SIZE) {
469                            end -= HISTORY_SIZE;
470                        }
471                        mHistoryDataEnd[i] = end;
472                        int noff = poff + (end*MotionEvent.NUM_SAMPLE_DATA);
473                        mHistoryData[noff + MotionEvent.SAMPLE_X] = newX;
474                        mHistoryData[noff + MotionEvent.SAMPLE_Y] = newY;
475                        mHistoryData[noff + MotionEvent.SAMPLE_PRESSURE]
476                                = rawData[ioff + MotionEvent.SAMPLE_PRESSURE];
477                        int start = mHistoryDataStart[i];
478                        if (end == start) {
479                            start++;
480                            if (start >= HISTORY_SIZE) {
481                                start -= HISTORY_SIZE;
482                            }
483                            mHistoryDataStart[i] = start;
484                        }
485                    }
486                }
487
488                // Now compute the average.
489                int start = mHistoryDataStart[i];
490                int end = mHistoryDataEnd[i];
491                int x=0, y=0;
492                int totalPressure = 0;
493                while (start != end) {
494                    int soff = poff + (start*MotionEvent.NUM_SAMPLE_DATA);
495                    int pressure = mHistoryData[soff + MotionEvent.SAMPLE_PRESSURE];
496                    if (pressure <= 0) pressure = 1;
497                    x += mHistoryData[soff + MotionEvent.SAMPLE_X] * pressure;
498                    y += mHistoryData[soff + MotionEvent.SAMPLE_Y] * pressure;
499                    totalPressure += pressure;
500                    start++;
501                    if (start >= HISTORY_SIZE) start = 0;
502                }
503                int eoff = poff + (end*MotionEvent.NUM_SAMPLE_DATA);
504                int pressure = mHistoryData[eoff + MotionEvent.SAMPLE_PRESSURE];
505                if (pressure <= 0) pressure = 1;
506                x += mHistoryData[eoff + MotionEvent.SAMPLE_X] * pressure;
507                y += mHistoryData[eoff + MotionEvent.SAMPLE_Y] * pressure;
508                totalPressure += pressure;
509                x /= totalPressure;
510                y /= totalPressure;
511                if (DEBUG_HACKS) Slog.v("InputDevice", "Averaging " + totalPressure
512                        + " weight: (" + x + "," + y + ")");
513                mAveragedData[ioff + MotionEvent.SAMPLE_X] = x;
514                mAveragedData[ioff + MotionEvent.SAMPLE_Y] = y;
515                mAveragedData[ioff + MotionEvent.SAMPLE_PRESSURE] =
516                        rawData[ioff + MotionEvent.SAMPLE_PRESSURE];
517                mAveragedData[ioff + MotionEvent.SAMPLE_SIZE] =
518                        rawData[ioff + MotionEvent.SAMPLE_SIZE];
519            }
520            return mAveragedData;
521        }
522
523        private boolean assignPointer(int nextIndex, boolean allowOverlap) {
524            final int lastNumPointers = mLastNumPointers;
525            final int[] next2Last = mNext2Last;
526            final long[] next2LastDistance = mNext2LastDistance;
527            final int[] last2Next = mLast2Next;
528            final int[] lastData = mLastData;
529            final int[] nextData = mNextData;
530            final int id = nextIndex * MotionEvent.NUM_SAMPLE_DATA;
531
532            if (DEBUG_POINTERS) Slog.v("InputDevice", "assignPointer: nextIndex="
533                    + nextIndex + " dataOff=" + id);
534            final int x1 = nextData[id + MotionEvent.SAMPLE_X];
535            final int y1 = nextData[id + MotionEvent.SAMPLE_Y];
536
537            long bestDistance = -1;
538            int bestIndex = -1;
539            for (int j=0; j<lastNumPointers; j++) {
540                // If we are not allowing multiple new points to be assigned
541                // to the same old pointer, then skip this one if it is already
542                // detected as a conflict (-2).
543                if (!allowOverlap && last2Next[j] < -1) {
544                    continue;
545                }
546                final int jd = j * MotionEvent.NUM_SAMPLE_DATA;
547                final int xd = lastData[jd + MotionEvent.SAMPLE_X] - x1;
548                final int yd = lastData[jd + MotionEvent.SAMPLE_Y] - y1;
549                final long distance = xd*(long)xd + yd*(long)yd;
550                if (bestDistance == -1 || distance < bestDistance) {
551                    bestDistance = distance;
552                    bestIndex = j;
553                }
554            }
555
556            if (DEBUG_POINTERS) Slog.v("InputDevice", "New index " + nextIndex
557                    + " best old index=" + bestIndex + " (distance="
558                    + bestDistance + ")");
559            next2Last[nextIndex] = bestIndex;
560            next2LastDistance[nextIndex] = bestDistance;
561
562            if (bestIndex < 0) {
563                return true;
564            }
565
566            if (last2Next[bestIndex] == -1) {
567                last2Next[bestIndex] = nextIndex;
568                return false;
569            }
570
571            if (DEBUG_POINTERS) Slog.v("InputDevice", "Old index " + bestIndex
572                    + " has multiple best new pointers!");
573
574            last2Next[bestIndex] = -2;
575            return true;
576        }
577
578        private int updatePointerIdentifiers() {
579            final int[] lastData = mLastData;
580            final int[] nextData = mNextData;
581            final int nextNumPointers = mNextNumPointers;
582            final int lastNumPointers = mLastNumPointers;
583
584            if (nextNumPointers == 1 && lastNumPointers == 1) {
585                System.arraycopy(nextData, 0, lastData, 0,
586                        MotionEvent.NUM_SAMPLE_DATA);
587                return -1;
588            }
589
590            // Clear our old state.
591            final int[] last2Next = mLast2Next;
592            for (int i=0; i<lastNumPointers; i++) {
593                last2Next[i] = -1;
594            }
595
596            if (DEBUG_POINTERS) Slog.v("InputDevice",
597                    "Update pointers: lastNumPointers=" + lastNumPointers
598                    + " nextNumPointers=" + nextNumPointers);
599
600            // Figure out the closes new points to the previous points.
601            final int[] next2Last = mNext2Last;
602            final long[] next2LastDistance = mNext2LastDistance;
603            boolean conflicts = false;
604            for (int i=0; i<nextNumPointers; i++) {
605                conflicts |= assignPointer(i, true);
606            }
607
608            // Resolve ambiguities in pointer mappings, when two or more
609            // new pointer locations find their best previous location is
610            // the same.
611            if (conflicts) {
612                if (DEBUG_POINTERS) Slog.v("InputDevice", "Resolving conflicts");
613
614                for (int i=0; i<lastNumPointers; i++) {
615                    if (last2Next[i] != -2) {
616                        continue;
617                    }
618
619                    // Note that this algorithm is far from perfect.  Ideally
620                    // we should do something like the one described at
621                    // http://portal.acm.org/citation.cfm?id=997856
622
623                    if (DEBUG_POINTERS) Slog.v("InputDevice",
624                            "Resolving last index #" + i);
625
626                    int numFound;
627                    do {
628                        numFound = 0;
629                        long worstDistance = 0;
630                        int worstJ = -1;
631                        for (int j=0; j<nextNumPointers; j++) {
632                            if (next2Last[j] != i) {
633                                continue;
634                            }
635                            numFound++;
636                            if (worstDistance < next2LastDistance[j]) {
637                                worstDistance = next2LastDistance[j];
638                                worstJ = j;
639                            }
640                        }
641
642                        if (worstJ >= 0) {
643                            if (DEBUG_POINTERS) Slog.v("InputDevice",
644                                    "Worst new pointer: " + worstJ
645                                    + " (distance=" + worstDistance + ")");
646                            if (assignPointer(worstJ, false)) {
647                                // In this case there is no last pointer
648                                // remaining for this new one!
649                                next2Last[worstJ] = -1;
650                            }
651                        }
652                    } while (numFound > 2);
653                }
654            }
655
656            int retIndex = -1;
657
658            if (lastNumPointers < nextNumPointers) {
659                // We have one or more new pointers that are down.  Create a
660                // new pointer identifier for one of them.
661                if (DEBUG_POINTERS) Slog.v("InputDevice", "Adding new pointer");
662                int nextId = 0;
663                int i=0;
664                while (i < lastNumPointers) {
665                    if (mPointerIds[i] > nextId) {
666                        // Found a hole, insert the pointer here.
667                        if (DEBUG_POINTERS) Slog.v("InputDevice",
668                                "Inserting new pointer at hole " + i);
669                        System.arraycopy(mPointerIds, i, mPointerIds,
670                                i+1, lastNumPointers-i);
671                        System.arraycopy(lastData, i*MotionEvent.NUM_SAMPLE_DATA,
672                                lastData, (i+1)*MotionEvent.NUM_SAMPLE_DATA,
673                                (lastNumPointers-i)*MotionEvent.NUM_SAMPLE_DATA);
674                        System.arraycopy(next2Last, i, next2Last,
675                                i+1, lastNumPointers-i);
676                        break;
677                    }
678                    i++;
679                    nextId++;
680                }
681
682                if (DEBUG_POINTERS) Slog.v("InputDevice",
683                        "New pointer id " + nextId + " at index " + i);
684
685                mLastNumPointers++;
686                retIndex = i;
687                mPointerIds[i] = nextId;
688
689                // And assign this identifier to the first new pointer.
690                for (int j=0; j<nextNumPointers; j++) {
691                    if (next2Last[j] < 0) {
692                        if (DEBUG_POINTERS) Slog.v("InputDevice",
693                                "Assigning new id to new pointer index " + j);
694                        next2Last[j] = i;
695                        break;
696                    }
697                }
698            }
699
700            // Propagate all of the current data into the appropriate
701            // location in the old data to match the pointer ID that was
702            // assigned to it.
703            for (int i=0; i<nextNumPointers; i++) {
704                int lastIndex = next2Last[i];
705                if (lastIndex >= 0) {
706                    if (DEBUG_POINTERS) Slog.v("InputDevice",
707                            "Copying next pointer index " + i
708                            + " to last index " + lastIndex);
709                    System.arraycopy(nextData, i*MotionEvent.NUM_SAMPLE_DATA,
710                            lastData, lastIndex*MotionEvent.NUM_SAMPLE_DATA,
711                            MotionEvent.NUM_SAMPLE_DATA);
712                }
713            }
714
715            if (lastNumPointers > nextNumPointers) {
716                // One or more pointers has gone up.  Find the first one,
717                // and adjust accordingly.
718                if (DEBUG_POINTERS) Slog.v("InputDevice", "Removing old pointer");
719                for (int i=0; i<lastNumPointers; i++) {
720                    if (last2Next[i] == -1) {
721                        if (DEBUG_POINTERS) Slog.v("InputDevice",
722                                "Removing old pointer at index " + i);
723                        retIndex = i;
724                        break;
725                    }
726                }
727            }
728
729            return retIndex;
730        }
731
732        void removeOldPointer(int index) {
733            final int lastNumPointers = mLastNumPointers;
734            if (index >= 0 && index < lastNumPointers) {
735                System.arraycopy(mPointerIds, index+1, mPointerIds,
736                        index, lastNumPointers-index-1);
737                System.arraycopy(mLastData, (index+1)*MotionEvent.NUM_SAMPLE_DATA,
738                        mLastData, (index)*MotionEvent.NUM_SAMPLE_DATA,
739                        (lastNumPointers-index-1)*MotionEvent.NUM_SAMPLE_DATA);
740                mLastNumPointers--;
741            }
742        }
743
744        MotionEvent generateAbsMotion(InputDevice device, long curTime,
745                long curTimeNano, Display display, int orientation,
746                int metaState) {
747
748            if (mSkipLastPointers) {
749                mSkipLastPointers = false;
750                mLastNumPointers = 0;
751            }
752
753            if (mNextNumPointers <= 0 && mLastNumPointers <= 0) {
754                return null;
755            }
756
757            final int lastNumPointers = mLastNumPointers;
758            final int nextNumPointers = mNextNumPointers;
759            if (mNextNumPointers > MAX_POINTERS) {
760                Slog.w("InputDevice", "Number of pointers " + mNextNumPointers
761                        + " exceeded maximum of " + MAX_POINTERS);
762                mNextNumPointers = MAX_POINTERS;
763            }
764
765            int upOrDownPointer = updatePointerIdentifiers();
766
767            final float[] reportData = mReportData;
768            final int[] rawData;
769            if (KeyInputQueue.BAD_TOUCH_HACK) {
770                rawData = generateAveragedData(upOrDownPointer, lastNumPointers,
771                        nextNumPointers);
772            } else {
773                rawData = mLastData;
774            }
775
776            final int numPointers = mLastNumPointers;
777
778            if (DEBUG_POINTERS) Slog.v("InputDevice", "Processing "
779                    + numPointers + " pointers (going from " + lastNumPointers
780                    + " to " + nextNumPointers + ")");
781
782            for (int i=0; i<numPointers; i++) {
783                final int pos = i * MotionEvent.NUM_SAMPLE_DATA;
784                reportData[pos + MotionEvent.SAMPLE_X] = rawData[pos + MotionEvent.SAMPLE_X];
785                reportData[pos + MotionEvent.SAMPLE_Y] = rawData[pos + MotionEvent.SAMPLE_Y];
786                reportData[pos + MotionEvent.SAMPLE_PRESSURE] = rawData[pos + MotionEvent.SAMPLE_PRESSURE];
787                reportData[pos + MotionEvent.SAMPLE_SIZE] = rawData[pos + MotionEvent.SAMPLE_SIZE];
788            }
789
790            int action;
791            int edgeFlags = 0;
792            if (nextNumPointers != lastNumPointers) {
793                if (nextNumPointers > lastNumPointers) {
794                    if (lastNumPointers == 0) {
795                        action = MotionEvent.ACTION_DOWN;
796                        mDownTime = curTime;
797                    } else {
798                        action = MotionEvent.ACTION_POINTER_DOWN
799                                | (upOrDownPointer << MotionEvent.ACTION_POINTER_INDEX_SHIFT);
800                    }
801                } else {
802                    if (numPointers == 1) {
803                        action = MotionEvent.ACTION_UP;
804                    } else {
805                        action = MotionEvent.ACTION_POINTER_UP
806                                | (upOrDownPointer << MotionEvent.ACTION_POINTER_INDEX_SHIFT);
807                    }
808                }
809                currentMove = null;
810            } else {
811                action = MotionEvent.ACTION_MOVE;
812            }
813
814            final int dispW = display.getWidth()-1;
815            final int dispH = display.getHeight()-1;
816            int w = dispW;
817            int h = dispH;
818            if (orientation == Surface.ROTATION_90
819                    || orientation == Surface.ROTATION_270) {
820                int tmp = w;
821                w = h;
822                h = tmp;
823            }
824
825            final AbsoluteInfo absX = device.absX;
826            final AbsoluteInfo absY = device.absY;
827            final AbsoluteInfo absPressure = device.absPressure;
828            final AbsoluteInfo absSize = device.absSize;
829            for (int i=0; i<numPointers; i++) {
830                final int j = i * MotionEvent.NUM_SAMPLE_DATA;
831
832                if (absX != null) {
833                    reportData[j + MotionEvent.SAMPLE_X] =
834                            ((reportData[j + MotionEvent.SAMPLE_X]-absX.minValue)
835                                / absX.range) * w;
836                }
837                if (absY != null) {
838                    reportData[j + MotionEvent.SAMPLE_Y] =
839                            ((reportData[j + MotionEvent.SAMPLE_Y]-absY.minValue)
840                                / absY.range) * h;
841                }
842                if (absPressure != null) {
843                    reportData[j + MotionEvent.SAMPLE_PRESSURE] =
844                            ((reportData[j + MotionEvent.SAMPLE_PRESSURE]-absPressure.minValue)
845                                / (float)absPressure.range);
846                }
847                if (absSize != null) {
848                    reportData[j + MotionEvent.SAMPLE_SIZE] =
849                            ((reportData[j + MotionEvent.SAMPLE_SIZE]-absSize.minValue)
850                                / (float)absSize.range);
851                }
852
853                switch (orientation) {
854                    case Surface.ROTATION_90: {
855                        final float temp = reportData[j + MotionEvent.SAMPLE_X];
856                        reportData[j + MotionEvent.SAMPLE_X] = reportData[j + MotionEvent.SAMPLE_Y];
857                        reportData[j + MotionEvent.SAMPLE_Y] = w-temp;
858                        break;
859                    }
860                    case Surface.ROTATION_180: {
861                        reportData[j + MotionEvent.SAMPLE_X] = w-reportData[j + MotionEvent.SAMPLE_X];
862                        reportData[j + MotionEvent.SAMPLE_Y] = h-reportData[j + MotionEvent.SAMPLE_Y];
863                        break;
864                    }
865                    case Surface.ROTATION_270: {
866                        final float temp = reportData[j + MotionEvent.SAMPLE_X];
867                        reportData[j + MotionEvent.SAMPLE_X] = h-reportData[j + MotionEvent.SAMPLE_Y];
868                        reportData[j + MotionEvent.SAMPLE_Y] = temp;
869                        break;
870                    }
871                }
872            }
873
874            // We only consider the first pointer when computing the edge
875            // flags, since they are global to the event.
876            if (action == MotionEvent.ACTION_DOWN) {
877                if (reportData[MotionEvent.SAMPLE_X] <= 0) {
878                    edgeFlags |= MotionEvent.EDGE_LEFT;
879                } else if (reportData[MotionEvent.SAMPLE_X] >= dispW) {
880                    edgeFlags |= MotionEvent.EDGE_RIGHT;
881                }
882                if (reportData[MotionEvent.SAMPLE_Y] <= 0) {
883                    edgeFlags |= MotionEvent.EDGE_TOP;
884                } else if (reportData[MotionEvent.SAMPLE_Y] >= dispH) {
885                    edgeFlags |= MotionEvent.EDGE_BOTTOM;
886                }
887            }
888
889            if (currentMove != null) {
890                if (false) Slog.i("InputDevice", "Adding batch x="
891                        + reportData[MotionEvent.SAMPLE_X]
892                        + " y=" + reportData[MotionEvent.SAMPLE_Y]
893                        + " to " + currentMove);
894                currentMove.addBatch(curTime, reportData, metaState);
895                if (WindowManagerPolicy.WATCH_POINTER) {
896                    Slog.i("KeyInputQueue", "Updating: " + currentMove);
897                }
898                return null;
899            }
900
901            MotionEvent me = MotionEvent.obtainNano(mDownTime, curTime,
902                    curTimeNano, action, numPointers, mPointerIds, reportData,
903                    metaState, xPrecision, yPrecision, device.id, edgeFlags);
904            if (action == MotionEvent.ACTION_MOVE) {
905                currentMove = me;
906            }
907
908            if (nextNumPointers < lastNumPointers) {
909                removeOldPointer(upOrDownPointer);
910            }
911
912            return me;
913        }
914
915        boolean hasMore() {
916            return mLastNumPointers != mNextNumPointers;
917        }
918
919        void finish() {
920            mNextNumPointers = mAddingPointerOffset = 0;
921            mNextData[MotionEvent.SAMPLE_PRESSURE] = 0;
922        }
923
924        MotionEvent generateRelMotion(InputDevice device, long curTime,
925                long curTimeNano, int orientation, int metaState) {
926
927            final float[] scaled = mReportData;
928
929            // For now we only support 1 pointer with relative motions.
930            scaled[MotionEvent.SAMPLE_X] = mNextData[MotionEvent.SAMPLE_X];
931            scaled[MotionEvent.SAMPLE_Y] = mNextData[MotionEvent.SAMPLE_Y];
932            scaled[MotionEvent.SAMPLE_PRESSURE] = 1.0f;
933            scaled[MotionEvent.SAMPLE_SIZE] = 0;
934            int edgeFlags = 0;
935
936            int action;
937            if (mNextNumPointers != mLastNumPointers) {
938                mNextData[MotionEvent.SAMPLE_X] =
939                        mNextData[MotionEvent.SAMPLE_Y] = 0;
940                if (mNextNumPointers > 0 && mLastNumPointers == 0) {
941                    action = MotionEvent.ACTION_DOWN;
942                    mDownTime = curTime;
943                } else if (mNextNumPointers == 0) {
944                    action = MotionEvent.ACTION_UP;
945                } else {
946                    action = MotionEvent.ACTION_MOVE;
947                }
948                mLastNumPointers = mNextNumPointers;
949                currentMove = null;
950            } else {
951                action = MotionEvent.ACTION_MOVE;
952            }
953
954            scaled[MotionEvent.SAMPLE_X] *= xMoveScale;
955            scaled[MotionEvent.SAMPLE_Y] *= yMoveScale;
956            switch (orientation) {
957                case Surface.ROTATION_90: {
958                    final float temp = scaled[MotionEvent.SAMPLE_X];
959                    scaled[MotionEvent.SAMPLE_X] = scaled[MotionEvent.SAMPLE_Y];
960                    scaled[MotionEvent.SAMPLE_Y] = -temp;
961                    break;
962                }
963                case Surface.ROTATION_180: {
964                    scaled[MotionEvent.SAMPLE_X] = -scaled[MotionEvent.SAMPLE_X];
965                    scaled[MotionEvent.SAMPLE_Y] = -scaled[MotionEvent.SAMPLE_Y];
966                    break;
967                }
968                case Surface.ROTATION_270: {
969                    final float temp = scaled[MotionEvent.SAMPLE_X];
970                    scaled[MotionEvent.SAMPLE_X] = -scaled[MotionEvent.SAMPLE_Y];
971                    scaled[MotionEvent.SAMPLE_Y] = temp;
972                    break;
973                }
974            }
975
976            if (currentMove != null) {
977                if (false) Slog.i("InputDevice", "Adding batch x="
978                        + scaled[MotionEvent.SAMPLE_X]
979                        + " y=" + scaled[MotionEvent.SAMPLE_Y]
980                        + " to " + currentMove);
981                currentMove.addBatch(curTime, scaled, metaState);
982                if (WindowManagerPolicy.WATCH_POINTER) {
983                    Slog.i("KeyInputQueue", "Updating: " + currentMove);
984                }
985                return null;
986            }
987
988            MotionEvent me = MotionEvent.obtainNano(mDownTime, curTime,
989                    curTimeNano, action, 1, mPointerIds, scaled, metaState,
990                    xPrecision, yPrecision, device.id, edgeFlags);
991            if (action == MotionEvent.ACTION_MOVE) {
992                currentMove = me;
993            }
994            return me;
995        }
996    }
997
998    static class AbsoluteInfo {
999        int minValue;
1000        int maxValue;
1001        int range;
1002        int flat;
1003        int fuzz;
1004
1005        final void dump(PrintWriter pw) {
1006            pw.print("minValue="); pw.print(minValue);
1007            pw.print(" maxValue="); pw.print(maxValue);
1008            pw.print(" range="); pw.print(range);
1009            pw.print(" flat="); pw.print(flat);
1010            pw.print(" fuzz="); pw.print(fuzz);
1011        }
1012    };
1013
1014    InputDevice(int _id, int _classes, String _name,
1015            AbsoluteInfo _absX, AbsoluteInfo _absY,
1016            AbsoluteInfo _absPressure, AbsoluteInfo _absSize) {
1017        id = _id;
1018        classes = _classes;
1019        name = _name;
1020        absX = _absX;
1021        absY = _absY;
1022        absPressure = _absPressure;
1023        absSize = _absSize;
1024    }
1025};
1026