1/*
2**
3** Copyright 2006, The Android Open Source Project
4**
5** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
8**
9**     http://www.apache.org/licenses/LICENSE-2.0
10**
11** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
15** limitations under the License.
16*/
17
18//#define LOG_NDEBUG 0
19
20#define LOG_TAG "AudioSystem-JNI"
21#include <utils/Log.h>
22
23#include <sstream>
24#include <vector>
25#include <jni.h>
26#include <nativehelper/JNIHelp.h>
27#include "core_jni_helpers.h"
28
29#include <media/AudioSystem.h>
30#include <media/AudioPolicy.h>
31#include <media/MicrophoneInfo.h>
32#include <nativehelper/ScopedLocalRef.h>
33#include <system/audio.h>
34#include <system/audio_policy.h>
35#include "android_media_AudioFormat.h"
36#include "android_media_AudioErrors.h"
37#include "android_media_MicrophoneInfo.h"
38
39// ----------------------------------------------------------------------------
40
41using namespace android;
42
43static const char* const kClassPathName = "android/media/AudioSystem";
44
45static jclass gArrayListClass;
46static struct {
47    jmethodID    add;
48    jmethodID    toArray;
49} gArrayListMethods;
50
51static jclass gBooleanClass;
52static jmethodID gBooleanCstor;
53
54static jclass gIntegerClass;
55static jmethodID gIntegerCstor;
56
57static jclass gMapClass;
58static jmethodID gMapPut;
59
60static jclass gAudioHandleClass;
61static jmethodID gAudioHandleCstor;
62static struct {
63    jfieldID    mId;
64} gAudioHandleFields;
65
66static jclass gAudioPortClass;
67static jmethodID gAudioPortCstor;
68static struct {
69    jfieldID    mHandle;
70    jfieldID    mRole;
71    jfieldID    mGains;
72    jfieldID    mActiveConfig;
73    // other fields unused by JNI
74} gAudioPortFields;
75
76static jclass gAudioPortConfigClass;
77static jmethodID gAudioPortConfigCstor;
78static struct {
79    jfieldID    mPort;
80    jfieldID    mSamplingRate;
81    jfieldID    mChannelMask;
82    jfieldID    mFormat;
83    jfieldID    mGain;
84    jfieldID    mConfigMask;
85} gAudioPortConfigFields;
86
87static jclass gAudioDevicePortClass;
88static jmethodID gAudioDevicePortCstor;
89
90static jclass gAudioDevicePortConfigClass;
91static jmethodID gAudioDevicePortConfigCstor;
92
93static jclass gAudioMixPortClass;
94static jmethodID gAudioMixPortCstor;
95
96static jclass gAudioMixPortConfigClass;
97static jmethodID gAudioMixPortConfigCstor;
98
99static jclass gAudioGainClass;
100static jmethodID gAudioGainCstor;
101
102static jclass gAudioGainConfigClass;
103static jmethodID gAudioGainConfigCstor;
104static struct {
105    jfieldID mIndex;
106    jfieldID mMode;
107    jfieldID mChannelMask;
108    jfieldID mValues;
109    jfieldID mRampDurationMs;
110    // other fields unused by JNI
111} gAudioGainConfigFields;
112
113static jclass gAudioPatchClass;
114static jmethodID gAudioPatchCstor;
115static struct {
116    jfieldID    mHandle;
117    // other fields unused by JNI
118} gAudioPatchFields;
119
120static jclass gAudioMixClass;
121static struct {
122    jfieldID    mRule;
123    jfieldID    mFormat;
124    jfieldID    mRouteFlags;
125    jfieldID    mDeviceType;
126    jfieldID    mDeviceAddress;
127    jfieldID    mMixType;
128    jfieldID    mCallbackFlags;
129} gAudioMixFields;
130
131static jclass gAudioFormatClass;
132static struct {
133    jfieldID    mEncoding;
134    jfieldID    mSampleRate;
135    jfieldID    mChannelMask;
136    // other fields unused by JNI
137} gAudioFormatFields;
138
139static jclass gAudioMixingRuleClass;
140static struct {
141    jfieldID    mCriteria;
142    // other fields unused by JNI
143} gAudioMixingRuleFields;
144
145static jclass gAudioMixMatchCriterionClass;
146static struct {
147    jfieldID    mAttr;
148    jfieldID    mIntProp;
149    jfieldID    mRule;
150} gAudioMixMatchCriterionFields;
151
152static jclass gAudioAttributesClass;
153static struct {
154    jfieldID    mUsage;
155    jfieldID    mSource;
156} gAudioAttributesFields;
157
158static const char* const kEventHandlerClassPathName =
159        "android/media/AudioPortEventHandler";
160static struct {
161    jfieldID    mJniCallback;
162} gEventHandlerFields;
163static struct {
164    jmethodID    postEventFromNative;
165} gAudioPortEventHandlerMethods;
166
167static struct {
168    jmethodID postDynPolicyEventFromNative;
169    jmethodID postRecordConfigEventFromNative;
170} gAudioPolicyEventHandlerMethods;
171
172static Mutex gLock;
173
174enum AudioError {
175    kAudioStatusOk = 0,
176    kAudioStatusError = 1,
177    kAudioStatusMediaServerDied = 100
178};
179
180enum  {
181    AUDIOPORT_EVENT_PORT_LIST_UPDATED = 1,
182    AUDIOPORT_EVENT_PATCH_LIST_UPDATED = 2,
183    AUDIOPORT_EVENT_SERVICE_DIED = 3,
184};
185
186#define MAX_PORT_GENERATION_SYNC_ATTEMPTS 5
187
188// ----------------------------------------------------------------------------
189// ref-counted object for audio port callbacks
190class JNIAudioPortCallback: public AudioSystem::AudioPortCallback
191{
192public:
193    JNIAudioPortCallback(JNIEnv* env, jobject thiz, jobject weak_thiz);
194    ~JNIAudioPortCallback();
195
196    virtual void onAudioPortListUpdate();
197    virtual void onAudioPatchListUpdate();
198    virtual void onServiceDied();
199
200private:
201    void sendEvent(int event);
202
203    jclass      mClass;     // Reference to AudioPortEventHandler class
204    jobject     mObject;    // Weak ref to AudioPortEventHandler Java object to call on
205};
206
207JNIAudioPortCallback::JNIAudioPortCallback(JNIEnv* env, jobject thiz, jobject weak_thiz)
208{
209
210    // Hold onto the AudioPortEventHandler class for use in calling the static method
211    // that posts events to the application thread.
212    jclass clazz = env->GetObjectClass(thiz);
213    if (clazz == NULL) {
214        ALOGE("Can't find class %s", kEventHandlerClassPathName);
215        return;
216    }
217    mClass = (jclass)env->NewGlobalRef(clazz);
218
219    // We use a weak reference so the AudioPortEventHandler object can be garbage collected.
220    // The reference is only used as a proxy for callbacks.
221    mObject  = env->NewGlobalRef(weak_thiz);
222}
223
224JNIAudioPortCallback::~JNIAudioPortCallback()
225{
226    // remove global references
227    JNIEnv *env = AndroidRuntime::getJNIEnv();
228    if (env == NULL) {
229        return;
230    }
231    env->DeleteGlobalRef(mObject);
232    env->DeleteGlobalRef(mClass);
233}
234
235void JNIAudioPortCallback::sendEvent(int event)
236{
237    JNIEnv *env = AndroidRuntime::getJNIEnv();
238    if (env == NULL) {
239        return;
240    }
241    env->CallStaticVoidMethod(mClass, gAudioPortEventHandlerMethods.postEventFromNative, mObject,
242                              event, 0, 0, NULL);
243    if (env->ExceptionCheck()) {
244        ALOGW("An exception occurred while notifying an event.");
245        env->ExceptionClear();
246    }
247}
248
249void JNIAudioPortCallback::onAudioPortListUpdate()
250{
251    sendEvent(AUDIOPORT_EVENT_PORT_LIST_UPDATED);
252}
253
254void JNIAudioPortCallback::onAudioPatchListUpdate()
255{
256    sendEvent(AUDIOPORT_EVENT_PATCH_LIST_UPDATED);
257}
258
259void JNIAudioPortCallback::onServiceDied()
260{
261    sendEvent(AUDIOPORT_EVENT_SERVICE_DIED);
262}
263
264static sp<JNIAudioPortCallback> setJniCallback(JNIEnv* env,
265                                       jobject thiz,
266                                       const sp<JNIAudioPortCallback>& callback)
267{
268    Mutex::Autolock l(gLock);
269    sp<JNIAudioPortCallback> old =
270            (JNIAudioPortCallback*)env->GetLongField(thiz, gEventHandlerFields.mJniCallback);
271    if (callback.get()) {
272        callback->incStrong((void*)setJniCallback);
273    }
274    if (old != 0) {
275        old->decStrong((void*)setJniCallback);
276    }
277    env->SetLongField(thiz, gEventHandlerFields.mJniCallback, (jlong)callback.get());
278    return old;
279}
280
281static int check_AudioSystem_Command(status_t status)
282{
283    switch (status) {
284    case DEAD_OBJECT:
285        return kAudioStatusMediaServerDied;
286    case NO_ERROR:
287        return kAudioStatusOk;
288    default:
289        break;
290    }
291    return kAudioStatusError;
292}
293
294static jint
295android_media_AudioSystem_muteMicrophone(JNIEnv *env, jobject thiz, jboolean on)
296{
297    return (jint) check_AudioSystem_Command(AudioSystem::muteMicrophone(on));
298}
299
300static jboolean
301android_media_AudioSystem_isMicrophoneMuted(JNIEnv *env, jobject thiz)
302{
303    bool state = false;
304    AudioSystem::isMicrophoneMuted(&state);
305    return state;
306}
307
308static jboolean
309android_media_AudioSystem_isStreamActive(JNIEnv *env, jobject thiz, jint stream, jint inPastMs)
310{
311    bool state = false;
312    AudioSystem::isStreamActive((audio_stream_type_t) stream, &state, inPastMs);
313    return state;
314}
315
316static jboolean
317android_media_AudioSystem_isStreamActiveRemotely(JNIEnv *env, jobject thiz, jint stream,
318        jint inPastMs)
319{
320    bool state = false;
321    AudioSystem::isStreamActiveRemotely((audio_stream_type_t) stream, &state, inPastMs);
322    return state;
323}
324
325static jboolean
326android_media_AudioSystem_isSourceActive(JNIEnv *env, jobject thiz, jint source)
327{
328    bool state = false;
329    AudioSystem::isSourceActive((audio_source_t) source, &state);
330    return state;
331}
332
333static jint
334android_media_AudioSystem_newAudioSessionId(JNIEnv *env, jobject thiz)
335{
336    return AudioSystem::newAudioUniqueId(AUDIO_UNIQUE_ID_USE_SESSION);
337}
338
339static jint
340android_media_AudioSystem_newAudioPlayerId(JNIEnv *env, jobject thiz)
341{
342    return AudioSystem::newAudioUniqueId(AUDIO_UNIQUE_ID_USE_PLAYER);
343}
344
345static jint
346android_media_AudioSystem_setParameters(JNIEnv *env, jobject thiz, jstring keyValuePairs)
347{
348    const jchar* c_keyValuePairs = env->GetStringCritical(keyValuePairs, 0);
349    String8 c_keyValuePairs8;
350    if (keyValuePairs) {
351        c_keyValuePairs8 = String8(
352            reinterpret_cast<const char16_t*>(c_keyValuePairs),
353            env->GetStringLength(keyValuePairs));
354        env->ReleaseStringCritical(keyValuePairs, c_keyValuePairs);
355    }
356    int status = check_AudioSystem_Command(AudioSystem::setParameters(c_keyValuePairs8));
357    return (jint) status;
358}
359
360static jstring
361android_media_AudioSystem_getParameters(JNIEnv *env, jobject thiz, jstring keys)
362{
363    const jchar* c_keys = env->GetStringCritical(keys, 0);
364    String8 c_keys8;
365    if (keys) {
366        c_keys8 = String8(reinterpret_cast<const char16_t*>(c_keys),
367                          env->GetStringLength(keys));
368        env->ReleaseStringCritical(keys, c_keys);
369    }
370    return env->NewStringUTF(AudioSystem::getParameters(c_keys8).string());
371}
372
373static void
374android_media_AudioSystem_error_callback(status_t err)
375{
376    JNIEnv *env = AndroidRuntime::getJNIEnv();
377    if (env == NULL) {
378        return;
379    }
380
381    jclass clazz = env->FindClass(kClassPathName);
382
383    env->CallStaticVoidMethod(clazz, env->GetStaticMethodID(clazz,
384                              "errorCallbackFromNative","(I)V"),
385                              check_AudioSystem_Command(err));
386
387    env->DeleteLocalRef(clazz);
388}
389
390static void
391android_media_AudioSystem_dyn_policy_callback(int event, String8 regId, int val)
392{
393    JNIEnv *env = AndroidRuntime::getJNIEnv();
394    if (env == NULL) {
395        return;
396    }
397
398    jclass clazz = env->FindClass(kClassPathName);
399    const char* zechars = regId.string();
400    jstring zestring = env->NewStringUTF(zechars);
401
402    env->CallStaticVoidMethod(clazz, gAudioPolicyEventHandlerMethods.postDynPolicyEventFromNative,
403            event, zestring, val);
404
405    env->ReleaseStringUTFChars(zestring, zechars);
406    env->DeleteLocalRef(clazz);
407}
408
409static void
410android_media_AudioSystem_recording_callback(int event, const record_client_info_t *clientInfo,
411        const audio_config_base_t *clientConfig, const audio_config_base_t *deviceConfig,
412        audio_patch_handle_t patchHandle)
413{
414    JNIEnv *env = AndroidRuntime::getJNIEnv();
415    if (env == NULL) {
416        return;
417    }
418    if (clientInfo == NULL || clientConfig == NULL || deviceConfig == NULL) {
419        ALOGE("Unexpected null client/device info or configurations in recording callback");
420        return;
421    }
422
423    // create an array for 2*3 integers to store the record configurations (client + device)
424    //                 plus 1 integer for the patch handle
425    const int REC_PARAM_SIZE = 7;
426    jintArray recParamArray = env->NewIntArray(REC_PARAM_SIZE);
427    if (recParamArray == NULL) {
428        ALOGE("recording callback: Couldn't allocate int array for configuration data");
429        return;
430    }
431    jint recParamData[REC_PARAM_SIZE];
432    recParamData[0] = (jint) audioFormatFromNative(clientConfig->format);
433    // FIXME this doesn't support index-based masks
434    recParamData[1] = (jint) inChannelMaskFromNative(clientConfig->channel_mask);
435    recParamData[2] = (jint) clientConfig->sample_rate;
436    recParamData[3] = (jint) audioFormatFromNative(deviceConfig->format);
437    // FIXME this doesn't support index-based masks
438    recParamData[4] = (jint) inChannelMaskFromNative(deviceConfig->channel_mask);
439    recParamData[5] = (jint) deviceConfig->sample_rate;
440    recParamData[6] = (jint) patchHandle;
441    env->SetIntArrayRegion(recParamArray, 0, REC_PARAM_SIZE, recParamData);
442
443    // callback into java
444    jclass clazz = env->FindClass(kClassPathName);
445    env->CallStaticVoidMethod(clazz,
446            gAudioPolicyEventHandlerMethods.postRecordConfigEventFromNative,
447            event, (jint) clientInfo->uid, clientInfo->session, clientInfo->source, recParamArray);
448    env->DeleteLocalRef(clazz);
449
450    env->DeleteLocalRef(recParamArray);
451}
452
453static jint
454android_media_AudioSystem_setDeviceConnectionState(JNIEnv *env, jobject thiz, jint device, jint state, jstring device_address, jstring device_name)
455{
456    const char *c_address = env->GetStringUTFChars(device_address, NULL);
457    const char *c_name = env->GetStringUTFChars(device_name, NULL);
458    int status = check_AudioSystem_Command(AudioSystem::setDeviceConnectionState(static_cast <audio_devices_t>(device),
459                                          static_cast <audio_policy_dev_state_t>(state),
460                                          c_address, c_name));
461    env->ReleaseStringUTFChars(device_address, c_address);
462    env->ReleaseStringUTFChars(device_name, c_name);
463    return (jint) status;
464}
465
466static jint
467android_media_AudioSystem_getDeviceConnectionState(JNIEnv *env, jobject thiz, jint device, jstring device_address)
468{
469    const char *c_address = env->GetStringUTFChars(device_address, NULL);
470    int state = static_cast <int>(AudioSystem::getDeviceConnectionState(static_cast <audio_devices_t>(device),
471                                          c_address));
472    env->ReleaseStringUTFChars(device_address, c_address);
473    return (jint) state;
474}
475
476static jint
477android_media_AudioSystem_handleDeviceConfigChange(JNIEnv *env, jobject thiz, jint device, jstring device_address, jstring device_name)
478{
479    const char *c_address = env->GetStringUTFChars(device_address, NULL);
480    const char *c_name = env->GetStringUTFChars(device_name, NULL);
481    int status = check_AudioSystem_Command(AudioSystem::handleDeviceConfigChange(static_cast <audio_devices_t>(device),
482                                          c_address, c_name));
483    env->ReleaseStringUTFChars(device_address, c_address);
484    env->ReleaseStringUTFChars(device_name, c_name);
485    return (jint) status;
486}
487
488static jint
489android_media_AudioSystem_setPhoneState(JNIEnv *env, jobject thiz, jint state)
490{
491    return (jint) check_AudioSystem_Command(AudioSystem::setPhoneState((audio_mode_t) state));
492}
493
494static jint
495android_media_AudioSystem_setForceUse(JNIEnv *env, jobject thiz, jint usage, jint config)
496{
497    return (jint) check_AudioSystem_Command(AudioSystem::setForceUse(static_cast <audio_policy_force_use_t>(usage),
498                                                           static_cast <audio_policy_forced_cfg_t>(config)));
499}
500
501static jint
502android_media_AudioSystem_getForceUse(JNIEnv *env, jobject thiz, jint usage)
503{
504    return static_cast <jint>(AudioSystem::getForceUse(static_cast <audio_policy_force_use_t>(usage)));
505}
506
507static jint
508android_media_AudioSystem_initStreamVolume(JNIEnv *env, jobject thiz, jint stream, jint indexMin, jint indexMax)
509{
510    return (jint) check_AudioSystem_Command(AudioSystem::initStreamVolume(static_cast <audio_stream_type_t>(stream),
511                                                                   indexMin,
512                                                                   indexMax));
513}
514
515static jint
516android_media_AudioSystem_setStreamVolumeIndex(JNIEnv *env,
517                                               jobject thiz,
518                                               jint stream,
519                                               jint index,
520                                               jint device)
521{
522    return (jint) check_AudioSystem_Command(
523            AudioSystem::setStreamVolumeIndex(static_cast <audio_stream_type_t>(stream),
524                                              index,
525                                              (audio_devices_t)device));
526}
527
528static jint
529android_media_AudioSystem_getStreamVolumeIndex(JNIEnv *env,
530                                               jobject thiz,
531                                               jint stream,
532                                               jint device)
533{
534    int index;
535    if (AudioSystem::getStreamVolumeIndex(static_cast <audio_stream_type_t>(stream),
536                                          &index,
537                                          (audio_devices_t)device)
538            != NO_ERROR) {
539        index = -1;
540    }
541    return (jint) index;
542}
543
544static jint
545android_media_AudioSystem_setMasterVolume(JNIEnv *env, jobject thiz, jfloat value)
546{
547    return (jint) check_AudioSystem_Command(AudioSystem::setMasterVolume(value));
548}
549
550static jfloat
551android_media_AudioSystem_getMasterVolume(JNIEnv *env, jobject thiz)
552{
553    float value;
554    if (AudioSystem::getMasterVolume(&value) != NO_ERROR) {
555        value = -1.0;
556    }
557    return value;
558}
559
560static jint
561android_media_AudioSystem_setMasterMute(JNIEnv *env, jobject thiz, jboolean mute)
562{
563    return (jint) check_AudioSystem_Command(AudioSystem::setMasterMute(mute));
564}
565
566static jboolean
567android_media_AudioSystem_getMasterMute(JNIEnv *env, jobject thiz)
568{
569    bool mute;
570    if (AudioSystem::getMasterMute(&mute) != NO_ERROR) {
571        mute = false;
572    }
573    return mute;
574}
575
576static jint
577android_media_AudioSystem_setMasterMono(JNIEnv *env, jobject thiz, jboolean mono)
578{
579    return (jint) check_AudioSystem_Command(AudioSystem::setMasterMono(mono));
580}
581
582static jboolean
583android_media_AudioSystem_getMasterMono(JNIEnv *env, jobject thiz)
584{
585    bool mono;
586    if (AudioSystem::getMasterMono(&mono) != NO_ERROR) {
587        mono = false;
588    }
589    return mono;
590}
591
592static jint
593android_media_AudioSystem_getDevicesForStream(JNIEnv *env, jobject thiz, jint stream)
594{
595    return (jint) AudioSystem::getDevicesForStream(static_cast <audio_stream_type_t>(stream));
596}
597
598static jint
599android_media_AudioSystem_getPrimaryOutputSamplingRate(JNIEnv *env, jobject clazz)
600{
601    return (jint) AudioSystem::getPrimaryOutputSamplingRate();
602}
603
604static jint
605android_media_AudioSystem_getPrimaryOutputFrameCount(JNIEnv *env, jobject clazz)
606{
607    return (jint) AudioSystem::getPrimaryOutputFrameCount();
608}
609
610static jint
611android_media_AudioSystem_getOutputLatency(JNIEnv *env, jobject clazz, jint stream)
612{
613    uint32_t afLatency;
614    if (AudioSystem::getOutputLatency(&afLatency, static_cast <audio_stream_type_t>(stream))
615            != NO_ERROR) {
616        afLatency = -1;
617    }
618    return (jint) afLatency;
619}
620
621static jint
622android_media_AudioSystem_setLowRamDevice(
623        JNIEnv *env, jobject clazz, jboolean isLowRamDevice, jlong totalMemory)
624{
625    return (jint) AudioSystem::setLowRamDevice((bool) isLowRamDevice, (int64_t) totalMemory);
626}
627
628static jint
629android_media_AudioSystem_checkAudioFlinger(JNIEnv *env, jobject clazz)
630{
631    return (jint) check_AudioSystem_Command(AudioSystem::checkAudioFlinger());
632}
633
634
635static bool useInChannelMask(audio_port_type_t type, audio_port_role_t role)
636{
637    return ((type == AUDIO_PORT_TYPE_DEVICE) && (role == AUDIO_PORT_ROLE_SOURCE)) ||
638                ((type == AUDIO_PORT_TYPE_MIX) && (role == AUDIO_PORT_ROLE_SINK));
639}
640
641static void convertAudioGainConfigToNative(JNIEnv *env,
642                                               struct audio_gain_config *nAudioGainConfig,
643                                               const jobject jAudioGainConfig,
644                                               bool useInMask)
645{
646    nAudioGainConfig->index = env->GetIntField(jAudioGainConfig, gAudioGainConfigFields.mIndex);
647    nAudioGainConfig->mode = env->GetIntField(jAudioGainConfig, gAudioGainConfigFields.mMode);
648    ALOGV("convertAudioGainConfigToNative got gain index %d", nAudioGainConfig->index);
649    jint jMask = env->GetIntField(jAudioGainConfig, gAudioGainConfigFields.mChannelMask);
650    audio_channel_mask_t nMask;
651    if (useInMask) {
652        nMask = inChannelMaskToNative(jMask);
653        ALOGV("convertAudioGainConfigToNative IN mask java %x native %x", jMask, nMask);
654    } else {
655        nMask = outChannelMaskToNative(jMask);
656        ALOGV("convertAudioGainConfigToNative OUT mask java %x native %x", jMask, nMask);
657    }
658    nAudioGainConfig->channel_mask = nMask;
659    nAudioGainConfig->ramp_duration_ms = env->GetIntField(jAudioGainConfig,
660                                                       gAudioGainConfigFields.mRampDurationMs);
661    jintArray jValues = (jintArray)env->GetObjectField(jAudioGainConfig,
662                                                       gAudioGainConfigFields.mValues);
663    int *nValues = env->GetIntArrayElements(jValues, NULL);
664    size_t size = env->GetArrayLength(jValues);
665    memcpy(nAudioGainConfig->values, nValues, size * sizeof(int));
666    env->DeleteLocalRef(jValues);
667}
668
669
670static jint convertAudioPortConfigToNative(JNIEnv *env,
671                                               struct audio_port_config *nAudioPortConfig,
672                                               const jobject jAudioPortConfig,
673                                               bool useConfigMask)
674{
675    jobject jAudioPort = env->GetObjectField(jAudioPortConfig, gAudioPortConfigFields.mPort);
676    jobject jHandle = env->GetObjectField(jAudioPort, gAudioPortFields.mHandle);
677    nAudioPortConfig->id = env->GetIntField(jHandle, gAudioHandleFields.mId);
678    nAudioPortConfig->role = (audio_port_role_t)env->GetIntField(jAudioPort,
679                                                                 gAudioPortFields.mRole);
680    if (env->IsInstanceOf(jAudioPort, gAudioDevicePortClass)) {
681        nAudioPortConfig->type = AUDIO_PORT_TYPE_DEVICE;
682    } else if (env->IsInstanceOf(jAudioPort, gAudioMixPortClass)) {
683        nAudioPortConfig->type = AUDIO_PORT_TYPE_MIX;
684    } else {
685        env->DeleteLocalRef(jAudioPort);
686        env->DeleteLocalRef(jHandle);
687        return (jint)AUDIO_JAVA_ERROR;
688    }
689    ALOGV("convertAudioPortConfigToNative handle %d role %d type %d",
690          nAudioPortConfig->id, nAudioPortConfig->role, nAudioPortConfig->type);
691
692    unsigned int configMask = 0;
693
694    nAudioPortConfig->sample_rate = env->GetIntField(jAudioPortConfig,
695                                                     gAudioPortConfigFields.mSamplingRate);
696    if (nAudioPortConfig->sample_rate != 0) {
697        configMask |= AUDIO_PORT_CONFIG_SAMPLE_RATE;
698    }
699
700    bool useInMask = useInChannelMask(nAudioPortConfig->type, nAudioPortConfig->role);
701    audio_channel_mask_t nMask;
702    jint jMask = env->GetIntField(jAudioPortConfig,
703                                   gAudioPortConfigFields.mChannelMask);
704    if (useInMask) {
705        nMask = inChannelMaskToNative(jMask);
706        ALOGV("convertAudioPortConfigToNative IN mask java %x native %x", jMask, nMask);
707    } else {
708        nMask = outChannelMaskToNative(jMask);
709        ALOGV("convertAudioPortConfigToNative OUT mask java %x native %x", jMask, nMask);
710    }
711    nAudioPortConfig->channel_mask = nMask;
712    if (nAudioPortConfig->channel_mask != AUDIO_CHANNEL_NONE) {
713        configMask |= AUDIO_PORT_CONFIG_CHANNEL_MASK;
714    }
715
716    jint jFormat = env->GetIntField(jAudioPortConfig, gAudioPortConfigFields.mFormat);
717    audio_format_t nFormat = audioFormatToNative(jFormat);
718    ALOGV("convertAudioPortConfigToNative format %d native %d", jFormat, nFormat);
719    nAudioPortConfig->format = nFormat;
720    if (nAudioPortConfig->format != AUDIO_FORMAT_DEFAULT &&
721            nAudioPortConfig->format != AUDIO_FORMAT_INVALID) {
722        configMask |= AUDIO_PORT_CONFIG_FORMAT;
723    }
724
725    jobject jGain = env->GetObjectField(jAudioPortConfig, gAudioPortConfigFields.mGain);
726    if (jGain != NULL) {
727        convertAudioGainConfigToNative(env, &nAudioPortConfig->gain, jGain, useInMask);
728        env->DeleteLocalRef(jGain);
729        configMask |= AUDIO_PORT_CONFIG_GAIN;
730    } else {
731        ALOGV("convertAudioPortConfigToNative no gain");
732        nAudioPortConfig->gain.index = -1;
733    }
734    if (useConfigMask) {
735        nAudioPortConfig->config_mask = env->GetIntField(jAudioPortConfig,
736                                                         gAudioPortConfigFields.mConfigMask);
737    } else {
738        nAudioPortConfig->config_mask = configMask;
739    }
740    env->DeleteLocalRef(jAudioPort);
741    env->DeleteLocalRef(jHandle);
742    return (jint)AUDIO_JAVA_SUCCESS;
743}
744
745static jint convertAudioPortConfigFromNative(JNIEnv *env,
746                                                 jobject jAudioPort,
747                                                 jobject *jAudioPortConfig,
748                                                 const struct audio_port_config *nAudioPortConfig)
749{
750    jint jStatus = AUDIO_JAVA_SUCCESS;
751    jobject jAudioGainConfig = NULL;
752    jobject jAudioGain = NULL;
753    jintArray jGainValues;
754    bool audioportCreated = false;
755
756    ALOGV("convertAudioPortConfigFromNative jAudioPort %p", jAudioPort);
757
758    if (jAudioPort == NULL) {
759        jobject jHandle = env->NewObject(gAudioHandleClass, gAudioHandleCstor,
760                                                 nAudioPortConfig->id);
761
762        ALOGV("convertAudioPortConfigFromNative handle %d is a %s", nAudioPortConfig->id,
763              nAudioPortConfig->type == AUDIO_PORT_TYPE_DEVICE ? "device" : "mix");
764
765        if (jHandle == NULL) {
766            return (jint)AUDIO_JAVA_ERROR;
767        }
768        // create dummy port and port config objects with just the correct handle
769        // and configuration data. The actual AudioPortConfig objects will be
770        // constructed by java code with correct class type (device, mix etc...)
771        // and reference to AudioPort instance in this client
772        jAudioPort = env->NewObject(gAudioPortClass, gAudioPortCstor,
773                                           jHandle, // handle
774                                           0,       // role
775                                           NULL,    // name
776                                           NULL,    // samplingRates
777                                           NULL,    // channelMasks
778                                           NULL,    // channelIndexMasks
779                                           NULL,    // formats
780                                           NULL);   // gains
781        env->DeleteLocalRef(jHandle);
782        if (jAudioPort == NULL) {
783            return (jint)AUDIO_JAVA_ERROR;
784        }
785        ALOGV("convertAudioPortConfigFromNative jAudioPort created for handle %d",
786              nAudioPortConfig->id);
787
788        audioportCreated = true;
789    }
790
791    bool useInMask = useInChannelMask(nAudioPortConfig->type, nAudioPortConfig->role);
792
793    audio_channel_mask_t nMask;
794    jint jMask;
795
796    int gainIndex = nAudioPortConfig->gain.index;
797    if (gainIndex >= 0) {
798        ALOGV("convertAudioPortConfigFromNative gain found with index %d mode %x",
799              gainIndex, nAudioPortConfig->gain.mode);
800        if (audioportCreated) {
801            ALOGV("convertAudioPortConfigFromNative creating gain");
802            jAudioGain = env->NewObject(gAudioGainClass, gAudioGainCstor,
803                                               gainIndex,
804                                               0,
805                                               0,
806                                               0,
807                                               0,
808                                               0,
809                                               0,
810                                               0,
811                                               0);
812            if (jAudioGain == NULL) {
813                ALOGV("convertAudioPortConfigFromNative creating gain FAILED");
814                jStatus = (jint)AUDIO_JAVA_ERROR;
815                goto exit;
816            }
817        } else {
818            ALOGV("convertAudioPortConfigFromNative reading gain from port");
819            jobjectArray jGains = (jobjectArray)env->GetObjectField(jAudioPort,
820                                                                      gAudioPortFields.mGains);
821            if (jGains == NULL) {
822                ALOGV("convertAudioPortConfigFromNative could not get gains from port");
823                jStatus = (jint)AUDIO_JAVA_ERROR;
824                goto exit;
825            }
826            jAudioGain = env->GetObjectArrayElement(jGains, gainIndex);
827            env->DeleteLocalRef(jGains);
828            if (jAudioGain == NULL) {
829                ALOGV("convertAudioPortConfigFromNative could not get gain at index %d", gainIndex);
830                jStatus = (jint)AUDIO_JAVA_ERROR;
831                goto exit;
832            }
833        }
834        int numValues;
835        if (useInMask) {
836            numValues = audio_channel_count_from_in_mask(nAudioPortConfig->gain.channel_mask);
837        } else {
838            numValues = audio_channel_count_from_out_mask(nAudioPortConfig->gain.channel_mask);
839        }
840        jGainValues = env->NewIntArray(numValues);
841        if (jGainValues == NULL) {
842            ALOGV("convertAudioPortConfigFromNative could not create gain values %d", numValues);
843            jStatus = (jint)AUDIO_JAVA_ERROR;
844            goto exit;
845        }
846        env->SetIntArrayRegion(jGainValues, 0, numValues,
847                               nAudioPortConfig->gain.values);
848
849        nMask = nAudioPortConfig->gain.channel_mask;
850        if (useInMask) {
851            jMask = inChannelMaskFromNative(nMask);
852            ALOGV("convertAudioPortConfigFromNative IN mask java %x native %x", jMask, nMask);
853        } else {
854            jMask = outChannelMaskFromNative(nMask);
855            ALOGV("convertAudioPortConfigFromNative OUT mask java %x native %x", jMask, nMask);
856        }
857
858        jAudioGainConfig = env->NewObject(gAudioGainConfigClass,
859                                        gAudioGainConfigCstor,
860                                        gainIndex,
861                                        jAudioGain,
862                                        nAudioPortConfig->gain.mode,
863                                        jMask,
864                                        jGainValues,
865                                        nAudioPortConfig->gain.ramp_duration_ms);
866        env->DeleteLocalRef(jGainValues);
867        if (jAudioGainConfig == NULL) {
868            ALOGV("convertAudioPortConfigFromNative could not create gain config");
869            jStatus = (jint)AUDIO_JAVA_ERROR;
870            goto exit;
871        }
872    }
873    jclass clazz;
874    jmethodID methodID;
875    if (audioportCreated) {
876        clazz = gAudioPortConfigClass;
877        methodID = gAudioPortConfigCstor;
878        ALOGV("convertAudioPortConfigFromNative building a generic port config");
879    } else {
880        if (env->IsInstanceOf(jAudioPort, gAudioDevicePortClass)) {
881            clazz = gAudioDevicePortConfigClass;
882            methodID = gAudioDevicePortConfigCstor;
883            ALOGV("convertAudioPortConfigFromNative building a device config");
884        } else if (env->IsInstanceOf(jAudioPort, gAudioMixPortClass)) {
885            clazz = gAudioMixPortConfigClass;
886            methodID = gAudioMixPortConfigCstor;
887            ALOGV("convertAudioPortConfigFromNative building a mix config");
888        } else {
889            jStatus = (jint)AUDIO_JAVA_ERROR;
890            goto exit;
891        }
892    }
893    nMask = nAudioPortConfig->channel_mask;
894    if (useInMask) {
895        jMask = inChannelMaskFromNative(nMask);
896        ALOGV("convertAudioPortConfigFromNative IN mask java %x native %x", jMask, nMask);
897    } else {
898        jMask = outChannelMaskFromNative(nMask);
899        ALOGV("convertAudioPortConfigFromNative OUT mask java %x native %x", jMask, nMask);
900    }
901
902    *jAudioPortConfig = env->NewObject(clazz, methodID,
903                                       jAudioPort,
904                                       nAudioPortConfig->sample_rate,
905                                       jMask,
906                                       audioFormatFromNative(nAudioPortConfig->format),
907                                       jAudioGainConfig);
908    if (*jAudioPortConfig == NULL) {
909        ALOGV("convertAudioPortConfigFromNative could not create new port config");
910        jStatus = (jint)AUDIO_JAVA_ERROR;
911    } else {
912        ALOGV("convertAudioPortConfigFromNative OK");
913    }
914
915exit:
916    if (audioportCreated) {
917        env->DeleteLocalRef(jAudioPort);
918        if (jAudioGain != NULL) {
919            env->DeleteLocalRef(jAudioGain);
920        }
921    }
922    if (jAudioGainConfig != NULL) {
923        env->DeleteLocalRef(jAudioGainConfig);
924    }
925    return jStatus;
926}
927
928static bool hasFormat(int* formats, size_t size, int format) {
929    for (size_t index = 0; index < size; index++) {
930        if (formats[index] == format) {
931            return true; // found
932        }
933    }
934    return false; // not found
935}
936
937// TODO: pull out to separate file
938template <typename T, size_t N>
939static constexpr size_t array_size(const T (&)[N]) {
940    return N;
941}
942
943static jint convertAudioPortFromNative(JNIEnv *env,
944                                           jobject *jAudioPort, const struct audio_port *nAudioPort)
945{
946    jint jStatus = (jint)AUDIO_JAVA_SUCCESS;
947    jintArray jSamplingRates = NULL;
948    jintArray jChannelMasks = NULL;
949    jintArray jChannelIndexMasks = NULL;
950    int* cFormats = NULL;
951    jintArray jFormats = NULL;
952    jobjectArray jGains = NULL;
953    jobject jHandle = NULL;
954    jstring jDeviceName = NULL;
955    bool useInMask;
956    size_t numPositionMasks = 0;
957    size_t numIndexMasks = 0;
958    size_t numUniqueFormats = 0;
959
960    ALOGV("convertAudioPortFromNative id %d role %d type %d name %s",
961        nAudioPort->id, nAudioPort->role, nAudioPort->type, nAudioPort->name);
962
963    // Verify audio port array count info.
964    if (nAudioPort->num_sample_rates > array_size(nAudioPort->sample_rates)
965            || nAudioPort->num_channel_masks > array_size(nAudioPort->channel_masks)
966            || nAudioPort->num_formats > array_size(nAudioPort->formats)
967            || nAudioPort->num_gains > array_size(nAudioPort->gains)) {
968
969        std::stringstream ss;
970        ss << "convertAudioPortFromNative array count out of bounds:"
971                << " num_sample_rates " << nAudioPort->num_sample_rates
972                << " num_channel_masks " << nAudioPort->num_channel_masks
973                << " num_formats " << nAudioPort->num_formats
974                << " num_gains " << nAudioPort->num_gains
975                ;
976        std::string s = ss.str();
977
978        // Prefer to log through Java wtf instead of native ALOGE.
979        ScopedLocalRef<jclass> jLogClass(env, env->FindClass("android/util/Log"));
980        jmethodID jWtfId = (jLogClass.get() == nullptr)
981                ? nullptr
982                : env->GetStaticMethodID(jLogClass.get(), "wtf",
983                        "(Ljava/lang/String;Ljava/lang/String;)I");
984        if (jWtfId != nullptr) {
985            ScopedLocalRef<jstring> jMessage(env, env->NewStringUTF(s.c_str()));
986            ScopedLocalRef<jstring> jTag(env, env->NewStringUTF(LOG_TAG));
987            (void)env->CallStaticIntMethod(jLogClass.get(), jWtfId, jTag.get(), jMessage.get());
988        } else {
989            ALOGE("%s", s.c_str());
990        }
991        jStatus = (jint)AUDIO_JAVA_ERROR;
992        goto exit;
993    }
994
995    jSamplingRates = env->NewIntArray(nAudioPort->num_sample_rates);
996    if (jSamplingRates == NULL) {
997        jStatus = (jint)AUDIO_JAVA_ERROR;
998        goto exit;
999    }
1000    if (nAudioPort->num_sample_rates) {
1001        env->SetIntArrayRegion(jSamplingRates, 0, nAudioPort->num_sample_rates,
1002                               (jint *)nAudioPort->sample_rates);
1003    }
1004
1005    // count up how many masks are positional and indexed
1006    for(size_t index = 0; index < nAudioPort->num_channel_masks; index++) {
1007        const audio_channel_mask_t mask = nAudioPort->channel_masks[index];
1008        if (audio_channel_mask_get_representation(mask) == AUDIO_CHANNEL_REPRESENTATION_INDEX) {
1009            numIndexMasks++;
1010        } else {
1011            numPositionMasks++;
1012        }
1013    }
1014
1015    jChannelMasks = env->NewIntArray(numPositionMasks);
1016    if (jChannelMasks == NULL) {
1017        jStatus = (jint)AUDIO_JAVA_ERROR;
1018        goto exit;
1019    }
1020    jChannelIndexMasks = env->NewIntArray(numIndexMasks);
1021    if (jChannelIndexMasks == NULL) {
1022        jStatus = (jint)AUDIO_JAVA_ERROR;
1023        goto exit;
1024    }
1025    useInMask = useInChannelMask(nAudioPort->type, nAudioPort->role);
1026
1027    // put the masks in the output arrays
1028    for (size_t maskIndex = 0, posMaskIndex = 0, indexedMaskIndex = 0;
1029         maskIndex < nAudioPort->num_channel_masks; maskIndex++) {
1030        const audio_channel_mask_t mask = nAudioPort->channel_masks[maskIndex];
1031        if (audio_channel_mask_get_representation(mask) == AUDIO_CHANNEL_REPRESENTATION_INDEX) {
1032            jint jMask = audio_channel_mask_get_bits(mask);
1033            env->SetIntArrayRegion(jChannelIndexMasks, indexedMaskIndex++, 1, &jMask);
1034        } else {
1035            jint jMask = useInMask ? inChannelMaskFromNative(mask)
1036                                   : outChannelMaskFromNative(mask);
1037            env->SetIntArrayRegion(jChannelMasks, posMaskIndex++, 1, &jMask);
1038        }
1039    }
1040
1041    // formats
1042    if (nAudioPort->num_formats != 0) {
1043        cFormats = new int[nAudioPort->num_formats];
1044        for (size_t index = 0; index < nAudioPort->num_formats; index++) {
1045            int format = audioFormatFromNative(nAudioPort->formats[index]);
1046            if (!hasFormat(cFormats, numUniqueFormats, format)) {
1047                cFormats[numUniqueFormats++] = format;
1048            }
1049        }
1050    }
1051    jFormats = env->NewIntArray(numUniqueFormats);
1052    if (jFormats == NULL) {
1053        jStatus = (jint)AUDIO_JAVA_ERROR;
1054        goto exit;
1055    }
1056    if (numUniqueFormats != 0) {
1057        env->SetIntArrayRegion(jFormats, 0, numUniqueFormats, cFormats);
1058    }
1059
1060    // gains
1061    jGains = env->NewObjectArray(nAudioPort->num_gains,
1062                                          gAudioGainClass, NULL);
1063    if (jGains == NULL) {
1064        jStatus = (jint)AUDIO_JAVA_ERROR;
1065        goto exit;
1066    }
1067
1068    for (size_t j = 0; j < nAudioPort->num_gains; j++) {
1069        audio_channel_mask_t nMask = nAudioPort->gains[j].channel_mask;
1070        jint jMask;
1071        if (useInMask) {
1072            jMask = inChannelMaskFromNative(nMask);
1073            ALOGV("convertAudioPortConfigFromNative IN mask java %x native %x", jMask, nMask);
1074        } else {
1075            jMask = outChannelMaskFromNative(nMask);
1076            ALOGV("convertAudioPortConfigFromNative OUT mask java %x native %x", jMask, nMask);
1077        }
1078
1079        jobject jGain = env->NewObject(gAudioGainClass, gAudioGainCstor,
1080                                                 j,
1081                                                 nAudioPort->gains[j].mode,
1082                                                 jMask,
1083                                                 nAudioPort->gains[j].min_value,
1084                                                 nAudioPort->gains[j].max_value,
1085                                                 nAudioPort->gains[j].default_value,
1086                                                 nAudioPort->gains[j].step_value,
1087                                                 nAudioPort->gains[j].min_ramp_ms,
1088                                                 nAudioPort->gains[j].max_ramp_ms);
1089        if (jGain == NULL) {
1090            jStatus = (jint)AUDIO_JAVA_ERROR;
1091            goto exit;
1092        }
1093        env->SetObjectArrayElement(jGains, j, jGain);
1094        env->DeleteLocalRef(jGain);
1095    }
1096
1097    jHandle = env->NewObject(gAudioHandleClass, gAudioHandleCstor,
1098                                             nAudioPort->id);
1099    if (jHandle == NULL) {
1100        jStatus = (jint)AUDIO_JAVA_ERROR;
1101        goto exit;
1102    }
1103
1104    jDeviceName = env->NewStringUTF(nAudioPort->name);
1105
1106    if (nAudioPort->type == AUDIO_PORT_TYPE_DEVICE) {
1107        ALOGV("convertAudioPortFromNative is a device %08x", nAudioPort->ext.device.type);
1108        jstring jAddress = env->NewStringUTF(nAudioPort->ext.device.address);
1109        *jAudioPort = env->NewObject(gAudioDevicePortClass, gAudioDevicePortCstor,
1110                                     jHandle, jDeviceName,
1111                                     jSamplingRates, jChannelMasks, jChannelIndexMasks,
1112                                     jFormats, jGains,
1113                                     nAudioPort->ext.device.type, jAddress);
1114        env->DeleteLocalRef(jAddress);
1115    } else if (nAudioPort->type == AUDIO_PORT_TYPE_MIX) {
1116        ALOGV("convertAudioPortFromNative is a mix");
1117        *jAudioPort = env->NewObject(gAudioMixPortClass, gAudioMixPortCstor,
1118                                     jHandle, nAudioPort->ext.mix.handle,
1119                                     nAudioPort->role, jDeviceName,
1120                                     jSamplingRates, jChannelMasks, jChannelIndexMasks,
1121                                     jFormats, jGains);
1122    } else {
1123        ALOGE("convertAudioPortFromNative unknown nAudioPort type %d", nAudioPort->type);
1124        jStatus = (jint)AUDIO_JAVA_ERROR;
1125        goto exit;
1126    }
1127    if (*jAudioPort == NULL) {
1128        jStatus = (jint)AUDIO_JAVA_ERROR;
1129        goto exit;
1130    }
1131
1132    jobject jAudioPortConfig;
1133    jStatus = convertAudioPortConfigFromNative(env,
1134                                                       *jAudioPort,
1135                                                       &jAudioPortConfig,
1136                                                       &nAudioPort->active_config);
1137    if (jStatus != AUDIO_JAVA_SUCCESS) {
1138        goto exit;
1139    }
1140
1141    env->SetObjectField(*jAudioPort, gAudioPortFields.mActiveConfig, jAudioPortConfig);
1142
1143exit:
1144    if (jDeviceName != NULL) {
1145        env->DeleteLocalRef(jDeviceName);
1146    }
1147    if (jSamplingRates != NULL) {
1148        env->DeleteLocalRef(jSamplingRates);
1149    }
1150    if (jChannelMasks != NULL) {
1151        env->DeleteLocalRef(jChannelMasks);
1152    }
1153    if (jChannelIndexMasks != NULL) {
1154        env->DeleteLocalRef(jChannelIndexMasks);
1155    }
1156    if (cFormats != NULL) {
1157        delete[] cFormats;
1158    }
1159    if (jFormats != NULL) {
1160        env->DeleteLocalRef(jFormats);
1161    }
1162    if (jGains != NULL) {
1163        env->DeleteLocalRef(jGains);
1164    }
1165    if (jHandle != NULL) {
1166        env->DeleteLocalRef(jHandle);
1167    }
1168
1169    return jStatus;
1170}
1171
1172static jint
1173android_media_AudioSystem_listAudioPorts(JNIEnv *env, jobject clazz,
1174                                         jobject jPorts, jintArray jGeneration)
1175{
1176    ALOGV("listAudioPorts");
1177
1178    if (jPorts == NULL) {
1179        ALOGE("listAudioPorts NULL AudioPort ArrayList");
1180        return (jint)AUDIO_JAVA_BAD_VALUE;
1181    }
1182    if (!env->IsInstanceOf(jPorts, gArrayListClass)) {
1183        ALOGE("listAudioPorts not an arraylist");
1184        return (jint)AUDIO_JAVA_BAD_VALUE;
1185    }
1186
1187    if (jGeneration == NULL || env->GetArrayLength(jGeneration) != 1) {
1188        return (jint)AUDIO_JAVA_BAD_VALUE;
1189    }
1190
1191    status_t status;
1192    unsigned int generation1;
1193    unsigned int generation;
1194    unsigned int numPorts;
1195    jint *nGeneration;
1196    struct audio_port *nPorts = NULL;
1197    int attempts = MAX_PORT_GENERATION_SYNC_ATTEMPTS;
1198    jint jStatus;
1199
1200    // get the port count and all the ports until they both return the same generation
1201    do {
1202        if (attempts-- < 0) {
1203            status = TIMED_OUT;
1204            break;
1205        }
1206
1207        numPorts = 0;
1208        status = AudioSystem::listAudioPorts(AUDIO_PORT_ROLE_NONE,
1209                                             AUDIO_PORT_TYPE_NONE,
1210                                                      &numPorts,
1211                                                      NULL,
1212                                                      &generation1);
1213        if (status != NO_ERROR) {
1214            ALOGE_IF(status != NO_ERROR, "AudioSystem::listAudioPorts error %d", status);
1215            break;
1216        }
1217        if (numPorts == 0) {
1218            jStatus = (jint)AUDIO_JAVA_SUCCESS;
1219            goto exit;
1220        }
1221        nPorts = (struct audio_port *)realloc(nPorts, numPorts * sizeof(struct audio_port));
1222
1223        status = AudioSystem::listAudioPorts(AUDIO_PORT_ROLE_NONE,
1224                                             AUDIO_PORT_TYPE_NONE,
1225                                                      &numPorts,
1226                                                      nPorts,
1227                                                      &generation);
1228        ALOGV("listAudioPorts AudioSystem::listAudioPorts numPorts %d generation %d generation1 %d",
1229              numPorts, generation, generation1);
1230    } while (generation1 != generation && status == NO_ERROR);
1231
1232    jStatus = nativeToJavaStatus(status);
1233    if (jStatus != AUDIO_JAVA_SUCCESS) {
1234        goto exit;
1235    }
1236
1237    for (size_t i = 0; i < numPorts; i++) {
1238        jobject jAudioPort;
1239        jStatus = convertAudioPortFromNative(env, &jAudioPort, &nPorts[i]);
1240        if (jStatus != AUDIO_JAVA_SUCCESS) {
1241            goto exit;
1242        }
1243        env->CallBooleanMethod(jPorts, gArrayListMethods.add, jAudioPort);
1244    }
1245
1246exit:
1247    nGeneration = env->GetIntArrayElements(jGeneration, NULL);
1248    if (nGeneration == NULL) {
1249        jStatus = (jint)AUDIO_JAVA_ERROR;
1250    } else {
1251        nGeneration[0] = generation1;
1252        env->ReleaseIntArrayElements(jGeneration, nGeneration, 0);
1253    }
1254    free(nPorts);
1255    return jStatus;
1256}
1257
1258static int
1259android_media_AudioSystem_createAudioPatch(JNIEnv *env, jobject clazz,
1260                                 jobjectArray jPatches, jobjectArray jSources, jobjectArray jSinks)
1261{
1262    status_t status;
1263    jint jStatus;
1264
1265    ALOGV("createAudioPatch");
1266    if (jPatches == NULL || jSources == NULL || jSinks == NULL) {
1267        return (jint)AUDIO_JAVA_BAD_VALUE;
1268    }
1269
1270    if (env->GetArrayLength(jPatches) != 1) {
1271        return (jint)AUDIO_JAVA_BAD_VALUE;
1272    }
1273    jint numSources = env->GetArrayLength(jSources);
1274    if (numSources == 0 || numSources > AUDIO_PATCH_PORTS_MAX) {
1275        return (jint)AUDIO_JAVA_BAD_VALUE;
1276    }
1277
1278    jint numSinks = env->GetArrayLength(jSinks);
1279    if (numSinks == 0 || numSinks > AUDIO_PATCH_PORTS_MAX) {
1280        return (jint)AUDIO_JAVA_BAD_VALUE;
1281    }
1282
1283    audio_patch_handle_t handle = (audio_patch_handle_t)0;
1284    jobject jPatch = env->GetObjectArrayElement(jPatches, 0);
1285    jobject jPatchHandle = NULL;
1286    if (jPatch != NULL) {
1287        if (!env->IsInstanceOf(jPatch, gAudioPatchClass)) {
1288            return (jint)AUDIO_JAVA_BAD_VALUE;
1289        }
1290        jPatchHandle = env->GetObjectField(jPatch, gAudioPatchFields.mHandle);
1291        handle = (audio_patch_handle_t)env->GetIntField(jPatchHandle, gAudioHandleFields.mId);
1292    }
1293
1294    struct audio_patch nPatch;
1295
1296    nPatch.id = handle;
1297    nPatch.num_sources = 0;
1298    nPatch.num_sinks = 0;
1299    jobject jSource = NULL;
1300    jobject jSink = NULL;
1301
1302    for (jint i = 0; i < numSources; i++) {
1303        jSource = env->GetObjectArrayElement(jSources, i);
1304        if (!env->IsInstanceOf(jSource, gAudioPortConfigClass)) {
1305            jStatus = (jint)AUDIO_JAVA_BAD_VALUE;
1306            goto exit;
1307        }
1308        jStatus = convertAudioPortConfigToNative(env, &nPatch.sources[i], jSource, false);
1309        env->DeleteLocalRef(jSource);
1310        jSource = NULL;
1311        if (jStatus != AUDIO_JAVA_SUCCESS) {
1312            goto exit;
1313        }
1314        nPatch.num_sources++;
1315    }
1316
1317    for (jint i = 0; i < numSinks; i++) {
1318        jSink = env->GetObjectArrayElement(jSinks, i);
1319        if (!env->IsInstanceOf(jSink, gAudioPortConfigClass)) {
1320            jStatus = (jint)AUDIO_JAVA_BAD_VALUE;
1321            goto exit;
1322        }
1323        jStatus = convertAudioPortConfigToNative(env, &nPatch.sinks[i], jSink, false);
1324        env->DeleteLocalRef(jSink);
1325        jSink = NULL;
1326        if (jStatus != AUDIO_JAVA_SUCCESS) {
1327            goto exit;
1328        }
1329        nPatch.num_sinks++;
1330    }
1331
1332    ALOGV("AudioSystem::createAudioPatch");
1333    status = AudioSystem::createAudioPatch(&nPatch, &handle);
1334    ALOGV("AudioSystem::createAudioPatch() returned %d hande %d", status, handle);
1335
1336    jStatus = nativeToJavaStatus(status);
1337    if (jStatus != AUDIO_JAVA_SUCCESS) {
1338        goto exit;
1339    }
1340
1341    if (jPatchHandle == NULL) {
1342        jPatchHandle = env->NewObject(gAudioHandleClass, gAudioHandleCstor,
1343                                           handle);
1344        if (jPatchHandle == NULL) {
1345            jStatus = (jint)AUDIO_JAVA_ERROR;
1346            goto exit;
1347        }
1348        jPatch = env->NewObject(gAudioPatchClass, gAudioPatchCstor, jPatchHandle, jSources, jSinks);
1349        if (jPatch == NULL) {
1350            jStatus = (jint)AUDIO_JAVA_ERROR;
1351            goto exit;
1352        }
1353        env->SetObjectArrayElement(jPatches, 0, jPatch);
1354    } else {
1355        env->SetIntField(jPatchHandle, gAudioHandleFields.mId, handle);
1356    }
1357
1358exit:
1359    if (jPatchHandle != NULL) {
1360        env->DeleteLocalRef(jPatchHandle);
1361    }
1362    if (jPatch != NULL) {
1363        env->DeleteLocalRef(jPatch);
1364    }
1365    if (jSource != NULL) {
1366        env->DeleteLocalRef(jSource);
1367    }
1368    if (jSink != NULL) {
1369        env->DeleteLocalRef(jSink);
1370    }
1371    return jStatus;
1372}
1373
1374static jint
1375android_media_AudioSystem_releaseAudioPatch(JNIEnv *env, jobject clazz,
1376                                               jobject jPatch)
1377{
1378    ALOGV("releaseAudioPatch");
1379    if (jPatch == NULL) {
1380        return (jint)AUDIO_JAVA_BAD_VALUE;
1381    }
1382
1383    audio_patch_handle_t handle = (audio_patch_handle_t)0;
1384    jobject jPatchHandle = NULL;
1385    if (!env->IsInstanceOf(jPatch, gAudioPatchClass)) {
1386        return (jint)AUDIO_JAVA_BAD_VALUE;
1387    }
1388    jPatchHandle = env->GetObjectField(jPatch, gAudioPatchFields.mHandle);
1389    handle = (audio_patch_handle_t)env->GetIntField(jPatchHandle, gAudioHandleFields.mId);
1390    env->DeleteLocalRef(jPatchHandle);
1391
1392    ALOGV("AudioSystem::releaseAudioPatch");
1393    status_t status = AudioSystem::releaseAudioPatch(handle);
1394    ALOGV("AudioSystem::releaseAudioPatch() returned %d", status);
1395    jint jStatus = nativeToJavaStatus(status);
1396    return jStatus;
1397}
1398
1399static jint
1400android_media_AudioSystem_listAudioPatches(JNIEnv *env, jobject clazz,
1401                                           jobject jPatches, jintArray jGeneration)
1402{
1403    ALOGV("listAudioPatches");
1404    if (jPatches == NULL) {
1405        ALOGE("listAudioPatches NULL AudioPatch ArrayList");
1406        return (jint)AUDIO_JAVA_BAD_VALUE;
1407    }
1408    if (!env->IsInstanceOf(jPatches, gArrayListClass)) {
1409        ALOGE("listAudioPatches not an arraylist");
1410        return (jint)AUDIO_JAVA_BAD_VALUE;
1411    }
1412
1413    if (jGeneration == NULL || env->GetArrayLength(jGeneration) != 1) {
1414        return (jint)AUDIO_JAVA_BAD_VALUE;
1415    }
1416
1417    status_t status;
1418    unsigned int generation1;
1419    unsigned int generation;
1420    unsigned int numPatches;
1421    jint *nGeneration;
1422    struct audio_patch *nPatches = NULL;
1423    jobjectArray jSources = NULL;
1424    jobject jSource = NULL;
1425    jobjectArray jSinks = NULL;
1426    jobject jSink = NULL;
1427    jobject jPatch = NULL;
1428    int attempts = MAX_PORT_GENERATION_SYNC_ATTEMPTS;
1429    jint jStatus;
1430
1431    // get the patch count and all the patches until they both return the same generation
1432    do {
1433        if (attempts-- < 0) {
1434            status = TIMED_OUT;
1435            break;
1436        }
1437
1438        numPatches = 0;
1439        status = AudioSystem::listAudioPatches(&numPatches,
1440                                               NULL,
1441                                               &generation1);
1442        if (status != NO_ERROR) {
1443            ALOGE_IF(status != NO_ERROR, "listAudioPatches AudioSystem::listAudioPatches error %d",
1444                                      status);
1445            break;
1446        }
1447        if (numPatches == 0) {
1448            jStatus = (jint)AUDIO_JAVA_SUCCESS;
1449            goto exit;
1450        }
1451
1452        nPatches = (struct audio_patch *)realloc(nPatches, numPatches * sizeof(struct audio_patch));
1453
1454        status = AudioSystem::listAudioPatches(&numPatches,
1455                                               nPatches,
1456                                               &generation);
1457        ALOGV("listAudioPatches AudioSystem::listAudioPatches numPatches %d generation %d generation1 %d",
1458              numPatches, generation, generation1);
1459
1460    } while (generation1 != generation && status == NO_ERROR);
1461
1462    jStatus = nativeToJavaStatus(status);
1463    if (jStatus != AUDIO_JAVA_SUCCESS) {
1464        goto exit;
1465    }
1466
1467    for (size_t i = 0; i < numPatches; i++) {
1468        jobject patchHandle = env->NewObject(gAudioHandleClass, gAudioHandleCstor,
1469                                                 nPatches[i].id);
1470        if (patchHandle == NULL) {
1471            jStatus = AUDIO_JAVA_ERROR;
1472            goto exit;
1473        }
1474        ALOGV("listAudioPatches patch %zu num_sources %d num_sinks %d",
1475              i, nPatches[i].num_sources, nPatches[i].num_sinks);
1476
1477        env->SetIntField(patchHandle, gAudioHandleFields.mId, nPatches[i].id);
1478
1479        // load sources
1480        jSources = env->NewObjectArray(nPatches[i].num_sources,
1481                                       gAudioPortConfigClass, NULL);
1482        if (jSources == NULL) {
1483            jStatus = AUDIO_JAVA_ERROR;
1484            goto exit;
1485        }
1486
1487        for (size_t j = 0; j < nPatches[i].num_sources; j++) {
1488            jStatus = convertAudioPortConfigFromNative(env,
1489                                                      NULL,
1490                                                      &jSource,
1491                                                      &nPatches[i].sources[j]);
1492            if (jStatus != AUDIO_JAVA_SUCCESS) {
1493                goto exit;
1494            }
1495            env->SetObjectArrayElement(jSources, j, jSource);
1496            env->DeleteLocalRef(jSource);
1497            jSource = NULL;
1498            ALOGV("listAudioPatches patch %zu source %zu is a %s handle %d",
1499                  i, j,
1500                  nPatches[i].sources[j].type == AUDIO_PORT_TYPE_DEVICE ? "device" : "mix",
1501                  nPatches[i].sources[j].id);
1502        }
1503        // load sinks
1504        jSinks = env->NewObjectArray(nPatches[i].num_sinks,
1505                                     gAudioPortConfigClass, NULL);
1506        if (jSinks == NULL) {
1507            jStatus = AUDIO_JAVA_ERROR;
1508            goto exit;
1509        }
1510
1511        for (size_t j = 0; j < nPatches[i].num_sinks; j++) {
1512            jStatus = convertAudioPortConfigFromNative(env,
1513                                                      NULL,
1514                                                      &jSink,
1515                                                      &nPatches[i].sinks[j]);
1516
1517            if (jStatus != AUDIO_JAVA_SUCCESS) {
1518                goto exit;
1519            }
1520            env->SetObjectArrayElement(jSinks, j, jSink);
1521            env->DeleteLocalRef(jSink);
1522            jSink = NULL;
1523            ALOGV("listAudioPatches patch %zu sink %zu is a %s handle %d",
1524                  i, j,
1525                  nPatches[i].sinks[j].type == AUDIO_PORT_TYPE_DEVICE ? "device" : "mix",
1526                  nPatches[i].sinks[j].id);
1527        }
1528
1529        jPatch = env->NewObject(gAudioPatchClass, gAudioPatchCstor,
1530                                       patchHandle, jSources, jSinks);
1531        env->DeleteLocalRef(jSources);
1532        jSources = NULL;
1533        env->DeleteLocalRef(jSinks);
1534        jSinks = NULL;
1535        if (jPatch == NULL) {
1536            jStatus = AUDIO_JAVA_ERROR;
1537            goto exit;
1538        }
1539        env->CallBooleanMethod(jPatches, gArrayListMethods.add, jPatch);
1540        env->DeleteLocalRef(jPatch);
1541        jPatch = NULL;
1542    }
1543
1544exit:
1545
1546    nGeneration = env->GetIntArrayElements(jGeneration, NULL);
1547    if (nGeneration == NULL) {
1548        jStatus = AUDIO_JAVA_ERROR;
1549    } else {
1550        nGeneration[0] = generation1;
1551        env->ReleaseIntArrayElements(jGeneration, nGeneration, 0);
1552    }
1553
1554    if (jSources != NULL) {
1555        env->DeleteLocalRef(jSources);
1556    }
1557    if (jSource != NULL) {
1558        env->DeleteLocalRef(jSource);
1559    }
1560    if (jSinks != NULL) {
1561        env->DeleteLocalRef(jSinks);
1562    }
1563    if (jSink != NULL) {
1564        env->DeleteLocalRef(jSink);
1565    }
1566    if (jPatch != NULL) {
1567        env->DeleteLocalRef(jPatch);
1568    }
1569    free(nPatches);
1570    return jStatus;
1571}
1572
1573static jint
1574android_media_AudioSystem_setAudioPortConfig(JNIEnv *env, jobject clazz,
1575                                 jobject jAudioPortConfig)
1576{
1577    ALOGV("setAudioPortConfig");
1578    if (jAudioPortConfig == NULL) {
1579        return AUDIO_JAVA_BAD_VALUE;
1580    }
1581    if (!env->IsInstanceOf(jAudioPortConfig, gAudioPortConfigClass)) {
1582        return AUDIO_JAVA_BAD_VALUE;
1583    }
1584    struct audio_port_config nAudioPortConfig;
1585    jint jStatus = convertAudioPortConfigToNative(env, &nAudioPortConfig, jAudioPortConfig, true);
1586    if (jStatus != AUDIO_JAVA_SUCCESS) {
1587        return jStatus;
1588    }
1589    status_t status = AudioSystem::setAudioPortConfig(&nAudioPortConfig);
1590    ALOGV("AudioSystem::setAudioPortConfig() returned %d", status);
1591    jStatus = nativeToJavaStatus(status);
1592    return jStatus;
1593}
1594
1595static void
1596android_media_AudioSystem_eventHandlerSetup(JNIEnv *env, jobject thiz, jobject weak_this)
1597{
1598    ALOGV("eventHandlerSetup");
1599
1600    sp<JNIAudioPortCallback> callback = new JNIAudioPortCallback(env, thiz, weak_this);
1601
1602    if (AudioSystem::addAudioPortCallback(callback) == NO_ERROR) {
1603        setJniCallback(env, thiz, callback);
1604    }
1605}
1606
1607static void
1608android_media_AudioSystem_eventHandlerFinalize(JNIEnv *env, jobject thiz)
1609{
1610    ALOGV("eventHandlerFinalize");
1611
1612    sp<JNIAudioPortCallback> callback = setJniCallback(env, thiz, 0);
1613
1614    if (callback != 0) {
1615        AudioSystem::removeAudioPortCallback(callback);
1616    }
1617}
1618
1619static jint
1620android_media_AudioSystem_getAudioHwSyncForSession(JNIEnv *env, jobject thiz, jint sessionId)
1621{
1622    return (jint) AudioSystem::getAudioHwSyncForSession((audio_session_t) sessionId);
1623}
1624
1625static void
1626android_media_AudioSystem_registerDynPolicyCallback(JNIEnv *env, jobject thiz)
1627{
1628    AudioSystem::setDynPolicyCallback(android_media_AudioSystem_dyn_policy_callback);
1629}
1630
1631static void
1632android_media_AudioSystem_registerRecordingCallback(JNIEnv *env, jobject thiz)
1633{
1634    AudioSystem::setRecordConfigCallback(android_media_AudioSystem_recording_callback);
1635}
1636
1637
1638static jint convertAudioMixToNative(JNIEnv *env,
1639                                    AudioMix *nAudioMix,
1640                                    const jobject jAudioMix)
1641{
1642    nAudioMix->mMixType = env->GetIntField(jAudioMix, gAudioMixFields.mMixType);
1643    nAudioMix->mRouteFlags = env->GetIntField(jAudioMix, gAudioMixFields.mRouteFlags);
1644    nAudioMix->mDeviceType = (audio_devices_t)
1645            env->GetIntField(jAudioMix, gAudioMixFields.mDeviceType);
1646
1647    jstring jDeviceAddress = (jstring)env->GetObjectField(jAudioMix,
1648                                                           gAudioMixFields.mDeviceAddress);
1649    const char *nDeviceAddress = env->GetStringUTFChars(jDeviceAddress, NULL);
1650    nAudioMix->mDeviceAddress = String8(nDeviceAddress);
1651    env->ReleaseStringUTFChars(jDeviceAddress, nDeviceAddress);
1652    env->DeleteLocalRef(jDeviceAddress);
1653
1654    nAudioMix->mCbFlags = env->GetIntField(jAudioMix, gAudioMixFields.mCallbackFlags);
1655
1656    jobject jFormat = env->GetObjectField(jAudioMix, gAudioMixFields.mFormat);
1657    nAudioMix->mFormat.sample_rate = env->GetIntField(jFormat,
1658                                                     gAudioFormatFields.mSampleRate);
1659    nAudioMix->mFormat.channel_mask = outChannelMaskToNative(env->GetIntField(jFormat,
1660                                                     gAudioFormatFields.mChannelMask));
1661    nAudioMix->mFormat.format = audioFormatToNative(env->GetIntField(jFormat,
1662                                                     gAudioFormatFields.mEncoding));
1663    env->DeleteLocalRef(jFormat);
1664
1665    jobject jRule = env->GetObjectField(jAudioMix, gAudioMixFields.mRule);
1666    jobject jRuleCriteria = env->GetObjectField(jRule, gAudioMixingRuleFields.mCriteria);
1667    env->DeleteLocalRef(jRule);
1668    jobjectArray jCriteria = (jobjectArray)env->CallObjectMethod(jRuleCriteria,
1669                                                                 gArrayListMethods.toArray);
1670    env->DeleteLocalRef(jRuleCriteria);
1671
1672    jint numCriteria = env->GetArrayLength(jCriteria);
1673    if (numCriteria > MAX_CRITERIA_PER_MIX) {
1674        numCriteria = MAX_CRITERIA_PER_MIX;
1675    }
1676
1677    for (jint i = 0; i < numCriteria; i++) {
1678        AudioMixMatchCriterion nCriterion;
1679
1680        jobject jCriterion = env->GetObjectArrayElement(jCriteria, i);
1681
1682        nCriterion.mRule = env->GetIntField(jCriterion, gAudioMixMatchCriterionFields.mRule);
1683
1684        const uint32_t match_rule = nCriterion.mRule & ~RULE_EXCLUSION_MASK;
1685        switch (match_rule) {
1686        case RULE_MATCH_UID:
1687            nCriterion.mValue.mUid = env->GetIntField(jCriterion,
1688                    gAudioMixMatchCriterionFields.mIntProp);
1689            break;
1690        case RULE_MATCH_ATTRIBUTE_USAGE:
1691        case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET: {
1692            jobject jAttributes = env->GetObjectField(jCriterion, gAudioMixMatchCriterionFields.mAttr);
1693            if (match_rule == RULE_MATCH_ATTRIBUTE_USAGE) {
1694                nCriterion.mValue.mUsage = (audio_usage_t)env->GetIntField(jAttributes,
1695                        gAudioAttributesFields.mUsage);
1696            } else {
1697                nCriterion.mValue.mSource = (audio_source_t)env->GetIntField(jAttributes,
1698                        gAudioAttributesFields.mSource);
1699            }
1700            env->DeleteLocalRef(jAttributes);
1701            }
1702            break;
1703        }
1704
1705        nAudioMix->mCriteria.add(nCriterion);
1706        env->DeleteLocalRef(jCriterion);
1707    }
1708
1709    env->DeleteLocalRef(jCriteria);
1710
1711    return (jint)AUDIO_JAVA_SUCCESS;
1712}
1713
1714static jint
1715android_media_AudioSystem_registerPolicyMixes(JNIEnv *env, jobject clazz,
1716                                              jobject jMixesList, jboolean registration)
1717{
1718    ALOGV("registerPolicyMixes");
1719
1720    if (jMixesList == NULL) {
1721        return (jint)AUDIO_JAVA_BAD_VALUE;
1722    }
1723    if (!env->IsInstanceOf(jMixesList, gArrayListClass)) {
1724        return (jint)AUDIO_JAVA_BAD_VALUE;
1725    }
1726    jobjectArray jMixes = (jobjectArray)env->CallObjectMethod(jMixesList,
1727                                                              gArrayListMethods.toArray);
1728    jint numMixes = env->GetArrayLength(jMixes);
1729    if (numMixes > MAX_MIXES_PER_POLICY) {
1730        numMixes = MAX_MIXES_PER_POLICY;
1731    }
1732
1733    status_t status;
1734    jint jStatus;
1735    jobject jAudioMix = NULL;
1736    Vector <AudioMix> mixes;
1737    for (jint i = 0; i < numMixes; i++) {
1738        jAudioMix = env->GetObjectArrayElement(jMixes, i);
1739        if (!env->IsInstanceOf(jAudioMix, gAudioMixClass)) {
1740            jStatus = (jint)AUDIO_JAVA_BAD_VALUE;
1741            goto exit;
1742        }
1743        AudioMix mix;
1744        jStatus = convertAudioMixToNative(env, &mix, jAudioMix);
1745        env->DeleteLocalRef(jAudioMix);
1746        jAudioMix = NULL;
1747        if (jStatus != AUDIO_JAVA_SUCCESS) {
1748            goto exit;
1749        }
1750        mixes.add(mix);
1751    }
1752
1753    ALOGV("AudioSystem::registerPolicyMixes numMixes %d registration %d", numMixes, registration);
1754    status = AudioSystem::registerPolicyMixes(mixes, registration);
1755    ALOGV("AudioSystem::registerPolicyMixes() returned %d", status);
1756
1757    jStatus = nativeToJavaStatus(status);
1758    if (jStatus != AUDIO_JAVA_SUCCESS) {
1759        goto exit;
1760    }
1761
1762exit:
1763    if (jAudioMix != NULL) {
1764        env->DeleteLocalRef(jAudioMix);
1765    }
1766    return jStatus;
1767}
1768
1769static jint
1770android_media_AudioSystem_systemReady(JNIEnv *env, jobject thiz)
1771{
1772    return nativeToJavaStatus(AudioSystem::systemReady());
1773}
1774
1775static jfloat
1776android_media_AudioSystem_getStreamVolumeDB(JNIEnv *env, jobject thiz,
1777                                            jint stream, jint index, jint device)
1778{
1779    return (jfloat)AudioSystem::getStreamVolumeDB((audio_stream_type_t)stream,
1780                                                  (int)index,
1781                                                  (audio_devices_t)device);
1782}
1783
1784static jboolean
1785android_media_AudioSystem_isOffloadSupported(JNIEnv *env, jobject thiz,
1786        jint encoding, jint sampleRate, jint channelMask, jint channelIndexMask)
1787{
1788    audio_offload_info_t format = AUDIO_INFO_INITIALIZER;
1789    format.format = (audio_format_t) audioFormatToNative(encoding);
1790    format.sample_rate = (uint32_t) sampleRate;
1791    format.channel_mask = nativeChannelMaskFromJavaChannelMasks(channelMask, channelIndexMask);
1792    format.stream_type = AUDIO_STREAM_MUSIC;
1793    format.has_video = false;
1794    format.is_streaming = false;
1795    // offload duration unknown at this point:
1796    // client side code cannot access "audio.offload.min.duration.secs" property to make a query
1797    // agnostic of duration, so using acceptable estimate of 2mn
1798    format.duration_us = 120 * 1000000;
1799    return AudioSystem::isOffloadSupported(format);
1800}
1801
1802static jint
1803android_media_AudioSystem_getMicrophones(JNIEnv *env, jobject thiz, jobject jMicrophonesInfo)
1804{
1805    ALOGV("getMicrophones");
1806
1807    if (jMicrophonesInfo == NULL) {
1808        ALOGE("jMicrophonesInfo NULL MicrophoneInfo ArrayList");
1809        return (jint)AUDIO_JAVA_BAD_VALUE;
1810    }
1811    if (!env->IsInstanceOf(jMicrophonesInfo, gArrayListClass)) {
1812        ALOGE("getMicrophones not an arraylist");
1813        return (jint)AUDIO_JAVA_BAD_VALUE;
1814    }
1815
1816    jint jStatus;
1817    std::vector<media::MicrophoneInfo> microphones;
1818    status_t status = AudioSystem::getMicrophones(&microphones);
1819    if (status != NO_ERROR) {
1820        ALOGE_IF(status != NO_ERROR, "AudioSystem::getMicrophones error %d", status);
1821        jStatus = nativeToJavaStatus(status);
1822        return jStatus;
1823    }
1824    if (microphones.size() == 0) {
1825        jStatus = (jint)AUDIO_JAVA_SUCCESS;
1826        return jStatus;
1827    }
1828    for (size_t i = 0; i < microphones.size(); i++) {
1829        jobject jMicrophoneInfo;
1830        jStatus = convertMicrophoneInfoFromNative(env, &jMicrophoneInfo, &microphones[i]);
1831        if (jStatus != AUDIO_JAVA_SUCCESS) {
1832            return jStatus;
1833        }
1834        env->CallBooleanMethod(jMicrophonesInfo, gArrayListMethods.add, jMicrophoneInfo);
1835        env->DeleteLocalRef(jMicrophoneInfo);
1836    }
1837
1838    return jStatus;
1839}
1840
1841static jint
1842android_media_AudioSystem_getSurroundFormats(JNIEnv *env, jobject thiz,
1843                                             jobject jSurroundFormats, jboolean reported)
1844{
1845    ALOGV("getSurroundFormats");
1846
1847    if (jSurroundFormats == NULL) {
1848        ALOGE("jSurroundFormats is NULL");
1849        return (jint)AUDIO_JAVA_BAD_VALUE;
1850    }
1851    if (!env->IsInstanceOf(jSurroundFormats, gMapClass)) {
1852        ALOGE("getSurroundFormats not a map");
1853        return (jint)AUDIO_JAVA_BAD_VALUE;
1854    }
1855
1856    jint jStatus;
1857    unsigned int numSurroundFormats = 0;
1858    audio_format_t *surroundFormats = NULL;
1859    bool *surroundFormatsEnabled = NULL;
1860    status_t status = AudioSystem::getSurroundFormats(
1861            &numSurroundFormats, surroundFormats, surroundFormatsEnabled, reported);
1862    if (status != NO_ERROR) {
1863        ALOGE_IF(status != NO_ERROR, "AudioSystem::getSurroundFormats error %d", status);
1864        jStatus = nativeToJavaStatus(status);
1865        goto exit;
1866    }
1867    if (numSurroundFormats == 0) {
1868        jStatus = (jint)AUDIO_JAVA_SUCCESS;
1869        goto exit;
1870    }
1871    surroundFormats = (audio_format_t *)calloc(numSurroundFormats, sizeof(audio_format_t));
1872    surroundFormatsEnabled = (bool *)calloc(numSurroundFormats, sizeof(bool));
1873    status = AudioSystem::getSurroundFormats(
1874            &numSurroundFormats, surroundFormats, surroundFormatsEnabled, reported);
1875    jStatus = nativeToJavaStatus(status);
1876    if (status != NO_ERROR) {
1877        ALOGE_IF(status != NO_ERROR, "AudioSystem::getSurroundFormats error %d", status);
1878        goto exit;
1879    }
1880    for (size_t i = 0; i < numSurroundFormats; i++) {
1881        jobject surroundFormat = env->NewObject(gIntegerClass, gIntegerCstor,
1882                                                audioFormatFromNative(surroundFormats[i]));
1883        jobject enabled = env->NewObject(gBooleanClass, gBooleanCstor, surroundFormatsEnabled[i]);
1884        env->CallObjectMethod(jSurroundFormats, gMapPut, surroundFormat, enabled);
1885        env->DeleteLocalRef(surroundFormat);
1886        env->DeleteLocalRef(enabled);
1887    }
1888
1889exit:
1890    free(surroundFormats);
1891    free(surroundFormatsEnabled);
1892    return jStatus;
1893}
1894
1895static jint
1896android_media_AudioSystem_setSurroundFormatEnabled(JNIEnv *env, jobject thiz,
1897                                                   jint audioFormat, jboolean enabled)
1898{
1899    status_t status = AudioSystem::setSurroundFormatEnabled(audioFormatToNative(audioFormat),
1900                                                            (bool)enabled);
1901    if (status != NO_ERROR) {
1902        ALOGE_IF(status != NO_ERROR, "AudioSystem::setSurroundFormatEnabled error %d", status);
1903    }
1904    return (jint)nativeToJavaStatus(status);
1905}
1906
1907// ----------------------------------------------------------------------------
1908
1909static const JNINativeMethod gMethods[] = {
1910    {"setParameters",        "(Ljava/lang/String;)I", (void *)android_media_AudioSystem_setParameters},
1911    {"getParameters",        "(Ljava/lang/String;)Ljava/lang/String;", (void *)android_media_AudioSystem_getParameters},
1912    {"muteMicrophone",      "(Z)I",     (void *)android_media_AudioSystem_muteMicrophone},
1913    {"isMicrophoneMuted",   "()Z",      (void *)android_media_AudioSystem_isMicrophoneMuted},
1914    {"isStreamActive",      "(II)Z",    (void *)android_media_AudioSystem_isStreamActive},
1915    {"isStreamActiveRemotely","(II)Z",  (void *)android_media_AudioSystem_isStreamActiveRemotely},
1916    {"isSourceActive",      "(I)Z",     (void *)android_media_AudioSystem_isSourceActive},
1917    {"newAudioSessionId",   "()I",      (void *)android_media_AudioSystem_newAudioSessionId},
1918    {"newAudioPlayerId",    "()I",      (void *)android_media_AudioSystem_newAudioPlayerId},
1919    {"setDeviceConnectionState", "(IILjava/lang/String;Ljava/lang/String;)I", (void *)android_media_AudioSystem_setDeviceConnectionState},
1920    {"getDeviceConnectionState", "(ILjava/lang/String;)I",  (void *)android_media_AudioSystem_getDeviceConnectionState},
1921    {"handleDeviceConfigChange", "(ILjava/lang/String;Ljava/lang/String;)I", (void *)android_media_AudioSystem_handleDeviceConfigChange},
1922    {"setPhoneState",       "(I)I",     (void *)android_media_AudioSystem_setPhoneState},
1923    {"setForceUse",         "(II)I",    (void *)android_media_AudioSystem_setForceUse},
1924    {"getForceUse",         "(I)I",     (void *)android_media_AudioSystem_getForceUse},
1925    {"initStreamVolume",    "(III)I",   (void *)android_media_AudioSystem_initStreamVolume},
1926    {"setStreamVolumeIndex","(III)I",   (void *)android_media_AudioSystem_setStreamVolumeIndex},
1927    {"getStreamVolumeIndex","(II)I",    (void *)android_media_AudioSystem_getStreamVolumeIndex},
1928    {"setMasterVolume",     "(F)I",     (void *)android_media_AudioSystem_setMasterVolume},
1929    {"getMasterVolume",     "()F",      (void *)android_media_AudioSystem_getMasterVolume},
1930    {"setMasterMute",       "(Z)I",     (void *)android_media_AudioSystem_setMasterMute},
1931    {"getMasterMute",       "()Z",      (void *)android_media_AudioSystem_getMasterMute},
1932    {"setMasterMono",       "(Z)I",     (void *)android_media_AudioSystem_setMasterMono},
1933    {"getMasterMono",       "()Z",      (void *)android_media_AudioSystem_getMasterMono},
1934    {"getDevicesForStream", "(I)I",     (void *)android_media_AudioSystem_getDevicesForStream},
1935    {"getPrimaryOutputSamplingRate", "()I", (void *)android_media_AudioSystem_getPrimaryOutputSamplingRate},
1936    {"getPrimaryOutputFrameCount",   "()I", (void *)android_media_AudioSystem_getPrimaryOutputFrameCount},
1937    {"getOutputLatency",    "(I)I",     (void *)android_media_AudioSystem_getOutputLatency},
1938    {"setLowRamDevice",     "(ZJ)I",    (void *)android_media_AudioSystem_setLowRamDevice},
1939    {"checkAudioFlinger",    "()I",     (void *)android_media_AudioSystem_checkAudioFlinger},
1940    {"listAudioPorts",      "(Ljava/util/ArrayList;[I)I",
1941                                                (void *)android_media_AudioSystem_listAudioPorts},
1942    {"createAudioPatch",    "([Landroid/media/AudioPatch;[Landroid/media/AudioPortConfig;[Landroid/media/AudioPortConfig;)I",
1943                                            (void *)android_media_AudioSystem_createAudioPatch},
1944    {"releaseAudioPatch",   "(Landroid/media/AudioPatch;)I",
1945                                            (void *)android_media_AudioSystem_releaseAudioPatch},
1946    {"listAudioPatches",    "(Ljava/util/ArrayList;[I)I",
1947                                                (void *)android_media_AudioSystem_listAudioPatches},
1948    {"setAudioPortConfig",   "(Landroid/media/AudioPortConfig;)I",
1949                                            (void *)android_media_AudioSystem_setAudioPortConfig},
1950    {"getAudioHwSyncForSession", "(I)I",
1951                                    (void *)android_media_AudioSystem_getAudioHwSyncForSession},
1952    {"registerPolicyMixes",    "(Ljava/util/ArrayList;Z)I",
1953                                            (void *)android_media_AudioSystem_registerPolicyMixes},
1954    {"native_register_dynamic_policy_callback", "()V",
1955                                    (void *)android_media_AudioSystem_registerDynPolicyCallback},
1956    {"native_register_recording_callback", "()V",
1957                                    (void *)android_media_AudioSystem_registerRecordingCallback},
1958    {"systemReady", "()I", (void *)android_media_AudioSystem_systemReady},
1959    {"getStreamVolumeDB", "(III)F", (void *)android_media_AudioSystem_getStreamVolumeDB},
1960    {"native_is_offload_supported", "(IIII)Z", (void *)android_media_AudioSystem_isOffloadSupported},
1961    {"getMicrophones", "(Ljava/util/ArrayList;)I", (void *)android_media_AudioSystem_getMicrophones},
1962    {"getSurroundFormats", "(Ljava/util/Map;Z)I", (void *)android_media_AudioSystem_getSurroundFormats},
1963    {"setSurroundFormatEnabled", "(IZ)I", (void *)android_media_AudioSystem_setSurroundFormatEnabled},
1964};
1965
1966
1967static const JNINativeMethod gEventHandlerMethods[] = {
1968    {"native_setup",
1969        "(Ljava/lang/Object;)V",
1970        (void *)android_media_AudioSystem_eventHandlerSetup},
1971    {"native_finalize",
1972        "()V",
1973        (void *)android_media_AudioSystem_eventHandlerFinalize},
1974};
1975
1976int register_android_media_AudioSystem(JNIEnv *env)
1977{
1978    jclass arrayListClass = FindClassOrDie(env, "java/util/ArrayList");
1979    gArrayListClass = MakeGlobalRefOrDie(env, arrayListClass);
1980    gArrayListMethods.add = GetMethodIDOrDie(env, arrayListClass, "add", "(Ljava/lang/Object;)Z");
1981    gArrayListMethods.toArray = GetMethodIDOrDie(env, arrayListClass, "toArray", "()[Ljava/lang/Object;");
1982
1983    jclass booleanClass = FindClassOrDie(env, "java/lang/Boolean");
1984    gBooleanClass = MakeGlobalRefOrDie(env, booleanClass);
1985    gBooleanCstor = GetMethodIDOrDie(env, booleanClass, "<init>", "(Z)V");
1986
1987    jclass integerClass = FindClassOrDie(env, "java/lang/Integer");
1988    gIntegerClass = MakeGlobalRefOrDie(env, integerClass);
1989    gIntegerCstor = GetMethodIDOrDie(env, integerClass, "<init>", "(I)V");
1990
1991    jclass mapClass = FindClassOrDie(env, "java/util/Map");
1992    gMapClass = MakeGlobalRefOrDie(env, mapClass);
1993    gMapPut = GetMethodIDOrDie(env, mapClass, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
1994
1995    jclass audioHandleClass = FindClassOrDie(env, "android/media/AudioHandle");
1996    gAudioHandleClass = MakeGlobalRefOrDie(env, audioHandleClass);
1997    gAudioHandleCstor = GetMethodIDOrDie(env, audioHandleClass, "<init>", "(I)V");
1998    gAudioHandleFields.mId = GetFieldIDOrDie(env, audioHandleClass, "mId", "I");
1999
2000    jclass audioPortClass = FindClassOrDie(env, "android/media/AudioPort");
2001    gAudioPortClass = MakeGlobalRefOrDie(env, audioPortClass);
2002    gAudioPortCstor = GetMethodIDOrDie(env, audioPortClass, "<init>",
2003            "(Landroid/media/AudioHandle;ILjava/lang/String;[I[I[I[I[Landroid/media/AudioGain;)V");
2004    gAudioPortFields.mHandle = GetFieldIDOrDie(env, audioPortClass, "mHandle",
2005                                               "Landroid/media/AudioHandle;");
2006    gAudioPortFields.mRole = GetFieldIDOrDie(env, audioPortClass, "mRole", "I");
2007    gAudioPortFields.mGains = GetFieldIDOrDie(env, audioPortClass, "mGains",
2008                                              "[Landroid/media/AudioGain;");
2009    gAudioPortFields.mActiveConfig = GetFieldIDOrDie(env, audioPortClass, "mActiveConfig",
2010                                                     "Landroid/media/AudioPortConfig;");
2011
2012    jclass audioPortConfigClass = FindClassOrDie(env, "android/media/AudioPortConfig");
2013    gAudioPortConfigClass = MakeGlobalRefOrDie(env, audioPortConfigClass);
2014    gAudioPortConfigCstor = GetMethodIDOrDie(env, audioPortConfigClass, "<init>",
2015            "(Landroid/media/AudioPort;IIILandroid/media/AudioGainConfig;)V");
2016    gAudioPortConfigFields.mPort = GetFieldIDOrDie(env, audioPortConfigClass, "mPort",
2017                                                   "Landroid/media/AudioPort;");
2018    gAudioPortConfigFields.mSamplingRate = GetFieldIDOrDie(env, audioPortConfigClass,
2019                                                           "mSamplingRate", "I");
2020    gAudioPortConfigFields.mChannelMask = GetFieldIDOrDie(env, audioPortConfigClass,
2021                                                          "mChannelMask", "I");
2022    gAudioPortConfigFields.mFormat = GetFieldIDOrDie(env, audioPortConfigClass, "mFormat", "I");
2023    gAudioPortConfigFields.mGain = GetFieldIDOrDie(env, audioPortConfigClass, "mGain",
2024                                                   "Landroid/media/AudioGainConfig;");
2025    gAudioPortConfigFields.mConfigMask = GetFieldIDOrDie(env, audioPortConfigClass, "mConfigMask",
2026                                                         "I");
2027
2028    jclass audioDevicePortConfigClass = FindClassOrDie(env, "android/media/AudioDevicePortConfig");
2029    gAudioDevicePortConfigClass = MakeGlobalRefOrDie(env, audioDevicePortConfigClass);
2030    gAudioDevicePortConfigCstor = GetMethodIDOrDie(env, audioDevicePortConfigClass, "<init>",
2031            "(Landroid/media/AudioDevicePort;IIILandroid/media/AudioGainConfig;)V");
2032
2033    jclass audioMixPortConfigClass = FindClassOrDie(env, "android/media/AudioMixPortConfig");
2034    gAudioMixPortConfigClass = MakeGlobalRefOrDie(env, audioMixPortConfigClass);
2035    gAudioMixPortConfigCstor = GetMethodIDOrDie(env, audioMixPortConfigClass, "<init>",
2036            "(Landroid/media/AudioMixPort;IIILandroid/media/AudioGainConfig;)V");
2037
2038    jclass audioDevicePortClass = FindClassOrDie(env, "android/media/AudioDevicePort");
2039    gAudioDevicePortClass = MakeGlobalRefOrDie(env, audioDevicePortClass);
2040    gAudioDevicePortCstor = GetMethodIDOrDie(env, audioDevicePortClass, "<init>",
2041            "(Landroid/media/AudioHandle;Ljava/lang/String;[I[I[I[I[Landroid/media/AudioGain;ILjava/lang/String;)V");
2042
2043    jclass audioMixPortClass = FindClassOrDie(env, "android/media/AudioMixPort");
2044    gAudioMixPortClass = MakeGlobalRefOrDie(env, audioMixPortClass);
2045    gAudioMixPortCstor = GetMethodIDOrDie(env, audioMixPortClass, "<init>",
2046            "(Landroid/media/AudioHandle;IILjava/lang/String;[I[I[I[I[Landroid/media/AudioGain;)V");
2047
2048    jclass audioGainClass = FindClassOrDie(env, "android/media/AudioGain");
2049    gAudioGainClass = MakeGlobalRefOrDie(env, audioGainClass);
2050    gAudioGainCstor = GetMethodIDOrDie(env, audioGainClass, "<init>", "(IIIIIIIII)V");
2051
2052    jclass audioGainConfigClass = FindClassOrDie(env, "android/media/AudioGainConfig");
2053    gAudioGainConfigClass = MakeGlobalRefOrDie(env, audioGainConfigClass);
2054    gAudioGainConfigCstor = GetMethodIDOrDie(env, audioGainConfigClass, "<init>",
2055                                             "(ILandroid/media/AudioGain;II[II)V");
2056    gAudioGainConfigFields.mIndex = GetFieldIDOrDie(env, gAudioGainConfigClass, "mIndex", "I");
2057    gAudioGainConfigFields.mMode = GetFieldIDOrDie(env, audioGainConfigClass, "mMode", "I");
2058    gAudioGainConfigFields.mChannelMask = GetFieldIDOrDie(env, audioGainConfigClass, "mChannelMask",
2059                                                          "I");
2060    gAudioGainConfigFields.mValues = GetFieldIDOrDie(env, audioGainConfigClass, "mValues", "[I");
2061    gAudioGainConfigFields.mRampDurationMs = GetFieldIDOrDie(env, audioGainConfigClass,
2062                                                             "mRampDurationMs", "I");
2063
2064    jclass audioPatchClass = FindClassOrDie(env, "android/media/AudioPatch");
2065    gAudioPatchClass = MakeGlobalRefOrDie(env, audioPatchClass);
2066    gAudioPatchCstor = GetMethodIDOrDie(env, audioPatchClass, "<init>",
2067"(Landroid/media/AudioHandle;[Landroid/media/AudioPortConfig;[Landroid/media/AudioPortConfig;)V");
2068    gAudioPatchFields.mHandle = GetFieldIDOrDie(env, audioPatchClass, "mHandle",
2069                                                "Landroid/media/AudioHandle;");
2070
2071    jclass eventHandlerClass = FindClassOrDie(env, kEventHandlerClassPathName);
2072    gAudioPortEventHandlerMethods.postEventFromNative = GetStaticMethodIDOrDie(
2073                                                    env, eventHandlerClass, "postEventFromNative",
2074                                                    "(Ljava/lang/Object;IIILjava/lang/Object;)V");
2075    gEventHandlerFields.mJniCallback = GetFieldIDOrDie(env,
2076                                                    eventHandlerClass, "mJniCallback", "J");
2077
2078    gAudioPolicyEventHandlerMethods.postDynPolicyEventFromNative =
2079            GetStaticMethodIDOrDie(env, env->FindClass(kClassPathName),
2080                    "dynamicPolicyCallbackFromNative", "(ILjava/lang/String;I)V");
2081    gAudioPolicyEventHandlerMethods.postRecordConfigEventFromNative =
2082            GetStaticMethodIDOrDie(env, env->FindClass(kClassPathName),
2083                    "recordingCallbackFromNative", "(IIII[I)V");
2084
2085    jclass audioMixClass = FindClassOrDie(env, "android/media/audiopolicy/AudioMix");
2086    gAudioMixClass = MakeGlobalRefOrDie(env, audioMixClass);
2087    gAudioMixFields.mRule = GetFieldIDOrDie(env, audioMixClass, "mRule",
2088                                                "Landroid/media/audiopolicy/AudioMixingRule;");
2089    gAudioMixFields.mFormat = GetFieldIDOrDie(env, audioMixClass, "mFormat",
2090                                                "Landroid/media/AudioFormat;");
2091    gAudioMixFields.mRouteFlags = GetFieldIDOrDie(env, audioMixClass, "mRouteFlags", "I");
2092    gAudioMixFields.mDeviceType = GetFieldIDOrDie(env, audioMixClass, "mDeviceSystemType", "I");
2093    gAudioMixFields.mDeviceAddress = GetFieldIDOrDie(env, audioMixClass, "mDeviceAddress",
2094                                                      "Ljava/lang/String;");
2095    gAudioMixFields.mMixType = GetFieldIDOrDie(env, audioMixClass, "mMixType", "I");
2096    gAudioMixFields.mCallbackFlags = GetFieldIDOrDie(env, audioMixClass, "mCallbackFlags", "I");
2097
2098    jclass audioFormatClass = FindClassOrDie(env, "android/media/AudioFormat");
2099    gAudioFormatClass = MakeGlobalRefOrDie(env, audioFormatClass);
2100    gAudioFormatFields.mEncoding = GetFieldIDOrDie(env, audioFormatClass, "mEncoding", "I");
2101    gAudioFormatFields.mSampleRate = GetFieldIDOrDie(env, audioFormatClass, "mSampleRate", "I");
2102    gAudioFormatFields.mChannelMask = GetFieldIDOrDie(env, audioFormatClass, "mChannelMask", "I");
2103
2104    jclass audioMixingRuleClass = FindClassOrDie(env, "android/media/audiopolicy/AudioMixingRule");
2105    gAudioMixingRuleClass = MakeGlobalRefOrDie(env, audioMixingRuleClass);
2106    gAudioMixingRuleFields.mCriteria = GetFieldIDOrDie(env, audioMixingRuleClass, "mCriteria",
2107                                                       "Ljava/util/ArrayList;");
2108
2109    jclass audioMixMatchCriterionClass =
2110                FindClassOrDie(env, "android/media/audiopolicy/AudioMixingRule$AudioMixMatchCriterion");
2111    gAudioMixMatchCriterionClass = MakeGlobalRefOrDie(env,audioMixMatchCriterionClass);
2112    gAudioMixMatchCriterionFields.mAttr = GetFieldIDOrDie(env, audioMixMatchCriterionClass, "mAttr",
2113                                                       "Landroid/media/AudioAttributes;");
2114    gAudioMixMatchCriterionFields.mIntProp = GetFieldIDOrDie(env, audioMixMatchCriterionClass, "mIntProp",
2115                                                       "I");
2116    gAudioMixMatchCriterionFields.mRule = GetFieldIDOrDie(env, audioMixMatchCriterionClass, "mRule",
2117                                                       "I");
2118
2119    jclass audioAttributesClass = FindClassOrDie(env, "android/media/AudioAttributes");
2120    gAudioAttributesClass = MakeGlobalRefOrDie(env, audioAttributesClass);
2121    gAudioAttributesFields.mUsage = GetFieldIDOrDie(env, audioAttributesClass, "mUsage", "I");
2122    gAudioAttributesFields.mSource = GetFieldIDOrDie(env, audioAttributesClass, "mSource", "I");
2123
2124    AudioSystem::setErrorCallback(android_media_AudioSystem_error_callback);
2125
2126    RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
2127    return RegisterMethodsOrDie(env, kEventHandlerClassPathName, gEventHandlerMethods,
2128                                NELEM(gEventHandlerMethods));
2129}
2130