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