1/*
2 * Copyright (C) 2010 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
17#define LOG_TAG "MotionEvent-JNI"
18
19#include "JNIHelp.h"
20
21#include <SkMatrix.h>
22#include <android_runtime/AndroidRuntime.h>
23#include <android_runtime/Log.h>
24#include <utils/Log.h>
25#include <input/Input.h>
26#include <ScopedUtfChars.h>
27#include "android_os_Parcel.h"
28#include "android_view_MotionEvent.h"
29#include "android_util_Binder.h"
30#include "android/graphics/Matrix.h"
31
32namespace android {
33
34// ----------------------------------------------------------------------------
35
36static struct {
37    jclass clazz;
38
39    jmethodID obtain;
40    jmethodID recycle;
41
42    jfieldID mNativePtr;
43} gMotionEventClassInfo;
44
45static struct {
46    jfieldID mPackedAxisBits;
47    jfieldID mPackedAxisValues;
48    jfieldID x;
49    jfieldID y;
50    jfieldID pressure;
51    jfieldID size;
52    jfieldID touchMajor;
53    jfieldID touchMinor;
54    jfieldID toolMajor;
55    jfieldID toolMinor;
56    jfieldID orientation;
57} gPointerCoordsClassInfo;
58
59static struct {
60    jfieldID id;
61    jfieldID toolType;
62} gPointerPropertiesClassInfo;
63
64// ----------------------------------------------------------------------------
65
66MotionEvent* android_view_MotionEvent_getNativePtr(JNIEnv* env, jobject eventObj) {
67    if (!eventObj) {
68        return NULL;
69    }
70    return reinterpret_cast<MotionEvent*>(
71            env->GetLongField(eventObj, gMotionEventClassInfo.mNativePtr));
72}
73
74static void android_view_MotionEvent_setNativePtr(JNIEnv* env, jobject eventObj,
75        MotionEvent* event) {
76    env->SetLongField(eventObj, gMotionEventClassInfo.mNativePtr,
77            reinterpret_cast<jlong>(event));
78}
79
80jobject android_view_MotionEvent_obtainAsCopy(JNIEnv* env, const MotionEvent* event) {
81    jobject eventObj = env->CallStaticObjectMethod(gMotionEventClassInfo.clazz,
82            gMotionEventClassInfo.obtain);
83    if (env->ExceptionCheck() || !eventObj) {
84        ALOGE("An exception occurred while obtaining a motion event.");
85        LOGE_EX(env);
86        env->ExceptionClear();
87        return NULL;
88    }
89
90    MotionEvent* destEvent = android_view_MotionEvent_getNativePtr(env, eventObj);
91    if (!destEvent) {
92        destEvent = new MotionEvent();
93        android_view_MotionEvent_setNativePtr(env, eventObj, destEvent);
94    }
95
96    destEvent->copyFrom(event, true);
97    return eventObj;
98}
99
100status_t android_view_MotionEvent_recycle(JNIEnv* env, jobject eventObj) {
101    env->CallVoidMethod(eventObj, gMotionEventClassInfo.recycle);
102    if (env->ExceptionCheck()) {
103        ALOGW("An exception occurred while recycling a motion event.");
104        LOGW_EX(env);
105        env->ExceptionClear();
106        return UNKNOWN_ERROR;
107    }
108    return OK;
109}
110
111// ----------------------------------------------------------------------------
112
113static const jint HISTORY_CURRENT = -0x80000000;
114
115static bool validatePointerCount(JNIEnv* env, jint pointerCount) {
116    if (pointerCount < 1) {
117        jniThrowException(env, "java/lang/IllegalArgumentException",
118                "pointerCount must be at least 1");
119        return false;
120    }
121    return true;
122}
123
124static bool validatePointerPropertiesArray(JNIEnv* env, jobjectArray pointerPropertiesObjArray,
125        size_t pointerCount) {
126    if (!pointerPropertiesObjArray) {
127        jniThrowException(env, "java/lang/IllegalArgumentException",
128                "pointerProperties array must not be null");
129        return false;
130    }
131    size_t length = size_t(env->GetArrayLength(pointerPropertiesObjArray));
132    if (length < pointerCount) {
133        jniThrowException(env, "java/lang/IllegalArgumentException",
134                "pointerProperties array must be large enough to hold all pointers");
135        return false;
136    }
137    return true;
138}
139
140static bool validatePointerCoordsObjArray(JNIEnv* env, jobjectArray pointerCoordsObjArray,
141        size_t pointerCount) {
142    if (!pointerCoordsObjArray) {
143        jniThrowException(env, "java/lang/IllegalArgumentException",
144                "pointerCoords array must not be null");
145        return false;
146    }
147    size_t length = size_t(env->GetArrayLength(pointerCoordsObjArray));
148    if (length < pointerCount) {
149        jniThrowException(env, "java/lang/IllegalArgumentException",
150                "pointerCoords array must be large enough to hold all pointers");
151        return false;
152    }
153    return true;
154}
155
156static bool validatePointerIndex(JNIEnv* env, jint pointerIndex, size_t pointerCount) {
157    if (pointerIndex < 0 || size_t(pointerIndex) >= pointerCount) {
158        jniThrowException(env, "java/lang/IllegalArgumentException",
159                "pointerIndex out of range");
160        return false;
161    }
162    return true;
163}
164
165static bool validateHistoryPos(JNIEnv* env, jint historyPos, size_t historySize) {
166    if (historyPos < 0 || size_t(historyPos) >= historySize) {
167        jniThrowException(env, "java/lang/IllegalArgumentException",
168                "historyPos out of range");
169        return false;
170    }
171    return true;
172}
173
174static bool validatePointerCoords(JNIEnv* env, jobject pointerCoordsObj) {
175    if (!pointerCoordsObj) {
176        jniThrowException(env, "java/lang/IllegalArgumentException",
177                "pointerCoords must not be null");
178        return false;
179    }
180    return true;
181}
182
183static bool validatePointerProperties(JNIEnv* env, jobject pointerPropertiesObj) {
184    if (!pointerPropertiesObj) {
185        jniThrowException(env, "java/lang/IllegalArgumentException",
186                "pointerProperties must not be null");
187        return false;
188    }
189    return true;
190}
191
192static void pointerCoordsToNative(JNIEnv* env, jobject pointerCoordsObj,
193        float xOffset, float yOffset, PointerCoords* outRawPointerCoords) {
194    outRawPointerCoords->clear();
195    outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_X,
196            env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.x) - xOffset);
197    outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_Y,
198            env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.y) - yOffset);
199    outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_PRESSURE,
200            env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.pressure));
201    outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_SIZE,
202            env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.size));
203    outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR,
204            env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.touchMajor));
205    outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR,
206            env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.touchMinor));
207    outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR,
208            env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.toolMajor));
209    outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR,
210            env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.toolMinor));
211    outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION,
212            env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.orientation));
213
214    BitSet64 bits =
215            BitSet64(env->GetLongField(pointerCoordsObj, gPointerCoordsClassInfo.mPackedAxisBits));
216    if (!bits.isEmpty()) {
217        jfloatArray valuesArray = jfloatArray(env->GetObjectField(pointerCoordsObj,
218                gPointerCoordsClassInfo.mPackedAxisValues));
219        if (valuesArray) {
220            jfloat* values = static_cast<jfloat*>(
221                    env->GetPrimitiveArrayCritical(valuesArray, NULL));
222
223            uint32_t index = 0;
224            do {
225                uint32_t axis = bits.clearFirstMarkedBit();
226                outRawPointerCoords->setAxisValue(axis, values[index++]);
227            } while (!bits.isEmpty());
228
229            env->ReleasePrimitiveArrayCritical(valuesArray, values, JNI_ABORT);
230            env->DeleteLocalRef(valuesArray);
231        }
232    }
233}
234
235static jfloatArray obtainPackedAxisValuesArray(JNIEnv* env, uint32_t minSize,
236        jobject outPointerCoordsObj) {
237    jfloatArray outValuesArray = jfloatArray(env->GetObjectField(outPointerCoordsObj,
238            gPointerCoordsClassInfo.mPackedAxisValues));
239    if (outValuesArray) {
240        uint32_t size = env->GetArrayLength(outValuesArray);
241        if (minSize <= size) {
242            return outValuesArray;
243        }
244        env->DeleteLocalRef(outValuesArray);
245    }
246    uint32_t size = 8;
247    while (size < minSize) {
248        size *= 2;
249    }
250    outValuesArray = env->NewFloatArray(size);
251    env->SetObjectField(outPointerCoordsObj,
252            gPointerCoordsClassInfo.mPackedAxisValues, outValuesArray);
253    return outValuesArray;
254}
255
256static void pointerCoordsFromNative(JNIEnv* env, const PointerCoords* rawPointerCoords,
257        float xOffset, float yOffset, jobject outPointerCoordsObj) {
258    env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.x,
259            rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_X) + xOffset);
260    env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.y,
261            rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_Y) + yOffset);
262    env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.pressure,
263            rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_PRESSURE));
264    env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.size,
265            rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_SIZE));
266    env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.touchMajor,
267            rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR));
268    env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.touchMinor,
269            rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR));
270    env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.toolMajor,
271            rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR));
272    env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.toolMinor,
273            rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR));
274    env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.orientation,
275            rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
276
277    uint64_t outBits = 0;
278    BitSet64 bits = BitSet64(rawPointerCoords->bits);
279    bits.clearBit(AMOTION_EVENT_AXIS_X);
280    bits.clearBit(AMOTION_EVENT_AXIS_Y);
281    bits.clearBit(AMOTION_EVENT_AXIS_PRESSURE);
282    bits.clearBit(AMOTION_EVENT_AXIS_SIZE);
283    bits.clearBit(AMOTION_EVENT_AXIS_TOUCH_MAJOR);
284    bits.clearBit(AMOTION_EVENT_AXIS_TOUCH_MINOR);
285    bits.clearBit(AMOTION_EVENT_AXIS_TOOL_MAJOR);
286    bits.clearBit(AMOTION_EVENT_AXIS_TOOL_MINOR);
287    bits.clearBit(AMOTION_EVENT_AXIS_ORIENTATION);
288    if (!bits.isEmpty()) {
289        uint32_t packedAxesCount = bits.count();
290        jfloatArray outValuesArray = obtainPackedAxisValuesArray(env, packedAxesCount,
291                outPointerCoordsObj);
292        if (!outValuesArray) {
293            return; // OOM
294        }
295
296        jfloat* outValues = static_cast<jfloat*>(env->GetPrimitiveArrayCritical(
297                outValuesArray, NULL));
298
299        const float* values = rawPointerCoords->values;
300        uint32_t index = 0;
301        do {
302            uint32_t axis = bits.clearFirstMarkedBit();
303            outBits |= BitSet64::valueForBit(axis);
304            outValues[index++] = rawPointerCoords->getAxisValue(axis);
305        } while (!bits.isEmpty());
306
307        env->ReleasePrimitiveArrayCritical(outValuesArray, outValues, 0);
308        env->DeleteLocalRef(outValuesArray);
309    }
310    env->SetLongField(outPointerCoordsObj, gPointerCoordsClassInfo.mPackedAxisBits, outBits);
311}
312
313static void pointerPropertiesToNative(JNIEnv* env, jobject pointerPropertiesObj,
314        PointerProperties* outPointerProperties) {
315    outPointerProperties->clear();
316    outPointerProperties->id = env->GetIntField(pointerPropertiesObj,
317            gPointerPropertiesClassInfo.id);
318    outPointerProperties->toolType = env->GetIntField(pointerPropertiesObj,
319            gPointerPropertiesClassInfo.toolType);
320}
321
322static void pointerPropertiesFromNative(JNIEnv* env, const PointerProperties* pointerProperties,
323        jobject outPointerPropertiesObj) {
324    env->SetIntField(outPointerPropertiesObj, gPointerPropertiesClassInfo.id,
325            pointerProperties->id);
326    env->SetIntField(outPointerPropertiesObj, gPointerPropertiesClassInfo.toolType,
327            pointerProperties->toolType);
328}
329
330
331// ----------------------------------------------------------------------------
332
333static jlong android_view_MotionEvent_nativeInitialize(JNIEnv* env, jclass clazz,
334        jlong nativePtr,
335        jint deviceId, jint source, jint action, jint flags, jint edgeFlags,
336        jint metaState, jint buttonState,
337        jfloat xOffset, jfloat yOffset, jfloat xPrecision, jfloat yPrecision,
338        jlong downTimeNanos, jlong eventTimeNanos,
339        jint pointerCount, jobjectArray pointerPropertiesObjArray,
340        jobjectArray pointerCoordsObjArray) {
341    if (!validatePointerCount(env, pointerCount)
342            || !validatePointerPropertiesArray(env, pointerPropertiesObjArray, pointerCount)
343            || !validatePointerCoordsObjArray(env, pointerCoordsObjArray, pointerCount)) {
344        return 0;
345    }
346
347    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
348    if (!event) {
349        event = new MotionEvent();
350    }
351
352    PointerProperties pointerProperties[pointerCount];
353    PointerCoords rawPointerCoords[pointerCount];
354
355    for (jint i = 0; i < pointerCount; i++) {
356        jobject pointerPropertiesObj = env->GetObjectArrayElement(pointerPropertiesObjArray, i);
357        if (!pointerPropertiesObj) {
358            goto Error;
359        }
360        pointerPropertiesToNative(env, pointerPropertiesObj, &pointerProperties[i]);
361        env->DeleteLocalRef(pointerPropertiesObj);
362
363        jobject pointerCoordsObj = env->GetObjectArrayElement(pointerCoordsObjArray, i);
364        if (!pointerCoordsObj) {
365            jniThrowNullPointerException(env, "pointerCoords");
366            goto Error;
367        }
368        pointerCoordsToNative(env, pointerCoordsObj, xOffset, yOffset, &rawPointerCoords[i]);
369        env->DeleteLocalRef(pointerCoordsObj);
370    }
371
372    event->initialize(deviceId, source, action, flags, edgeFlags, metaState, buttonState,
373            xOffset, yOffset, xPrecision, yPrecision,
374            downTimeNanos, eventTimeNanos, pointerCount, pointerProperties, rawPointerCoords);
375
376    return reinterpret_cast<jlong>(event);
377
378Error:
379    if (!nativePtr) {
380        delete event;
381    }
382    return 0;
383}
384
385static jlong android_view_MotionEvent_nativeCopy(JNIEnv* env, jclass clazz,
386        jlong destNativePtr, jlong sourceNativePtr, jboolean keepHistory) {
387    MotionEvent* destEvent = reinterpret_cast<MotionEvent*>(destNativePtr);
388    if (!destEvent) {
389        destEvent = new MotionEvent();
390    }
391    MotionEvent* sourceEvent = reinterpret_cast<MotionEvent*>(sourceNativePtr);
392    destEvent->copyFrom(sourceEvent, keepHistory);
393    return reinterpret_cast<jlong>(destEvent);
394}
395
396static void android_view_MotionEvent_nativeDispose(JNIEnv* env, jclass clazz,
397        jlong nativePtr) {
398    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
399    delete event;
400}
401
402static void android_view_MotionEvent_nativeAddBatch(JNIEnv* env, jclass clazz,
403        jlong nativePtr, jlong eventTimeNanos, jobjectArray pointerCoordsObjArray,
404        jint metaState) {
405    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
406    size_t pointerCount = event->getPointerCount();
407    if (!validatePointerCoordsObjArray(env, pointerCoordsObjArray, pointerCount)) {
408        return;
409    }
410
411    PointerCoords rawPointerCoords[pointerCount];
412
413    for (size_t i = 0; i < pointerCount; i++) {
414        jobject pointerCoordsObj = env->GetObjectArrayElement(pointerCoordsObjArray, i);
415        if (!pointerCoordsObj) {
416            jniThrowNullPointerException(env, "pointerCoords");
417            return;
418        }
419        pointerCoordsToNative(env, pointerCoordsObj,
420                event->getXOffset(), event->getYOffset(), &rawPointerCoords[i]);
421        env->DeleteLocalRef(pointerCoordsObj);
422    }
423
424    event->addSample(eventTimeNanos, rawPointerCoords);
425    event->setMetaState(event->getMetaState() | metaState);
426}
427
428static jint android_view_MotionEvent_nativeGetDeviceId(JNIEnv* env, jclass clazz,
429        jlong nativePtr) {
430    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
431    return event->getDeviceId();
432}
433
434static jint android_view_MotionEvent_nativeGetSource(JNIEnv* env, jclass clazz,
435        jlong nativePtr) {
436    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
437    return event->getSource();
438}
439
440static void android_view_MotionEvent_nativeSetSource(JNIEnv* env, jclass clazz,
441        jlong nativePtr, jint source) {
442    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
443    event->setSource(source);
444}
445
446static jint android_view_MotionEvent_nativeGetAction(JNIEnv* env, jclass clazz,
447        jlong nativePtr) {
448    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
449    return event->getAction();
450}
451
452static void android_view_MotionEvent_nativeSetAction(JNIEnv* env, jclass clazz,
453        jlong nativePtr, jint action) {
454    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
455    event->setAction(action);
456}
457
458static jboolean android_view_MotionEvent_nativeIsTouchEvent(JNIEnv* env, jclass clazz,
459        jlong nativePtr) {
460    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
461    return event->isTouchEvent();
462}
463
464static jint android_view_MotionEvent_nativeGetFlags(JNIEnv* env, jclass clazz,
465        jlong nativePtr) {
466    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
467    return event->getFlags();
468}
469
470static void android_view_MotionEvent_nativeSetFlags(JNIEnv* env, jclass clazz,
471        jlong nativePtr, jint flags) {
472    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
473    event->setFlags(flags);
474}
475
476static jint android_view_MotionEvent_nativeGetEdgeFlags(JNIEnv* env, jclass clazz,
477        jlong nativePtr) {
478    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
479    return event->getEdgeFlags();
480}
481
482static void android_view_MotionEvent_nativeSetEdgeFlags(JNIEnv* env, jclass clazz,
483        jlong nativePtr, jint edgeFlags) {
484    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
485    event->setEdgeFlags(edgeFlags);
486}
487
488static jint android_view_MotionEvent_nativeGetMetaState(JNIEnv* env, jclass clazz,
489        jlong nativePtr) {
490    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
491    return event->getMetaState();
492}
493
494static jint android_view_MotionEvent_nativeGetButtonState(JNIEnv* env, jclass clazz,
495        jlong nativePtr) {
496    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
497    return event->getButtonState();
498}
499
500static void android_view_MotionEvent_nativeOffsetLocation(JNIEnv* env, jclass clazz,
501        jlong nativePtr, jfloat deltaX, jfloat deltaY) {
502    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
503    return event->offsetLocation(deltaX, deltaY);
504}
505
506static jfloat android_view_MotionEvent_nativeGetXOffset(JNIEnv* env, jclass clazz,
507        jlong nativePtr) {
508    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
509    return event->getXOffset();
510}
511
512static jfloat android_view_MotionEvent_nativeGetYOffset(JNIEnv* env, jclass clazz,
513        jlong nativePtr) {
514    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
515    return event->getYOffset();
516}
517
518static jfloat android_view_MotionEvent_nativeGetXPrecision(JNIEnv* env, jclass clazz,
519        jlong nativePtr) {
520    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
521    return event->getXPrecision();
522}
523
524static jfloat android_view_MotionEvent_nativeGetYPrecision(JNIEnv* env, jclass clazz,
525        jlong nativePtr) {
526    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
527    return event->getYPrecision();
528}
529
530static jlong android_view_MotionEvent_nativeGetDownTimeNanos(JNIEnv* env, jclass clazz,
531        jlong nativePtr) {
532    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
533    return event->getDownTime();
534}
535
536static void android_view_MotionEvent_nativeSetDownTimeNanos(JNIEnv* env, jclass clazz,
537        jlong nativePtr, jlong downTimeNanos) {
538    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
539    event->setDownTime(downTimeNanos);
540}
541
542static jint android_view_MotionEvent_nativeGetPointerCount(JNIEnv* env, jclass clazz,
543        jlong nativePtr) {
544    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
545    return jint(event->getPointerCount());
546}
547
548static jint android_view_MotionEvent_nativeGetPointerId(JNIEnv* env, jclass clazz,
549        jlong nativePtr, jint pointerIndex) {
550    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
551    size_t pointerCount = event->getPointerCount();
552    if (!validatePointerIndex(env, pointerIndex, pointerCount)) {
553        return -1;
554    }
555    return event->getPointerId(pointerIndex);
556}
557
558static jint android_view_MotionEvent_nativeGetToolType(JNIEnv* env, jclass clazz,
559        jlong nativePtr, jint pointerIndex) {
560    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
561    size_t pointerCount = event->getPointerCount();
562    if (!validatePointerIndex(env, pointerIndex, pointerCount)) {
563        return -1;
564    }
565    return event->getToolType(pointerIndex);
566}
567
568static jint android_view_MotionEvent_nativeFindPointerIndex(JNIEnv* env, jclass clazz,
569        jlong nativePtr, jint pointerId) {
570    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
571    return jint(event->findPointerIndex(pointerId));
572}
573
574static jint android_view_MotionEvent_nativeGetHistorySize(JNIEnv* env, jclass clazz,
575        jlong nativePtr) {
576    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
577    return jint(event->getHistorySize());
578}
579
580static jlong android_view_MotionEvent_nativeGetEventTimeNanos(JNIEnv* env, jclass clazz,
581        jlong nativePtr, jint historyPos) {
582    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
583    if (historyPos == HISTORY_CURRENT) {
584        return event->getEventTime();
585    } else {
586        size_t historySize = event->getHistorySize();
587        if (!validateHistoryPos(env, historyPos, historySize)) {
588            return 0;
589        }
590        return event->getHistoricalEventTime(historyPos);
591    }
592}
593
594static jfloat android_view_MotionEvent_nativeGetRawAxisValue(JNIEnv* env, jclass clazz,
595        jlong nativePtr, jint axis, jint pointerIndex, jint historyPos) {
596    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
597    size_t pointerCount = event->getPointerCount();
598    if (!validatePointerIndex(env, pointerIndex, pointerCount)) {
599        return 0;
600    }
601
602    if (historyPos == HISTORY_CURRENT) {
603        return event->getRawAxisValue(axis, pointerIndex);
604    } else {
605        size_t historySize = event->getHistorySize();
606        if (!validateHistoryPos(env, historyPos, historySize)) {
607            return 0;
608        }
609        return event->getHistoricalRawAxisValue(axis, pointerIndex, historyPos);
610    }
611}
612
613static jfloat android_view_MotionEvent_nativeGetAxisValue(JNIEnv* env, jclass clazz,
614        jlong nativePtr, jint axis, jint pointerIndex, jint historyPos) {
615    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
616    size_t pointerCount = event->getPointerCount();
617    if (!validatePointerIndex(env, pointerIndex, pointerCount)) {
618        return 0;
619    }
620
621    if (historyPos == HISTORY_CURRENT) {
622        return event->getAxisValue(axis, pointerIndex);
623    } else {
624        size_t historySize = event->getHistorySize();
625        if (!validateHistoryPos(env, historyPos, historySize)) {
626            return 0;
627        }
628        return event->getHistoricalAxisValue(axis, pointerIndex, historyPos);
629    }
630}
631
632static void android_view_MotionEvent_nativeGetPointerCoords(JNIEnv* env, jclass clazz,
633        jlong nativePtr, jint pointerIndex, jint historyPos, jobject outPointerCoordsObj) {
634    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
635    size_t pointerCount = event->getPointerCount();
636    if (!validatePointerIndex(env, pointerIndex, pointerCount)
637            || !validatePointerCoords(env, outPointerCoordsObj)) {
638        return;
639    }
640
641    const PointerCoords* rawPointerCoords;
642    if (historyPos == HISTORY_CURRENT) {
643        rawPointerCoords = event->getRawPointerCoords(pointerIndex);
644    } else {
645        size_t historySize = event->getHistorySize();
646        if (!validateHistoryPos(env, historyPos, historySize)) {
647            return;
648        }
649        rawPointerCoords = event->getHistoricalRawPointerCoords(pointerIndex, historyPos);
650    }
651    pointerCoordsFromNative(env, rawPointerCoords, event->getXOffset(), event->getYOffset(),
652            outPointerCoordsObj);
653}
654
655static void android_view_MotionEvent_nativeGetPointerProperties(JNIEnv* env, jclass clazz,
656        jlong nativePtr, jint pointerIndex, jobject outPointerPropertiesObj) {
657    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
658    size_t pointerCount = event->getPointerCount();
659    if (!validatePointerIndex(env, pointerIndex, pointerCount)
660            || !validatePointerProperties(env, outPointerPropertiesObj)) {
661        return;
662    }
663
664    const PointerProperties* pointerProperties = event->getPointerProperties(pointerIndex);
665    pointerPropertiesFromNative(env, pointerProperties, outPointerPropertiesObj);
666}
667
668static void android_view_MotionEvent_nativeScale(JNIEnv* env, jclass clazz,
669        jlong nativePtr, jfloat scale) {
670    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
671    event->scale(scale);
672}
673
674static void android_view_MotionEvent_nativeTransform(JNIEnv* env, jclass clazz,
675        jlong nativePtr, jobject matrixObj) {
676    SkMatrix* matrix = android_graphics_Matrix_getSkMatrix(env, matrixObj);
677    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
678
679    float m[9];
680    m[0] = SkScalarToFloat(matrix->get(SkMatrix::kMScaleX));
681    m[1] = SkScalarToFloat(matrix->get(SkMatrix::kMSkewX));
682    m[2] = SkScalarToFloat(matrix->get(SkMatrix::kMTransX));
683    m[3] = SkScalarToFloat(matrix->get(SkMatrix::kMSkewY));
684    m[4] = SkScalarToFloat(matrix->get(SkMatrix::kMScaleY));
685    m[5] = SkScalarToFloat(matrix->get(SkMatrix::kMTransY));
686    m[6] = SkScalarToFloat(matrix->get(SkMatrix::kMPersp0));
687    m[7] = SkScalarToFloat(matrix->get(SkMatrix::kMPersp1));
688    m[8] = SkScalarToFloat(matrix->get(SkMatrix::kMPersp2));
689    event->transform(m);
690}
691
692static jlong android_view_MotionEvent_nativeReadFromParcel(JNIEnv* env, jclass clazz,
693        jlong nativePtr, jobject parcelObj) {
694    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
695    if (!event) {
696        event = new MotionEvent();
697    }
698
699    Parcel* parcel = parcelForJavaObject(env, parcelObj);
700
701    status_t status = event->readFromParcel(parcel);
702    if (status) {
703        if (!nativePtr) {
704            delete event;
705        }
706        jniThrowRuntimeException(env, "Failed to read MotionEvent parcel.");
707        return 0;
708    }
709    return reinterpret_cast<jlong>(event);
710}
711
712static void android_view_MotionEvent_nativeWriteToParcel(JNIEnv* env, jclass clazz,
713        jlong nativePtr, jobject parcelObj) {
714    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
715    Parcel* parcel = parcelForJavaObject(env, parcelObj);
716
717    status_t status = event->writeToParcel(parcel);
718    if (status) {
719        jniThrowRuntimeException(env, "Failed to write MotionEvent parcel.");
720    }
721}
722
723static jstring android_view_MotionEvent_nativeAxisToString(JNIEnv* env, jclass clazz,
724        jint axis) {
725    return env->NewStringUTF(MotionEvent::getLabel(static_cast<int32_t>(axis)));
726}
727
728static jint android_view_MotionEvent_nativeAxisFromString(JNIEnv* env, jclass clazz,
729        jstring label) {
730    ScopedUtfChars axisLabel(env, label);
731    return static_cast<jint>(MotionEvent::getAxisFromLabel(axisLabel.c_str()));
732}
733
734// ----------------------------------------------------------------------------
735
736static JNINativeMethod gMotionEventMethods[] = {
737    /* name, signature, funcPtr */
738    { "nativeInitialize",
739            "(JIIIIIIIFFFFJJI[Landroid/view/MotionEvent$PointerProperties;"
740                    "[Landroid/view/MotionEvent$PointerCoords;)J",
741            (void*)android_view_MotionEvent_nativeInitialize },
742    { "nativeCopy",
743            "(JJZ)J",
744            (void*)android_view_MotionEvent_nativeCopy },
745    { "nativeDispose",
746            "(J)V",
747            (void*)android_view_MotionEvent_nativeDispose },
748    { "nativeAddBatch",
749            "(JJ[Landroid/view/MotionEvent$PointerCoords;I)V",
750            (void*)android_view_MotionEvent_nativeAddBatch },
751    { "nativeGetDeviceId",
752            "(J)I",
753            (void*)android_view_MotionEvent_nativeGetDeviceId },
754    { "nativeGetSource",
755            "(J)I",
756            (void*)android_view_MotionEvent_nativeGetSource },
757    { "nativeSetSource",
758            "(JI)I",
759            (void*)android_view_MotionEvent_nativeSetSource },
760    { "nativeGetAction",
761            "(J)I",
762            (void*)android_view_MotionEvent_nativeGetAction },
763    { "nativeSetAction",
764            "(JI)V",
765            (void*)android_view_MotionEvent_nativeSetAction },
766    { "nativeIsTouchEvent",
767            "(J)Z",
768            (void*)android_view_MotionEvent_nativeIsTouchEvent },
769    { "nativeGetFlags",
770            "(J)I",
771            (void*)android_view_MotionEvent_nativeGetFlags },
772    { "nativeSetFlags",
773            "(JI)V",
774            (void*)android_view_MotionEvent_nativeSetFlags },
775    { "nativeGetEdgeFlags",
776            "(J)I",
777            (void*)android_view_MotionEvent_nativeGetEdgeFlags },
778    { "nativeSetEdgeFlags",
779            "(JI)V",
780            (void*)android_view_MotionEvent_nativeSetEdgeFlags },
781    { "nativeGetMetaState",
782            "(J)I",
783            (void*)android_view_MotionEvent_nativeGetMetaState },
784    { "nativeGetButtonState",
785            "(J)I",
786            (void*)android_view_MotionEvent_nativeGetButtonState },
787    { "nativeOffsetLocation",
788            "(JFF)V",
789            (void*)android_view_MotionEvent_nativeOffsetLocation },
790    { "nativeGetXOffset",
791            "(J)F",
792            (void*)android_view_MotionEvent_nativeGetXOffset },
793    { "nativeGetYOffset",
794            "(J)F",
795            (void*)android_view_MotionEvent_nativeGetYOffset },
796    { "nativeGetXPrecision",
797            "(J)F",
798            (void*)android_view_MotionEvent_nativeGetXPrecision },
799    { "nativeGetYPrecision",
800            "(J)F",
801            (void*)android_view_MotionEvent_nativeGetYPrecision },
802    { "nativeGetDownTimeNanos",
803            "(J)J",
804            (void*)android_view_MotionEvent_nativeGetDownTimeNanos },
805    { "nativeSetDownTimeNanos",
806            "(JJ)V",
807            (void*)android_view_MotionEvent_nativeSetDownTimeNanos },
808    { "nativeGetPointerCount",
809            "(J)I",
810            (void*)android_view_MotionEvent_nativeGetPointerCount },
811    { "nativeGetPointerId",
812            "(JI)I",
813            (void*)android_view_MotionEvent_nativeGetPointerId },
814    { "nativeGetToolType",
815            "(JI)I",
816            (void*)android_view_MotionEvent_nativeGetToolType },
817    { "nativeFindPointerIndex",
818            "(JI)I",
819            (void*)android_view_MotionEvent_nativeFindPointerIndex },
820    { "nativeGetHistorySize",
821            "(J)I",
822            (void*)android_view_MotionEvent_nativeGetHistorySize },
823    { "nativeGetEventTimeNanos",
824            "(JI)J",
825            (void*)android_view_MotionEvent_nativeGetEventTimeNanos },
826    { "nativeGetRawAxisValue",
827            "(JIII)F",
828            (void*)android_view_MotionEvent_nativeGetRawAxisValue },
829    { "nativeGetAxisValue",
830            "(JIII)F",
831            (void*)android_view_MotionEvent_nativeGetAxisValue },
832    { "nativeGetPointerCoords",
833            "(JIILandroid/view/MotionEvent$PointerCoords;)V",
834            (void*)android_view_MotionEvent_nativeGetPointerCoords },
835    { "nativeGetPointerProperties",
836            "(JILandroid/view/MotionEvent$PointerProperties;)V",
837            (void*)android_view_MotionEvent_nativeGetPointerProperties },
838    { "nativeScale",
839            "(JF)V",
840            (void*)android_view_MotionEvent_nativeScale },
841    { "nativeTransform",
842            "(JLandroid/graphics/Matrix;)V",
843            (void*)android_view_MotionEvent_nativeTransform },
844    { "nativeReadFromParcel",
845            "(JLandroid/os/Parcel;)J",
846            (void*)android_view_MotionEvent_nativeReadFromParcel },
847    { "nativeWriteToParcel",
848            "(JLandroid/os/Parcel;)V",
849            (void*)android_view_MotionEvent_nativeWriteToParcel },
850    { "nativeAxisToString", "(I)Ljava/lang/String;",
851            (void*)android_view_MotionEvent_nativeAxisToString },
852    { "nativeAxisFromString", "(Ljava/lang/String;)I",
853            (void*)android_view_MotionEvent_nativeAxisFromString },
854};
855
856#define FIND_CLASS(var, className) \
857        var = env->FindClass(className); \
858        LOG_FATAL_IF(! var, "Unable to find class " className);
859
860#define GET_STATIC_METHOD_ID(var, clazz, methodName, fieldDescriptor) \
861        var = env->GetStaticMethodID(clazz, methodName, fieldDescriptor); \
862        LOG_FATAL_IF(! var, "Unable to find static method" methodName);
863
864#define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \
865        var = env->GetMethodID(clazz, methodName, fieldDescriptor); \
866        LOG_FATAL_IF(! var, "Unable to find method" methodName);
867
868#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
869        var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
870        LOG_FATAL_IF(! var, "Unable to find field " fieldName);
871
872int register_android_view_MotionEvent(JNIEnv* env) {
873    int res = jniRegisterNativeMethods(env, "android/view/MotionEvent",
874            gMotionEventMethods, NELEM(gMotionEventMethods));
875    LOG_FATAL_IF(res < 0, "Unable to register native methods.");
876
877    FIND_CLASS(gMotionEventClassInfo.clazz, "android/view/MotionEvent");
878    gMotionEventClassInfo.clazz = jclass(env->NewGlobalRef(gMotionEventClassInfo.clazz));
879
880    GET_STATIC_METHOD_ID(gMotionEventClassInfo.obtain, gMotionEventClassInfo.clazz,
881            "obtain", "()Landroid/view/MotionEvent;");
882    GET_METHOD_ID(gMotionEventClassInfo.recycle, gMotionEventClassInfo.clazz,
883            "recycle", "()V");
884    GET_FIELD_ID(gMotionEventClassInfo.mNativePtr, gMotionEventClassInfo.clazz,
885            "mNativePtr", "J");
886
887    jclass clazz;
888    FIND_CLASS(clazz, "android/view/MotionEvent$PointerCoords");
889
890    GET_FIELD_ID(gPointerCoordsClassInfo.mPackedAxisBits, clazz,
891            "mPackedAxisBits", "J");
892    GET_FIELD_ID(gPointerCoordsClassInfo.mPackedAxisValues, clazz,
893            "mPackedAxisValues", "[F");
894    GET_FIELD_ID(gPointerCoordsClassInfo.x, clazz,
895            "x", "F");
896    GET_FIELD_ID(gPointerCoordsClassInfo.y, clazz,
897            "y", "F");
898    GET_FIELD_ID(gPointerCoordsClassInfo.pressure, clazz,
899            "pressure", "F");
900    GET_FIELD_ID(gPointerCoordsClassInfo.size, clazz,
901            "size", "F");
902    GET_FIELD_ID(gPointerCoordsClassInfo.touchMajor, clazz,
903            "touchMajor", "F");
904    GET_FIELD_ID(gPointerCoordsClassInfo.touchMinor, clazz,
905            "touchMinor", "F");
906    GET_FIELD_ID(gPointerCoordsClassInfo.toolMajor, clazz,
907            "toolMajor", "F");
908    GET_FIELD_ID(gPointerCoordsClassInfo.toolMinor, clazz,
909            "toolMinor", "F");
910    GET_FIELD_ID(gPointerCoordsClassInfo.orientation, clazz,
911            "orientation", "F");
912
913    FIND_CLASS(clazz, "android/view/MotionEvent$PointerProperties");
914
915    GET_FIELD_ID(gPointerPropertiesClassInfo.id, clazz,
916            "id", "I");
917    GET_FIELD_ID(gPointerPropertiesClassInfo.toolType, clazz,
918            "toolType", "I");
919
920    return 0;
921}
922
923} // namespace android
924