1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 //#define LOG_NDEBUG 0 18 19 #define LOG_TAG "AudioVolumeGroups-JNI" 20 21 #include <inttypes.h> 22 #include <jni.h> 23 #include <nativehelper/JNIHelp.h> 24 #include "core_jni_helpers.h" 25 26 #include <utils/Log.h> 27 #include <vector> 28 29 #include <media/AudioSystem.h> 30 #include <media/AudioPolicy.h> 31 32 #include <nativehelper/ScopedUtfChars.h> 33 34 #include "android_media_AudioAttributes.h" 35 #include "android_media_AudioErrors.h" 36 37 // ---------------------------------------------------------------------------- 38 39 using namespace android; 40 41 // ---------------------------------------------------------------------------- 42 static const char* const kClassPathName = "android/media/audiopolicy/AudioVolumeGroup"; 43 static const char* const kAudioVolumeGroupClassPathName = 44 "android/media/audiopolicy/AudioVolumeGroup"; 45 46 static jclass gAudioVolumeGroupClass; 47 static jmethodID gAudioVolumeGroupCstor; 48 static struct { 49 jfieldID mName; 50 jfieldID mId; 51 } gAudioVolumeGroupFields; 52 53 static jclass gArrayListClass; 54 static jmethodID gArrayListCstor; 55 static struct { 56 jmethodID add; 57 jmethodID toArray; 58 } gArrayListMethods; 59 60 61 static jint convertAudioVolumeGroupsFromNative( 62 JNIEnv *env, jobject *jGroup, const AudioVolumeGroup &group) 63 { 64 jint jStatus = (jint)AUDIO_JAVA_SUCCESS; 65 jstring jName = NULL; 66 jint Id = NULL; 67 68 jintArray jLegacyStreamTypes = NULL; 69 jobjectArray jAudioAttributes = NULL; 70 jint numAttributes; 71 jobject jAudioAttribute = NULL; 72 73 jName = env->NewStringUTF(group.getName().c_str()); 74 Id = static_cast<jint>(group.getId()); 75 76 // Legacy stream types array 77 jLegacyStreamTypes = env->NewIntArray(group.getStreamTypes().size()); 78 if (jLegacyStreamTypes == NULL) { 79 jStatus = (jint)AUDIO_JAVA_ERROR; 80 goto exit; 81 } 82 for (size_t streamIndex = 0; streamIndex < group.getStreamTypes().size(); streamIndex++) { 83 jint jStream = group.getStreamTypes()[streamIndex]; 84 env->SetIntArrayRegion(jLegacyStreamTypes, streamIndex, 1, &jStream); 85 } 86 87 // Audio Attributes array 88 numAttributes = group.getAudioAttributes().size(); 89 jStatus = JNIAudioAttributeHelper::getJavaArray(env, &jAudioAttributes, numAttributes); 90 if (jStatus != (jint)AUDIO_JAVA_SUCCESS) { 91 goto exit; 92 } 93 94 for (size_t j = 0; j < static_cast<size_t>(numAttributes); j++) { 95 auto attributes = group.getAudioAttributes()[j]; 96 97 jStatus = JNIAudioAttributeHelper::nativeToJava(env, &jAudioAttribute, attributes); 98 if (jStatus != AUDIO_JAVA_SUCCESS) { 99 goto exit; 100 } 101 env->SetObjectArrayElement(jAudioAttributes, j, jAudioAttribute); 102 } 103 104 *jGroup = env->NewObject(gAudioVolumeGroupClass, gAudioVolumeGroupCstor, 105 jName, Id, jAudioAttributes, jLegacyStreamTypes); 106 exit: 107 if (jName != NULL) { 108 env->DeleteLocalRef(jName); 109 } 110 return jStatus; 111 } 112 113 static jint 114 android_media_AudioSystem_listAudioVolumeGroups(JNIEnv *env, jobject clazz, jobject jVolumeGroups) 115 { 116 if (env == NULL) { 117 return AUDIO_JAVA_DEAD_OBJECT; 118 } 119 if (jVolumeGroups == NULL) { 120 ALOGE("listAudioVolumeGroups NULL AudioVolumeGroups"); 121 return (jint)AUDIO_JAVA_BAD_VALUE; 122 } 123 if (!env->IsInstanceOf(jVolumeGroups, gArrayListClass)) { 124 ALOGE("listAudioVolumeGroups not an arraylist"); 125 return (jint)AUDIO_JAVA_BAD_VALUE; 126 } 127 128 status_t status; 129 AudioVolumeGroupVector groups; 130 jint jStatus; 131 jobject jGroup = NULL; 132 133 status = AudioSystem::listAudioVolumeGroups(groups); 134 if (status != NO_ERROR) { 135 ALOGE("AudioSystem::listAudioVolumeGroups error %d", status); 136 return nativeToJavaStatus(status); 137 } 138 for (const auto &group : groups) { 139 jStatus = convertAudioVolumeGroupsFromNative(env, &jGroup, group); 140 if (jStatus != AUDIO_JAVA_SUCCESS) { 141 goto exit; 142 } 143 env->CallBooleanMethod(jVolumeGroups, gArrayListMethods.add, jGroup); 144 } 145 exit: 146 if (jGroup != NULL) { 147 env->DeleteLocalRef(jGroup); 148 } 149 return jStatus; 150 } 151 152 /* 153 * JNI registration. 154 */ 155 static const JNINativeMethod gMethods[] = { 156 {"native_list_audio_volume_groups", "(Ljava/util/ArrayList;)I", 157 (void *)android_media_AudioSystem_listAudioVolumeGroups}, 158 }; 159 160 int register_android_media_AudioVolumeGroups(JNIEnv *env) 161 { 162 jclass arrayListClass = FindClassOrDie(env, "java/util/ArrayList"); 163 gArrayListClass = MakeGlobalRefOrDie(env, arrayListClass); 164 gArrayListCstor = GetMethodIDOrDie(env, arrayListClass, "<init>", "()V"); 165 gArrayListMethods.add = GetMethodIDOrDie(env, arrayListClass, "add", "(Ljava/lang/Object;)Z"); 166 gArrayListMethods.toArray = GetMethodIDOrDie(env, arrayListClass, 167 "toArray", "()[Ljava/lang/Object;"); 168 169 jclass audioVolumeGroupClass = FindClassOrDie(env, kAudioVolumeGroupClassPathName); 170 gAudioVolumeGroupClass = MakeGlobalRefOrDie(env, audioVolumeGroupClass); 171 gAudioVolumeGroupCstor = GetMethodIDOrDie( 172 env, audioVolumeGroupClass, "<init>", 173 "(Ljava/lang/String;I[Landroid/media/AudioAttributes;[I)V"); 174 175 gAudioVolumeGroupFields.mName = GetFieldIDOrDie( 176 env, audioVolumeGroupClass, "mName", "Ljava/lang/String;"); 177 gAudioVolumeGroupFields.mId = GetFieldIDOrDie( 178 env, audioVolumeGroupClass, "mId", "I"); 179 180 env->DeleteLocalRef(audioVolumeGroupClass); 181 182 return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods)); 183 } 184