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 "AudioVolumeGroupCallback-JNI"
20 
21 #include <utils/Log.h>
22 #include <nativehelper/JNIHelp.h>
23 #include "core_jni_helpers.h"
24 
25 #include "android_media_AudioVolumeGroupCallback.h"
26 
27 
28 // ----------------------------------------------------------------------------
29 using namespace android;
30 
31 static const char* const kAudioVolumeGroupChangeHandlerClassPathName =
32         "android/media/audiopolicy/AudioVolumeGroupChangeHandler";
33 
34 static struct {
35     jfieldID    mJniCallback;
36 } gAudioVolumeGroupChangeHandlerFields;
37 
38 static struct {
39     jmethodID    postEventFromNative;
40 } gAudioVolumeGroupChangeHandlerMethods;
41 
42 static Mutex gLock;
43 
JNIAudioVolumeGroupCallback(JNIEnv * env,jobject thiz,jobject weak_thiz)44 JNIAudioVolumeGroupCallback::JNIAudioVolumeGroupCallback(JNIEnv* env,
45                                                          jobject thiz,
46                                                          jobject weak_thiz)
47 {
48     jclass clazz = env->GetObjectClass(thiz);
49     if (clazz == NULL) {
50         ALOGE("Can't find class %s", kAudioVolumeGroupChangeHandlerClassPathName);
51         return;
52     }
53     mClass = (jclass)env->NewGlobalRef(clazz);
54 
55     // We use a weak reference so the AudioVolumeGroupChangeHandler object can be garbage collected.
56     // The reference is only used as a proxy for callbacks.
57     mObject  = env->NewGlobalRef(weak_thiz);
58 }
59 
~JNIAudioVolumeGroupCallback()60 JNIAudioVolumeGroupCallback::~JNIAudioVolumeGroupCallback()
61 {
62     // remove global references
63     JNIEnv *env = AndroidRuntime::getJNIEnv();
64     if (env == NULL) {
65         return;
66     }
67     env->DeleteGlobalRef(mObject);
68     env->DeleteGlobalRef(mClass);
69 }
70 
onAudioVolumeGroupChanged(volume_group_t group,int flags)71 void JNIAudioVolumeGroupCallback::onAudioVolumeGroupChanged(volume_group_t group, int flags)
72 {
73     JNIEnv *env = AndroidRuntime::getJNIEnv();
74     if (env == NULL) {
75         return;
76     }
77     ALOGV("%s volume group id %d", __FUNCTION__, group);
78     env->CallStaticVoidMethod(mClass,
79                               gAudioVolumeGroupChangeHandlerMethods.postEventFromNative,
80                               mObject,
81                               AUDIOVOLUMEGROUP_EVENT_VOLUME_CHANGED, group, flags, NULL);
82     if (env->ExceptionCheck()) {
83         ALOGW("An exception occurred while notifying an event.");
84         env->ExceptionClear();
85     }
86 }
87 
onServiceDied()88 void JNIAudioVolumeGroupCallback::onServiceDied()
89 {
90     JNIEnv *env = AndroidRuntime::getJNIEnv();
91     if (env == NULL) {
92         return;
93     }
94     env->CallStaticVoidMethod(mClass,
95                               gAudioVolumeGroupChangeHandlerMethods.postEventFromNative,
96                               mObject,
97                               AUDIOVOLUMEGROUP_EVENT_SERVICE_DIED, 0, 0, NULL);
98     if (env->ExceptionCheck()) {
99         ALOGW("An exception occurred while notifying an event.");
100         env->ExceptionClear();
101     }
102 }
103 
104 static
setJniCallback(JNIEnv * env,jobject thiz,const sp<JNIAudioVolumeGroupCallback> & callback)105 sp<JNIAudioVolumeGroupCallback> setJniCallback(JNIEnv* env,
106                                                jobject thiz,
107                                                const sp<JNIAudioVolumeGroupCallback>& callback)
108 {
109     Mutex::Autolock l(gLock);
110     sp<JNIAudioVolumeGroupCallback> old = (JNIAudioVolumeGroupCallback*)env->GetLongField(
111                 thiz, gAudioVolumeGroupChangeHandlerFields.mJniCallback);
112     if (callback.get()) {
113         callback->incStrong((void*)setJniCallback);
114     }
115     if (old != 0) {
116         old->decStrong((void*)setJniCallback);
117     }
118     env->SetLongField(thiz, gAudioVolumeGroupChangeHandlerFields.mJniCallback,
119                       (jlong)callback.get());
120     return old;
121 }
122 
123 static void
android_media_AudioVolumeGroupChangeHandler_eventHandlerSetup(JNIEnv * env,jobject thiz,jobject weak_this)124 android_media_AudioVolumeGroupChangeHandler_eventHandlerSetup(JNIEnv *env,
125                                                               jobject thiz,
126                                                               jobject weak_this)
127 {
128     ALOGV("%s", __FUNCTION__);
129     sp<JNIAudioVolumeGroupCallback> callback =
130             new JNIAudioVolumeGroupCallback(env, thiz, weak_this);
131 
132     if (AudioSystem::addAudioVolumeGroupCallback(callback) == NO_ERROR) {
133         setJniCallback(env, thiz, callback);
134     }
135 }
136 
137 static void
android_media_AudioVolumeGroupChangeHandler_eventHandlerFinalize(JNIEnv * env,jobject thiz)138 android_media_AudioVolumeGroupChangeHandler_eventHandlerFinalize(JNIEnv *env, jobject thiz)
139 {
140     ALOGV("%s", __FUNCTION__);
141     sp<JNIAudioVolumeGroupCallback> callback = setJniCallback(env, thiz, 0);
142     if (callback != 0) {
143         AudioSystem::removeAudioVolumeGroupCallback(callback);
144     }
145 }
146 
147 /*
148  * JNI registration.
149  */
150 static const JNINativeMethod gMethods[] = {
151     {"native_setup", "(Ljava/lang/Object;)V",
152         (void *)android_media_AudioVolumeGroupChangeHandler_eventHandlerSetup},
153     {"native_finalize",  "()V",
154         (void *)android_media_AudioVolumeGroupChangeHandler_eventHandlerFinalize},
155 };
156 
register_android_media_AudioVolumeGroupChangeHandler(JNIEnv * env)157 int register_android_media_AudioVolumeGroupChangeHandler(JNIEnv *env)
158 {
159     jclass audioVolumeGroupChangeHandlerClass =
160             FindClassOrDie(env, kAudioVolumeGroupChangeHandlerClassPathName);
161     gAudioVolumeGroupChangeHandlerMethods.postEventFromNative =
162             GetStaticMethodIDOrDie(env, audioVolumeGroupChangeHandlerClass, "postEventFromNative",
163                                    "(Ljava/lang/Object;IIILjava/lang/Object;)V");
164 
165     gAudioVolumeGroupChangeHandlerFields.mJniCallback =
166             GetFieldIDOrDie(env, audioVolumeGroupChangeHandlerClass, "mJniCallback", "J");
167 
168     env->DeleteLocalRef(audioVolumeGroupChangeHandlerClass);
169 
170     return RegisterMethodsOrDie(env,
171                                 kAudioVolumeGroupChangeHandlerClassPathName,
172                                 gMethods,
173                                 NELEM(gMethods));
174 }
175 
176