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