Merge "Unhide audio recording notification API"
diff --git a/api/current.txt b/api/current.txt
index 9a5ffdc..4f801cb 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -19297,6 +19297,7 @@
method public void adjustVolume(int, int);
method public void dispatchMediaKeyEvent(android.view.KeyEvent);
method public int generateAudioSessionId();
+ method public android.media.AudioRecordConfiguration[] getActiveRecordConfigurations();
method public android.media.AudioDeviceInfo[] getDevices(int);
method public int getMode();
method public java.lang.String getParameters(java.lang.String);
@@ -19319,6 +19320,7 @@
method public void playSoundEffect(int);
method public void playSoundEffect(int, float);
method public void registerAudioDeviceCallback(android.media.AudioDeviceCallback, android.os.Handler);
+ method public void registerAudioRecordingCallback(android.media.AudioManager.AudioRecordingCallback, android.os.Handler);
method public deprecated void registerMediaButtonEventReceiver(android.content.ComponentName);
method public deprecated void registerMediaButtonEventReceiver(android.app.PendingIntent);
method public deprecated void registerRemoteControlClient(android.media.RemoteControlClient);
@@ -19342,6 +19344,7 @@
method public void stopBluetoothSco();
method public void unloadSoundEffects();
method public void unregisterAudioDeviceCallback(android.media.AudioDeviceCallback);
+ method public void unregisterAudioRecordingCallback(android.media.AudioManager.AudioRecordingCallback);
method public deprecated void unregisterMediaButtonEventReceiver(android.content.ComponentName);
method public deprecated void unregisterMediaButtonEventReceiver(android.app.PendingIntent);
method public deprecated void unregisterRemoteControlClient(android.media.RemoteControlClient);
@@ -19438,6 +19441,11 @@
field public static final deprecated int VIBRATE_TYPE_RINGER = 0; // 0x0
}
+ public static abstract class AudioManager.AudioRecordingCallback {
+ ctor public AudioManager.AudioRecordingCallback();
+ method public void onRecordConfigChanged();
+ }
+
public static abstract interface AudioManager.OnAudioFocusChangeListener {
method public abstract void onAudioFocusChange(int);
}
@@ -19508,6 +19516,14 @@
method public abstract void onRoutingChanged(android.media.AudioRecord);
}
+ public class AudioRecordConfiguration implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getAudioSessionId();
+ method public int getClientAudioSource();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.media.AudioRecordConfiguration> CREATOR;
+ }
+
public abstract interface AudioRouting {
method public abstract void addOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
method public abstract android.media.AudioDeviceInfo getPreferredDevice();
diff --git a/api/system-current.txt b/api/system-current.txt
index 3bafa01..47a51d4 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -20623,6 +20623,7 @@
method public void adjustVolume(int, int);
method public void dispatchMediaKeyEvent(android.view.KeyEvent);
method public int generateAudioSessionId();
+ method public android.media.AudioRecordConfiguration[] getActiveRecordConfigurations();
method public android.media.AudioDeviceInfo[] getDevices(int);
method public int getMode();
method public java.lang.String getParameters(java.lang.String);
@@ -20647,6 +20648,7 @@
method public void playSoundEffect(int, float);
method public void registerAudioDeviceCallback(android.media.AudioDeviceCallback, android.os.Handler);
method public int registerAudioPolicy(android.media.audiopolicy.AudioPolicy);
+ method public void registerAudioRecordingCallback(android.media.AudioManager.AudioRecordingCallback, android.os.Handler);
method public deprecated void registerMediaButtonEventReceiver(android.content.ComponentName);
method public deprecated void registerMediaButtonEventReceiver(android.app.PendingIntent);
method public deprecated void registerRemoteControlClient(android.media.RemoteControlClient);
@@ -20673,6 +20675,7 @@
method public void unloadSoundEffects();
method public void unregisterAudioDeviceCallback(android.media.AudioDeviceCallback);
method public void unregisterAudioPolicyAsync(android.media.audiopolicy.AudioPolicy);
+ method public void unregisterAudioRecordingCallback(android.media.AudioManager.AudioRecordingCallback);
method public deprecated void unregisterMediaButtonEventReceiver(android.content.ComponentName);
method public deprecated void unregisterMediaButtonEventReceiver(android.app.PendingIntent);
method public deprecated void unregisterRemoteControlClient(android.media.RemoteControlClient);
@@ -20772,6 +20775,11 @@
field public static final deprecated int VIBRATE_TYPE_RINGER = 0; // 0x0
}
+ public static abstract class AudioManager.AudioRecordingCallback {
+ ctor public AudioManager.AudioRecordingCallback();
+ method public void onRecordConfigChanged();
+ }
+
public static abstract interface AudioManager.OnAudioFocusChangeListener {
method public abstract void onAudioFocusChange(int);
}
@@ -20845,6 +20853,14 @@
method public abstract void onRoutingChanged(android.media.AudioRecord);
}
+ public class AudioRecordConfiguration implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getAudioSessionId();
+ method public int getClientAudioSource();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.media.AudioRecordConfiguration> CREATOR;
+ }
+
public abstract interface AudioRouting {
method public abstract void addOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
method public abstract android.media.AudioDeviceInfo getPreferredDevice();
diff --git a/api/test-current.txt b/api/test-current.txt
index 5f76739..6b5b8b0 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -19305,6 +19305,7 @@
method public void adjustVolume(int, int);
method public void dispatchMediaKeyEvent(android.view.KeyEvent);
method public int generateAudioSessionId();
+ method public android.media.AudioRecordConfiguration[] getActiveRecordConfigurations();
method public android.media.AudioDeviceInfo[] getDevices(int);
method public int getMode();
method public java.lang.String getParameters(java.lang.String);
@@ -19327,6 +19328,7 @@
method public void playSoundEffect(int);
method public void playSoundEffect(int, float);
method public void registerAudioDeviceCallback(android.media.AudioDeviceCallback, android.os.Handler);
+ method public void registerAudioRecordingCallback(android.media.AudioManager.AudioRecordingCallback, android.os.Handler);
method public deprecated void registerMediaButtonEventReceiver(android.content.ComponentName);
method public deprecated void registerMediaButtonEventReceiver(android.app.PendingIntent);
method public deprecated void registerRemoteControlClient(android.media.RemoteControlClient);
@@ -19350,6 +19352,7 @@
method public void stopBluetoothSco();
method public void unloadSoundEffects();
method public void unregisterAudioDeviceCallback(android.media.AudioDeviceCallback);
+ method public void unregisterAudioRecordingCallback(android.media.AudioManager.AudioRecordingCallback);
method public deprecated void unregisterMediaButtonEventReceiver(android.content.ComponentName);
method public deprecated void unregisterMediaButtonEventReceiver(android.app.PendingIntent);
method public deprecated void unregisterRemoteControlClient(android.media.RemoteControlClient);
@@ -19446,6 +19449,11 @@
field public static final deprecated int VIBRATE_TYPE_RINGER = 0; // 0x0
}
+ public static abstract class AudioManager.AudioRecordingCallback {
+ ctor public AudioManager.AudioRecordingCallback();
+ method public void onRecordConfigChanged();
+ }
+
public static abstract interface AudioManager.OnAudioFocusChangeListener {
method public abstract void onAudioFocusChange(int);
}
@@ -19516,6 +19524,14 @@
method public abstract void onRoutingChanged(android.media.AudioRecord);
}
+ public class AudioRecordConfiguration implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getAudioSessionId();
+ method public int getClientAudioSource();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.media.AudioRecordConfiguration> CREATOR;
+ }
+
public abstract interface AudioRouting {
method public abstract void addOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
method public abstract android.media.AudioDeviceInfo getPreferredDevice();
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 5ad6b08..dc534be 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -2163,7 +2163,7 @@
* audio service.
*/
private final ServiceEventHandlerDelegate mServiceEventHandlerDelegate =
- new ServiceEventHandlerDelegate();
+ new ServiceEventHandlerDelegate(null);
/**
* Event types
@@ -2177,10 +2177,14 @@
private class ServiceEventHandlerDelegate {
private final Handler mHandler;
- ServiceEventHandlerDelegate() {
+ ServiceEventHandlerDelegate(Handler handler) {
Looper looper;
- if ((looper = Looper.myLooper()) == null) {
- looper = Looper.getMainLooper();
+ if (handler == null) {
+ if ((looper = Looper.myLooper()) == null) {
+ looper = Looper.getMainLooper();
+ }
+ } else {
+ looper = handler.getLooper();
}
if (looper != null) {
@@ -2201,27 +2205,9 @@
}
break;
case MSSG_RECORDING_CONFIG_CHANGE:
- // optimizing for the case of a single callback
- AudioRecordingCallback singleCallback = null;
- ArrayList<AudioRecordingCallback> multipleCallbacks = null;
- synchronized(mRecordCallbackLock) {
- if ((mRecordCallbackList != null)
- && (mRecordCallbackList.size() != 0)) {
- if (mRecordCallbackList.size() == 1) {
- singleCallback = mRecordCallbackList.get(0);
- } else {
- multipleCallbacks =
- new ArrayList<AudioRecordingCallback>(
- mRecordCallbackList);
- }
- }
- }
- if (singleCallback != null) {
- singleCallback.onRecordConfigChanged();
- } else if (multipleCallbacks != null) {
- for (int i=0 ; i < multipleCallbacks.size() ; i++) {
- multipleCallbacks.get(i).onRecordConfigChanged();
- }
+ final AudioRecordingCallback cb = (AudioRecordingCallback) msg.obj;
+ if (cb != null) {
+ cb.onRecordConfigChanged();
}
break;
default:
@@ -2798,34 +2784,51 @@
//====================================================================
// Recording configuration
/**
- * @hide
- * candidate for public API
+ * Interface for receiving update notifications about the recording configuration. Extend
+ * this abstract class and register it with
+ * {@link AudioManager#registerAudioRecordingCallback(AudioRecordingCallback, Handler)}
+ * to be notified.
+ * Use {@link AudioManager#getActiveRecordConfigurations()} to query the current configuration.
*/
public static abstract class AudioRecordingCallback {
/**
- * @hide
- * candidate for public API
+ * Called whenever the device recording configuration has changed.
*/
public void onRecordConfigChanged() {}
}
+ private static class AudioRecordingCallbackInfo {
+ final AudioRecordingCallback mCb;
+ final Handler mHandler;
+ AudioRecordingCallbackInfo(AudioRecordingCallback cb, Handler handler) {
+ mCb = cb;
+ mHandler = handler;
+ }
+ }
+
/**
- * @hide
- * candidate for public API
- * @param non-null callback
+ * Register a callback to be notified of audio recording changes through
+ * {@link AudioRecordingCallback}
+ * @param cb non-null callback to register
+ * @param handler 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 registerAudioRecordingCallback(@NonNull AudioRecordingCallback cb) {
+ public void registerAudioRecordingCallback(@NonNull AudioRecordingCallback cb, Handler handler)
+ {
if (cb == null) {
throw new IllegalArgumentException("Illegal null AudioRecordingCallback argument");
}
+
synchronized(mRecordCallbackLock) {
// lazy initialization of the list of recording callbacks
if (mRecordCallbackList == null) {
- mRecordCallbackList = new ArrayList<AudioRecordingCallback>();
+ mRecordCallbackList = new ArrayList<AudioRecordingCallbackInfo>();
}
final int oldCbCount = mRecordCallbackList.size();
- if (!mRecordCallbackList.contains(cb)) {
- mRecordCallbackList.add(cb);
+ if (!hasRecordCallback_sync(cb)) {
+ mRecordCallbackList.add(new AudioRecordingCallbackInfo(cb,
+ new ServiceEventHandlerDelegate(handler).getHandler()));
final int newCbCount = mRecordCallbackList.size();
if ((oldCbCount == 0) && (newCbCount > 0)) {
// register binder for callbacks
@@ -2844,9 +2847,9 @@
}
/**
- * @hide
- * candidate for public API
- * @param non-null callback
+ * Unregister an audio recording callback previously registered with
+ * {@link #registerAudioRecordingCallback(AudioRecordingCallback, Handler)}.
+ * @param cb non-null callback to unregister
*/
public void unregisterAudioRecordingCallback(@NonNull AudioRecordingCallback cb) {
if (cb == null) {
@@ -2857,7 +2860,7 @@
return;
}
final int oldCbCount = mRecordCallbackList.size();
- if (mRecordCallbackList.remove(cb)) {
+ if (removeRecordCallback_sync(cb)) {
final int newCbCount = mRecordCallbackList.size();
if ((oldCbCount > 0) && (newCbCount == 0)) {
// unregister binder for callbacks
@@ -2876,8 +2879,7 @@
}
/**
- * @hide
- * candidate for public API
+ * Returns the current active audio recording configurations of the device.
* @return a non-null array of recording configurations. An array of length 0 indicates there is
* no recording active when queried.
*/
@@ -2902,18 +2904,57 @@
/**
* All operations on this list are sync'd on mRecordCallbackLock.
- * List is lazy-initialized in {@link #registerAudioRecordingCallback(AudioRecordingCallback)}.
+ * List is lazy-initialized in
+ * {@link #registerAudioRecordingCallback(AudioRecordingCallback, Handler)}.
* List can be null.
*/
- private List<AudioRecordingCallback> mRecordCallbackList;
+ private List<AudioRecordingCallbackInfo> mRecordCallbackList;
private final Object mRecordCallbackLock = new Object();
+ /**
+ * Must be called synchronized on mRecordCallbackLock
+ */
+ private boolean hasRecordCallback_sync(@NonNull AudioRecordingCallback cb) {
+ if (mRecordCallbackList != null) {
+ for (int i=0 ; i < mRecordCallbackList.size() ; i++) {
+ if (cb.equals(mRecordCallbackList.get(i).mCb)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Must be called synchronized on mRecordCallbackLock
+ */
+ private boolean removeRecordCallback_sync(@NonNull AudioRecordingCallback cb) {
+ if (mRecordCallbackList != null) {
+ for (int i=0 ; i < mRecordCallbackList.size() ; i++) {
+ if (cb.equals(mRecordCallbackList.get(i).mCb)) {
+ mRecordCallbackList.remove(i);
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
private final IRecordingConfigDispatcher mRecCb = new IRecordingConfigDispatcher.Stub() {
public void dispatchRecordingConfigChange() {
- final Message m = mServiceEventHandlerDelegate.getHandler().obtainMessage(
- MSSG_RECORDING_CONFIG_CHANGE/*what*/);
- mServiceEventHandlerDelegate.getHandler().sendMessage(m);
+ synchronized(mRecordCallbackLock) {
+ if (mRecordCallbackList != null) {
+ for (int i=0 ; i < mRecordCallbackList.size() ; i++) {
+ final AudioRecordingCallbackInfo arci = mRecordCallbackList.get(i);
+ if (arci.mHandler != null) {
+ final Message m = arci.mHandler.obtainMessage(
+ MSSG_RECORDING_CONFIG_CHANGE/*what*/, arci.mCb/*obj*/);
+ arci.mHandler.sendMessage(m);
+ }
+ }
+ }
+ }
}
};
diff --git a/media/java/android/media/AudioRecordConfiguration.java b/media/java/android/media/AudioRecordConfiguration.java
index aefe692..c7d219d 100644
--- a/media/java/android/media/AudioRecordConfiguration.java
+++ b/media/java/android/media/AudioRecordConfiguration.java
@@ -22,8 +22,9 @@
import java.util.Objects;
/**
- * @hide
- * Candidate for public API, see AudioManager.getActiveRecordConfiguration()
+ * The AudioRecordConfiguration class collects the information describing an audio recording
+ * session. This information is returned through the
+ * {@link AudioManager#getActiveRecordConfigurations()} method.
*
*/
public class AudioRecordConfiguration implements Parcelable {
@@ -41,19 +42,23 @@
}
/**
- * @return one of AudioSource.MIC, AudioSource.VOICE_UPLINK,
- * AudioSource.VOICE_DOWNLINK, AudioSource.VOICE_CALL,
- * AudioSource.CAMCORDER, AudioSource.VOICE_RECOGNITION,
- * AudioSource.VOICE_COMMUNICATION.
+ * Returns the audio source being used for the recording.
+ * @return one of {@link MediaRecorder.AudioSource#MIC},
+ * {@link MediaRecorder.AudioSource#VOICE_UPLINK},
+ * {@link MediaRecorder.AudioSource#VOICE_DOWNLINK},
+ * {@link MediaRecorder.AudioSource#VOICE_CALL},
+ * {@link MediaRecorder.AudioSource#CAMCORDER},
+ * {@link MediaRecorder.AudioSource#VOICE_RECOGNITION},
+ * {@link MediaRecorder.AudioSource#VOICE_COMMUNICATION}.
*/
public int getClientAudioSource() { return mClientSource; }
/**
- * @return the session number of the recorder.
+ * Returns the session number of the recording, see {@link AudioRecord#getAudioSessionId()}.
+ * @return the session number.
*/
public int getAudioSessionId() { return mSessionId; }
-
public static final Parcelable.Creator<AudioRecordConfiguration> CREATOR
= new Parcelable.Creator<AudioRecordConfiguration>() {
/**