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