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