diff --git a/api/current.txt b/api/current.txt index 9a5ffdc07e7ce..4f801cb970828 100644 --- a/api/current.txt +++ b/api/current.txt @@ -19297,6 +19297,7 @@ package android.media { 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 @@ package android.media { 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 @@ package android.media { 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 @@ package android.media { 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 @@ package android.media { 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 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 3bafa011ee65c..47a51d4112ca3 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -20623,6 +20623,7 @@ package android.media { 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 @@ package android.media { 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 @@ package android.media { 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 @@ package android.media { 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 @@ package android.media { 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 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 5f76739907efe..6b5b8b02c36de 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -19305,6 +19305,7 @@ package android.media { 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 @@ package android.media { 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 @@ package android.media { 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 @@ package android.media { 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 @@ package android.media { 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 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 5ad6b08f542b8..dc534be03cbbb 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -2163,7 +2163,7 @@ public class AudioManager { * audio service. */ private final ServiceEventHandlerDelegate mServiceEventHandlerDelegate = - new ServiceEventHandlerDelegate(); + new ServiceEventHandlerDelegate(null); /** * Event types @@ -2177,10 +2177,14 @@ public class AudioManager { 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 @@ public class AudioManager { } break; case MSSG_RECORDING_CONFIG_CHANGE: - // optimizing for the case of a single callback - AudioRecordingCallback singleCallback = null; - ArrayList multipleCallbacks = null; - synchronized(mRecordCallbackLock) { - if ((mRecordCallbackList != null) - && (mRecordCallbackList.size() != 0)) { - if (mRecordCallbackList.size() == 1) { - singleCallback = mRecordCallbackList.get(0); - } else { - multipleCallbacks = - new ArrayList( - 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 @@ public class AudioManager { //==================================================================== // 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 null, 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(); + mRecordCallbackList = new ArrayList(); } 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 @@ public class AudioManager { } /** - * @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 @@ public class AudioManager { 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 @@ public class AudioManager { } /** - * @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 @@ public class AudioManager { /** * 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 mRecordCallbackList; + private List 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 aefe692de6514..c7d219d981f4c 100644 --- a/media/java/android/media/AudioRecordConfiguration.java +++ b/media/java/android/media/AudioRecordConfiguration.java @@ -22,8 +22,9 @@ import android.os.Parcelable; 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 @@ public class AudioRecordConfiguration implements Parcelable { } /** - * @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 CREATOR = new Parcelable.Creator() { /**