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