• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 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 
17 //#define LOG_NDEBUG 0
18 
19 #define LOG_TAG "AudioRecord-JNI"
20 
21 #include <inttypes.h>
22 #include <jni.h>
23 #include <nativehelper/JNIHelp.h>
24 #include "core_jni_helpers.h"
25 
26 #include <utils/Log.h>
27 #include <media/AudioRecord.h>
28 #include <media/MicrophoneInfo.h>
29 #include <vector>
30 
31 #include <android/content/AttributionSourceState.h>
32 #include <android_os_Parcel.h>
33 
34 #include <nativehelper/ScopedUtfChars.h>
35 
36 #include "android_media_AudioFormat.h"
37 #include "android_media_AudioErrors.h"
38 #include "android_media_DeviceCallback.h"
39 #include "android_media_MediaMetricsJNI.h"
40 #include "android_media_MicrophoneInfo.h"
41 #include "android_media_AudioAttributes.h"
42 
43 
44 // ----------------------------------------------------------------------------
45 
46 using namespace android;
47 
48 // ----------------------------------------------------------------------------
49 static const char* const kClassPathName = "android/media/AudioRecord";
50 
51 static jclass gArrayListClass;
52 static struct {
53     jmethodID add;
54 } gArrayListMethods;
55 
56 struct audio_record_fields_t {
57     // these fields provide access from C++ to the...
58     jmethodID postNativeEventInJava; //... event post callback method
59     jfieldID  nativeRecorderInJavaObj; // provides access to the C++ AudioRecord object
60     jfieldID  nativeCallbackCookie;    // provides access to the AudioRecord callback data
61     jfieldID  nativeDeviceCallback;    // provides access to the JNIDeviceCallback instance
62 };
63 static audio_record_fields_t     javaAudioRecordFields;
64 static struct {
65     jfieldID  fieldFramePosition;     // AudioTimestamp.framePosition
66     jfieldID  fieldNanoTime;          // AudioTimestamp.nanoTime
67 } javaAudioTimestampFields;
68 
69 struct audiorecord_callback_cookie {
70     jclass      audioRecord_class;
71     jobject     audioRecord_ref;
72     bool        busy;
73     Condition   cond;
74 };
75 
76 static Mutex sLock;
77 static SortedVector <audiorecord_callback_cookie *> sAudioRecordCallBackCookies;
78 
79 // ----------------------------------------------------------------------------
80 
81 #define AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT      (-16)
82 #define AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK  (-17)
83 #define AUDIORECORD_ERROR_SETUP_INVALIDFORMAT       (-18)
84 #define AUDIORECORD_ERROR_SETUP_INVALIDSOURCE       (-19)
85 #define AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED    (-20)
86 
87 // ----------------------------------------------------------------------------
recorderCallback(int event,void * user,void * info)88 static void recorderCallback(int event, void* user, void *info) {
89 
90     audiorecord_callback_cookie *callbackInfo = (audiorecord_callback_cookie *)user;
91     {
92         Mutex::Autolock l(sLock);
93         if (sAudioRecordCallBackCookies.indexOf(callbackInfo) < 0) {
94             return;
95         }
96         callbackInfo->busy = true;
97     }
98 
99     switch (event) {
100     case AudioRecord::EVENT_MARKER: {
101         JNIEnv *env = AndroidRuntime::getJNIEnv();
102         if (user != NULL && env != NULL) {
103             env->CallStaticVoidMethod(
104                 callbackInfo->audioRecord_class,
105                 javaAudioRecordFields.postNativeEventInJava,
106                 callbackInfo->audioRecord_ref, event, 0,0, NULL);
107             if (env->ExceptionCheck()) {
108                 env->ExceptionDescribe();
109                 env->ExceptionClear();
110             }
111         }
112         } break;
113 
114     case AudioRecord::EVENT_NEW_POS: {
115         JNIEnv *env = AndroidRuntime::getJNIEnv();
116         if (user != NULL && env != NULL) {
117             env->CallStaticVoidMethod(
118                 callbackInfo->audioRecord_class,
119                 javaAudioRecordFields.postNativeEventInJava,
120                 callbackInfo->audioRecord_ref, event, 0,0, NULL);
121             if (env->ExceptionCheck()) {
122                 env->ExceptionDescribe();
123                 env->ExceptionClear();
124             }
125         }
126         } break;
127     }
128 
129     {
130         Mutex::Autolock l(sLock);
131         callbackInfo->busy = false;
132         callbackInfo->cond.broadcast();
133     }
134 }
135 
getJniDeviceCallback(JNIEnv * env,jobject thiz)136 static sp<JNIDeviceCallback> getJniDeviceCallback(JNIEnv* env, jobject thiz)
137 {
138     Mutex::Autolock l(sLock);
139     JNIDeviceCallback* const cb =
140             (JNIDeviceCallback*)env->GetLongField(thiz,
141                                                   javaAudioRecordFields.nativeDeviceCallback);
142     return sp<JNIDeviceCallback>(cb);
143 }
144 
setJniDeviceCallback(JNIEnv * env,jobject thiz,const sp<JNIDeviceCallback> & cb)145 static sp<JNIDeviceCallback> setJniDeviceCallback(JNIEnv* env,
146                                                   jobject thiz,
147                                                   const sp<JNIDeviceCallback>& cb)
148 {
149     Mutex::Autolock l(sLock);
150     sp<JNIDeviceCallback> old =
151             (JNIDeviceCallback*)env->GetLongField(thiz,
152                                                   javaAudioRecordFields.nativeDeviceCallback);
153     if (cb.get()) {
154         cb->incStrong((void*)setJniDeviceCallback);
155     }
156     if (old != 0) {
157         old->decStrong((void*)setJniDeviceCallback);
158     }
159     env->SetLongField(thiz, javaAudioRecordFields.nativeDeviceCallback, (jlong)cb.get());
160     return old;
161 }
162 
163 // ----------------------------------------------------------------------------
getAudioRecord(JNIEnv * env,jobject thiz)164 static sp<AudioRecord> getAudioRecord(JNIEnv* env, jobject thiz)
165 {
166     Mutex::Autolock l(sLock);
167     AudioRecord* const ar =
168             (AudioRecord*)env->GetLongField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
169     return sp<AudioRecord>(ar);
170 }
171 
setAudioRecord(JNIEnv * env,jobject thiz,const sp<AudioRecord> & ar)172 static sp<AudioRecord> setAudioRecord(JNIEnv* env, jobject thiz, const sp<AudioRecord>& ar)
173 {
174     Mutex::Autolock l(sLock);
175     sp<AudioRecord> old =
176             (AudioRecord*)env->GetLongField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
177     if (ar.get()) {
178         ar->incStrong((void*)setAudioRecord);
179     }
180     if (old != 0) {
181         old->decStrong((void*)setAudioRecord);
182     }
183     env->SetLongField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, (jlong)ar.get());
184     return old;
185 }
186 
187 // ----------------------------------------------------------------------------
android_media_AudioRecord_setup(JNIEnv * env,jobject thiz,jobject weak_this,jobject jaa,jintArray jSampleRate,jint channelMask,jint channelIndexMask,jint audioFormat,jint buffSizeInBytes,jintArray jSession,jobject jAttributionSource,jlong nativeRecordInJavaObj,jint sharedAudioHistoryMs)188 static jint android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this,
189                                             jobject jaa, jintArray jSampleRate, jint channelMask,
190                                             jint channelIndexMask, jint audioFormat,
191                                             jint buffSizeInBytes, jintArray jSession,
192                                             jobject jAttributionSource, jlong nativeRecordInJavaObj,
193                                             jint sharedAudioHistoryMs) {
194     //ALOGV(">> Entering android_media_AudioRecord_setup");
195     //ALOGV("sampleRate=%d, audioFormat=%d, channel mask=%x, buffSizeInBytes=%d "
196     //     "nativeRecordInJavaObj=0x%llX",
197     //     sampleRateInHertz, audioFormat, channelMask, buffSizeInBytes, nativeRecordInJavaObj);
198     audio_channel_mask_t localChanMask = inChannelMaskToNative(channelMask);
199 
200     if (jSession == NULL) {
201         ALOGE("Error creating AudioRecord: invalid session ID pointer");
202         return (jint) AUDIO_JAVA_ERROR;
203     }
204 
205     jint* nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
206     if (nSession == NULL) {
207         ALOGE("Error creating AudioRecord: Error retrieving session id pointer");
208         return (jint) AUDIO_JAVA_ERROR;
209     }
210     audio_session_t sessionId = (audio_session_t) nSession[0];
211     env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
212     nSession = NULL;
213 
214     sp<AudioRecord> lpRecorder = 0;
215     audiorecord_callback_cookie *lpCallbackData = NULL;
216 
217     jclass clazz = env->GetObjectClass(thiz);
218     if (clazz == NULL) {
219         ALOGE("Can't find %s when setting up callback.", kClassPathName);
220         return (jint) AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
221     }
222 
223     // if we pass in an existing *Native* AudioRecord, we don't need to create/initialize one.
224     if (nativeRecordInJavaObj == 0) {
225         if (jaa == 0) {
226             ALOGE("Error creating AudioRecord: invalid audio attributes");
227             return (jint) AUDIO_JAVA_ERROR;
228         }
229 
230         if (jSampleRate == 0) {
231             ALOGE("Error creating AudioRecord: invalid sample rates");
232             return (jint) AUDIO_JAVA_ERROR;
233         }
234         jint elements[1];
235         env->GetIntArrayRegion(jSampleRate, 0, 1, elements);
236         int sampleRateInHertz = elements[0];
237 
238         // channel index mask takes priority over channel position masks.
239         if (channelIndexMask) {
240             // Java channel index masks need the representation bits set.
241             localChanMask = audio_channel_mask_from_representation_and_bits(
242                     AUDIO_CHANNEL_REPRESENTATION_INDEX,
243                     channelIndexMask);
244         }
245         // Java channel position masks map directly to the native definition
246 
247         if (!audio_is_input_channel(localChanMask)) {
248             ALOGE("Error creating AudioRecord: channel mask %#x is not valid.", localChanMask);
249             return (jint) AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK;
250         }
251         uint32_t channelCount = audio_channel_count_from_in_mask(localChanMask);
252 
253         // compare the format against the Java constants
254         audio_format_t format = audioFormatToNative(audioFormat);
255         if (format == AUDIO_FORMAT_INVALID) {
256             ALOGE("Error creating AudioRecord: unsupported audio format %d.", audioFormat);
257             return (jint) AUDIORECORD_ERROR_SETUP_INVALIDFORMAT;
258         }
259 
260         size_t bytesPerSample = audio_bytes_per_sample(format);
261 
262         if (buffSizeInBytes == 0) {
263             ALOGE("Error creating AudioRecord: frameCount is 0.");
264             return (jint) AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT;
265         }
266         size_t frameSize = channelCount * bytesPerSample;
267         size_t frameCount = buffSizeInBytes / frameSize;
268 
269         // create an uninitialized AudioRecord object
270         Parcel* parcel = parcelForJavaObject(env, jAttributionSource);
271         android::content::AttributionSourceState attributionSource;
272         attributionSource.readFromParcel(parcel);
273 
274         lpRecorder = new AudioRecord(attributionSource);
275 
276         // read the AudioAttributes values
277         auto paa = JNIAudioAttributeHelper::makeUnique();
278         jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jaa, paa.get());
279         if (jStatus != (jint)AUDIO_JAVA_SUCCESS) {
280             return jStatus;
281         }
282         ALOGV("AudioRecord_setup for source=%d tags=%s flags=%08x", paa->source, paa->tags, paa->flags);
283 
284         audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE;
285         if (paa->flags & AUDIO_FLAG_HW_HOTWORD) {
286             flags = AUDIO_INPUT_FLAG_HW_HOTWORD;
287         }
288         // create the callback information:
289         // this data will be passed with every AudioRecord callback
290         lpCallbackData = new audiorecord_callback_cookie;
291         lpCallbackData->audioRecord_class = (jclass)env->NewGlobalRef(clazz);
292         // we use a weak reference so the AudioRecord object can be garbage collected.
293         lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this);
294         lpCallbackData->busy = false;
295 
296         const status_t status =
297                 lpRecorder->set(paa->source, sampleRateInHertz,
298                                 format, // word length, PCM
299                                 localChanMask, frameCount,
300                                 recorderCallback, // callback_t
301                                 lpCallbackData,   // void* user
302                                 0,                // notificationFrames,
303                                 true,             // threadCanCallJava
304                                 sessionId, AudioRecord::TRANSFER_DEFAULT, flags, -1,
305                                 -1, // default uid, pid
306                                 paa.get(), AUDIO_PORT_HANDLE_NONE, MIC_DIRECTION_UNSPECIFIED,
307                                 MIC_FIELD_DIMENSION_DEFAULT, sharedAudioHistoryMs);
308 
309         if (status != NO_ERROR) {
310             ALOGE("Error creating AudioRecord instance: initialization check failed with status %d.",
311                     status);
312             goto native_init_failure;
313         }
314         // Set caller name so it can be logged in destructor.
315         // MediaMetricsConstants.h: AMEDIAMETRICS_PROP_CALLERNAME_VALUE_JAVA
316         lpRecorder->setCallerName("java");
317     } else { // end if nativeRecordInJavaObj == 0)
318         lpRecorder = (AudioRecord*)nativeRecordInJavaObj;
319         // TODO: We need to find out which members of the Java AudioRecord might need to be
320         // initialized from the Native AudioRecord
321         // these are directly returned from getters:
322         //  mSampleRate
323         //  mRecordSource
324         //  mAudioFormat
325         //  mChannelMask
326         //  mChannelCount
327         //  mState (?)
328         //  mRecordingState (?)
329         //  mPreferredDevice
330 
331         // create the callback information:
332         // this data will be passed with every AudioRecord callback
333         lpCallbackData = new audiorecord_callback_cookie;
334         lpCallbackData->audioRecord_class = (jclass)env->NewGlobalRef(clazz);
335         // we use a weak reference so the AudioRecord object can be garbage collected.
336         lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this);
337         lpCallbackData->busy = false;
338     }
339 
340     nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
341     if (nSession == NULL) {
342         ALOGE("Error creating AudioRecord: Error retrieving session id pointer");
343         goto native_init_failure;
344     }
345     // read the audio session ID back from AudioRecord in case a new session was created during set()
346     nSession[0] = lpRecorder->getSessionId();
347     env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
348     nSession = NULL;
349 
350     {
351         const jint elements[1] = { (jint) lpRecorder->getSampleRate() };
352         env->SetIntArrayRegion(jSampleRate, 0, 1, elements);
353     }
354 
355     {   // scope for the lock
356         Mutex::Autolock l(sLock);
357         sAudioRecordCallBackCookies.add(lpCallbackData);
358     }
359     // save our newly created C++ AudioRecord in the "nativeRecorderInJavaObj" field
360     // of the Java object
361     setAudioRecord(env, thiz, lpRecorder);
362 
363     // save our newly created callback information in the "nativeCallbackCookie" field
364     // of the Java object (in mNativeCallbackCookie) so we can free the memory in finalize()
365     env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, (jlong)lpCallbackData);
366 
367     return (jint) AUDIO_JAVA_SUCCESS;
368 
369     // failure:
370 native_init_failure:
371     env->DeleteGlobalRef(lpCallbackData->audioRecord_class);
372     env->DeleteGlobalRef(lpCallbackData->audioRecord_ref);
373     delete lpCallbackData;
374     env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0);
375 
376     // lpRecorder goes out of scope, so reference count drops to zero
377     return (jint) AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
378 }
379 
380 // ----------------------------------------------------------------------------
381 static jint
android_media_AudioRecord_start(JNIEnv * env,jobject thiz,jint event,jint triggerSession)382 android_media_AudioRecord_start(JNIEnv *env, jobject thiz, jint event, jint triggerSession)
383 {
384     sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
385     if (lpRecorder == NULL ) {
386         jniThrowException(env, "java/lang/IllegalStateException", NULL);
387         return (jint) AUDIO_JAVA_ERROR;
388     }
389 
390     return nativeToJavaStatus(
391             lpRecorder->start((AudioSystem::sync_event_t)event, (audio_session_t) triggerSession));
392 }
393 
394 
395 // ----------------------------------------------------------------------------
396 static void
android_media_AudioRecord_stop(JNIEnv * env,jobject thiz)397 android_media_AudioRecord_stop(JNIEnv *env, jobject thiz)
398 {
399     sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
400     if (lpRecorder == NULL ) {
401         jniThrowException(env, "java/lang/IllegalStateException", NULL);
402         return;
403     }
404 
405     lpRecorder->stop();
406     //ALOGV("Called lpRecorder->stop()");
407 }
408 
409 
410 // ----------------------------------------------------------------------------
411 
412 #define CALLBACK_COND_WAIT_TIMEOUT_MS 1000
android_media_AudioRecord_release(JNIEnv * env,jobject thiz)413 static void android_media_AudioRecord_release(JNIEnv *env,  jobject thiz) {
414     sp<AudioRecord> lpRecorder = setAudioRecord(env, thiz, 0);
415     if (lpRecorder == NULL) {
416         return;
417     }
418     ALOGV("About to delete lpRecorder: %p", lpRecorder.get());
419     lpRecorder->stop();
420 
421     audiorecord_callback_cookie *lpCookie = (audiorecord_callback_cookie *)env->GetLongField(
422         thiz, javaAudioRecordFields.nativeCallbackCookie);
423 
424     // reset the native resources in the Java object so any attempt to access
425     // them after a call to release fails.
426     env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0);
427 
428     // delete the callback information
429     if (lpCookie) {
430         Mutex::Autolock l(sLock);
431         ALOGV("deleting lpCookie: %p", lpCookie);
432         while (lpCookie->busy) {
433             if (lpCookie->cond.waitRelative(sLock,
434                                             milliseconds(CALLBACK_COND_WAIT_TIMEOUT_MS)) !=
435                                                     NO_ERROR) {
436                 break;
437             }
438         }
439         sAudioRecordCallBackCookies.remove(lpCookie);
440         env->DeleteGlobalRef(lpCookie->audioRecord_class);
441         env->DeleteGlobalRef(lpCookie->audioRecord_ref);
442         delete lpCookie;
443     }
444 }
445 
446 
447 // ----------------------------------------------------------------------------
android_media_AudioRecord_finalize(JNIEnv * env,jobject thiz)448 static void android_media_AudioRecord_finalize(JNIEnv *env,  jobject thiz) {
449     android_media_AudioRecord_release(env, thiz);
450 }
451 
452 // overloaded JNI array helper functions
453 static inline
envGetArrayElements(JNIEnv * env,jbyteArray array,jboolean * isCopy)454 jbyte *envGetArrayElements(JNIEnv *env, jbyteArray array, jboolean *isCopy) {
455     return env->GetByteArrayElements(array, isCopy);
456 }
457 
458 static inline
envReleaseArrayElements(JNIEnv * env,jbyteArray array,jbyte * elems,jint mode)459 void envReleaseArrayElements(JNIEnv *env, jbyteArray array, jbyte *elems, jint mode) {
460     env->ReleaseByteArrayElements(array, elems, mode);
461 }
462 
463 static inline
envGetArrayElements(JNIEnv * env,jshortArray array,jboolean * isCopy)464 jshort *envGetArrayElements(JNIEnv *env, jshortArray array, jboolean *isCopy) {
465     return env->GetShortArrayElements(array, isCopy);
466 }
467 
468 static inline
envReleaseArrayElements(JNIEnv * env,jshortArray array,jshort * elems,jint mode)469 void envReleaseArrayElements(JNIEnv *env, jshortArray array, jshort *elems, jint mode) {
470     env->ReleaseShortArrayElements(array, elems, mode);
471 }
472 
473 static inline
envGetArrayElements(JNIEnv * env,jfloatArray array,jboolean * isCopy)474 jfloat *envGetArrayElements(JNIEnv *env, jfloatArray array, jboolean *isCopy) {
475     return env->GetFloatArrayElements(array, isCopy);
476 }
477 
478 static inline
envReleaseArrayElements(JNIEnv * env,jfloatArray array,jfloat * elems,jint mode)479 void envReleaseArrayElements(JNIEnv *env, jfloatArray array, jfloat *elems, jint mode) {
480     env->ReleaseFloatArrayElements(array, elems, mode);
481 }
482 
483 static inline
interpretReadSizeError(ssize_t readSize)484 jint interpretReadSizeError(ssize_t readSize) {
485     if (readSize == WOULD_BLOCK) {
486         return (jint)0;
487     } else if (readSize == NO_INIT) {
488         return AUDIO_JAVA_DEAD_OBJECT;
489     } else {
490         ALOGE("Error %zd during AudioRecord native read", readSize);
491         return nativeToJavaStatus(readSize);
492     }
493 }
494 
495 template <typename T>
android_media_AudioRecord_readInArray(JNIEnv * env,jobject thiz,T javaAudioData,jint offsetInSamples,jint sizeInSamples,jboolean isReadBlocking)496 static jint android_media_AudioRecord_readInArray(JNIEnv *env,  jobject thiz,
497                                                   T javaAudioData,
498                                                   jint offsetInSamples, jint sizeInSamples,
499                                                   jboolean isReadBlocking) {
500     // get the audio recorder from which we'll read new audio samples
501     sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
502     if (lpRecorder == NULL) {
503         ALOGE("Unable to retrieve AudioRecord object");
504         return (jint)AUDIO_JAVA_INVALID_OPERATION;
505     }
506 
507     if (javaAudioData == NULL) {
508         ALOGE("Invalid Java array to store recorded audio");
509         return (jint)AUDIO_JAVA_BAD_VALUE;
510     }
511 
512     // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such
513     // a way that it becomes much more efficient. When doing so, we will have to prevent the
514     // AudioSystem callback to be called while in critical section (in case of media server
515     // process crash for instance)
516 
517     // get the pointer to where we'll record the audio
518     auto *recordBuff = envGetArrayElements(env, javaAudioData, NULL);
519     if (recordBuff == NULL) {
520         ALOGE("Error retrieving destination for recorded audio data");
521         return (jint)AUDIO_JAVA_BAD_VALUE;
522     }
523 
524     // read the new audio data from the native AudioRecord object
525     const size_t sizeInBytes = sizeInSamples * sizeof(*recordBuff);
526     ssize_t readSize = lpRecorder->read(
527             recordBuff + offsetInSamples, sizeInBytes, isReadBlocking == JNI_TRUE /* blocking */);
528 
529     envReleaseArrayElements(env, javaAudioData, recordBuff, 0);
530 
531     if (readSize < 0) {
532         return interpretReadSizeError(readSize);
533     }
534     return (jint)(readSize / sizeof(*recordBuff));
535 }
536 
537 // ----------------------------------------------------------------------------
android_media_AudioRecord_readInDirectBuffer(JNIEnv * env,jobject thiz,jobject jBuffer,jint sizeInBytes,jboolean isReadBlocking)538 static jint android_media_AudioRecord_readInDirectBuffer(JNIEnv *env,  jobject thiz,
539                                                          jobject jBuffer, jint sizeInBytes,
540                                                          jboolean isReadBlocking) {
541     // get the audio recorder from which we'll read new audio samples
542     sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
543     if (lpRecorder==NULL)
544         return (jint)AUDIO_JAVA_INVALID_OPERATION;
545 
546     // direct buffer and direct access supported?
547     long capacity = env->GetDirectBufferCapacity(jBuffer);
548     if (capacity == -1) {
549         // buffer direct access is not supported
550         ALOGE("Buffer direct access is not supported, can't record");
551         return (jint)AUDIO_JAVA_BAD_VALUE;
552     }
553     //ALOGV("capacity = %ld", capacity);
554     jbyte* nativeFromJavaBuf = (jbyte*) env->GetDirectBufferAddress(jBuffer);
555     if (nativeFromJavaBuf==NULL) {
556         ALOGE("Buffer direct access is not supported, can't record");
557         return (jint)AUDIO_JAVA_BAD_VALUE;
558     }
559 
560     // read new data from the recorder
561     ssize_t readSize = lpRecorder->read(nativeFromJavaBuf,
562                                         capacity < sizeInBytes ? capacity : sizeInBytes,
563                                         isReadBlocking == JNI_TRUE /* blocking */);
564     if (readSize < 0) {
565         return interpretReadSizeError(readSize);
566     }
567     return (jint)readSize;
568 }
569 
570 // ----------------------------------------------------------------------------
android_media_AudioRecord_get_buffer_size_in_frames(JNIEnv * env,jobject thiz)571 static jint android_media_AudioRecord_get_buffer_size_in_frames(JNIEnv *env,  jobject thiz) {
572     sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
573     if (lpRecorder == NULL) {
574         jniThrowException(env, "java/lang/IllegalStateException",
575             "Unable to retrieve AudioRecord pointer for frameCount()");
576         return (jint)AUDIO_JAVA_ERROR;
577     }
578     return lpRecorder->frameCount();
579 }
580 
581 // ----------------------------------------------------------------------------
android_media_AudioRecord_set_marker_pos(JNIEnv * env,jobject thiz,jint markerPos)582 static jint android_media_AudioRecord_set_marker_pos(JNIEnv *env,  jobject thiz,
583         jint markerPos) {
584 
585     sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
586     if (lpRecorder == NULL) {
587         jniThrowException(env, "java/lang/IllegalStateException",
588             "Unable to retrieve AudioRecord pointer for setMarkerPosition()");
589         return (jint)AUDIO_JAVA_ERROR;
590     }
591     return nativeToJavaStatus( lpRecorder->setMarkerPosition(markerPos) );
592 }
593 
594 
595 // ----------------------------------------------------------------------------
android_media_AudioRecord_get_marker_pos(JNIEnv * env,jobject thiz)596 static jint android_media_AudioRecord_get_marker_pos(JNIEnv *env,  jobject thiz) {
597 
598     sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
599     uint32_t markerPos = 0;
600 
601     if (lpRecorder == NULL) {
602         jniThrowException(env, "java/lang/IllegalStateException",
603             "Unable to retrieve AudioRecord pointer for getMarkerPosition()");
604         return (jint)AUDIO_JAVA_ERROR;
605     }
606     lpRecorder->getMarkerPosition(&markerPos);
607     return (jint)markerPos;
608 }
609 
610 
611 // ----------------------------------------------------------------------------
android_media_AudioRecord_set_pos_update_period(JNIEnv * env,jobject thiz,jint period)612 static jint android_media_AudioRecord_set_pos_update_period(JNIEnv *env,  jobject thiz,
613         jint period) {
614 
615     sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
616 
617     if (lpRecorder == NULL) {
618         jniThrowException(env, "java/lang/IllegalStateException",
619             "Unable to retrieve AudioRecord pointer for setPositionUpdatePeriod()");
620         return (jint)AUDIO_JAVA_ERROR;
621     }
622     return nativeToJavaStatus( lpRecorder->setPositionUpdatePeriod(period) );
623 }
624 
625 
626 // ----------------------------------------------------------------------------
android_media_AudioRecord_get_pos_update_period(JNIEnv * env,jobject thiz)627 static jint android_media_AudioRecord_get_pos_update_period(JNIEnv *env,  jobject thiz) {
628 
629     sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
630     uint32_t period = 0;
631 
632     if (lpRecorder == NULL) {
633         jniThrowException(env, "java/lang/IllegalStateException",
634             "Unable to retrieve AudioRecord pointer for getPositionUpdatePeriod()");
635         return (jint)AUDIO_JAVA_ERROR;
636     }
637     lpRecorder->getPositionUpdatePeriod(&period);
638     return (jint)period;
639 }
640 
641 
642 // ----------------------------------------------------------------------------
643 // returns the minimum required size for the successful creation of an AudioRecord instance.
644 // returns 0 if the parameter combination is not supported.
645 // return -1 if there was an error querying the buffer size.
android_media_AudioRecord_get_min_buff_size(JNIEnv * env,jobject thiz,jint sampleRateInHertz,jint channelCount,jint audioFormat)646 static jint android_media_AudioRecord_get_min_buff_size(JNIEnv *env,  jobject thiz,
647     jint sampleRateInHertz, jint channelCount, jint audioFormat) {
648 
649     ALOGV(">> android_media_AudioRecord_get_min_buff_size(%d, %d, %d)",
650           sampleRateInHertz, channelCount, audioFormat);
651 
652     size_t frameCount = 0;
653     audio_format_t format = audioFormatToNative(audioFormat);
654     status_t result = AudioRecord::getMinFrameCount(&frameCount,
655             sampleRateInHertz,
656             format,
657             audio_channel_in_mask_from_count(channelCount));
658 
659     if (result == BAD_VALUE) {
660         return 0;
661     }
662     if (result != NO_ERROR) {
663         return -1;
664     }
665     return frameCount * channelCount * audio_bytes_per_sample(format);
666 }
667 
android_media_AudioRecord_setInputDevice(JNIEnv * env,jobject thiz,jint device_id)668 static jboolean android_media_AudioRecord_setInputDevice(
669         JNIEnv *env,  jobject thiz, jint device_id) {
670 
671     sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
672     if (lpRecorder == 0) {
673         return false;
674     }
675     return lpRecorder->setInputDevice(device_id) == NO_ERROR;
676 }
677 
android_media_AudioRecord_getRoutedDeviceId(JNIEnv * env,jobject thiz)678 static jint android_media_AudioRecord_getRoutedDeviceId(
679                 JNIEnv *env,  jobject thiz) {
680 
681     sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
682     if (lpRecorder == 0) {
683         return 0;
684     }
685     return (jint)lpRecorder->getRoutedDeviceId();
686 }
687 
android_media_AudioRecord_enableDeviceCallback(JNIEnv * env,jobject thiz)688 static void android_media_AudioRecord_enableDeviceCallback(
689                 JNIEnv *env,  jobject thiz) {
690 
691     sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
692     if (lpRecorder == 0) {
693         return;
694     }
695     sp<JNIDeviceCallback> cb = getJniDeviceCallback(env, thiz);
696     if (cb != 0) {
697         return;
698     }
699     audiorecord_callback_cookie *cookie =
700             (audiorecord_callback_cookie *)env->GetLongField(thiz,
701                                                      javaAudioRecordFields.nativeCallbackCookie);
702     if (cookie == NULL) {
703         return;
704     }
705 
706     cb = new JNIDeviceCallback(env, thiz, cookie->audioRecord_ref,
707                                javaAudioRecordFields.postNativeEventInJava);
708     status_t status = lpRecorder->addAudioDeviceCallback(cb);
709     if (status == NO_ERROR) {
710         setJniDeviceCallback(env, thiz, cb);
711     }
712 }
713 
android_media_AudioRecord_disableDeviceCallback(JNIEnv * env,jobject thiz)714 static void android_media_AudioRecord_disableDeviceCallback(
715                 JNIEnv *env,  jobject thiz) {
716 
717     sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
718     if (lpRecorder == 0) {
719         return;
720     }
721     sp<JNIDeviceCallback> cb = setJniDeviceCallback(env, thiz, 0);
722     if (cb != 0) {
723         lpRecorder->removeAudioDeviceCallback(cb);
724     }
725 }
726 
727 // ----------------------------------------------------------------------------
android_media_AudioRecord_get_timestamp(JNIEnv * env,jobject thiz,jobject timestamp,jint timebase)728 static jint android_media_AudioRecord_get_timestamp(JNIEnv *env, jobject thiz,
729         jobject timestamp, jint timebase) {
730     sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
731 
732     if (lpRecorder == NULL) {
733         jniThrowException(env, "java/lang/IllegalStateException",
734             "Unable to retrieve AudioRecord pointer for getTimestamp()");
735         return (jint)AUDIO_JAVA_ERROR;
736     }
737 
738     ExtendedTimestamp ts;
739     jint status = nativeToJavaStatus(lpRecorder->getTimestamp(&ts));
740 
741     if (status == AUDIO_JAVA_SUCCESS) {
742         // set the data
743         int64_t position, time;
744 
745         status = nativeToJavaStatus(ts.getBestTimestamp(&position, &time, timebase));
746         if (status == AUDIO_JAVA_SUCCESS) {
747             env->SetLongField(
748                     timestamp, javaAudioTimestampFields.fieldFramePosition, position);
749             env->SetLongField(
750                     timestamp, javaAudioTimestampFields.fieldNanoTime, time);
751         }
752     }
753     return status;
754 }
755 
756 // ----------------------------------------------------------------------------
757 static jobject
android_media_AudioRecord_native_getMetrics(JNIEnv * env,jobject thiz)758 android_media_AudioRecord_native_getMetrics(JNIEnv *env, jobject thiz)
759 {
760     ALOGV("android_media_AudioRecord_native_getMetrics");
761 
762     sp<AudioRecord> lpRecord = getAudioRecord(env, thiz);
763 
764     if (lpRecord == NULL) {
765         ALOGE("Unable to retrieve AudioRecord pointer for getMetrics()");
766         jniThrowException(env, "java/lang/IllegalStateException", NULL);
767         return (jobject) NULL;
768     }
769 
770     // get what we have for the metrics from the record session
771     mediametrics::Item *item = NULL;
772 
773     status_t err = lpRecord->getMetrics(item);
774     if (err != OK) {
775         ALOGE("getMetrics failed");
776         jniThrowException(env, "java/lang/IllegalStateException", NULL);
777         return (jobject) NULL;
778     }
779 
780     jobject mybundle = MediaMetricsJNI::writeMetricsToBundle(env, item, NULL /* mybundle */);
781 
782     // housekeeping
783     delete item;
784     item = NULL;
785 
786     return mybundle;
787 }
788 
789 // ----------------------------------------------------------------------------
android_media_AudioRecord_get_active_microphones(JNIEnv * env,jobject thiz,jobject jActiveMicrophones)790 static jint android_media_AudioRecord_get_active_microphones(JNIEnv *env,
791         jobject thiz, jobject jActiveMicrophones) {
792     if (jActiveMicrophones == NULL) {
793         ALOGE("jActiveMicrophones is null");
794         return (jint)AUDIO_JAVA_BAD_VALUE;
795     }
796     if (!env->IsInstanceOf(jActiveMicrophones, gArrayListClass)) {
797         ALOGE("getActiveMicrophones not an arraylist");
798         return (jint)AUDIO_JAVA_BAD_VALUE;
799     }
800 
801     sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
802     if (lpRecorder == NULL) {
803         jniThrowException(env, "java/lang/IllegalStateException",
804             "Unable to retrieve AudioRecord pointer for getActiveMicrophones()");
805         return (jint)AUDIO_JAVA_ERROR;
806     }
807 
808     jint jStatus = AUDIO_JAVA_SUCCESS;
809     std::vector<media::MicrophoneInfo> activeMicrophones;
810     status_t status = lpRecorder->getActiveMicrophones(&activeMicrophones);
811     if (status != NO_ERROR) {
812         ALOGE_IF(status != NO_ERROR, "AudioRecord::getActiveMicrophones error %d", status);
813         jStatus = nativeToJavaStatus(status);
814         return jStatus;
815     }
816 
817     for (size_t i = 0; i < activeMicrophones.size(); i++) {
818         jobject jMicrophoneInfo;
819         jStatus = convertMicrophoneInfoFromNative(env, &jMicrophoneInfo, &activeMicrophones[i]);
820         if (jStatus != AUDIO_JAVA_SUCCESS) {
821             return jStatus;
822         }
823         env->CallBooleanMethod(jActiveMicrophones, gArrayListMethods.add, jMicrophoneInfo);
824         env->DeleteLocalRef(jMicrophoneInfo);
825     }
826     return jStatus;
827 }
828 
android_media_AudioRecord_set_preferred_microphone_direction(JNIEnv * env,jobject thiz,jint direction)829 static int android_media_AudioRecord_set_preferred_microphone_direction(
830                                 JNIEnv *env, jobject thiz, jint direction) {
831     sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
832     if (lpRecorder == NULL) {
833         jniThrowException(env, "java/lang/IllegalStateException",
834             "Unable to retrieve AudioRecord pointer for setPreferredMicrophoneDirection()");
835         return (jint)AUDIO_JAVA_ERROR;
836     }
837 
838     jint jStatus = AUDIO_JAVA_SUCCESS;
839     status_t status = lpRecorder->setPreferredMicrophoneDirection(
840                             static_cast<audio_microphone_direction_t>(direction));
841     if (status != NO_ERROR) {
842         jStatus = nativeToJavaStatus(status);
843     }
844 
845     return jStatus;
846 }
847 
android_media_AudioRecord_set_preferred_microphone_field_dimension(JNIEnv * env,jobject thiz,jfloat zoom)848 static int android_media_AudioRecord_set_preferred_microphone_field_dimension(
849                                 JNIEnv *env, jobject thiz, jfloat zoom) {
850     sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
851     if (lpRecorder == NULL) {
852         jniThrowException(env, "java/lang/IllegalStateException",
853             "Unable to retrieve AudioRecord pointer for setPreferredMicrophoneFieldDimension()");
854         return (jint)AUDIO_JAVA_ERROR;
855     }
856 
857     jint jStatus = AUDIO_JAVA_SUCCESS;
858     status_t status = lpRecorder->setPreferredMicrophoneFieldDimension(zoom);
859     if (status != NO_ERROR) {
860         jStatus = nativeToJavaStatus(status);
861     }
862 
863     return jStatus;
864 }
865 
android_media_AudioRecord_setLogSessionId(JNIEnv * env,jobject thiz,jstring jlogSessionId)866 static void android_media_AudioRecord_setLogSessionId(JNIEnv *env, jobject thiz,
867                                                       jstring jlogSessionId) {
868     sp<AudioRecord> record = getAudioRecord(env, thiz);
869     if (record == nullptr) {
870         jniThrowException(env, "java/lang/IllegalStateException",
871                           "Unable to retrieve AudioRecord pointer for setLogSessionId()");
872     }
873     if (jlogSessionId == nullptr) {
874         ALOGV("%s: logSessionId nullptr", __func__);
875         record->setLogSessionId(nullptr);
876         return;
877     }
878     ScopedUtfChars logSessionId(env, jlogSessionId);
879     ALOGV("%s: logSessionId '%s'", __func__, logSessionId.c_str());
880     record->setLogSessionId(logSessionId.c_str());
881 }
882 
android_media_AudioRecord_shareAudioHistory(JNIEnv * env,jobject thiz,jstring jSharedPackageName,jlong jSharedStartMs)883 static jint android_media_AudioRecord_shareAudioHistory(JNIEnv *env, jobject thiz,
884                                                         jstring jSharedPackageName,
885                                                         jlong jSharedStartMs) {
886     sp<AudioRecord> record = getAudioRecord(env, thiz);
887     if (record == nullptr) {
888         jniThrowException(env, "java/lang/IllegalStateException",
889                           "Unable to retrieve AudioRecord pointer for setLogSessionId()");
890     }
891     if (jSharedPackageName == nullptr) {
892         jniThrowException(env, "java/lang/IllegalArgumentException", "package name cannot be null");
893     }
894     ScopedUtfChars nSharedPackageName(env, jSharedPackageName);
895     ALOGV("%s: nSharedPackageName '%s'", __func__, nSharedPackageName.c_str());
896     return nativeToJavaStatus(record->shareAudioHistory(nSharedPackageName.c_str(),
897                                                         static_cast<int64_t>(jSharedStartMs)));
898 }
899 
900 // ----------------------------------------------------------------------------
android_media_AudioRecord_get_port_id(JNIEnv * env,jobject thiz)901 static jint android_media_AudioRecord_get_port_id(JNIEnv *env,  jobject thiz) {
902     sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
903     if (lpRecorder == NULL) {
904         jniThrowException(env, "java/lang/IllegalStateException",
905                           "Unable to retrieve AudioRecord pointer for getId()");
906         return (jint)AUDIO_PORT_HANDLE_NONE;
907     }
908     return (jint)lpRecorder->getPortId();
909 }
910 
911 
912 // ----------------------------------------------------------------------------
913 // ----------------------------------------------------------------------------
914 static const JNINativeMethod gMethods[] = {
915         // name,               signature,  funcPtr
916         {"native_start", "(II)I", (void *)android_media_AudioRecord_start},
917         {"native_stop", "()V", (void *)android_media_AudioRecord_stop},
918         {"native_setup",
919          "(Ljava/lang/Object;Ljava/lang/Object;[IIIII[ILandroid/os/Parcel;JI)I",
920          (void *)android_media_AudioRecord_setup},
921         {"native_finalize", "()V", (void *)android_media_AudioRecord_finalize},
922         {"native_release", "()V", (void *)android_media_AudioRecord_release},
923         {"native_read_in_byte_array", "([BIIZ)I",
924          (void *)android_media_AudioRecord_readInArray<jbyteArray>},
925         {"native_read_in_short_array", "([SIIZ)I",
926          (void *)android_media_AudioRecord_readInArray<jshortArray>},
927         {"native_read_in_float_array", "([FIIZ)I",
928          (void *)android_media_AudioRecord_readInArray<jfloatArray>},
929         {"native_read_in_direct_buffer", "(Ljava/lang/Object;IZ)I",
930          (void *)android_media_AudioRecord_readInDirectBuffer},
931         {"native_get_buffer_size_in_frames", "()I",
932          (void *)android_media_AudioRecord_get_buffer_size_in_frames},
933         {"native_set_marker_pos", "(I)I", (void *)android_media_AudioRecord_set_marker_pos},
934         {"native_get_marker_pos", "()I", (void *)android_media_AudioRecord_get_marker_pos},
935         {"native_set_pos_update_period", "(I)I",
936          (void *)android_media_AudioRecord_set_pos_update_period},
937         {"native_get_pos_update_period", "()I",
938          (void *)android_media_AudioRecord_get_pos_update_period},
939         {"native_get_min_buff_size", "(III)I", (void *)android_media_AudioRecord_get_min_buff_size},
940         {"native_getMetrics", "()Landroid/os/PersistableBundle;",
941          (void *)android_media_AudioRecord_native_getMetrics},
942         {"native_setInputDevice", "(I)Z", (void *)android_media_AudioRecord_setInputDevice},
943         {"native_getRoutedDeviceId", "()I", (void *)android_media_AudioRecord_getRoutedDeviceId},
944         {"native_enableDeviceCallback", "()V",
945          (void *)android_media_AudioRecord_enableDeviceCallback},
946         {"native_disableDeviceCallback", "()V",
947          (void *)android_media_AudioRecord_disableDeviceCallback},
948         {"native_get_timestamp", "(Landroid/media/AudioTimestamp;I)I",
949          (void *)android_media_AudioRecord_get_timestamp},
950         {"native_get_active_microphones", "(Ljava/util/ArrayList;)I",
951          (void *)android_media_AudioRecord_get_active_microphones},
952         {"native_getPortId", "()I", (void *)android_media_AudioRecord_get_port_id},
953         {"native_set_preferred_microphone_direction", "(I)I",
954          (void *)android_media_AudioRecord_set_preferred_microphone_direction},
955         {"native_set_preferred_microphone_field_dimension", "(F)I",
956          (void *)android_media_AudioRecord_set_preferred_microphone_field_dimension},
957         {"native_setLogSessionId", "(Ljava/lang/String;)V",
958          (void *)android_media_AudioRecord_setLogSessionId},
959         {"native_shareAudioHistory", "(Ljava/lang/String;J)I",
960          (void *)android_media_AudioRecord_shareAudioHistory},
961 };
962 
963 // field names found in android/media/AudioRecord.java
964 #define JAVA_POSTEVENT_CALLBACK_NAME  "postEventFromNative"
965 #define JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME  "mNativeRecorderInJavaObj"
966 #define JAVA_NATIVECALLBACKINFO_FIELD_NAME       "mNativeCallbackCookie"
967 #define JAVA_NATIVEDEVICECALLBACK_FIELD_NAME       "mNativeDeviceCallback"
968 
969 // ----------------------------------------------------------------------------
register_android_media_AudioRecord(JNIEnv * env)970 int register_android_media_AudioRecord(JNIEnv *env)
971 {
972     javaAudioRecordFields.postNativeEventInJava = NULL;
973     javaAudioRecordFields.nativeRecorderInJavaObj = NULL;
974     javaAudioRecordFields.nativeCallbackCookie = NULL;
975     javaAudioRecordFields.nativeDeviceCallback = NULL;
976 
977 
978     // Get the AudioRecord class
979     jclass audioRecordClass = FindClassOrDie(env, kClassPathName);
980     // Get the postEvent method
981     javaAudioRecordFields.postNativeEventInJava = GetStaticMethodIDOrDie(env,
982             audioRecordClass, JAVA_POSTEVENT_CALLBACK_NAME,
983             "(Ljava/lang/Object;IIILjava/lang/Object;)V");
984 
985     // Get the variables
986     //    mNativeRecorderInJavaObj
987     javaAudioRecordFields.nativeRecorderInJavaObj = GetFieldIDOrDie(env,
988             audioRecordClass, JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME, "J");
989     //     mNativeCallbackCookie
990     javaAudioRecordFields.nativeCallbackCookie = GetFieldIDOrDie(env,
991             audioRecordClass, JAVA_NATIVECALLBACKINFO_FIELD_NAME, "J");
992 
993     javaAudioRecordFields.nativeDeviceCallback = GetFieldIDOrDie(env,
994             audioRecordClass, JAVA_NATIVEDEVICECALLBACK_FIELD_NAME, "J");
995 
996     // Get the RecordTimestamp class and fields
997     jclass audioTimestampClass = FindClassOrDie(env, "android/media/AudioTimestamp");
998     javaAudioTimestampFields.fieldFramePosition =
999             GetFieldIDOrDie(env, audioTimestampClass, "framePosition", "J");
1000     javaAudioTimestampFields.fieldNanoTime =
1001             GetFieldIDOrDie(env, audioTimestampClass, "nanoTime", "J");
1002 
1003     jclass arrayListClass = FindClassOrDie(env, "java/util/ArrayList");
1004     gArrayListClass = MakeGlobalRefOrDie(env, arrayListClass);
1005     gArrayListMethods.add = GetMethodIDOrDie(env, arrayListClass, "add", "(Ljava/lang/Object;)Z");
1006 
1007     return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
1008 }
1009 
1010 // ----------------------------------------------------------------------------
1011