android_hardware_SensorManager.cpp revision fa2672b71d9501463d8b9de5162f069b9e48384b
1/*
2 * Copyright 2008, 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#define LOG_TAG "SensorManager"
17
18#include "JNIHelp.h"
19#include "android_os_MessageQueue.h"
20#include "core_jni_helpers.h"
21#include "jni.h"
22
23#include <ScopedUtfChars.h>
24#include <ScopedLocalRef.h>
25#include <android_runtime/AndroidRuntime.h>
26#include <gui/Sensor.h>
27#include <gui/SensorEventQueue.h>
28#include <gui/SensorManager.h>
29#include <cutils/native_handle.h>
30#include <utils/Log.h>
31#include <utils/Looper.h>
32#include <utils/Vector.h>
33
34#include <map>
35
36namespace {
37
38using namespace android;
39
40struct {
41    jclass clazz;
42    jmethodID dispatchSensorEvent;
43    jmethodID dispatchFlushCompleteEvent;
44    jmethodID dispatchAdditionalInfoEvent;
45} gBaseEventQueueClassInfo;
46
47struct SensorOffsets
48{
49    jclass      clazz;
50    //fields
51    jfieldID    name;
52    jfieldID    vendor;
53    jfieldID    version;
54    jfieldID    handle;
55    jfieldID    range;
56    jfieldID    resolution;
57    jfieldID    power;
58    jfieldID    minDelay;
59    jfieldID    fifoReservedEventCount;
60    jfieldID    fifoMaxEventCount;
61    jfieldID    stringType;
62    jfieldID    requiredPermission;
63    jfieldID    maxDelay;
64    jfieldID    flags;
65    //methods
66    jmethodID   setType;
67    jmethodID   setUuid;
68    jmethodID   init;
69} gSensorOffsets;
70
71struct ListOffsets {
72    jclass      clazz;
73    jmethodID   add;
74} gListOffsets;
75
76/*
77 * nativeClassInit is not inteneded to be thread-safe. It should be called before other native...
78 * functions (except nativeCreate).
79 */
80static void
81nativeClassInit (JNIEnv *_env, jclass _this)
82{
83    //android.hardware.Sensor
84    SensorOffsets& sensorOffsets = gSensorOffsets;
85    jclass sensorClass = (jclass) _env->NewGlobalRef(_env->FindClass("android/hardware/Sensor"));
86    sensorOffsets.clazz       = sensorClass;
87    sensorOffsets.name        = _env->GetFieldID(sensorClass, "mName",      "Ljava/lang/String;");
88    sensorOffsets.vendor      = _env->GetFieldID(sensorClass, "mVendor",    "Ljava/lang/String;");
89    sensorOffsets.version     = _env->GetFieldID(sensorClass, "mVersion",   "I");
90    sensorOffsets.handle      = _env->GetFieldID(sensorClass, "mHandle",    "I");
91    sensorOffsets.range       = _env->GetFieldID(sensorClass, "mMaxRange",  "F");
92    sensorOffsets.resolution  = _env->GetFieldID(sensorClass, "mResolution","F");
93    sensorOffsets.power       = _env->GetFieldID(sensorClass, "mPower",     "F");
94    sensorOffsets.minDelay    = _env->GetFieldID(sensorClass, "mMinDelay",  "I");
95    sensorOffsets.fifoReservedEventCount =
96            _env->GetFieldID(sensorClass, "mFifoReservedEventCount",  "I");
97    sensorOffsets.fifoMaxEventCount = _env->GetFieldID(sensorClass, "mFifoMaxEventCount",  "I");
98    sensorOffsets.stringType = _env->GetFieldID(sensorClass, "mStringType", "Ljava/lang/String;");
99    sensorOffsets.requiredPermission = _env->GetFieldID(sensorClass, "mRequiredPermission",
100                                                        "Ljava/lang/String;");
101    sensorOffsets.maxDelay    = _env->GetFieldID(sensorClass, "mMaxDelay",  "I");
102    sensorOffsets.flags = _env->GetFieldID(sensorClass, "mFlags",  "I");
103
104    sensorOffsets.setType = _env->GetMethodID(sensorClass, "setType", "(I)Z");
105    sensorOffsets.setUuid = _env->GetMethodID(sensorClass, "setUuid", "(JJ)V");
106    sensorOffsets.init = _env->GetMethodID(sensorClass, "<init>", "()V");
107
108    // java.util.List;
109    ListOffsets& listOffsets = gListOffsets;
110    jclass listClass = (jclass) _env->NewGlobalRef(_env->FindClass("java/util/List"));
111    listOffsets.clazz = listClass;
112    listOffsets.add = _env->GetMethodID(listClass, "add", "(Ljava/lang/Object;)Z");
113}
114
115/**
116 * A key comparator predicate.
117 * It is used to intern strings associated with Sensor data.
118 * It defines a 'Strict weak ordering' for the interned strings.
119 */
120class InternedStringCompare {
121public:
122    bool operator()(const String8* string1, const String8* string2) const {
123        if (string1 == NULL) {
124            return string2 != NULL;
125        }
126        if (string2 == NULL) {
127            return false;
128        }
129        return string1->compare(*string2) < 0;
130    }
131};
132
133/**
134 * A localized interning mechanism for Sensor strings.
135 * We implement our own interning to avoid the overhead of using java.lang.String#intern().
136 * It is common that Vendor, StringType, and RequirePermission data is common between many of the
137 * Sensors, by interning the memory usage to represent Sensors is optimized.
138 */
139static jstring
140getInternedString(JNIEnv *env, const String8* string) {
141    static std::map<const String8*, jstring, InternedStringCompare> internedStrings;
142
143    jstring internedString;
144    std::map<const String8*, jstring>::iterator iterator = internedStrings.find(string);
145    if (iterator != internedStrings.end()) {
146        internedString = iterator->second;
147    } else {
148        jstring localString = env->NewStringUTF(string->string());
149        // we are implementing our own interning so expect these strings to be backed by global refs
150        internedString = (jstring) env->NewGlobalRef(localString);
151        internedStrings.insert(std::make_pair(string, internedString));
152        env->DeleteLocalRef(localString);
153    }
154    return internedString;
155}
156
157static jlong
158nativeCreate
159(JNIEnv *env, jclass clazz, jstring opPackageName)
160{
161    ScopedUtfChars opPackageNameUtf(env, opPackageName);
162    return (jlong) &SensorManager::getInstanceForPackage(String16(opPackageNameUtf.c_str()));
163}
164
165static jobject
166translateNativeSensorToJavaSensor(JNIEnv *env, jobject sensor, const Sensor& nativeSensor) {
167    const SensorOffsets& sensorOffsets(gSensorOffsets);
168
169    if (sensor == NULL) {
170        // Sensor sensor = new Sensor();
171        sensor = env->NewObject(sensorOffsets.clazz, sensorOffsets.init, "");
172    }
173
174    if (sensor != NULL) {
175        jstring name = env->NewStringUTF(nativeSensor.getName().string());
176        jstring vendor = env->NewStringUTF(nativeSensor.getVendor().string());
177        jstring requiredPermission =
178                env->NewStringUTF(nativeSensor.getRequiredPermission().string());
179
180        env->SetObjectField(sensor, sensorOffsets.name,      name);
181        env->SetObjectField(sensor, sensorOffsets.vendor,    vendor);
182        env->SetIntField(sensor, sensorOffsets.version,      nativeSensor.getVersion());
183        env->SetIntField(sensor, sensorOffsets.handle,       nativeSensor.getHandle());
184        env->SetFloatField(sensor, sensorOffsets.range,      nativeSensor.getMaxValue());
185        env->SetFloatField(sensor, sensorOffsets.resolution, nativeSensor.getResolution());
186        env->SetFloatField(sensor, sensorOffsets.power,      nativeSensor.getPowerUsage());
187        env->SetIntField(sensor, sensorOffsets.minDelay,     nativeSensor.getMinDelay());
188        env->SetIntField(sensor, sensorOffsets.fifoReservedEventCount,
189                         nativeSensor.getFifoReservedEventCount());
190        env->SetIntField(sensor, sensorOffsets.fifoMaxEventCount,
191                         nativeSensor.getFifoMaxEventCount());
192        env->SetObjectField(sensor, sensorOffsets.requiredPermission,
193                            requiredPermission);
194        env->SetIntField(sensor, sensorOffsets.maxDelay, nativeSensor.getMaxDelay());
195        env->SetIntField(sensor, sensorOffsets.flags, nativeSensor.getFlags());
196
197        if (env->CallBooleanMethod(sensor, sensorOffsets.setType, nativeSensor.getType())
198                == JNI_FALSE) {
199            jstring stringType = getInternedString(env, &nativeSensor.getStringType());
200            env->SetObjectField(sensor, sensorOffsets.stringType, stringType);
201        }
202
203        // TODO(b/29547335): Rename "setUuid" method to "setId".
204        int64_t id = nativeSensor.getId();
205        env->CallVoidMethod(sensor, sensorOffsets.setUuid, id, 0);
206    }
207    return sensor;
208}
209
210static jboolean
211nativeGetSensorAtIndex(JNIEnv *env, jclass clazz, jlong sensorManager, jobject sensor, jint index)
212{
213    SensorManager* mgr = reinterpret_cast<SensorManager*>(sensorManager);
214
215    Sensor const* const* sensorList;
216    ssize_t count = mgr->getSensorList(&sensorList);
217    if (ssize_t(index) >= count) {
218        return false;
219    }
220
221    return translateNativeSensorToJavaSensor(env, sensor, *sensorList[index]) != NULL;
222}
223
224static void
225nativeGetDynamicSensors(JNIEnv *env, jclass clazz, jlong sensorManager, jobject sensorList) {
226
227    SensorManager* mgr = reinterpret_cast<SensorManager*>(sensorManager);
228    const ListOffsets& listOffsets(gListOffsets);
229
230    Vector<Sensor> nativeList;
231
232    mgr->getDynamicSensorList(nativeList);
233
234    ALOGI("DYNS native SensorManager.getDynamicSensorList return %zu sensors", nativeList.size());
235    for (size_t i = 0; i < nativeList.size(); ++i) {
236        jobject sensor = translateNativeSensorToJavaSensor(env, NULL, nativeList[i]);
237        // add to list
238        env->CallBooleanMethod(sensorList, listOffsets.add, sensor);
239    }
240}
241
242static jboolean nativeIsDataInjectionEnabled(JNIEnv *_env, jclass _this, jlong sensorManager) {
243    SensorManager* mgr = reinterpret_cast<SensorManager*>(sensorManager);
244    return mgr->isDataInjectionEnabled();
245}
246
247static jint nativeCreateDirectChannel(JNIEnv *_env, jclass _this, jlong sensorManager,
248        jlong size, jint channelType, jlongArray channelData) {
249    jint ret = -1;
250    jsize len = _env->GetArrayLength(channelData);
251    if (len > 2) {
252        jlong *data = _env->GetLongArrayElements(channelData, NULL);
253        if (data != nullptr) {
254            // construct native handle from jlong*
255            jlong numFd = data[0];
256            jlong numInt = data[1];
257            if ((numFd + numInt + 2) == len) {
258                native_handle_t *nativeHandle = native_handle_create(numFd, numInt);
259                if (nativeHandle != nullptr) {
260                    const jlong *readPointer = data + 2;
261                    int *writePointer = nativeHandle->data;
262                    size_t n = static_cast<size_t>(numFd + numInt);
263                    while (n--) {
264                        // native type of data is int, jlong is just to ensure Java does not
265                        // truncate data on 64-bit system. The cast here is safe.
266                        *writePointer++ = static_cast<int>(*readPointer++);
267                    }
268
269                    SensorManager* mgr = reinterpret_cast<SensorManager*>(sensorManager);
270                    ret = mgr->createDirectChannel(size, channelType, nativeHandle);
271
272                    // do not native_handle_close() here as handle is owned by java
273                    native_handle_delete(nativeHandle);
274                }
275            }
276            // unidirectional parameter passing, thus JNI_ABORT
277            _env->ReleaseLongArrayElements(channelData, data, JNI_ABORT);
278        }
279    }
280    return ret;
281}
282
283static void nativeDestroyDirectChannel(JNIEnv *_env, jclass _this, jlong sensorManager,
284        jint channelHandle) {
285    SensorManager* mgr = reinterpret_cast<SensorManager*>(sensorManager);
286    mgr->destroyDirectChannel(channelHandle);
287}
288
289static jint nativeConfigDirectChannel(JNIEnv *_env, jclass _this, jlong sensorManager,
290        jint channelHandle, jint sensorHandle, jint rate) {
291    SensorManager* mgr = reinterpret_cast<SensorManager*>(sensorManager);
292    return mgr->configureDirectChannel(channelHandle, sensorHandle, rate);
293}
294
295//----------------------------------------------------------------------------
296
297class Receiver : public LooperCallback {
298    sp<SensorEventQueue> mSensorQueue;
299    sp<MessageQueue> mMessageQueue;
300    jobject mReceiverWeakGlobal;
301    jfloatArray mFloatScratch;
302    jintArray   mIntScratch;
303public:
304    Receiver(const sp<SensorEventQueue>& sensorQueue,
305            const sp<MessageQueue>& messageQueue,
306            jobject receiverWeak) {
307        JNIEnv* env = AndroidRuntime::getJNIEnv();
308        mSensorQueue = sensorQueue;
309        mMessageQueue = messageQueue;
310        mReceiverWeakGlobal = env->NewGlobalRef(receiverWeak);
311
312        mIntScratch = (jintArray) env->NewGlobalRef(env->NewIntArray(16));
313        mFloatScratch = (jfloatArray) env->NewGlobalRef(env->NewFloatArray(16));
314    }
315    ~Receiver() {
316        JNIEnv* env = AndroidRuntime::getJNIEnv();
317        env->DeleteGlobalRef(mReceiverWeakGlobal);
318        env->DeleteGlobalRef(mFloatScratch);
319        env->DeleteGlobalRef(mIntScratch);
320    }
321    sp<SensorEventQueue> getSensorEventQueue() const {
322        return mSensorQueue;
323    }
324
325    void destroy() {
326        mMessageQueue->getLooper()->removeFd( mSensorQueue->getFd() );
327    }
328
329private:
330    virtual void onFirstRef() {
331        LooperCallback::onFirstRef();
332        mMessageQueue->getLooper()->addFd(mSensorQueue->getFd(), 0,
333                ALOOPER_EVENT_INPUT, this, mSensorQueue.get());
334    }
335
336    virtual int handleEvent(int fd, int events, void* data) {
337        JNIEnv* env = AndroidRuntime::getJNIEnv();
338        sp<SensorEventQueue> q = reinterpret_cast<SensorEventQueue *>(data);
339        ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal));
340
341        ssize_t n;
342        ASensorEvent buffer[16];
343        while ((n = q->read(buffer, 16)) > 0) {
344            for (int i=0 ; i<n ; i++) {
345                if (buffer[i].type == SENSOR_TYPE_STEP_COUNTER) {
346                    // step-counter returns a uint64, but the java API only deals with floats
347                    float value = float(buffer[i].u64.step_counter);
348                    env->SetFloatArrayRegion(mFloatScratch, 0, 1, &value);
349                } else if (buffer[i].type == SENSOR_TYPE_DYNAMIC_SENSOR_META) {
350                    float value[2];
351                    value[0] = buffer[i].dynamic_sensor_meta.connected ? 1.f: 0.f;
352                    value[1] = float(buffer[i].dynamic_sensor_meta.handle);
353                    env->SetFloatArrayRegion(mFloatScratch, 0, 2, value);
354                } else if (buffer[i].type == SENSOR_TYPE_ADDITIONAL_INFO) {
355                    env->SetIntArrayRegion(mIntScratch, 0, 14,
356                                           buffer[i].additional_info.data_int32);
357                    env->SetFloatArrayRegion(mFloatScratch, 0, 14,
358                                             buffer[i].additional_info.data_float);
359                } else {
360                    env->SetFloatArrayRegion(mFloatScratch, 0, 16, buffer[i].data);
361                }
362
363                if (buffer[i].type == SENSOR_TYPE_META_DATA) {
364                    // This is a flush complete sensor event. Call dispatchFlushCompleteEvent
365                    // method.
366                    if (receiverObj.get()) {
367                        env->CallVoidMethod(receiverObj.get(),
368                                            gBaseEventQueueClassInfo.dispatchFlushCompleteEvent,
369                                            buffer[i].meta_data.sensor);
370                    }
371                } else if (buffer[i].type == SENSOR_TYPE_ADDITIONAL_INFO) {
372                    // This is a flush complete sensor event. Call dispatchAdditionalInfoEvent
373                    // method.
374                    if (receiverObj.get()) {
375                        int type = buffer[i].additional_info.type;
376                        int serial = buffer[i].additional_info.serial;
377                        env->CallVoidMethod(receiverObj.get(),
378                                            gBaseEventQueueClassInfo.dispatchAdditionalInfoEvent,
379                                            buffer[i].sensor,
380                                            type, serial,
381                                            mFloatScratch,
382                                            mIntScratch,
383                                            buffer[i].timestamp);
384                    }
385                }else {
386                    int8_t status;
387                    switch (buffer[i].type) {
388                    case SENSOR_TYPE_ORIENTATION:
389                    case SENSOR_TYPE_MAGNETIC_FIELD:
390                    case SENSOR_TYPE_ACCELEROMETER:
391                    case SENSOR_TYPE_GYROSCOPE:
392                    case SENSOR_TYPE_GRAVITY:
393                    case SENSOR_TYPE_LINEAR_ACCELERATION:
394                        status = buffer[i].vector.status;
395                        break;
396                    case SENSOR_TYPE_HEART_RATE:
397                        status = buffer[i].heart_rate.status;
398                        break;
399                    default:
400                        status = SENSOR_STATUS_ACCURACY_HIGH;
401                        break;
402                    }
403                    if (receiverObj.get()) {
404                        env->CallVoidMethod(receiverObj.get(),
405                                            gBaseEventQueueClassInfo.dispatchSensorEvent,
406                                            buffer[i].sensor,
407                                            mFloatScratch,
408                                            status,
409                                            buffer[i].timestamp);
410                    }
411                }
412                if (env->ExceptionCheck()) {
413                    mSensorQueue->sendAck(buffer, n);
414                    ALOGE("Exception dispatching input event.");
415                    return 1;
416                }
417            }
418            mSensorQueue->sendAck(buffer, n);
419        }
420        if (n<0 && n != -EAGAIN) {
421            // FIXME: error receiving events, what to do in this case?
422        }
423        return 1;
424    }
425};
426
427static jlong nativeInitSensorEventQueue(JNIEnv *env, jclass clazz, jlong sensorManager,
428        jobject eventQWeak, jobject msgQ, jstring packageName, jint mode) {
429    SensorManager* mgr = reinterpret_cast<SensorManager*>(sensorManager);
430    ScopedUtfChars packageUtf(env, packageName);
431    String8 clientName(packageUtf.c_str());
432    sp<SensorEventQueue> queue(mgr->createEventQueue(clientName, mode));
433
434    sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, msgQ);
435    if (messageQueue == NULL) {
436        jniThrowRuntimeException(env, "MessageQueue is not initialized.");
437        return 0;
438    }
439
440    sp<Receiver> receiver = new Receiver(queue, messageQueue, eventQWeak);
441    receiver->incStrong((void*)nativeInitSensorEventQueue);
442    return jlong(receiver.get());
443}
444
445static jint nativeEnableSensor(JNIEnv *env, jclass clazz, jlong eventQ, jint handle, jint rate_us,
446                               jint maxBatchReportLatency) {
447    sp<Receiver> receiver(reinterpret_cast<Receiver *>(eventQ));
448    return receiver->getSensorEventQueue()->enableSensor(handle, rate_us, maxBatchReportLatency,
449                                                         0);
450}
451
452static jint nativeDisableSensor(JNIEnv *env, jclass clazz, jlong eventQ, jint handle) {
453    sp<Receiver> receiver(reinterpret_cast<Receiver *>(eventQ));
454    return receiver->getSensorEventQueue()->disableSensor(handle);
455}
456
457static void nativeDestroySensorEventQueue(JNIEnv *env, jclass clazz, jlong eventQ) {
458    sp<Receiver> receiver(reinterpret_cast<Receiver *>(eventQ));
459    receiver->destroy();
460    receiver->decStrong((void*)nativeInitSensorEventQueue);
461}
462
463static jint nativeFlushSensor(JNIEnv *env, jclass clazz, jlong eventQ) {
464    sp<Receiver> receiver(reinterpret_cast<Receiver *>(eventQ));
465    return receiver->getSensorEventQueue()->flush();
466}
467
468static jint nativeInjectSensorData(JNIEnv *env, jclass clazz, jlong eventQ, jint handle,
469        jfloatArray values, jint accuracy, jlong timestamp) {
470    sp<Receiver> receiver(reinterpret_cast<Receiver *>(eventQ));
471    // Create a sensor_event from the above data which can be injected into the HAL.
472    ASensorEvent sensor_event;
473    memset(&sensor_event, 0, sizeof(sensor_event));
474    sensor_event.sensor = handle;
475    sensor_event.timestamp = timestamp;
476    env->GetFloatArrayRegion(values, 0, env->GetArrayLength(values), sensor_event.data);
477    return receiver->getSensorEventQueue()->injectSensorEvent(sensor_event);
478}
479//----------------------------------------------------------------------------
480
481static const JNINativeMethod gSystemSensorManagerMethods[] = {
482    {"nativeClassInit",
483            "()V",
484            (void*)nativeClassInit },
485    {"nativeCreate",
486             "(Ljava/lang/String;)J",
487             (void*)nativeCreate },
488
489    {"nativeGetSensorAtIndex",
490            "(JLandroid/hardware/Sensor;I)Z",
491            (void*)nativeGetSensorAtIndex },
492
493    {"nativeGetDynamicSensors",
494            "(JLjava/util/List;)V",
495            (void*)nativeGetDynamicSensors },
496
497    {"nativeIsDataInjectionEnabled",
498            "(J)Z",
499            (void*)nativeIsDataInjectionEnabled },
500
501    {"nativeCreateDirectChannel",
502            "(JJI[J)I",
503            (void*)nativeCreateDirectChannel },
504
505    {"nativeDestroyDirectChannel",
506            "(JI)V",
507            (void*)nativeDestroyDirectChannel },
508
509    {"nativeConfigDirectChannel",
510            "(JIII)I",
511            (void*)nativeConfigDirectChannel },
512};
513
514static const JNINativeMethod gBaseEventQueueMethods[] = {
515    {"nativeInitBaseEventQueue",
516             "(JLjava/lang/ref/WeakReference;Landroid/os/MessageQueue;Ljava/lang/String;ILjava/lang/String;)J",
517             (void*)nativeInitSensorEventQueue },
518
519    {"nativeEnableSensor",
520            "(JIII)I",
521            (void*)nativeEnableSensor },
522
523    {"nativeDisableSensor",
524            "(JI)I",
525            (void*)nativeDisableSensor },
526
527    {"nativeDestroySensorEventQueue",
528            "(J)V",
529            (void*)nativeDestroySensorEventQueue },
530
531    {"nativeFlushSensor",
532            "(J)I",
533            (void*)nativeFlushSensor },
534
535    {"nativeInjectSensorData",
536            "(JI[FIJ)I",
537            (void*)nativeInjectSensorData },
538};
539
540} //unnamed namespace
541
542int register_android_hardware_SensorManager(JNIEnv *env)
543{
544    RegisterMethodsOrDie(env, "android/hardware/SystemSensorManager",
545            gSystemSensorManagerMethods, NELEM(gSystemSensorManagerMethods));
546
547    RegisterMethodsOrDie(env, "android/hardware/SystemSensorManager$BaseEventQueue",
548            gBaseEventQueueMethods, NELEM(gBaseEventQueueMethods));
549
550    gBaseEventQueueClassInfo.clazz = FindClassOrDie(env,
551            "android/hardware/SystemSensorManager$BaseEventQueue");
552
553    gBaseEventQueueClassInfo.dispatchSensorEvent = GetMethodIDOrDie(env,
554            gBaseEventQueueClassInfo.clazz, "dispatchSensorEvent", "(I[FIJ)V");
555
556    gBaseEventQueueClassInfo.dispatchFlushCompleteEvent = GetMethodIDOrDie(env,
557            gBaseEventQueueClassInfo.clazz, "dispatchFlushCompleteEvent", "(I)V");
558
559    gBaseEventQueueClassInfo.dispatchAdditionalInfoEvent = GetMethodIDOrDie(env,
560            gBaseEventQueueClassInfo.clazz, "dispatchAdditionalInfoEvent", "(III[F[I)V");
561
562    return 0;
563}
564