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 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 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 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 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 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 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 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 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