diff options
-rw-r--r-- | core/jni/Android.mk | 1 | ||||
-rw-r--r-- | core/jni/android_media_AudioRecord.cpp | 92 | ||||
-rw-r--r-- | core/jni/android_media_AudioSystem.cpp | 5 | ||||
-rw-r--r-- | core/jni/android_media_AudioTrack.cpp | 50 | ||||
-rw-r--r-- | core/jni/android_media_DeviceCallback.cpp | 82 | ||||
-rw-r--r-- | core/jni/android_media_DeviceCallback.h | 47 | ||||
-rw-r--r-- | media/java/android/media/AudioDevicePort.java | 10 | ||||
-rw-r--r-- | media/java/android/media/AudioDevicesManager.java | 17 | ||||
-rw-r--r-- | media/java/android/media/AudioManager.java | 12 | ||||
-rw-r--r-- | media/java/android/media/AudioMixPort.java | 19 | ||||
-rw-r--r-- | media/java/android/media/AudioPort.java | 8 | ||||
-rw-r--r-- | media/java/android/media/AudioRecord.java | 58 | ||||
-rw-r--r-- | media/java/android/media/AudioSystem.java | 6 | ||||
-rw-r--r-- | media/java/android/media/AudioTrack.java | 53 |
14 files changed, 421 insertions, 39 deletions
diff --git a/core/jni/Android.mk b/core/jni/Android.mk index bbdd860..5448214 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -132,6 +132,7 @@ LOCAL_SRC_FILES:= \ android_media_AudioRecord.cpp \ android_media_AudioSystem.cpp \ android_media_AudioTrack.cpp \ + android_media_DeviceCallback.cpp \ android_media_JetPlayer.cpp \ android_media_RemoteDisplay.cpp \ android_media_ToneGenerator.cpp \ diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp index 8b65fd1..87b81d5 100644 --- a/core/jni/android_media_AudioRecord.cpp +++ b/core/jni/android_media_AudioRecord.cpp @@ -30,6 +30,7 @@ #include "android_media_AudioFormat.h" #include "android_media_AudioErrors.h" +#include "android_media_DeviceCallback.h" // ---------------------------------------------------------------------------- @@ -44,6 +45,7 @@ struct audio_record_fields_t { jmethodID postNativeEventInJava; //... event post callback method jfieldID nativeRecorderInJavaObj; // provides access to the C++ AudioRecord object jfieldID nativeCallbackCookie; // provides access to the AudioRecord callback data + jfieldID nativeDeviceCallback; // provides access to the JNIDeviceCallback instance }; struct audio_attributes_fields_t { jfieldID fieldRecSource; // AudioAttributes.mSource @@ -120,6 +122,33 @@ static void recorderCallback(int event, void* user, void *info) { } } +static sp<JNIDeviceCallback> getJniDeviceCallback(JNIEnv* env, jobject thiz) +{ + Mutex::Autolock l(sLock); + JNIDeviceCallback* const cb = + (JNIDeviceCallback*)env->GetLongField(thiz, + javaAudioRecordFields.nativeDeviceCallback); + return sp<JNIDeviceCallback>(cb); +} + +static sp<JNIDeviceCallback> setJniDeviceCallback(JNIEnv* env, + jobject thiz, + const sp<JNIDeviceCallback>& cb) +{ + Mutex::Autolock l(sLock); + sp<JNIDeviceCallback> old = + (JNIDeviceCallback*)env->GetLongField(thiz, + javaAudioRecordFields.nativeDeviceCallback); + if (cb.get()) { + cb->incStrong((void*)setJniDeviceCallback); + } + if (old != 0) { + old->decStrong((void*)setJniDeviceCallback); + } + env->SetLongField(thiz, javaAudioRecordFields.nativeDeviceCallback, (jlong)cb.get()); + return old; +} + // ---------------------------------------------------------------------------- static sp<AudioRecord> getAudioRecord(JNIEnv* env, jobject thiz) { @@ -593,9 +622,63 @@ static jboolean android_media_AudioRecord_setInputDevice( JNIEnv *env, jobject thiz, jint device_id) { sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); + if (lpRecorder == 0) { + return 0; + } return lpRecorder->setInputDevice(device_id) == NO_ERROR; } +static jint android_media_AudioRecord_getRoutedDeviceId( + JNIEnv *env, jobject thiz) { + + sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); + if (lpRecorder == 0) { + return 0; + } + return (jint)lpRecorder->getRoutedDeviceId(); +} + +static void android_media_AudioRecord_enableDeviceCallback( + JNIEnv *env, jobject thiz) { + + sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); + if (lpRecorder == 0) { + return; + } + sp<JNIDeviceCallback> cb = getJniDeviceCallback(env, thiz); + if (cb != 0) { + return; + } + audiorecord_callback_cookie *cookie = + (audiorecord_callback_cookie *)env->GetLongField(thiz, + javaAudioRecordFields.nativeCallbackCookie); + if (cookie == NULL) { + return; + } + + cb = new JNIDeviceCallback(env, thiz, cookie->audioRecord_ref, + javaAudioRecordFields.postNativeEventInJava); + status_t status = lpRecorder->addAudioDeviceCallback(cb); + if (status == NO_ERROR) { + setJniDeviceCallback(env, thiz, cb); + } +} + +static void android_media_AudioRecord_disableDeviceCallback( + JNIEnv *env, jobject thiz) { + + sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); + if (lpRecorder == 0) { + return; + } + sp<JNIDeviceCallback> cb = setJniDeviceCallback(env, thiz, 0); + if (cb != 0) { + lpRecorder->removeAudioDeviceCallback(cb); + } +} + + + // ---------------------------------------------------------------------------- // ---------------------------------------------------------------------------- static JNINativeMethod gMethods[] = { @@ -628,12 +711,17 @@ static JNINativeMethod gMethods[] = { {"native_get_min_buff_size", "(III)I", (void *)android_media_AudioRecord_get_min_buff_size}, {"native_setInputDevice", "(I)Z", (void *)android_media_AudioRecord_setInputDevice}, + {"native_getRoutedDeviceId", "()I", (void *)android_media_AudioRecord_getRoutedDeviceId}, + {"native_enableDeviceCallback", "()V", (void *)android_media_AudioRecord_enableDeviceCallback}, + {"native_disableDeviceCallback", "()V", + (void *)android_media_AudioRecord_disableDeviceCallback}, }; // field names found in android/media/AudioRecord.java #define JAVA_POSTEVENT_CALLBACK_NAME "postEventFromNative" #define JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME "mNativeRecorderInJavaObj" #define JAVA_NATIVECALLBACKINFO_FIELD_NAME "mNativeCallbackCookie" +#define JAVA_NATIVEDEVICECALLBACK_FIELD_NAME "mNativeDeviceCallback" // ---------------------------------------------------------------------------- int register_android_media_AudioRecord(JNIEnv *env) @@ -641,6 +729,7 @@ int register_android_media_AudioRecord(JNIEnv *env) javaAudioRecordFields.postNativeEventInJava = NULL; javaAudioRecordFields.nativeRecorderInJavaObj = NULL; javaAudioRecordFields.nativeCallbackCookie = NULL; + javaAudioRecordFields.nativeDeviceCallback = NULL; // Get the AudioRecord class @@ -658,6 +747,9 @@ int register_android_media_AudioRecord(JNIEnv *env) javaAudioRecordFields.nativeCallbackCookie = GetFieldIDOrDie(env, audioRecordClass, JAVA_NATIVECALLBACKINFO_FIELD_NAME, "J"); + javaAudioRecordFields.nativeDeviceCallback = GetFieldIDOrDie(env, + audioRecordClass, JAVA_NATIVEDEVICECALLBACK_FIELD_NAME, "J"); + // Get the AudioAttributes class and fields jclass audioAttrClass = FindClassOrDie(env, kAudioAttributesClassPathName); javaAudioAttrFields.fieldRecSource = GetFieldIDOrDie(env, audioAttrClass, "mSource", "I"); diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp index 3655adc..eab5668 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -937,7 +937,8 @@ static jint convertAudioPortFromNative(JNIEnv *env, } else if (nAudioPort->type == AUDIO_PORT_TYPE_MIX) { ALOGV("convertAudioPortFromNative is a mix"); *jAudioPort = env->NewObject(gAudioMixPortClass, gAudioMixPortCstor, - jHandle, nAudioPort->role, jDeviceName, + jHandle, nAudioPort->ext.mix.handle, + nAudioPort->role, jDeviceName, jSamplingRates, jChannelMasks, jFormats, jGains); } else { @@ -1670,7 +1671,7 @@ int register_android_media_AudioSystem(JNIEnv *env) jclass audioMixPortClass = FindClassOrDie(env, "android/media/AudioMixPort"); gAudioMixPortClass = MakeGlobalRefOrDie(env, audioMixPortClass); gAudioMixPortCstor = GetMethodIDOrDie(env, audioMixPortClass, "<init>", - "(Landroid/media/AudioHandle;ILjava/lang/String;[I[I[I[Landroid/media/AudioGain;)V"); + "(Landroid/media/AudioHandle;IILjava/lang/String;[I[I[I[Landroid/media/AudioGain;)V"); jclass audioGainClass = FindClassOrDie(env, "android/media/AudioGain"); gAudioGainClass = MakeGlobalRefOrDie(env, audioGainClass); diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp index 26b82c5..662ecd3 100644 --- a/core/jni/android_media_AudioTrack.cpp +++ b/core/jni/android_media_AudioTrack.cpp @@ -36,6 +36,7 @@ #include "android_media_AudioFormat.h" #include "android_media_AudioErrors.h" #include "android_media_PlaybackSettings.h" +#include "android_media_DeviceCallback.h" // ---------------------------------------------------------------------------- @@ -79,6 +80,7 @@ class AudioTrackJniStorage { sp<MemoryHeapBase> mMemHeap; sp<MemoryBase> mMemBase; audiotrack_callback_cookie mCallbackData; + sp<JNIDeviceCallback> mDeviceCallback; AudioTrackJniStorage() { mCallbackData.audioTrack_class = 0; @@ -977,6 +979,51 @@ static jboolean android_media_AudioTrack_setOutputDevice( return lpTrack->setOutputDevice(device_id) == NO_ERROR; } +static jint android_media_AudioTrack_getRoutedDeviceId( + JNIEnv *env, jobject thiz) { + + sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); + if (lpTrack == NULL) { + return 0; + } + return (jint)lpTrack->getRoutedDeviceId(); +} + +static void android_media_AudioTrack_enableDeviceCallback( + JNIEnv *env, jobject thiz) { + + sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); + if (lpTrack == NULL) { + return; + } + AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetLongField( + thiz, javaAudioTrackFields.jniData); + if (pJniStorage == NULL || pJniStorage->mDeviceCallback != 0) { + return; + } + pJniStorage->mDeviceCallback = + new JNIDeviceCallback(env, thiz, pJniStorage->mCallbackData.audioTrack_ref, + javaAudioTrackFields.postNativeEventInJava); + lpTrack->addAudioDeviceCallback(pJniStorage->mDeviceCallback); +} + +static void android_media_AudioTrack_disableDeviceCallback( + JNIEnv *env, jobject thiz) { + + sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); + if (lpTrack == NULL) { + return; + } + AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetLongField( + thiz, javaAudioTrackFields.jniData); + if (pJniStorage == NULL || pJniStorage->mDeviceCallback == 0) { + return; + } + lpTrack->removeAudioDeviceCallback(pJniStorage->mDeviceCallback); + pJniStorage->mDeviceCallback.clear(); +} + + // ---------------------------------------------------------------------------- // ---------------------------------------------------------------------------- static JNINativeMethod gMethods[] = { @@ -1030,6 +1077,9 @@ static JNINativeMethod gMethods[] = { "(I)I", (void *)android_media_AudioTrack_attachAuxEffect}, {"native_setOutputDevice", "(I)Z", (void *)android_media_AudioTrack_setOutputDevice}, + {"native_getRoutedDeviceId", "()I", (void *)android_media_AudioTrack_getRoutedDeviceId}, + {"native_enableDeviceCallback", "()V", (void *)android_media_AudioTrack_enableDeviceCallback}, + {"native_disableDeviceCallback", "()V", (void *)android_media_AudioTrack_disableDeviceCallback}, }; diff --git a/core/jni/android_media_DeviceCallback.cpp b/core/jni/android_media_DeviceCallback.cpp new file mode 100644 index 0000000..e159373 --- /dev/null +++ b/core/jni/android_media_DeviceCallback.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2015 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. + */ + +//#define LOG_NDEBUG 0 + +#define LOG_TAG "AudioDeviceCallback-JNI" + +#include <utils/Log.h> +#include <JNIHelp.h> +#include <JniConstants.h> +#include "core_jni_helpers.h" +#include <media/AudioSystem.h> + +#include "android_media_DeviceCallback.h" + + +// ---------------------------------------------------------------------------- + +using namespace android; + +JNIDeviceCallback::JNIDeviceCallback(JNIEnv* env, jobject thiz, jobject weak_thiz, + jmethodID postEventFromNative) +{ + + // Hold onto the AudioTrack/AudioRecord class for use in calling the static method + // that posts events to the application thread. + jclass clazz = env->GetObjectClass(thiz); + if (clazz == NULL) { + return; + } + mClass = (jclass)env->NewGlobalRef(clazz); + + // We use a weak reference so the AudioTrack/AudioRecord object can be garbage collected. + // The reference is only used as a proxy for callbacks. + mObject = env->NewGlobalRef(weak_thiz); + + mPostEventFromNative = postEventFromNative; +} + +JNIDeviceCallback::~JNIDeviceCallback() +{ + // remove global references + JNIEnv *env = AndroidRuntime::getJNIEnv(); + if (env == NULL) { + return; + } + env->DeleteGlobalRef(mObject); + env->DeleteGlobalRef(mClass); +} + +void JNIDeviceCallback::onAudioDeviceUpdate(audio_io_handle_t audioIo, + audio_port_handle_t deviceId) +{ + JNIEnv *env = AndroidRuntime::getJNIEnv(); + if (env == NULL) { + return; + } + + ALOGV("%s audioIo %d deviceId %d", __FUNCTION__, audioIo, deviceId); + env->CallStaticVoidMethod(mClass, + mPostEventFromNative, + mObject, + AUDIO_NATIVE_EVENT_ROUTING_CHANGE, deviceId, 0, NULL); + if (env->ExceptionCheck()) { + ALOGW("An exception occurred while notifying an event."); + env->ExceptionClear(); + } +} + diff --git a/core/jni/android_media_DeviceCallback.h b/core/jni/android_media_DeviceCallback.h new file mode 100644 index 0000000..7ae788e --- /dev/null +++ b/core/jni/android_media_DeviceCallback.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2015 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. + */ + +#ifndef ANDROID_MEDIA_DEVICE_CALLBACK_H +#define ANDROID_MEDIA_DEVICE_CALLBACK_H + +#include <system/audio.h> +#include <media/AudioSystem.h> + +namespace android { + +// keep in sync with AudioSystem.java +#define AUDIO_NATIVE_EVENT_ROUTING_CHANGE 1000 + +class JNIDeviceCallback: public AudioSystem::AudioDeviceCallback +{ +public: + JNIDeviceCallback(JNIEnv* env, jobject thiz, jobject weak_thiz, jmethodID postEventFromNative); + ~JNIDeviceCallback(); + + virtual void onAudioDeviceUpdate(audio_io_handle_t audioIo, + audio_port_handle_t deviceId); + +private: + void sendEvent(int event); + + jclass mClass; // Reference to AudioTrack/AudioRecord class + jobject mObject; // Weak ref to AudioTrack/AudioRecord Java object to call on + jmethodID mPostEventFromNative; // postEventFromNative method ID. +}; + +}; // namespace android + +#endif // ANDROID_MEDIA_DEVICE_CALLBACK_H diff --git a/media/java/android/media/AudioDevicePort.java b/media/java/android/media/AudioDevicePort.java index 82da27d..c078260 100644 --- a/media/java/android/media/AudioDevicePort.java +++ b/media/java/android/media/AudioDevicePort.java @@ -83,6 +83,16 @@ public class AudioDevicePort extends AudioPort { if (o == null || !(o instanceof AudioDevicePort)) { return false; } + AudioDevicePort other = (AudioDevicePort)o; + if (mType != other.type()) { + return false; + } + if (mAddress == null && other.address() != null) { + return false; + } + if (!mAddress.equals(other.address())) { + return false; + } return super.equals(o); } diff --git a/media/java/android/media/AudioDevicesManager.java b/media/java/android/media/AudioDevicesManager.java index ca238d7..8b83c17 100644 --- a/media/java/android/media/AudioDevicesManager.java +++ b/media/java/android/media/AudioDevicesManager.java @@ -96,7 +96,7 @@ public class AudioDevicesManager { * @param flags A set of bitflags specifying the criteria to test. * @see {@link LIST_DEVICES_OUTPUTS} and {@link LIST_DEVICES_INPUTS} **/ - private boolean checkFlags(AudioDevicePort port, int flags) { + private static boolean checkFlags(AudioDevicePort port, int flags) { return port.role() == AudioPort.ROLE_SINK && (flags & LIST_DEVICES_OUTPUTS) != 0 || port.role() == AudioPort.ROLE_SOURCE && (flags & LIST_DEVICES_INPUTS) != 0; } @@ -110,8 +110,21 @@ public class AudioDevicesManager { * @return A (possibly zero-length) array of AudioDeviceInfo objects. */ public AudioDeviceInfo[] listDevices(int flags) { + return listDevicesStatic(flags); + } + + /** + * Generates a list of AudioDeviceInfo objects corresponding to the audio devices currently + * connected to the system and meeting the criteria specified in the <code>flags</code> + * parameter. + * @param flags A set of bitflags specifying the criteria to test. + * @see {@link LIST_DEVICES_OUTPUTS}, {@link LIST_DEVICES_INPUTS} and {@link LIST_DEVICES_ALL}. + * @return A (possibly zero-length) array of AudioDeviceInfo objects. + * @hide + */ + public static AudioDeviceInfo[] listDevicesStatic(int flags) { ArrayList<AudioDevicePort> ports = new ArrayList<AudioDevicePort>(); - int status = mAudioManager.listAudioDevicePorts(ports); + int status = AudioManager.listAudioDevicePorts(ports); if (status != AudioManager.SUCCESS) { // fail and bail! return new AudioDeviceInfo[0]; diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 6eaf812..19900d0 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -3401,7 +3401,7 @@ public class AudioManager { * @param ports An AudioPort ArrayList where the list will be returned. * @hide */ - public int listAudioPorts(ArrayList<AudioPort> ports) { + public static int listAudioPorts(ArrayList<AudioPort> ports) { return updateAudioPortCache(ports, null); } @@ -3410,7 +3410,7 @@ public class AudioManager { * @see listAudioPorts(ArrayList<AudioPort>) * @hide */ - public int listAudioDevicePorts(ArrayList<AudioDevicePort> devices) { + public static int listAudioDevicePorts(ArrayList<AudioDevicePort> devices) { ArrayList<AudioPort> ports = new ArrayList<AudioPort>(); int status = updateAudioPortCache(ports, null); if (status == SUCCESS) { @@ -3447,7 +3447,7 @@ public class AudioManager { * patch[0] contains the newly created patch * @hide */ - public int createAudioPatch(AudioPatch[] patch, + public static int createAudioPatch(AudioPatch[] patch, AudioPortConfig[] sources, AudioPortConfig[] sinks) { return AudioSystem.createAudioPatch(patch, sources, sinks); @@ -3464,7 +3464,7 @@ public class AudioManager { * - {@link #ERROR} if patch cannot be released for any other reason. * @hide */ - public int releaseAudioPatch(AudioPatch patch) { + public static int releaseAudioPatch(AudioPatch patch) { return AudioSystem.releaseAudioPatch(patch); } @@ -3473,7 +3473,7 @@ public class AudioManager { * @param patches An AudioPatch array where the list will be returned. * @hide */ - public int listAudioPatches(ArrayList<AudioPatch> patches) { + public static int listAudioPatches(ArrayList<AudioPatch> patches) { return updateAudioPortCache(null, patches); } @@ -3482,7 +3482,7 @@ public class AudioManager { * AudioGain.buildConfig() * @hide */ - public int setAudioPortGain(AudioPort port, AudioGainConfig gain) { + public static int setAudioPortGain(AudioPort port, AudioGainConfig gain) { if (port == null || gain == null) { return ERROR_BAD_VALUE; } diff --git a/media/java/android/media/AudioMixPort.java b/media/java/android/media/AudioMixPort.java index 9fac8d1..ab55c8d 100644 --- a/media/java/android/media/AudioMixPort.java +++ b/media/java/android/media/AudioMixPort.java @@ -20,16 +20,21 @@ package android.media; * The AudioMixPort is a specialized type of AudioPort * describing an audio mix or stream at an input or output stream of the audio * framework. + * In addition to base audio port attributes, the mix descriptor contains: + * - the unique audio I/O handle assigned by AudioFlinger to this mix. * @see AudioPort * @hide */ public class AudioMixPort extends AudioPort { - AudioMixPort(AudioHandle handle, int role, String deviceName, + private final int mIoHandle; + + AudioMixPort(AudioHandle handle, int ioHandle, int role, String deviceName, int[] samplingRates, int[] channelMasks, int[] formats, AudioGain[] gains) { super(handle, role, deviceName, samplingRates, channelMasks, formats, gains); + mIoHandle = ioHandle; } /** @@ -41,11 +46,23 @@ public class AudioMixPort extends AudioPort { return new AudioMixPortConfig(this, samplingRate, channelMask, format, gain); } + /** + * Get the device type (e.g AudioManager.DEVICE_OUT_SPEAKER) + */ + public int ioHandle() { + return mIoHandle; + } + @Override public boolean equals(Object o) { if (o == null || !(o instanceof AudioMixPort)) { return false; } + AudioMixPort other = (AudioMixPort)o; + if (mIoHandle != other.ioHandle()) { + return false; + } + return super.equals(o); } diff --git a/media/java/android/media/AudioPort.java b/media/java/android/media/AudioPort.java index 88e784a..7328d7a 100644 --- a/media/java/android/media/AudioPort.java +++ b/media/java/android/media/AudioPort.java @@ -93,6 +93,14 @@ public class AudioPort { } /** + * Get the system unique device ID. + */ + public int id() { + return mHandle.id(); + } + + + /** * Get the audio port role */ public int role() { diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java index 472da02..11671d8 100644 --- a/media/java/android/media/AudioRecord.java +++ b/media/java/android/media/AudioRecord.java @@ -116,11 +116,6 @@ public class AudioRecord */ private static final int NATIVE_EVENT_NEW_POS = 3; - /** - * Event id denotes when the routing changes. - */ - private final static int NATIVE_EVENT_ROUTING_CHANGE = 1000; - private final static String TAG = "android.media.AudioRecord"; /** @hide */ @@ -161,6 +156,12 @@ public class AudioRecord @SuppressWarnings("unused") private long mNativeCallbackCookie; + /** + * Accessed by native methods: provides access to the JNIDeviceCallback instance. + */ + @SuppressWarnings("unused") + private long mNativeDeviceCallback; + //--------------------------------------------------------- // Member variables @@ -1205,6 +1206,17 @@ public class AudioRecord * Returns an {@link AudioDeviceInfo} identifying the current routing of this AudioRecord. */ public AudioDeviceInfo getRoutedDevice() { + int deviceId = native_getRoutedDeviceId(); + if (deviceId == 0) { + return null; + } + AudioDeviceInfo[] devices = + AudioDevicesManager.listDevicesStatic(AudioDevicesManager.LIST_DEVICES_INPUTS); + for (int i = 0; i < devices.length; i++) { + if (devices[i].getId() == deviceId) { + return devices[i]; + } + } return null; } @@ -1224,6 +1236,9 @@ public class AudioRecord android.os.Handler handler) { if (listener != null && !mRoutingChangeListeners.containsKey(listener)) { synchronized (mRoutingChangeListeners) { + if (mRoutingChangeListeners.size() == 0) { + native_enableDeviceCallback(); + } mRoutingChangeListeners.put( listener, new NativeRoutingEventHandlerDelegate(this, listener, handler)); } @@ -1238,6 +1253,9 @@ public class AudioRecord synchronized (mRoutingChangeListeners) { if (mRoutingChangeListeners.containsKey(listener)) { mRoutingChangeListeners.remove(listener); + if (mRoutingChangeListeners.size() == 0) { + native_disableDeviceCallback(); + } } } } @@ -1271,7 +1289,7 @@ public class AudioRecord return; } switch(msg.what) { - case NATIVE_EVENT_ROUTING_CHANGE: + case AudioSystem.NATIVE_EVENT_ROUTING_CHANGE: if (listener != null) { listener.onAudioRecordRouting(record); } @@ -1299,10 +1317,11 @@ public class AudioRecord synchronized (mRoutingChangeListeners) { values = mRoutingChangeListeners.values(); } + AudioManager.resetAudioPortGeneration(); for(NativeRoutingEventHandlerDelegate delegate : values) { Handler handler = delegate.getHandler(); if (handler != null) { - handler.sendEmptyMessage(NATIVE_EVENT_ROUTING_CHANGE); + handler.sendEmptyMessage(AudioSystem.NATIVE_EVENT_ROUTING_CHANGE); } } } @@ -1341,10 +1360,14 @@ public class AudioRecord return false; } - mPreferredDevice = deviceInfo; - int preferredDeviceId = mPreferredDevice != null ? deviceInfo.getId() : 0; - - return native_setInputDevice(preferredDeviceId); + int preferredDeviceId = deviceInfo != null ? deviceInfo.getId() : 0; + boolean status = native_setInputDevice(preferredDeviceId); + if (status == true) { + synchronized (this) { + mPreferredDevice = deviceInfo; + } + } + return status; } /** @@ -1352,7 +1375,9 @@ public class AudioRecord * is not guarenteed to correspond to the actual device being used for recording. */ public AudioDeviceInfo getPreferredInputDevice() { - return mPreferredDevice; + synchronized (this) { + return mPreferredDevice; + } } //--------------------------------------------------------- @@ -1435,6 +1460,11 @@ public class AudioRecord return; } + if (what == AudioSystem.NATIVE_EVENT_ROUTING_CHANGE) { + recorder.broadcastRoutingChange(); + return; + } + if (recorder.mEventHandler != null) { Message m = recorder.mEventHandler.obtainMessage(what, arg1, arg2, obj); @@ -1486,7 +1516,9 @@ public class AudioRecord int sampleRateInHz, int channelCount, int audioFormat); private native final boolean native_setInputDevice(int deviceId); - + private native final int native_getRoutedDeviceId(); + private native final void native_enableDeviceCallback(); + private native final void native_disableDeviceCallback(); //--------------------------------------------------------- // Utility methods diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index 3dae543..ee12374 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -721,5 +721,11 @@ public class AudioSystem (1 << STREAM_RING) | (1 << STREAM_NOTIFICATION) | (1 << STREAM_SYSTEM); + + /** + * Event posted by AudioTrack and AudioRecord JNI (JNIDeviceCallback) when routing changes. + * Keep in sync with core/jni/android_media_DeviceCallback.h. + */ + final static int NATIVE_EVENT_ROUTING_CHANGE = 1000; } diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java index cb05cc5..a66a1e5 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -178,12 +178,6 @@ public class AudioTrack */ private static final int NATIVE_EVENT_NEW_POS = 4; - /** - * Event id denotes when the routing changes. - */ - private final static int NATIVE_EVENT_ROUTING_CHANGE = 1000; - - private final static String TAG = "android.media.AudioTrack"; @@ -2057,11 +2051,14 @@ public class AudioTrack if (deviceInfo != null && !deviceInfo.isSink()) { return false; } - - mPreferredDevice = deviceInfo; - int preferredDeviceId = mPreferredDevice != null ? deviceInfo.getId() : 0; - - return native_setOutputDevice(preferredDeviceId); + int preferredDeviceId = deviceInfo != null ? deviceInfo.getId() : 0; + boolean status = native_setOutputDevice(preferredDeviceId); + if (status == true) { + synchronized (this) { + mPreferredDevice = deviceInfo; + } + } + return status; } /** @@ -2069,7 +2066,9 @@ public class AudioTrack * is not guaranteed to correspond to the actual device being used for playback. */ public AudioDeviceInfo getPreferredOutputDevice() { - return mPreferredDevice; + synchronized (this) { + return mPreferredDevice; + } } //-------------------------------------------------------------------------- @@ -2079,6 +2078,17 @@ public class AudioTrack * Returns an {@link AudioDeviceInfo} identifying the current routing of this AudioTrack. */ public AudioDeviceInfo getRoutedDevice() { + int deviceId = native_getRoutedDeviceId(); + if (deviceId == 0) { + return null; + } + AudioDeviceInfo[] devices = + AudioDevicesManager.listDevicesStatic(AudioDevicesManager.LIST_DEVICES_OUTPUTS); + for (int i = 0; i < devices.length; i++) { + if (devices[i].getId() == deviceId) { + return devices[i]; + } + } return null; } @@ -2098,6 +2108,9 @@ public class AudioTrack android.os.Handler handler) { if (listener != null && !mRoutingChangeListeners.containsKey(listener)) { synchronized (mRoutingChangeListeners) { + if (mRoutingChangeListeners.size() == 0) { + native_enableDeviceCallback(); + } mRoutingChangeListeners.put( listener, new NativeRoutingEventHandlerDelegate(this, listener, handler)); } @@ -2113,6 +2126,9 @@ public class AudioTrack if (mRoutingChangeListeners.containsKey(listener)) { mRoutingChangeListeners.remove(listener); } + if (mRoutingChangeListeners.size() == 0) { + native_disableDeviceCallback(); + } } } @@ -2124,10 +2140,11 @@ public class AudioTrack synchronized (mRoutingChangeListeners) { values = mRoutingChangeListeners.values(); } + AudioManager.resetAudioPortGeneration(); for(NativeRoutingEventHandlerDelegate delegate : values) { Handler handler = delegate.getHandler(); if (handler != null) { - handler.sendEmptyMessage(NATIVE_EVENT_ROUTING_CHANGE); + handler.sendEmptyMessage(AudioSystem.NATIVE_EVENT_ROUTING_CHANGE); } } } @@ -2240,7 +2257,7 @@ public class AudioTrack return; } switch(msg.what) { - case NATIVE_EVENT_ROUTING_CHANGE: + case AudioSystem.NATIVE_EVENT_ROUTING_CHANGE: if (listener != null) { listener.onAudioTrackRouting(track); } @@ -2273,6 +2290,10 @@ public class AudioTrack return; } + if (what == AudioSystem.NATIVE_EVENT_ROUTING_CHANGE) { + track.broadcastRoutingChange(); + return; + } NativePositionEventHandlerDelegate delegate = track.mEventHandlerDelegate; if (delegate != null) { Handler handler = delegate.getHandler(); @@ -2281,7 +2302,6 @@ public class AudioTrack handler.sendMessage(m); } } - } @@ -2362,6 +2382,9 @@ public class AudioTrack private native final int native_setAuxEffectSendLevel(float level); private native final boolean native_setOutputDevice(int deviceId); + private native final int native_getRoutedDeviceId(); + private native final void native_enableDeviceCallback(); + private native final void native_disableDeviceCallback(); //--------------------------------------------------------- // Utility methods |