Merge "Native Audio Routing and (Java) AudioRouting (interface)."
diff --git a/api/current.txt b/api/current.txt
index c7bd93b..7ff1e6e 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -19402,9 +19402,10 @@
method public abstract void onAudioFocusChange(int);
}
- public class AudioRecord {
+ public class AudioRecord implements android.media.AudioRouting {
ctor public AudioRecord(int, int, int, int, int) throws java.lang.IllegalArgumentException;
- method public void addOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener, android.os.Handler);
+ method public deprecated void addOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener, android.os.Handler);
+ method public void addOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
method public int getAudioFormat();
method public int getAudioSessionId();
method public int getAudioSource();
@@ -19428,7 +19429,8 @@
method public int read(java.nio.ByteBuffer, int);
method public int read(java.nio.ByteBuffer, int, int);
method public void release();
- method public void removeOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener);
+ method public deprecated void removeOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener);
+ method public void removeOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener);
method public int setNotificationMarkerPosition(int);
method public int setPositionNotificationPeriod(int);
method public boolean setPreferredDevice(android.media.AudioDeviceInfo);
@@ -19466,17 +19468,29 @@
method public abstract void onRoutingChanged(android.media.AudioRecord);
}
+ public abstract interface AudioRouting {
+ method public abstract void addOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
+ method public abstract android.media.AudioDeviceInfo getPreferredDevice();
+ method public abstract void removeOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener);
+ method public abstract boolean setPreferredDevice(android.media.AudioDeviceInfo);
+ }
+
+ public static abstract interface AudioRouting.OnRoutingChangedListener {
+ method public abstract void onRoutingChanged(android.media.AudioRouting);
+ }
+
public final class AudioTimestamp {
ctor public AudioTimestamp();
field public long framePosition;
field public long nanoTime;
}
- public class AudioTrack {
+ public class AudioTrack implements android.media.AudioRouting {
ctor public AudioTrack(int, int, int, int, int, int) throws java.lang.IllegalArgumentException;
ctor public AudioTrack(int, int, int, int, int, int, int) throws java.lang.IllegalArgumentException;
ctor public AudioTrack(android.media.AudioAttributes, android.media.AudioFormat, int, int, int) throws java.lang.IllegalArgumentException;
- method public void addOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener, android.os.Handler);
+ method public deprecated void addOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener, android.os.Handler);
+ method public void addOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
method public int attachAuxEffect(int);
method public void flush();
method public int getAudioFormat();
@@ -19506,7 +19520,8 @@
method public void play() throws java.lang.IllegalStateException;
method public void release();
method public int reloadStaticData();
- method public void removeOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener);
+ method public deprecated void removeOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener);
+ method public void removeOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener);
method public int setAuxEffectSendLevel(float);
method public int setLoopPoints(int, int, int);
method public int setNotificationMarkerPosition(int);
@@ -19559,8 +19574,8 @@
method public abstract void onPeriodicNotification(android.media.AudioTrack);
}
- public static abstract interface AudioTrack.OnRoutingChangedListener {
- method public abstract void onRoutingChanged(android.media.AudioTrack);
+ public static abstract deprecated interface AudioTrack.OnRoutingChangedListener {
+ method public abstract deprecated void onRoutingChanged(android.media.AudioTrack);
}
public class CamcorderProfile {
diff --git a/api/system-current.txt b/api/system-current.txt
index 3267b95..1379c8e 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -20706,10 +20706,11 @@
method public abstract void onAudioFocusChange(int);
}
- public class AudioRecord {
+ public class AudioRecord implements android.media.AudioRouting {
ctor public AudioRecord(int, int, int, int, int) throws java.lang.IllegalArgumentException;
ctor public AudioRecord(android.media.AudioAttributes, android.media.AudioFormat, int, int) throws java.lang.IllegalArgumentException;
- method public void addOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener, android.os.Handler);
+ method public deprecated void addOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener, android.os.Handler);
+ method public void addOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
method public int getAudioFormat();
method public int getAudioSessionId();
method public int getAudioSource();
@@ -20733,7 +20734,8 @@
method public int read(java.nio.ByteBuffer, int);
method public int read(java.nio.ByteBuffer, int, int);
method public void release();
- method public void removeOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener);
+ method public deprecated void removeOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener);
+ method public void removeOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener);
method public int setNotificationMarkerPosition(int);
method public int setPositionNotificationPeriod(int);
method public boolean setPreferredDevice(android.media.AudioDeviceInfo);
@@ -20773,17 +20775,29 @@
method public abstract void onRoutingChanged(android.media.AudioRecord);
}
+ public abstract interface AudioRouting {
+ method public abstract void addOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
+ method public abstract android.media.AudioDeviceInfo getPreferredDevice();
+ method public abstract void removeOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener);
+ method public abstract boolean setPreferredDevice(android.media.AudioDeviceInfo);
+ }
+
+ public static abstract interface AudioRouting.OnRoutingChangedListener {
+ method public abstract void onRoutingChanged(android.media.AudioRouting);
+ }
+
public final class AudioTimestamp {
ctor public AudioTimestamp();
field public long framePosition;
field public long nanoTime;
}
- public class AudioTrack {
+ public class AudioTrack implements android.media.AudioRouting {
ctor public AudioTrack(int, int, int, int, int, int) throws java.lang.IllegalArgumentException;
ctor public AudioTrack(int, int, int, int, int, int, int) throws java.lang.IllegalArgumentException;
ctor public AudioTrack(android.media.AudioAttributes, android.media.AudioFormat, int, int, int) throws java.lang.IllegalArgumentException;
- method public void addOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener, android.os.Handler);
+ method public deprecated void addOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener, android.os.Handler);
+ method public void addOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
method public int attachAuxEffect(int);
method public void flush();
method public int getAudioFormat();
@@ -20813,7 +20827,8 @@
method public void play() throws java.lang.IllegalStateException;
method public void release();
method public int reloadStaticData();
- method public void removeOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener);
+ method public deprecated void removeOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener);
+ method public void removeOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener);
method public int setAuxEffectSendLevel(float);
method public int setLoopPoints(int, int, int);
method public int setNotificationMarkerPosition(int);
@@ -20866,8 +20881,8 @@
method public abstract void onPeriodicNotification(android.media.AudioTrack);
}
- public static abstract interface AudioTrack.OnRoutingChangedListener {
- method public abstract void onRoutingChanged(android.media.AudioTrack);
+ public static abstract deprecated interface AudioTrack.OnRoutingChangedListener {
+ method public abstract deprecated void onRoutingChanged(android.media.AudioTrack);
}
public class CamcorderProfile {
diff --git a/api/test-current.txt b/api/test-current.txt
index 92b1680..0603478 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -19410,9 +19410,10 @@
method public abstract void onAudioFocusChange(int);
}
- public class AudioRecord {
+ public class AudioRecord implements android.media.AudioRouting {
ctor public AudioRecord(int, int, int, int, int) throws java.lang.IllegalArgumentException;
- method public void addOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener, android.os.Handler);
+ method public deprecated void addOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener, android.os.Handler);
+ method public void addOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
method public int getAudioFormat();
method public int getAudioSessionId();
method public int getAudioSource();
@@ -19436,7 +19437,8 @@
method public int read(java.nio.ByteBuffer, int);
method public int read(java.nio.ByteBuffer, int, int);
method public void release();
- method public void removeOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener);
+ method public deprecated void removeOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener);
+ method public void removeOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener);
method public int setNotificationMarkerPosition(int);
method public int setPositionNotificationPeriod(int);
method public boolean setPreferredDevice(android.media.AudioDeviceInfo);
@@ -19474,17 +19476,29 @@
method public abstract void onRoutingChanged(android.media.AudioRecord);
}
+ public abstract interface AudioRouting {
+ method public abstract void addOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
+ method public abstract android.media.AudioDeviceInfo getPreferredDevice();
+ method public abstract void removeOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener);
+ method public abstract boolean setPreferredDevice(android.media.AudioDeviceInfo);
+ }
+
+ public static abstract interface AudioRouting.OnRoutingChangedListener {
+ method public abstract void onRoutingChanged(android.media.AudioRouting);
+ }
+
public final class AudioTimestamp {
ctor public AudioTimestamp();
field public long framePosition;
field public long nanoTime;
}
- public class AudioTrack {
+ public class AudioTrack implements android.media.AudioRouting {
ctor public AudioTrack(int, int, int, int, int, int) throws java.lang.IllegalArgumentException;
ctor public AudioTrack(int, int, int, int, int, int, int) throws java.lang.IllegalArgumentException;
ctor public AudioTrack(android.media.AudioAttributes, android.media.AudioFormat, int, int, int) throws java.lang.IllegalArgumentException;
- method public void addOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener, android.os.Handler);
+ method public deprecated void addOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener, android.os.Handler);
+ method public void addOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
method public int attachAuxEffect(int);
method public void flush();
method public int getAudioFormat();
@@ -19514,7 +19528,8 @@
method public void play() throws java.lang.IllegalStateException;
method public void release();
method public int reloadStaticData();
- method public void removeOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener);
+ method public deprecated void removeOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener);
+ method public void removeOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener);
method public int setAuxEffectSendLevel(float);
method public int setLoopPoints(int, int, int);
method public int setNotificationMarkerPosition(int);
@@ -19567,8 +19582,8 @@
method public abstract void onPeriodicNotification(android.media.AudioTrack);
}
- public static abstract interface AudioTrack.OnRoutingChangedListener {
- method public abstract void onRoutingChanged(android.media.AudioTrack);
+ public static abstract deprecated interface AudioTrack.OnRoutingChangedListener {
+ method public abstract deprecated void onRoutingChanged(android.media.AudioTrack);
}
public class CamcorderProfile {
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 974b62e..7c21893 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -51,7 +51,7 @@
* been read yet. Data should be read from the audio hardware in chunks of sizes inferior to
* the total recording buffer size.
*/
-public class AudioRecord
+public class AudioRecord implements AudioRouting
{
//---------------------------------------------------------
// Constants
@@ -392,6 +392,20 @@
}
/**
+ * A constructor which explicitly connects a Native (C++) AudioRecord. For use by
+ * the AudioRecordRoutingProxy subclass.
+ * @param nativeRecordInJavaObj A C/C++ pointer to a native AudioRecord
+ * (associated with an OpenSL ES recorder).
+ */
+ /*package*/ AudioRecord(long nativeRecordInJavaObj) {
+ mNativeRecorderInJavaObj = nativeRecordInJavaObj;
+
+ // other initialization here...
+
+ mState = STATE_INITIALIZED;
+ }
+
+ /**
* Builder class for {@link AudioRecord} objects.
* Use this class to configure and create an <code>AudioRecord</code> instance. By setting the
* recording source and audio format parameters, you indicate which of
@@ -1221,23 +1235,6 @@
return native_set_marker_pos(markerInFrames);
}
-
- //--------------------------------------------------------------------------
- // (Re)Routing Info
- //--------------------
- /**
- * Defines the interface by which applications can receive notifications of routing
- * changes for the associated {@link AudioRecord}.
- */
- public interface OnRoutingChangedListener {
- /**
- * Called when the routing of an AudioRecord changes from either and explicit or
- * policy rerouting. Use {@link #getRoutedDevice()} to retrieve the newly routed-from
- * device.
- */
- public void onRoutingChanged(AudioRecord audioRecord);
- }
-
/**
* Returns an {@link AudioDeviceInfo} identifying the current routing of this AudioRecord.
* Note: The query is only valid if the AudioRecord is currently recording. If it is not,
@@ -1258,6 +1255,89 @@
return null;
}
+ /*
+ * Call BEFORE adding a routing callback handler.
+ */
+ private void testEnableNativeRoutingCallbacks() {
+ if (mRoutingChangeListeners.size() == 0 && mNewRoutingChangeListeners.size() == 0) {
+ native_enableDeviceCallback();
+ }
+ }
+
+ /*
+ * Call AFTER removing a routing callback handler.
+ */
+ private void testDisableNativeRoutingCallbacks() {
+ if (mRoutingChangeListeners.size() == 0 && mNewRoutingChangeListeners.size() == 0) {
+ native_disableDeviceCallback();
+ }
+ }
+
+ //--------------------------------------------------------------------------
+ // >= "N" (Re)Routing Info
+ //--------------------
+ /**
+ * The list of AudioRouting.OnRoutingChangedListener interfaces added (with
+ * {@link AudioRecord#addOnRoutingListener(AudioRouting.OnRoutingChangedListener,
+ * android.os.Handler)}
+ * by an app to receive (re)routing notifications.
+ */
+ private ArrayMap<AudioRouting.OnRoutingChangedListener, NativeNewRoutingEventHandlerDelegate>
+ mNewRoutingChangeListeners =
+ new ArrayMap<AudioRouting.OnRoutingChangedListener, NativeNewRoutingEventHandlerDelegate>();
+
+ /**
+ * Adds an {@link AudioRouting.OnRoutingChangedListener} to receive notifications of
+ * routing changes on this AudioRecord.
+ * @param listener The {@link AudioRouting.OnRoutingChangedListener} interface to receive
+ * notifications of rerouting events.
+ * @param handler Specifies the {@link Handler} object for the thread on which to execute
+ * the callback. If <code>null</code>, the {@link Handler} associated with the main
+ * {@link Looper} will be used.
+ */
+ public void addOnRoutingListener(AudioRouting.OnRoutingChangedListener listener,
+ android.os.Handler handler) {
+ if (listener != null && !mNewRoutingChangeListeners.containsKey(listener)) {
+ synchronized (mNewRoutingChangeListeners) {
+ testEnableNativeRoutingCallbacks();
+ mNewRoutingChangeListeners.put(
+ listener, new NativeNewRoutingEventHandlerDelegate(this, listener,
+ handler != null ? handler : new Handler(mInitializationLooper)));
+ }
+ }
+ }
+
+ /**
+ * Removes an {@link AudioRouting.OnRoutingChangedListener} which has been previously added
+ * to receive rerouting notifications.
+ * @param listener The previously added {@link AudioRouting.OnRoutingChangedListener} interface
+ * to remove.
+ */
+ public void removeOnRoutingListener(AudioRouting.OnRoutingChangedListener listener) {
+ synchronized (mNewRoutingChangeListeners) {
+ if (mNewRoutingChangeListeners.containsKey(listener)) {
+ mNewRoutingChangeListeners.remove(listener);
+ testDisableNativeRoutingCallbacks();
+ }
+ }
+ }
+
+ //--------------------------------------------------------------------------
+ // Marshmallow (Re)Routing Info
+ //--------------------
+ /**
+ * Defines the interface by which applications can receive notifications of routing
+ * changes for the associated {@link AudioRecord}.
+ */
+ public interface OnRoutingChangedListener {
+ /**
+ * Called when the routing of an AudioRecord changes from either and explicit or
+ * policy rerouting. Use {@link #getRoutedDevice()} to retrieve the newly routed-from
+ * device.
+ */
+ public void onRoutingChanged(AudioRecord audioRecord);
+ }
+
/**
* The list of AudioRecord.OnRoutingChangedListener interface added (with
* {@link AudioRecord#addOnRoutingChangedListener(OnRoutingChangedListener,android.os.Handler)}
@@ -1276,13 +1356,12 @@
* the callback. If <code>null</code>, the {@link Handler} associated with the main
* {@link Looper} will be used.
*/
+ @Deprecated
public void addOnRoutingChangedListener(OnRoutingChangedListener listener,
android.os.Handler handler) {
if (listener != null && !mRoutingChangeListeners.containsKey(listener)) {
synchronized (mRoutingChangeListeners) {
- if (mRoutingChangeListeners.size() == 0) {
- native_enableDeviceCallback();
- }
+ testEnableNativeRoutingCallbacks();
mRoutingChangeListeners.put(
listener, new NativeRoutingEventHandlerDelegate(this, listener,
handler != null ? handler : new Handler(mInitializationLooper)));
@@ -1291,22 +1370,73 @@
}
/**
- * Removes an {@link OnRoutingChangedListener} which has been previously added
+ * Removes an {@link OnRoutingChangedListener} which has been previously added
* to receive rerouting notifications.
* @param listener The previously added {@link OnRoutingChangedListener} interface to remove.
*/
+ @Deprecated
public void removeOnRoutingChangedListener(OnRoutingChangedListener listener) {
synchronized (mRoutingChangeListeners) {
if (mRoutingChangeListeners.containsKey(listener)) {
mRoutingChangeListeners.remove(listener);
- if (mRoutingChangeListeners.size() == 0) {
- native_disableDeviceCallback();
- }
+ testDisableNativeRoutingCallbacks();
}
}
}
/**
+ * >= "N" Routing
+ * Helper class to handle the forwarding of native events to the appropriate listener
+ * (potentially) handled in a different thread
+ */
+ private class NativeNewRoutingEventHandlerDelegate {
+ private final Handler mHandler;
+
+ NativeNewRoutingEventHandlerDelegate(final AudioRecord record,
+ final AudioRouting.OnRoutingChangedListener listener,
+ Handler handler) {
+ // find the looper for our new event handler
+ Looper looper;
+ if (handler != null) {
+ looper = handler.getLooper();
+ } else {
+ // no given handler, use the looper the AudioRecord was created in
+ looper = mInitializationLooper;
+ }
+
+ // construct the event handler with this looper
+ if (looper != null) {
+ // implement the event handler delegate
+ mHandler = new Handler(looper) {
+ @Override
+ public void handleMessage(Message msg) {
+ if (record == null) {
+ return;
+ }
+ switch(msg.what) {
+ case AudioSystem.NATIVE_EVENT_ROUTING_CHANGE:
+ if (listener != null) {
+ listener.onRoutingChanged(record);
+ }
+ break;
+ default:
+ loge("Unknown native event type: " + msg.what);
+ break;
+ }
+ }
+ };
+ } else {
+ mHandler = null;
+ }
+ }
+
+ Handler getHandler() {
+ return mHandler;
+ }
+ }
+
+ /**
+ * Marshmallow Routing
* Helper class to handle the forwarding of native events to the appropriate listener
* (potentially) handled in a different thread
*/
@@ -1355,21 +1485,34 @@
return mHandler;
}
}
+
/**
* Sends device list change notification to all listeners.
*/
private void broadcastRoutingChange() {
+ AudioManager.resetAudioPortGeneration();
+ // Marshmallow Routing
Collection<NativeRoutingEventHandlerDelegate> values;
synchronized (mRoutingChangeListeners) {
values = mRoutingChangeListeners.values();
}
- AudioManager.resetAudioPortGeneration();
for(NativeRoutingEventHandlerDelegate delegate : values) {
Handler handler = delegate.getHandler();
if (handler != null) {
handler.sendEmptyMessage(AudioSystem.NATIVE_EVENT_ROUTING_CHANGE);
}
}
+ // >= "N" Routing
+ Collection<NativeNewRoutingEventHandlerDelegate> newValues;
+ synchronized (mNewRoutingChangeListeners) {
+ newValues = mNewRoutingChangeListeners.values();
+ }
+ for(NativeNewRoutingEventHandlerDelegate delegate : newValues) {
+ Handler handler = delegate.getHandler();
+ if (handler != null) {
+ handler.sendEmptyMessage(AudioSystem.NATIVE_EVENT_ROUTING_CHANGE);
+ }
+ }
}
/**
diff --git a/media/java/android/media/AudioRecordRoutingProxy.java b/media/java/android/media/AudioRecordRoutingProxy.java
new file mode 100644
index 0000000..b0c19e4
--- /dev/null
+++ b/media/java/android/media/AudioRecordRoutingProxy.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2008 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;
+
+/**
+ * An AudioRecord connected to a native (C/C++) which allows access only to routing methods.
+ */
+class AudioRecordRoutingProxy extends AudioRecord {
+ /**
+ * A constructor which explicitly connects a Native (C++) AudioRecord. For use by
+ * the AudioRecordRoutingProxy subclass.
+ * @param nativeRecordInJavaObj A C/C++ pointer to a native AudioRecord
+ * (associated with an OpenSL ES recorder).
+ */
+ public AudioRecordRoutingProxy(long nativeRecordInJavaObj) {
+ super(nativeRecordInJavaObj);
+ }
+}
diff --git a/media/java/android/media/AudioRouting.java b/media/java/android/media/AudioRouting.java
new file mode 100644
index 0000000..2161cf3
--- /dev/null
+++ b/media/java/android/media/AudioRouting.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2016 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;
+
+/**
+ * AudioRouting defines an interface for controlling routing and routing notifications in
+ * AudioTrack and AudioRecord objects.
+ */
+public interface AudioRouting {
+ /**
+ * Specifies an audio device (via an {@link AudioDeviceInfo} object) to route
+ * the output/input to/from.
+ * @param deviceInfo The {@link AudioDeviceInfo} specifying the audio sink or source.
+ * If deviceInfo is null, default routing is restored.
+ * @return true if succesful, false if the specified {@link AudioDeviceInfo} is non-null and
+ * does not correspond to a valid audio device.
+ */
+ public boolean setPreferredDevice(AudioDeviceInfo deviceInfo);
+
+ /**
+ * Returns the selected output/input specified by {@link #setPreferredDevice}. Note that this
+ * is not guaranteed to correspond to the actual device being used for playback/recording.
+ */
+ public AudioDeviceInfo getPreferredDevice();
+
+ /**
+ * Adds an {@link AudioRouting.OnRoutingChangedListener} to receive notifications of routing
+ * changes on this AudioTrack/AudioRecord.
+ * @param listener The {@link AudioRouting.OnRoutingChangedListener} interface to receive
+ * notifications of rerouting events.
+ * @param handler Specifies the {@link Handler} object for the thread on which to execute
+ * the callback. If <code>null</code>, the {@link Handler} associated with the main
+ * {@link Looper} will be used.
+ */
+ public void addOnRoutingListener(OnRoutingChangedListener listener,
+ Handler handler);
+
+ /**
+ * Removes an {@link AudioRouting.OnRoutingChangedListener} which has been previously added
+ * to receive rerouting notifications.
+ * @param listener The previously added {@link AudioRouting.OnRoutingChangedListener} interface
+ * to remove.
+ */
+ public void removeOnRoutingListener(OnRoutingChangedListener listener);
+
+ /**
+ * Defines the interface by which applications can receive notifications of routing
+ * changes for the associated {@link AudioRouting}.
+ */
+ public interface OnRoutingChangedListener {
+ public void onRoutingChanged(AudioRouting router);
+ }
+}
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index a810ff1..c110ce8 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -78,7 +78,7 @@
*
* AudioTrack is not final and thus permits subclasses, but such use is not recommended.
*/
-public class AudioTrack
+public class AudioTrack implements AudioRouting
{
//---------------------------------------------------------
// Constants
@@ -318,10 +318,11 @@
// Used exclusively by native code
//--------------------
/**
+ * @hide
* Accessed by native methods: provides access to C++ AudioTrack object.
*/
@SuppressWarnings("unused")
- private long mNativeTrackInJavaObj;
+ protected long mNativeTrackInJavaObj;
/**
* Accessed by native methods: provides access to the JNI data (i.e. resources used by
* the native AudioTrack object, but not stored in it).
@@ -524,6 +525,31 @@
}
/**
+ * A constructor which explicitly connects a Native (C++) AudioTrack. For use by
+ * the AudioTrackRoutingProxy subclass.
+ * @param nativeTrackInJavaObj a C/C++ pointer to a native AudioTrack
+ * (associated with an OpenSL ES player).
+ */
+ /*package*/ AudioTrack(long nativeTrackInJavaObj) {
+ mNativeTrackInJavaObj = nativeTrackInJavaObj;
+
+ // "final"s
+ mAttributes = null;
+ mAppOps = null;
+
+ // remember which looper is associated with the AudioTrack instantiation
+ Looper looper;
+ if ((looper = Looper.myLooper()) == null) {
+ looper = Looper.getMainLooper();
+ }
+ mInitializationLooper = looper;
+
+ // other initialization...
+
+ mState = STATE_INITIALIZED;
+ }
+
+ /**
* Builder class for {@link AudioTrack} objects.
* Use this class to configure and create an <code>AudioTrack</code> instance. By setting audio
* attributes and audio format parameters, you indicate which of those vary from the default
@@ -2247,22 +2273,6 @@
}
}
- //--------------------------------------------------------------------------
- // (Re)Routing Info
- //--------------------
- /**
- * Defines the interface by which applications can receive notifications of routing
- * changes for the associated {@link AudioTrack}.
- */
- public interface OnRoutingChangedListener {
- /**
- * Called when the routing of an AudioTrack changes from either and explicit or
- * policy rerouting. Use {@link #getRoutedDevice()} to retrieve the newly routed-to
- * device.
- */
- public void onRoutingChanged(AudioTrack audioTrack);
- }
-
/**
* Returns an {@link AudioDeviceInfo} identifying the current routing of this AudioTrack.
* Note: The query is only valid if the AudioTrack is currently playing. If it is not,
@@ -2283,6 +2293,89 @@
return null;
}
+ /*
+ * Call BEFORE adding a routing callback handler.
+ */
+ private void testEnableNativeRoutingCallbacks() {
+ if (mRoutingChangeListeners.size() == 0 && mNewRoutingChangeListeners.size() == 0) {
+ native_enableDeviceCallback();
+ }
+ }
+
+ /*
+ * Call AFTER removing a routing callback handler.
+ */
+ private void testDisableNativeRoutingCallbacks() {
+ if (mRoutingChangeListeners.size() == 0 && mNewRoutingChangeListeners.size() == 0) {
+ native_disableDeviceCallback();
+ }
+ }
+
+ //--------------------------------------------------------------------------
+ // >= "N" (Re)Routing Info
+ //--------------------
+ /**
+ * The list of AudioRouting.OnRoutingChangedListener interfaces added (with
+ * {@link AudioTrack#addOnRoutingListener(AudioRouting.OnRoutingChangedListener,
+ * android.os.Handler)}
+ * by an app to receive (re)routing notifications.
+ */
+ private ArrayMap<AudioRouting.OnRoutingChangedListener, NativeNewRoutingEventHandlerDelegate>
+ mNewRoutingChangeListeners =
+ new ArrayMap<AudioRouting.OnRoutingChangedListener, NativeNewRoutingEventHandlerDelegate>();
+
+ /**
+ * Adds an {@link AudioRouting.OnRoutingChangedListener} to receive notifications of routing
+ * changes on this AudioTrack.
+ * @param listener The {@link AudioRouting.OnRoutingChangedListener} interface to receive
+ * notifications of rerouting events.
+ * @param handler Specifies the {@link Handler} object for the thread on which to execute
+ * the callback. If <code>null</code>, the {@link Handler} associated with the main
+ * {@link Looper} will be used.
+ */
+ public void addOnRoutingListener(AudioRouting.OnRoutingChangedListener listener,
+ Handler handler) {
+ if (listener != null && !mNewRoutingChangeListeners.containsKey(listener)) {
+ synchronized (mNewRoutingChangeListeners) {
+ testEnableNativeRoutingCallbacks();
+ mNewRoutingChangeListeners.put(
+ listener, new NativeNewRoutingEventHandlerDelegate(this, listener,
+ handler != null ? handler : new Handler(mInitializationLooper)));
+ }
+ }
+ }
+
+ /**
+ * Removes an {@link AudioRouting.OnRoutingChangedListener} which has been previously added
+ * to receive rerouting notifications.
+ * @param listener The previously added {@link AudioRouting.OnRoutingChangedListener} interface
+ * to remove.
+ */
+ public void removeOnRoutingListener(AudioRouting.OnRoutingChangedListener listener) {
+ if (mNewRoutingChangeListeners.containsKey(listener)) {
+ mNewRoutingChangeListeners.remove(listener);
+ }
+ testDisableNativeRoutingCallbacks();
+ }
+
+ //--------------------------------------------------------------------------
+ // Marshmallow (Re)Routing Info
+ //--------------------
+ /**
+ * Defines the interface by which applications can receive notifications of routing
+ * changes for the associated {@link AudioTrack}.
+ */
+ @Deprecated
+ public interface OnRoutingChangedListener {
+ /**
+ * Called when the routing of an AudioTrack changes from either and explicit or
+ * policy rerouting. Use {@link #getRoutedDevice()} to retrieve the newly routed-to
+ * device.
+ */
+ @Deprecated
+ public void onRoutingChanged(AudioTrack audioTrack);
+ }
+
/**
* The list of AudioTrack.OnRoutingChangedListener interfaces added (with
* {@link AudioTrack#addOnRoutingChangedListener(OnRoutingChangedListener, android.os.Handler)}
@@ -2301,13 +2394,12 @@
* the callback. If <code>null</code>, the {@link Handler} associated with the main
* {@link Looper} will be used.
*/
+ @Deprecated
public void addOnRoutingChangedListener(OnRoutingChangedListener listener,
android.os.Handler handler) {
if (listener != null && !mRoutingChangeListeners.containsKey(listener)) {
synchronized (mRoutingChangeListeners) {
- if (mRoutingChangeListeners.size() == 0) {
- native_enableDeviceCallback();
- }
+ testEnableNativeRoutingCallbacks();
mRoutingChangeListeners.put(
listener, new NativeRoutingEventHandlerDelegate(this, listener,
handler != null ? handler : new Handler(mInitializationLooper)));
@@ -2320,14 +2412,13 @@
* to receive rerouting notifications.
* @param listener The previously added {@link OnRoutingChangedListener} interface to remove.
*/
+ @Deprecated
public void removeOnRoutingChangedListener(OnRoutingChangedListener listener) {
synchronized (mRoutingChangeListeners) {
if (mRoutingChangeListeners.containsKey(listener)) {
mRoutingChangeListeners.remove(listener);
}
- if (mRoutingChangeListeners.size() == 0) {
- native_disableDeviceCallback();
- }
+ testDisableNativeRoutingCallbacks();
}
}
@@ -2335,17 +2426,30 @@
* Sends device list change notification to all listeners.
*/
private void broadcastRoutingChange() {
+ AudioManager.resetAudioPortGeneration();
+
+ // Marshmallow Routing
Collection<NativeRoutingEventHandlerDelegate> values;
synchronized (mRoutingChangeListeners) {
values = mRoutingChangeListeners.values();
}
- AudioManager.resetAudioPortGeneration();
for(NativeRoutingEventHandlerDelegate delegate : values) {
Handler handler = delegate.getHandler();
if (handler != null) {
handler.sendEmptyMessage(AudioSystem.NATIVE_EVENT_ROUTING_CHANGE);
}
}
+ // >= "N" Routing
+ Collection<NativeNewRoutingEventHandlerDelegate> newValues;
+ synchronized (mNewRoutingChangeListeners) {
+ newValues = mNewRoutingChangeListeners.values();
+ }
+ for(NativeNewRoutingEventHandlerDelegate delegate : newValues) {
+ Handler handler = delegate.getHandler();
+ if (handler != null) {
+ handler.sendEmptyMessage(AudioSystem.NATIVE_EVENT_ROUTING_CHANGE);
+ }
+ }
}
//---------------------------------------------------------
@@ -2428,6 +2532,7 @@
}
/**
+ * Marshmallow Routing API.
* Helper class to handle the forwarding of native events to the appropriate listener
* (potentially) handled in a different thread
*/
@@ -2477,6 +2582,57 @@
}
}
+ /**
+ * Marshmallow Routing API.
+ * Helper class to handle the forwarding of native events to the appropriate listener
+ * (potentially) handled in a different thread
+ */
+ private class NativeNewRoutingEventHandlerDelegate {
+ private final Handler mHandler;
+
+ NativeNewRoutingEventHandlerDelegate(final AudioTrack track,
+ final AudioRouting.OnRoutingChangedListener listener,
+ Handler handler) {
+ // find the looper for our new event handler
+ Looper looper;
+ if (handler != null) {
+ looper = handler.getLooper();
+ } else {
+ // no given handler, use the looper the AudioTrack was created in
+ looper = mInitializationLooper;
+ }
+
+ // construct the event handler with this looper
+ if (looper != null) {
+ // implement the event handler delegate
+ mHandler = new Handler(looper) {
+ @Override
+ public void handleMessage(Message msg) {
+ if (track == null) {
+ return;
+ }
+ switch(msg.what) {
+ case AudioSystem.NATIVE_EVENT_ROUTING_CHANGE:
+ if (listener != null) {
+ listener.onRoutingChanged(track);
+ }
+ break;
+ default:
+ loge("Unknown native event type: " + msg.what);
+ break;
+ }
+ }
+ };
+ } else {
+ mHandler = null;
+ }
+ }
+
+ Handler getHandler() {
+ return mHandler;
+ }
+ }
+
//---------------------------------------------------------
// Java methods called from the native side
//--------------------
diff --git a/media/java/android/media/AudioTrackRoutingProxy.java b/media/java/android/media/AudioTrackRoutingProxy.java
new file mode 100644
index 0000000..9b97ae9
--- /dev/null
+++ b/media/java/android/media/AudioTrackRoutingProxy.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2008 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;
+
+/**
+ * An AudioTrack connected to a native (C/C++) which allows access only to routing methods.
+ */
+class AudioTrackRoutingProxy extends AudioTrack {
+ /**
+ * A constructor which explicitly connects a Native (C++) AudioTrack. For use by
+ * the AudioTrackRoutingProxy subclass.
+ * @param nativeTrackInJavaObj a C/C++ pointer to a native AudioTrack
+ * (associated with an OpenSL ES player).
+ */
+ public AudioTrackRoutingProxy(long nativeTrackInJavaObj) {
+ super(nativeTrackInJavaObj);
+ }
+}