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 "AudioProductStrategies-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/AudioProductStrategy";
43 static const char* const kAudioProductStrategyClassPathName =
44 "android/media/audiopolicy/AudioProductStrategy";
45
46 static const char* const kAudioAttributesGroupsClassPathName =
47 "android/media/audiopolicy/AudioProductStrategy$AudioAttributesGroup";
48
49 static jclass gAudioProductStrategyClass;
50 static jmethodID gAudioProductStrategyCstor;
51 static struct {
52 jfieldID mAudioAttributesGroups;
53 jfieldID mName;
54 jfieldID mId;
55 } gAudioProductStrategyFields;
56
57 static jclass gAudioAttributesGroupClass;
58 static jmethodID gAudioAttributesGroupCstor;
59 static struct {
60 jfieldID mVolumeGroupId;
61 jfieldID mLegacyStreamType;
62 jfieldID mAudioAttributes;
63 } gAudioAttributesGroupsFields;
64
65 static jclass gArrayListClass;
66 static struct {
67 jmethodID add;
68 jmethodID toArray;
69 } gArrayListMethods;
70
71
convertAudioProductStrategiesFromNative(JNIEnv * env,jobject * jAudioStrategy,const AudioProductStrategy & strategy)72 static jint convertAudioProductStrategiesFromNative(
73 JNIEnv *env, jobject *jAudioStrategy, const AudioProductStrategy &strategy)
74 {
75 jint jStatus = (jint)AUDIO_JAVA_SUCCESS;
76 jobjectArray jAudioAttributesGroups = NULL;
77 jobjectArray jAudioAttributes = NULL;
78 jobject jAudioAttribute = NULL;
79 jstring jName = NULL;
80 jint jStrategyId = NULL;
81 jint numAttributesGroups;
82 size_t indexGroup = 0;
83
84 jName = env->NewStringUTF(strategy.getName().c_str());
85 jStrategyId = static_cast<jint>(strategy.getId());
86
87 // Audio Attributes Group array
88 int attrGroupIndex = 0;
89 std::map<int /**attributesGroupIndex*/, std::vector<VolumeGroupAttributes> > groups;
90 for (const auto &attr : strategy.getVolumeGroupAttributes()) {
91 int groupId = attr.getGroupId();
92 int streamType = attr.getStreamType();
93 const auto &iter = std::find_if(begin(groups), end(groups),
94 [groupId, streamType](const auto &iter) {
95 const auto &frontAttr = iter.second.front();
96 return frontAttr.getGroupId() == groupId && frontAttr.getStreamType() == streamType;
97 });
98 // Same Volume Group Id and same stream type
99 if (iter != end(groups)) {
100 groups[iter->first].push_back(attr);
101 } else {
102 // Add a new Group of AudioAttributes for this product strategy
103 groups[attrGroupIndex++].push_back(attr);
104 }
105 }
106 numAttributesGroups = groups.size();
107
108 jAudioAttributesGroups = env->NewObjectArray(numAttributesGroups, gAudioAttributesGroupClass, NULL);
109
110 for (const auto &iter : groups) {
111 std::vector<VolumeGroupAttributes> volumeGroupAttributes = iter.second;
112 jint numAttributes = volumeGroupAttributes.size();
113 jint jGroupId = volumeGroupAttributes.front().getGroupId();
114 jint jLegacyStreamType = volumeGroupAttributes.front().getStreamType();
115
116 jStatus = JNIAudioAttributeHelper::getJavaArray(env, &jAudioAttributes, numAttributes);
117 if (jStatus != (jint)AUDIO_JAVA_SUCCESS) {
118 goto exit;
119 }
120 for (size_t j = 0; j < static_cast<size_t>(numAttributes); j++) {
121 auto attributes = volumeGroupAttributes[j].getAttributes();
122
123 jStatus = JNIAudioAttributeHelper::nativeToJava(env, &jAudioAttribute, attributes);
124 if (jStatus != AUDIO_JAVA_SUCCESS) {
125 goto exit;
126 }
127 env->SetObjectArrayElement(jAudioAttributes, j, jAudioAttribute);
128 }
129 jobject jAudioAttributesGroup = env->NewObject(gAudioAttributesGroupClass,
130 gAudioAttributesGroupCstor,
131 jGroupId,
132 jLegacyStreamType,
133 jAudioAttributes);
134 env->SetObjectArrayElement(jAudioAttributesGroups, indexGroup++, jAudioAttributesGroup);
135
136 if (jAudioAttributes != NULL) {
137 env->DeleteLocalRef(jAudioAttributes);
138 jAudioAttributes = NULL;
139 }
140 if (jAudioAttribute != NULL) {
141 env->DeleteLocalRef(jAudioAttribute);
142 jAudioAttribute = NULL;
143 }
144 if (jAudioAttributesGroup != NULL) {
145 env->DeleteLocalRef(jAudioAttributesGroup);
146 jAudioAttributesGroup = NULL;
147 }
148 }
149 *jAudioStrategy = env->NewObject(gAudioProductStrategyClass, gAudioProductStrategyCstor,
150 jName,
151 jStrategyId,
152 jAudioAttributesGroups);
153 exit:
154 if (jAudioAttributes != NULL) {
155 env->DeleteLocalRef(jAudioAttributes);
156 }
157 if (jAudioAttribute != NULL) {
158 env->DeleteLocalRef(jAudioAttribute);
159 jAudioAttribute = NULL;
160 }
161 if (jAudioAttributesGroups != NULL) {
162 env->DeleteLocalRef(jAudioAttributesGroups);
163 }
164 if (jName != NULL) {
165 env->DeleteLocalRef(jName);
166 }
167 return jStatus;
168 }
169
170 static jint
android_media_AudioSystem_listAudioProductStrategies(JNIEnv * env,jobject clazz,jobject jStrategies)171 android_media_AudioSystem_listAudioProductStrategies(JNIEnv *env, jobject clazz,
172 jobject jStrategies)
173 {
174 if (env == NULL) {
175 return AUDIO_JAVA_DEAD_OBJECT;
176 }
177 if (jStrategies == NULL) {
178 ALOGE("listAudioProductStrategies NULL AudioProductStrategies");
179 return (jint)AUDIO_JAVA_BAD_VALUE;
180 }
181 if (!env->IsInstanceOf(jStrategies, gArrayListClass)) {
182 ALOGE("listAudioProductStrategies not an arraylist");
183 return (jint)AUDIO_JAVA_BAD_VALUE;
184 }
185
186 status_t status;
187 AudioProductStrategyVector strategies;
188 jint jStatus;
189 jobject jStrategy = NULL;
190
191 status = AudioSystem::listAudioProductStrategies(strategies);
192 if (status != NO_ERROR) {
193 ALOGE("AudioSystem::listAudioProductStrategies error %d", status);
194 return nativeToJavaStatus(status);
195 }
196 for (const auto &strategy : strategies) {
197 jStatus = convertAudioProductStrategiesFromNative(env, &jStrategy, strategy);
198 if (jStatus != AUDIO_JAVA_SUCCESS) {
199 goto exit;
200 }
201 env->CallBooleanMethod(jStrategies, gArrayListMethods.add, jStrategy);
202 }
203 exit:
204 if (jStrategy != NULL) {
205 env->DeleteLocalRef(jStrategy);
206 }
207 return jStatus;
208 }
209
210 /*
211 * JNI registration.
212 */
213 static const JNINativeMethod gMethods[] = {
214 {"native_list_audio_product_strategies", "(Ljava/util/ArrayList;)I",
215 (void *)android_media_AudioSystem_listAudioProductStrategies},
216 };
217
register_android_media_AudioProductStrategies(JNIEnv * env)218 int register_android_media_AudioProductStrategies(JNIEnv *env)
219 {
220 jclass arrayListClass = FindClassOrDie(env, "java/util/ArrayList");
221 gArrayListClass = MakeGlobalRefOrDie(env, arrayListClass);
222 gArrayListMethods.add = GetMethodIDOrDie(env, arrayListClass, "add", "(Ljava/lang/Object;)Z");
223 gArrayListMethods.toArray = GetMethodIDOrDie(env, arrayListClass,
224 "toArray", "()[Ljava/lang/Object;");
225
226 jclass audioProductStrategyClass = FindClassOrDie(env, kAudioProductStrategyClassPathName);
227 gAudioProductStrategyClass = MakeGlobalRefOrDie(env, audioProductStrategyClass);
228 gAudioProductStrategyCstor = GetMethodIDOrDie(
229 env, audioProductStrategyClass, "<init>",
230 "(Ljava/lang/String;I[Landroid/media/audiopolicy/AudioProductStrategy$AudioAttributesGroup;)V");
231 gAudioProductStrategyFields.mAudioAttributesGroups = GetFieldIDOrDie(
232 env, audioProductStrategyClass, "mAudioAttributesGroups",
233 "[Landroid/media/audiopolicy/AudioProductStrategy$AudioAttributesGroup;");
234 gAudioProductStrategyFields.mName = GetFieldIDOrDie(
235 env, audioProductStrategyClass, "mName", "Ljava/lang/String;");
236 gAudioProductStrategyFields.mId = GetFieldIDOrDie(
237 env, audioProductStrategyClass, "mId", "I");
238
239 jclass audioAttributesGroupClass = FindClassOrDie(env, kAudioAttributesGroupsClassPathName);
240 gAudioAttributesGroupClass = MakeGlobalRefOrDie(env, audioAttributesGroupClass);
241 gAudioAttributesGroupCstor = GetMethodIDOrDie(env, audioAttributesGroupClass, "<init>",
242 "(II[Landroid/media/AudioAttributes;)V");
243 gAudioAttributesGroupsFields.mVolumeGroupId = GetFieldIDOrDie(
244 env, audioAttributesGroupClass, "mVolumeGroupId", "I");
245 gAudioAttributesGroupsFields.mLegacyStreamType = GetFieldIDOrDie(
246 env, audioAttributesGroupClass, "mLegacyStreamType", "I");
247 gAudioAttributesGroupsFields.mAudioAttributes = GetFieldIDOrDie(
248 env, audioAttributesGroupClass, "mAudioAttributes",
249 "[Landroid/media/AudioAttributes;");
250
251 env->DeleteLocalRef(audioProductStrategyClass);
252 env->DeleteLocalRef(audioAttributesGroupClass);
253
254 return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
255 }
256