API changes for audio recording notifications

Make AudioRecordConfiguration final since it is parcelable.
In AudioRecordingCallback, pass the array of active recording
  configurations.
Add @IntDef for return values for
  AudioRecordConfiguration.getClientAudioSource()

Bug 27385560

Change-Id: I01193577f50e50496742d888b45f89a2c3b67904
diff --git a/api/current.txt b/api/current.txt
index f7a316d..d025672 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -19934,7 +19934,7 @@
 
   public static abstract class AudioManager.AudioRecordingCallback {
     ctor public AudioManager.AudioRecordingCallback();
-    method public void onRecordConfigChanged();
+    method public void onRecordConfigChanged(android.media.AudioRecordConfiguration[]);
   }
 
   public static abstract interface AudioManager.OnAudioFocusChangeListener {
@@ -20008,7 +20008,7 @@
     method public abstract void onRoutingChanged(android.media.AudioRecord);
   }
 
-  public class AudioRecordConfiguration implements android.os.Parcelable {
+  public final class AudioRecordConfiguration implements android.os.Parcelable {
     method public int describeContents();
     method public android.media.AudioDeviceInfo getAudioDevice();
     method public int getClientAudioSessionId();
diff --git a/api/system-current.txt b/api/system-current.txt
index b2b5982..6e217da5 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -21439,7 +21439,7 @@
 
   public static abstract class AudioManager.AudioRecordingCallback {
     ctor public AudioManager.AudioRecordingCallback();
-    method public void onRecordConfigChanged();
+    method public void onRecordConfigChanged(android.media.AudioRecordConfiguration[]);
   }
 
   public static abstract interface AudioManager.OnAudioFocusChangeListener {
@@ -21516,7 +21516,7 @@
     method public abstract void onRoutingChanged(android.media.AudioRecord);
   }
 
-  public class AudioRecordConfiguration implements android.os.Parcelable {
+  public final class AudioRecordConfiguration implements android.os.Parcelable {
     method public int describeContents();
     method public android.media.AudioDeviceInfo getAudioDevice();
     method public int getClientAudioSessionId();
diff --git a/api/test-current.txt b/api/test-current.txt
index 472daf6..9d3f97d 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -19943,7 +19943,7 @@
 
   public static abstract class AudioManager.AudioRecordingCallback {
     ctor public AudioManager.AudioRecordingCallback();
-    method public void onRecordConfigChanged();
+    method public void onRecordConfigChanged(android.media.AudioRecordConfiguration[]);
   }
 
   public static abstract interface AudioManager.OnAudioFocusChangeListener {
@@ -20017,7 +20017,7 @@
     method public abstract void onRoutingChanged(android.media.AudioRecord);
   }
 
-  public class AudioRecordConfiguration implements android.os.Parcelable {
+  public final class AudioRecordConfiguration implements android.os.Parcelable {
     method public int describeContents();
     method public android.media.AudioDeviceInfo getAudioDevice();
     method public int getClientAudioSessionId();
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index c7e96cf..8206d23 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -2147,9 +2147,10 @@
                                 }
                                 break;
                             case MSSG_RECORDING_CONFIG_CHANGE:
-                                final AudioRecordingCallback cb = (AudioRecordingCallback) msg.obj;
-                                if (cb != null) {
-                                    cb.onRecordConfigChanged();
+                                final RecordConfigChangeCallbackData cbData =
+                                        (RecordConfigChangeCallbackData) msg.obj;
+                                if (cbData.mCb != null) {
+                                    cbData.mCb.onRecordConfigChanged(cbData.mConfigs);
                                 }
                                 break;
                             default:
@@ -2734,8 +2735,10 @@
     public static abstract class AudioRecordingCallback {
         /**
          * Called whenever the device recording configuration has changed.
+         * @param configs array containing the results of
+         *      {@link AudioManager#getActiveRecordConfigurations()}.
          */
-        public void onRecordConfigChanged() {}
+        public void onRecordConfigChanged(AudioRecordConfiguration[] configs) {}
     }
 
     private static class AudioRecordingCallbackInfo {
@@ -2747,6 +2750,17 @@
         }
     }
 
+    private final static class RecordConfigChangeCallbackData {
+        final AudioRecordingCallback mCb;
+        final AudioRecordConfiguration[] mConfigs;
+
+        RecordConfigChangeCallbackData(AudioRecordingCallback cb,
+                AudioRecordConfiguration[] configs) {
+            mCb = cb;
+            mConfigs = configs;
+        }
+    }
+
     /**
      * Register a callback to be notified of audio recording changes through
      * {@link AudioRecordingCallback}
@@ -2882,14 +2896,15 @@
 
     private final IRecordingConfigDispatcher mRecCb = new IRecordingConfigDispatcher.Stub() {
 
-        public void dispatchRecordingConfigChange() {
+        public void dispatchRecordingConfigChange(AudioRecordConfiguration[] configs) {
             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*/);
+                                    MSSG_RECORDING_CONFIG_CHANGE/*what*/,
+                                    new RecordConfigChangeCallbackData(arci.mCb, configs)/*obj*/);
                             arci.mHandler.sendMessage(m);
                         }
                     }
diff --git a/media/java/android/media/AudioRecordConfiguration.java b/media/java/android/media/AudioRecordConfiguration.java
index c2cd9b3..de78a5a 100644
--- a/media/java/android/media/AudioRecordConfiguration.java
+++ b/media/java/android/media/AudioRecordConfiguration.java
@@ -16,10 +16,13 @@
 
 package android.media;
 
+import android.annotation.IntDef;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.Log;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Objects;
 
@@ -29,7 +32,7 @@
  * {@link AudioManager#getActiveRecordConfigurations()} method.
  *
  */
-public class AudioRecordConfiguration implements Parcelable {
+public final class AudioRecordConfiguration implements Parcelable {
     private final static String TAG = new String("AudioRecordConfiguration");
 
     private final int mSessionId;
@@ -53,6 +56,19 @@
         mPatchHandle = patchHandle;
     }
 
+    /** @hide */
+    @IntDef({
+        MediaRecorder.AudioSource.DEFAULT,
+        MediaRecorder.AudioSource.VOICE_UPLINK,
+        MediaRecorder.AudioSource.VOICE_DOWNLINK,
+        MediaRecorder.AudioSource.VOICE_CALL,
+        MediaRecorder.AudioSource.CAMCORDER,
+        MediaRecorder.AudioSource.VOICE_RECOGNITION,
+        MediaRecorder.AudioSource.VOICE_COMMUNICATION
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AudioSource {}
+
     /**
      * Returns the audio source being used for the recording.
      * @return one of {@link MediaRecorder.AudioSource#MIC},
@@ -63,7 +79,7 @@
      *       {@link MediaRecorder.AudioSource#VOICE_RECOGNITION},
      *       {@link MediaRecorder.AudioSource#VOICE_COMMUNICATION}.
      */
-    public int getClientAudioSource() { return mClientSource; }
+    public @AudioSource int getClientAudioSource() { return mClientSource; }
 
     /**
      * Returns the session number of the recording, see {@link AudioRecord#getAudioSessionId()}.
diff --git a/media/java/android/media/IRecordingConfigDispatcher.aidl b/media/java/android/media/IRecordingConfigDispatcher.aidl
index a5eb8b9f..eaa92ca 100644
--- a/media/java/android/media/IRecordingConfigDispatcher.aidl
+++ b/media/java/android/media/IRecordingConfigDispatcher.aidl
@@ -16,6 +16,8 @@
 
 package android.media;
 
+import android.media.AudioRecordConfiguration;
+
 /**
  * AIDL for the RecordingActivity monitor in AudioService to signal audio recording updates.
  *
@@ -23,6 +25,6 @@
  */
 oneway interface IRecordingConfigDispatcher {
 
-    void dispatchRecordingConfigChange();
+    void dispatchRecordingConfigChange(in AudioRecordConfiguration[] configs);
 
 }
diff --git a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
index 7e76ac4..86dcd0f 100644
--- a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
@@ -54,12 +54,15 @@
         if (MediaRecorder.isSystemOnlyAudioSource(source)) {
             return;
         }
-        if (updateSnapshot(event, session, source, recordingInfo)) {
-            final Iterator<RecMonitorClient> clientIterator = mClients.iterator();
+        final AudioRecordConfiguration[] configs =
+                updateSnapshot(event, session, source, recordingInfo);
+        if (configs != null){
             synchronized(mClients) {
+                final Iterator<RecMonitorClient> clientIterator = mClients.iterator();
                 while (clientIterator.hasNext()) {
                     try {
-                        clientIterator.next().mDispatcherCb.dispatchRecordingConfigChange();
+                        clientIterator.next().mDispatcherCb.dispatchRecordingConfigChange(
+                                configs);
                     } catch (RemoteException e) {
                         Log.w(TAG, "Could not call dispatchRecordingConfigChange() on client", e);
                     }
@@ -115,14 +118,19 @@
      * @param recordingFormat see
      *     {@link AudioSystem.AudioRecordingCallback#onRecordingConfigurationChanged(int, int, int, int[])}
      *     for the definition of the contents of the array
-     * @return true if the list of active recording sessions has been modified, false otherwise.
+     * @return null if the list of active recording sessions has not been modified, an array
+     *     with the current active configurations otherwise.
      */
-    private boolean updateSnapshot(int event, int session, int source, int[] recordingInfo) {
+    private AudioRecordConfiguration[] updateSnapshot(int event, int session, int source,
+            int[] recordingInfo) {
+        final boolean configChanged;
+        final AudioRecordConfiguration[] configs;
         synchronized(mRecordConfigs) {
             switch (event) {
             case AudioManager.RECORD_CONFIG_EVENT_STOP:
                 // return failure if an unknown recording session stopped
-                return (mRecordConfigs.remove(new Integer(session)) != null);
+                configChanged = (mRecordConfigs.remove(new Integer(session)) != null);
+                break;
             case AudioManager.RECORD_CONFIG_EVENT_START:
                 final AudioFormat clientFormat = new AudioFormat.Builder()
                         .setEncoding(recordingInfo[0])
@@ -143,25 +151,32 @@
                             new AudioRecordConfiguration(session, source,
                                     clientFormat, deviceFormat, patchHandle);
                     if (updatedConfig.equals(mRecordConfigs.get(sessionKey))) {
-                        return false;
+                        configChanged = false;
                     } else {
                         // config exists but has been modified
                         mRecordConfigs.remove(sessionKey);
                         mRecordConfigs.put(sessionKey, updatedConfig);
-                        return true;
+                        configChanged = true;
                     }
                 } else {
                     mRecordConfigs.put(sessionKey,
                             new AudioRecordConfiguration(session, source,
                                     clientFormat, deviceFormat, patchHandle));
-                    return true;
+                    configChanged = true;
                 }
+                break;
             default:
                 Log.e(TAG, String.format("Unknown event %d for session %d, source %d",
                         event, session, source));
-                return false;
+                configChanged = false;
+            }
+            if (configChanged) {
+                configs = mRecordConfigs.values().toArray(new AudioRecordConfiguration[0]);
+            } else {
+                configs = null;
             }
         }
+        return configs;
     }
 
     /**