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
convertAudioVolumeGroupsFromNative(JNIEnv * env,jobject * jGroup,const AudioVolumeGroup & group)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 // Native & Java audio attributes default initializers are not aligned for the source.
98 // Given the volume group class concerns only playback, this field must be equal to the
99 // default java initializer.
100 attributes.source = AUDIO_SOURCE_INVALID;
101
102 jStatus = JNIAudioAttributeHelper::nativeToJava(env, &jAudioAttribute, attributes);
103 if (jStatus != AUDIO_JAVA_SUCCESS) {
104 goto exit;
105 }
106 env->SetObjectArrayElement(jAudioAttributes, j, jAudioAttribute);
107 }
108
109 *jGroup = env->NewObject(gAudioVolumeGroupClass, gAudioVolumeGroupCstor,
110 jName, Id, jAudioAttributes, jLegacyStreamTypes);
111 exit:
112 if (jName != NULL) {
113 env->DeleteLocalRef(jName);
114 }
115 return jStatus;
116 }
117
118 static jint
android_media_AudioSystem_listAudioVolumeGroups(JNIEnv * env,jobject clazz,jobject jVolumeGroups)119 android_media_AudioSystem_listAudioVolumeGroups(JNIEnv *env, jobject clazz, jobject jVolumeGroups)
120 {
121 if (env == NULL) {
122 return AUDIO_JAVA_DEAD_OBJECT;
123 }
124 if (jVolumeGroups == NULL) {
125 ALOGE("listAudioVolumeGroups NULL AudioVolumeGroups");
126 return (jint)AUDIO_JAVA_BAD_VALUE;
127 }
128 if (!env->IsInstanceOf(jVolumeGroups, gArrayListClass)) {
129 ALOGE("listAudioVolumeGroups not an arraylist");
130 return (jint)AUDIO_JAVA_BAD_VALUE;
131 }
132
133 status_t status;
134 AudioVolumeGroupVector groups;
135 jint jStatus;
136 jobject jGroup = NULL;
137
138 status = AudioSystem::listAudioVolumeGroups(groups);
139 if (status != NO_ERROR) {
140 ALOGE("AudioSystem::listAudioVolumeGroups error %d", status);
141 return nativeToJavaStatus(status);
142 }
143 for (const auto &group : groups) {
144 jStatus = convertAudioVolumeGroupsFromNative(env, &jGroup, group);
145 if (jStatus != AUDIO_JAVA_SUCCESS) {
146 goto exit;
147 }
148 env->CallBooleanMethod(jVolumeGroups, gArrayListMethods.add, jGroup);
149 }
150 exit:
151 if (jGroup != NULL) {
152 env->DeleteLocalRef(jGroup);
153 }
154 return jStatus;
155 }
156
157 /*
158 * JNI registration.
159 */
160 static const JNINativeMethod gMethods[] = {
161 {"native_list_audio_volume_groups", "(Ljava/util/ArrayList;)I",
162 (void *)android_media_AudioSystem_listAudioVolumeGroups},
163 };
164
register_android_media_AudioVolumeGroups(JNIEnv * env)165 int register_android_media_AudioVolumeGroups(JNIEnv *env)
166 {
167 jclass arrayListClass = FindClassOrDie(env, "java/util/ArrayList");
168 gArrayListClass = MakeGlobalRefOrDie(env, arrayListClass);
169 gArrayListCstor = GetMethodIDOrDie(env, arrayListClass, "<init>", "()V");
170 gArrayListMethods.add = GetMethodIDOrDie(env, arrayListClass, "add", "(Ljava/lang/Object;)Z");
171 gArrayListMethods.toArray = GetMethodIDOrDie(env, arrayListClass,
172 "toArray", "()[Ljava/lang/Object;");
173
174 jclass audioVolumeGroupClass = FindClassOrDie(env, kAudioVolumeGroupClassPathName);
175 gAudioVolumeGroupClass = MakeGlobalRefOrDie(env, audioVolumeGroupClass);
176 gAudioVolumeGroupCstor = GetMethodIDOrDie(
177 env, audioVolumeGroupClass, "<init>",
178 "(Ljava/lang/String;I[Landroid/media/AudioAttributes;[I)V");
179
180 gAudioVolumeGroupFields.mName = GetFieldIDOrDie(
181 env, audioVolumeGroupClass, "mName", "Ljava/lang/String;");
182 gAudioVolumeGroupFields.mId = GetFieldIDOrDie(
183 env, audioVolumeGroupClass, "mId", "I");
184
185 env->DeleteLocalRef(audioVolumeGroupClass);
186
187 return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
188 }
189