Loading core/jni/android_media_AudioSystem.cpp +121 −1 Original line number Diff line number Diff line Loading @@ -99,6 +99,9 @@ static struct { // other fields unused by JNI } gAudioPatchFields; static const char* const kEventHandlerClassPathName = "android/media/AudioPortEventHandler"; static jmethodID gPostEventFromNative; enum AudioError { kAudioStatusOk = 0, Loading @@ -106,8 +109,85 @@ enum AudioError { kAudioStatusMediaServerDied = 100 }; enum { AUDIOPORT_EVENT_PORT_LIST_UPDATED = 1, AUDIOPORT_EVENT_PATCH_LIST_UPDATED = 2, AUDIOPORT_EVENT_SERVICE_DIED = 3, }; #define MAX_PORT_GENERATION_SYNC_ATTEMPTS 5 // ---------------------------------------------------------------------------- // ref-counted object for callbacks class JNIAudioPortCallback: public AudioSystem::AudioPortCallback { public: JNIAudioPortCallback(JNIEnv* env, jobject thiz, jobject weak_thiz); ~JNIAudioPortCallback(); virtual void onAudioPortListUpdate(); virtual void onAudioPatchListUpdate(); virtual void onServiceDied(); private: void sendEvent(int event); jclass mClass; // Reference to AudioPortEventHandlerDelegate class jobject mObject; // Weak ref to AudioPortEventHandlerDelegate Java object to call on }; JNIAudioPortCallback::JNIAudioPortCallback(JNIEnv* env, jobject thiz, jobject weak_thiz) { // Hold onto the SoundTriggerModule class for use in calling the static method // that posts events to the application thread. jclass clazz = env->GetObjectClass(thiz); if (clazz == NULL) { ALOGE("Can't find class %s", kEventHandlerClassPathName); return; } mClass = (jclass)env->NewGlobalRef(clazz); // We use a weak reference so the SoundTriggerModule object can be garbage collected. // The reference is only used as a proxy for callbacks. mObject = env->NewGlobalRef(weak_thiz); } JNIAudioPortCallback::~JNIAudioPortCallback() { // remove global references JNIEnv *env = AndroidRuntime::getJNIEnv(); env->DeleteGlobalRef(mObject); env->DeleteGlobalRef(mClass); } void JNIAudioPortCallback::sendEvent(int event) { JNIEnv *env = AndroidRuntime::getJNIEnv(); env->CallStaticVoidMethod(mClass, gPostEventFromNative, mObject, event, 0, 0, NULL); if (env->ExceptionCheck()) { ALOGW("An exception occurred while notifying an event."); env->ExceptionClear(); } } void JNIAudioPortCallback::onAudioPortListUpdate() { sendEvent(AUDIOPORT_EVENT_PORT_LIST_UPDATED); } void JNIAudioPortCallback::onAudioPatchListUpdate() { sendEvent(AUDIOPORT_EVENT_PATCH_LIST_UPDATED); } void JNIAudioPortCallback::onServiceDied() { sendEvent(AUDIOPORT_EVENT_SERVICE_DIED); } static int check_AudioSystem_Command(status_t status) { switch (status) { Loading Loading @@ -1145,6 +1225,26 @@ exit: return jStatus; } static void android_media_AudioSystem_eventHandlerSetup(JNIEnv *env, jobject thiz, jobject weak_this) { ALOGV("eventHandlerSetup"); sp<JNIAudioPortCallback> callback = new JNIAudioPortCallback(env, thiz, weak_this); AudioSystem::setAudioPortCallback(callback); } static void android_media_AudioSystem_eventHandlerFinalize(JNIEnv *env, jobject thiz) { ALOGV("eventHandlerFinalize"); sp<JNIAudioPortCallback> callback; AudioSystem::setAudioPortCallback(callback); } // ---------------------------------------------------------------------------- static JNINativeMethod gMethods[] = { Loading Loading @@ -1184,6 +1284,15 @@ static JNINativeMethod gMethods[] = { }; static JNINativeMethod gEventHandlerMethods[] = { {"native_setup", "(Ljava/lang/Object;)V", (void *)android_media_AudioSystem_eventHandlerSetup}, {"native_finalize", "()V", (void *)android_media_AudioSystem_eventHandlerFinalize}, }; int register_android_media_AudioSystem(JNIEnv *env) { Loading Loading @@ -1265,8 +1374,19 @@ int register_android_media_AudioSystem(JNIEnv *env) gAudioPatchFields.mHandle = env->GetFieldID(audioPatchClass, "mHandle", "Landroid/media/AudioHandle;"); jclass eventHandlerClass = env->FindClass(kEventHandlerClassPathName); gPostEventFromNative = env->GetStaticMethodID(eventHandlerClass, "postEventFromNative", "(Ljava/lang/Object;IIILjava/lang/Object;)V"); AudioSystem::setErrorCallback(android_media_AudioSystem_error_callback); return AndroidRuntime::registerNativeMethods(env, int status = AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods)); if (status == 0) { status = AndroidRuntime::registerNativeMethods(env, kEventHandlerClassPathName, gEventHandlerMethods, NELEM(gEventHandlerMethods)); } return status; } media/java/android/media/AudioManager.java +7 −2 Original line number Diff line number Diff line Loading @@ -41,6 +41,7 @@ import android.view.KeyEvent; import java.util.HashMap; import java.util.ArrayList; /** * AudioManager provides access to volume and ringer mode control. * <p> Loading @@ -61,6 +62,7 @@ public class AudioManager { private final boolean mUseVolumeKeySounds; private final Binder mToken = new Binder(); private static String TAG = "AudioManager"; AudioPortEventHandler mAudioPortEventHandler; /** * Broadcast intent, a hint for applications that audio is about to become Loading Loading @@ -438,6 +440,7 @@ public class AudioManager { com.android.internal.R.bool.config_useMasterVolume); mUseVolumeKeySounds = mContext.getResources().getBoolean( com.android.internal.R.bool.config_useVolumeKeySounds); mAudioPortEventHandler = new AudioPortEventHandler(this); } private static IAudioService getService() Loading Loading @@ -3116,17 +3119,19 @@ public class AudioManager { } /** * Register an audio port update listener. * Register an audio port list update listener. * @hide */ public void registerAudioPortUpdateListener(OnAudioPortUpdateListener l) { mAudioPortEventHandler.registerListener(l); } /** * Unregister an audio port update listener. * Unregister an audio port list update listener. * @hide */ public void unregisterAudioPortUpdateListener(OnAudioPortUpdateListener l) { mAudioPortEventHandler.unregisterListener(l); } // Loading media/java/android/media/AudioPortEventHandler.java 0 → 100644 +168 −0 Original line number Diff line number Diff line /* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.media; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.util.Log; import java.util.ArrayList; import java.lang.ref.WeakReference; /** * The AudioPortEventHandler handles AudioManager.OnAudioPortUpdateListener callbacks * posted from JNI * @hide */ class AudioPortEventHandler { private final Handler mHandler; private ArrayList<AudioManager.OnAudioPortUpdateListener> mListeners; private AudioManager mAudioManager; private static String TAG = "AudioPortEventHandler"; private static final int AUDIOPORT_EVENT_PORT_LIST_UPDATED = 1; private static final int AUDIOPORT_EVENT_PATCH_LIST_UPDATED = 2; private static final int AUDIOPORT_EVENT_SERVICE_DIED = 3; private static final int AUDIOPORT_EVENT_NEW_LISTENER = 4; AudioPortEventHandler(AudioManager audioManager) { mAudioManager = audioManager; mListeners = new ArrayList<AudioManager.OnAudioPortUpdateListener>(); // find the looper for our new event handler Looper looper = Looper.myLooper(); if (looper == null) { throw new IllegalArgumentException("Calling thread not associated with a looper"); } mHandler = new Handler(looper) { @Override public void handleMessage(Message msg) { Log.i(TAG, "handleMessage: "+msg.what); ArrayList<AudioManager.OnAudioPortUpdateListener> listeners; synchronized (this) { if (msg.what == AUDIOPORT_EVENT_NEW_LISTENER) { listeners = new ArrayList<AudioManager.OnAudioPortUpdateListener>(); if (mListeners.contains(msg.obj)) { listeners.add((AudioManager.OnAudioPortUpdateListener)msg.obj); } } else { listeners = mListeners; } } if (listeners.isEmpty()) { return; } // reset audio port cache if the event corresponds to a change coming // from audio policy service or if mediaserver process died. if (msg.what == AUDIOPORT_EVENT_PORT_LIST_UPDATED || msg.what == AUDIOPORT_EVENT_PATCH_LIST_UPDATED || msg.what == AUDIOPORT_EVENT_SERVICE_DIED) { mAudioManager.resetAudioPortGeneration(); } ArrayList<AudioPort> ports = new ArrayList<AudioPort>(); ArrayList<AudioPatch> patches = new ArrayList<AudioPatch>(); if (msg.what != AUDIOPORT_EVENT_SERVICE_DIED) { int status = mAudioManager.updateAudioPortCache(ports, patches); if (status != AudioManager.SUCCESS) { return; } } switch (msg.what) { case AUDIOPORT_EVENT_NEW_LISTENER: case AUDIOPORT_EVENT_PORT_LIST_UPDATED: AudioPort[] portList = ports.toArray(new AudioPort[0]); for (int i = 0; i < listeners.size(); i++) { listeners.get(i).OnAudioPortListUpdate(portList); } if (msg.what == AUDIOPORT_EVENT_PORT_LIST_UPDATED) { break; } // FALL THROUGH case AUDIOPORT_EVENT_PATCH_LIST_UPDATED: AudioPatch[] patchList = patches.toArray(new AudioPatch[0]); for (int i = 0; i < listeners.size(); i++) { listeners.get(i).OnAudioPatchListUpdate(patchList); } break; case AUDIOPORT_EVENT_SERVICE_DIED: for (int i = 0; i < listeners.size(); i++) { listeners.get(i).OnServiceDied(); } break; default: break; } } }; native_setup(new WeakReference<AudioPortEventHandler>(this)); } private native void native_setup(Object module_this); @Override protected void finalize() { native_finalize(); } private native void native_finalize(); void registerListener(AudioManager.OnAudioPortUpdateListener l) { synchronized (this) { mListeners.add(l); } if (mHandler != null) { Message m = mHandler.obtainMessage(AUDIOPORT_EVENT_NEW_LISTENER, 0, 0, l); mHandler.sendMessage(m); } } void unregisterListener(AudioManager.OnAudioPortUpdateListener l) { synchronized (this) { mListeners.remove(l); } } Handler handler() { return mHandler; } @SuppressWarnings("unused") private static void postEventFromNative(Object module_ref, int what, int arg1, int arg2, Object obj) { AudioPortEventHandler eventHandler = (AudioPortEventHandler)((WeakReference)module_ref).get(); if (eventHandler == null) { return; } if (eventHandler != null) { Handler handler = eventHandler.handler(); if (handler != null) { Message m = handler.obtainMessage(what, arg1, arg2, obj); handler.sendMessage(m); } } } } Loading
core/jni/android_media_AudioSystem.cpp +121 −1 Original line number Diff line number Diff line Loading @@ -99,6 +99,9 @@ static struct { // other fields unused by JNI } gAudioPatchFields; static const char* const kEventHandlerClassPathName = "android/media/AudioPortEventHandler"; static jmethodID gPostEventFromNative; enum AudioError { kAudioStatusOk = 0, Loading @@ -106,8 +109,85 @@ enum AudioError { kAudioStatusMediaServerDied = 100 }; enum { AUDIOPORT_EVENT_PORT_LIST_UPDATED = 1, AUDIOPORT_EVENT_PATCH_LIST_UPDATED = 2, AUDIOPORT_EVENT_SERVICE_DIED = 3, }; #define MAX_PORT_GENERATION_SYNC_ATTEMPTS 5 // ---------------------------------------------------------------------------- // ref-counted object for callbacks class JNIAudioPortCallback: public AudioSystem::AudioPortCallback { public: JNIAudioPortCallback(JNIEnv* env, jobject thiz, jobject weak_thiz); ~JNIAudioPortCallback(); virtual void onAudioPortListUpdate(); virtual void onAudioPatchListUpdate(); virtual void onServiceDied(); private: void sendEvent(int event); jclass mClass; // Reference to AudioPortEventHandlerDelegate class jobject mObject; // Weak ref to AudioPortEventHandlerDelegate Java object to call on }; JNIAudioPortCallback::JNIAudioPortCallback(JNIEnv* env, jobject thiz, jobject weak_thiz) { // Hold onto the SoundTriggerModule class for use in calling the static method // that posts events to the application thread. jclass clazz = env->GetObjectClass(thiz); if (clazz == NULL) { ALOGE("Can't find class %s", kEventHandlerClassPathName); return; } mClass = (jclass)env->NewGlobalRef(clazz); // We use a weak reference so the SoundTriggerModule object can be garbage collected. // The reference is only used as a proxy for callbacks. mObject = env->NewGlobalRef(weak_thiz); } JNIAudioPortCallback::~JNIAudioPortCallback() { // remove global references JNIEnv *env = AndroidRuntime::getJNIEnv(); env->DeleteGlobalRef(mObject); env->DeleteGlobalRef(mClass); } void JNIAudioPortCallback::sendEvent(int event) { JNIEnv *env = AndroidRuntime::getJNIEnv(); env->CallStaticVoidMethod(mClass, gPostEventFromNative, mObject, event, 0, 0, NULL); if (env->ExceptionCheck()) { ALOGW("An exception occurred while notifying an event."); env->ExceptionClear(); } } void JNIAudioPortCallback::onAudioPortListUpdate() { sendEvent(AUDIOPORT_EVENT_PORT_LIST_UPDATED); } void JNIAudioPortCallback::onAudioPatchListUpdate() { sendEvent(AUDIOPORT_EVENT_PATCH_LIST_UPDATED); } void JNIAudioPortCallback::onServiceDied() { sendEvent(AUDIOPORT_EVENT_SERVICE_DIED); } static int check_AudioSystem_Command(status_t status) { switch (status) { Loading Loading @@ -1145,6 +1225,26 @@ exit: return jStatus; } static void android_media_AudioSystem_eventHandlerSetup(JNIEnv *env, jobject thiz, jobject weak_this) { ALOGV("eventHandlerSetup"); sp<JNIAudioPortCallback> callback = new JNIAudioPortCallback(env, thiz, weak_this); AudioSystem::setAudioPortCallback(callback); } static void android_media_AudioSystem_eventHandlerFinalize(JNIEnv *env, jobject thiz) { ALOGV("eventHandlerFinalize"); sp<JNIAudioPortCallback> callback; AudioSystem::setAudioPortCallback(callback); } // ---------------------------------------------------------------------------- static JNINativeMethod gMethods[] = { Loading Loading @@ -1184,6 +1284,15 @@ static JNINativeMethod gMethods[] = { }; static JNINativeMethod gEventHandlerMethods[] = { {"native_setup", "(Ljava/lang/Object;)V", (void *)android_media_AudioSystem_eventHandlerSetup}, {"native_finalize", "()V", (void *)android_media_AudioSystem_eventHandlerFinalize}, }; int register_android_media_AudioSystem(JNIEnv *env) { Loading Loading @@ -1265,8 +1374,19 @@ int register_android_media_AudioSystem(JNIEnv *env) gAudioPatchFields.mHandle = env->GetFieldID(audioPatchClass, "mHandle", "Landroid/media/AudioHandle;"); jclass eventHandlerClass = env->FindClass(kEventHandlerClassPathName); gPostEventFromNative = env->GetStaticMethodID(eventHandlerClass, "postEventFromNative", "(Ljava/lang/Object;IIILjava/lang/Object;)V"); AudioSystem::setErrorCallback(android_media_AudioSystem_error_callback); return AndroidRuntime::registerNativeMethods(env, int status = AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods)); if (status == 0) { status = AndroidRuntime::registerNativeMethods(env, kEventHandlerClassPathName, gEventHandlerMethods, NELEM(gEventHandlerMethods)); } return status; }
media/java/android/media/AudioManager.java +7 −2 Original line number Diff line number Diff line Loading @@ -41,6 +41,7 @@ import android.view.KeyEvent; import java.util.HashMap; import java.util.ArrayList; /** * AudioManager provides access to volume and ringer mode control. * <p> Loading @@ -61,6 +62,7 @@ public class AudioManager { private final boolean mUseVolumeKeySounds; private final Binder mToken = new Binder(); private static String TAG = "AudioManager"; AudioPortEventHandler mAudioPortEventHandler; /** * Broadcast intent, a hint for applications that audio is about to become Loading Loading @@ -438,6 +440,7 @@ public class AudioManager { com.android.internal.R.bool.config_useMasterVolume); mUseVolumeKeySounds = mContext.getResources().getBoolean( com.android.internal.R.bool.config_useVolumeKeySounds); mAudioPortEventHandler = new AudioPortEventHandler(this); } private static IAudioService getService() Loading Loading @@ -3116,17 +3119,19 @@ public class AudioManager { } /** * Register an audio port update listener. * Register an audio port list update listener. * @hide */ public void registerAudioPortUpdateListener(OnAudioPortUpdateListener l) { mAudioPortEventHandler.registerListener(l); } /** * Unregister an audio port update listener. * Unregister an audio port list update listener. * @hide */ public void unregisterAudioPortUpdateListener(OnAudioPortUpdateListener l) { mAudioPortEventHandler.unregisterListener(l); } // Loading
media/java/android/media/AudioPortEventHandler.java 0 → 100644 +168 −0 Original line number Diff line number Diff line /* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.media; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.util.Log; import java.util.ArrayList; import java.lang.ref.WeakReference; /** * The AudioPortEventHandler handles AudioManager.OnAudioPortUpdateListener callbacks * posted from JNI * @hide */ class AudioPortEventHandler { private final Handler mHandler; private ArrayList<AudioManager.OnAudioPortUpdateListener> mListeners; private AudioManager mAudioManager; private static String TAG = "AudioPortEventHandler"; private static final int AUDIOPORT_EVENT_PORT_LIST_UPDATED = 1; private static final int AUDIOPORT_EVENT_PATCH_LIST_UPDATED = 2; private static final int AUDIOPORT_EVENT_SERVICE_DIED = 3; private static final int AUDIOPORT_EVENT_NEW_LISTENER = 4; AudioPortEventHandler(AudioManager audioManager) { mAudioManager = audioManager; mListeners = new ArrayList<AudioManager.OnAudioPortUpdateListener>(); // find the looper for our new event handler Looper looper = Looper.myLooper(); if (looper == null) { throw new IllegalArgumentException("Calling thread not associated with a looper"); } mHandler = new Handler(looper) { @Override public void handleMessage(Message msg) { Log.i(TAG, "handleMessage: "+msg.what); ArrayList<AudioManager.OnAudioPortUpdateListener> listeners; synchronized (this) { if (msg.what == AUDIOPORT_EVENT_NEW_LISTENER) { listeners = new ArrayList<AudioManager.OnAudioPortUpdateListener>(); if (mListeners.contains(msg.obj)) { listeners.add((AudioManager.OnAudioPortUpdateListener)msg.obj); } } else { listeners = mListeners; } } if (listeners.isEmpty()) { return; } // reset audio port cache if the event corresponds to a change coming // from audio policy service or if mediaserver process died. if (msg.what == AUDIOPORT_EVENT_PORT_LIST_UPDATED || msg.what == AUDIOPORT_EVENT_PATCH_LIST_UPDATED || msg.what == AUDIOPORT_EVENT_SERVICE_DIED) { mAudioManager.resetAudioPortGeneration(); } ArrayList<AudioPort> ports = new ArrayList<AudioPort>(); ArrayList<AudioPatch> patches = new ArrayList<AudioPatch>(); if (msg.what != AUDIOPORT_EVENT_SERVICE_DIED) { int status = mAudioManager.updateAudioPortCache(ports, patches); if (status != AudioManager.SUCCESS) { return; } } switch (msg.what) { case AUDIOPORT_EVENT_NEW_LISTENER: case AUDIOPORT_EVENT_PORT_LIST_UPDATED: AudioPort[] portList = ports.toArray(new AudioPort[0]); for (int i = 0; i < listeners.size(); i++) { listeners.get(i).OnAudioPortListUpdate(portList); } if (msg.what == AUDIOPORT_EVENT_PORT_LIST_UPDATED) { break; } // FALL THROUGH case AUDIOPORT_EVENT_PATCH_LIST_UPDATED: AudioPatch[] patchList = patches.toArray(new AudioPatch[0]); for (int i = 0; i < listeners.size(); i++) { listeners.get(i).OnAudioPatchListUpdate(patchList); } break; case AUDIOPORT_EVENT_SERVICE_DIED: for (int i = 0; i < listeners.size(); i++) { listeners.get(i).OnServiceDied(); } break; default: break; } } }; native_setup(new WeakReference<AudioPortEventHandler>(this)); } private native void native_setup(Object module_this); @Override protected void finalize() { native_finalize(); } private native void native_finalize(); void registerListener(AudioManager.OnAudioPortUpdateListener l) { synchronized (this) { mListeners.add(l); } if (mHandler != null) { Message m = mHandler.obtainMessage(AUDIOPORT_EVENT_NEW_LISTENER, 0, 0, l); mHandler.sendMessage(m); } } void unregisterListener(AudioManager.OnAudioPortUpdateListener l) { synchronized (this) { mListeners.remove(l); } } Handler handler() { return mHandler; } @SuppressWarnings("unused") private static void postEventFromNative(Object module_ref, int what, int arg1, int arg2, Object obj) { AudioPortEventHandler eventHandler = (AudioPortEventHandler)((WeakReference)module_ref).get(); if (eventHandler == null) { return; } if (eventHandler != null) { Handler handler = eventHandler.handler(); if (handler != null) { Message m = handler.obtainMessage(what, arg1, arg2, obj); handler.sendMessage(m); } } } }