Audio record notification: report audio device information
Support querying the AudioDeviceInfo in AudioRecordConfiguration.
When AudioService (through RecordingActivityMonitor) receives
a recording event on an existing session, report it as an
update if the recording configuration has changed.
Bug 22876530
Change-Id: I1b72c08aa0589077fe8ad254087965e6384ce50a
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 7d9e4a2..2fb7498 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -389,7 +389,8 @@
static void
android_media_AudioSystem_recording_callback(int event, int session, int source,
- const audio_config_base_t *clientConfig, const audio_config_base_t *deviceConfig)
+ const audio_config_base_t *clientConfig, const audio_config_base_t *deviceConfig,
+ audio_patch_handle_t patchHandle)
{
JNIEnv *env = AndroidRuntime::getJNIEnv();
if (env == NULL) {
@@ -401,12 +402,14 @@
}
// create an array for 2*3 integers to store the record configurations (client + device)
- jintArray recParamArray = env->NewIntArray(6);
+ // plus 1 integer for the patch handle
+ const int REC_PARAM_SIZE = 7;
+ jintArray recParamArray = env->NewIntArray(REC_PARAM_SIZE);
if (recParamArray == NULL) {
ALOGE("recording callback: Couldn't allocate int array for configuration data");
return;
}
- jint recParamData[6];
+ jint recParamData[REC_PARAM_SIZE];
recParamData[0] = (jint) audioFormatFromNative(clientConfig->format);
// FIXME this doesn't support index-based masks
recParamData[1] = (jint) inChannelMaskFromNative(clientConfig->channel_mask);
@@ -415,7 +418,8 @@
// FIXME this doesn't support index-based masks
recParamData[4] = (jint) inChannelMaskFromNative(deviceConfig->channel_mask);
recParamData[5] = (jint) deviceConfig->sample_rate;
- env->SetIntArrayRegion(recParamArray, 0, 6, recParamData);
+ recParamData[6] = (jint) patchHandle;
+ env->SetIntArrayRegion(recParamArray, 0, REC_PARAM_SIZE, recParamData);
// callback into java
jclass clazz = env->FindClass(kClassPathName);
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index 22f4f04..0c8c530 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -877,6 +877,26 @@
}
@Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ AudioFormat that = (AudioFormat) o;
+
+ if (mPropertySetMask != that.mPropertySetMask) return false;
+
+ // return false if any of the properties is set and the values differ
+ return !((((mPropertySetMask & AUDIO_FORMAT_HAS_PROPERTY_ENCODING) != 0)
+ && (mEncoding != that.mEncoding))
+ || (((mPropertySetMask & AUDIO_FORMAT_HAS_PROPERTY_SAMPLE_RATE) != 0)
+ && (mSampleRate != that.mSampleRate))
+ || (((mPropertySetMask & AUDIO_FORMAT_HAS_PROPERTY_CHANNEL_MASK) != 0)
+ && (mChannelMask != that.mChannelMask))
+ || (((mPropertySetMask & AUDIO_FORMAT_HAS_PROPERTY_CHANNEL_INDEX_MASK) != 0)
+ && (mChannelIndexMask != that.mChannelIndexMask)));
+ }
+
+ @Override
public int hashCode() {
return Objects.hash(mPropertySetMask, mSampleRate, mEncoding, mChannelMask,
mChannelIndexMask);
diff --git a/media/java/android/media/AudioPatch.java b/media/java/android/media/AudioPatch.java
index acadb41..6c70213 100644
--- a/media/java/android/media/AudioPatch.java
+++ b/media/java/android/media/AudioPatch.java
@@ -53,6 +53,13 @@
return mSinks;
}
+ /**
+ * Get the system unique patch ID.
+ */
+ public int id() {
+ return mHandle.id();
+ }
+
@Override
public String toString() {
StringBuilder s = new StringBuilder();
diff --git a/media/java/android/media/AudioRecordConfiguration.java b/media/java/android/media/AudioRecordConfiguration.java
index 61d239c..c2cd9b3 100644
--- a/media/java/android/media/AudioRecordConfiguration.java
+++ b/media/java/android/media/AudioRecordConfiguration.java
@@ -18,7 +18,9 @@
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.Log;
+import java.util.ArrayList;
import java.util.Objects;
/**
@@ -28,6 +30,7 @@
*
*/
public class AudioRecordConfiguration implements Parcelable {
+ private final static String TAG = new String("AudioRecordConfiguration");
private final int mSessionId;
@@ -36,30 +39,18 @@
private final AudioFormat mDeviceFormat;
private final AudioFormat mClientFormat;
- private final AudioDeviceInfo mRecDevice;
-
- /**
- * @hide
- */
- public AudioRecordConfiguration(int session, int source,
- AudioFormat clientFormat, AudioFormat deviceFormat) {
- mSessionId = session;
- mClientSource = source;
- mDeviceFormat = deviceFormat;
- mClientFormat = clientFormat;
- mRecDevice = null;
- }
+ private final int mPatchHandle;
/**
* @hide
*/
public AudioRecordConfiguration(int session, int source, AudioFormat devFormat,
- AudioFormat clientFormat, AudioDeviceInfo device) {
+ AudioFormat clientFormat, int patchHandle) {
mSessionId = session;
mClientSource = source;
mDeviceFormat = devFormat;
mClientFormat = clientFormat;
- mRecDevice = device;
+ mPatchHandle = patchHandle;
}
/**
@@ -96,10 +87,38 @@
public AudioFormat getClientFormat() { return mClientFormat; }
/**
- * Returns the audio input device used for this recording.
- * @return the audio recording device
+ * Returns information about the audio input device used for this recording.
+ * @return the audio recording device or null if this information cannot be retrieved
*/
- public AudioDeviceInfo getAudioDevice() { return mRecDevice; }
+ public AudioDeviceInfo getAudioDevice() {
+ // build the AudioDeviceInfo from the patch handle
+ ArrayList<AudioPatch> patches = new ArrayList<AudioPatch>();
+ if (AudioManager.listAudioPatches(patches) != AudioManager.SUCCESS) {
+ Log.e(TAG, "Error retrieving list of audio patches");
+ return null;
+ }
+ for (int i = 0 ; i < patches.size() ; i++) {
+ final AudioPatch patch = patches.get(i);
+ if (patch.id() == mPatchHandle) {
+ final AudioPortConfig[] sources = patch.sources();
+ if ((sources != null) && (sources.length > 0)) {
+ // not supporting multiple sources, so just look at the first source
+ final int devId = sources[0].port().id();
+ final AudioDeviceInfo[] devices =
+ AudioManager.getDevicesStatic(AudioManager.GET_DEVICES_INPUTS);
+ for (int j = 0; j < devices.length; j++) {
+ if (devices[j].getId() == devId) {
+ return devices[j];
+ }
+ }
+ }
+ // patch handle is unique, there won't be another with the same handle
+ break;
+ }
+ }
+ Log.e(TAG, "Couldn't find device for recording, did recording end already?");
+ return null;
+ }
public static final Parcelable.Creator<AudioRecordConfiguration> CREATOR
= new Parcelable.Creator<AudioRecordConfiguration>() {
@@ -132,7 +151,7 @@
dest.writeInt(mClientSource);
mClientFormat.writeToParcel(dest, 0);
mDeviceFormat.writeToParcel(dest, 0);
- //TODO marshall device info
+ dest.writeInt(mPatchHandle);
}
private AudioRecordConfiguration(Parcel in) {
@@ -140,8 +159,7 @@
mClientSource = in.readInt();
mClientFormat = AudioFormat.CREATOR.createFromParcel(in);
mDeviceFormat = AudioFormat.CREATOR.createFromParcel(in);
- //TODO unmarshall device info
- mRecDevice = null;
+ mPatchHandle = in.readInt();
}
@Override
@@ -149,8 +167,12 @@
if (this == o) return true;
if (o == null || !(o instanceof AudioRecordConfiguration)) return false;
- final AudioRecordConfiguration that = (AudioRecordConfiguration) o;
- return ((mSessionId == that.mSessionId)
- && (mClientSource == that.mClientSource));
+ AudioRecordConfiguration that = (AudioRecordConfiguration) o;
+
+ return ((mSessionId == that.mSessionId)
+ && (mClientSource == that.mClientSource)
+ && (mPatchHandle == that.mPatchHandle)
+ && (mClientFormat.equals(that.mClientFormat))
+ && (mDeviceFormat.equals(that.mDeviceFormat)));
}
}
\ No newline at end of file
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 59782cb..247c4ae 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -279,8 +279,14 @@
* @param session
* @param source
* @param recordingFormat an array of ints containing respectively the client and device
- * recording configuration. Each set of parameters contains the following parameters
- * in this order: format, channel mask, sample rate
+ * recording configurations (2*3 ints), followed by the patch handle:
+ * index 0: client format
+ * 1: client channel mask
+ * 2: client sample rate
+ * 3: device format
+ * 4: device channel mask
+ * 5: device sample rate
+ * 6: patch handle
*/
void onRecordingConfigurationChanged(int event, int session, int source,
int[] recordingFormat);
diff --git a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
index 4b0a142..7e76ac4 100644
--- a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
@@ -50,11 +50,11 @@
* Implementation of android.media.AudioSystem.AudioRecordingCallback
*/
public void onRecordingConfigurationChanged(int event, int session, int source,
- int[] recordingFormat) {
+ int[] recordingInfo) {
if (MediaRecorder.isSystemOnlyAudioSource(source)) {
return;
}
- if (updateSnapshot(event, session, source, recordingFormat)) {
+ if (updateSnapshot(event, session, source, recordingInfo)) {
final Iterator<RecMonitorClient> clientIterator = mClients.iterator();
synchronized(mClients) {
while (clientIterator.hasNext()) {
@@ -117,7 +117,7 @@
* for the definition of the contents of the array
* @return true if the list of active recording sessions has been modified, false otherwise.
*/
- private boolean updateSnapshot(int event, int session, int source, int[] recordingFormat) {
+ private boolean updateSnapshot(int event, int session, int source, int[] recordingInfo) {
synchronized(mRecordConfigs) {
switch (event) {
case AudioManager.RECORD_CONFIG_EVENT_STOP:
@@ -125,26 +125,35 @@
return (mRecordConfigs.remove(new Integer(session)) != null);
case AudioManager.RECORD_CONFIG_EVENT_START:
final AudioFormat clientFormat = new AudioFormat.Builder()
- .setEncoding(recordingFormat[0])
+ .setEncoding(recordingInfo[0])
// FIXME this doesn't support index-based masks
- .setChannelMask(recordingFormat[1])
- .setSampleRate(recordingFormat[2])
+ .setChannelMask(recordingInfo[1])
+ .setSampleRate(recordingInfo[2])
.build();
final AudioFormat deviceFormat = new AudioFormat.Builder()
- .setEncoding(recordingFormat[3])
+ .setEncoding(recordingInfo[3])
// FIXME this doesn't support index-based masks
- .setChannelMask(recordingFormat[4])
- .setSampleRate(recordingFormat[5])
+ .setChannelMask(recordingInfo[4])
+ .setSampleRate(recordingInfo[5])
.build();
- if (mRecordConfigs.containsKey(new Integer(session))) {
- // start of session that's already tracked, not worth an update
- // TO DO in the future when tracking record format: there might be a record
- // format change during a recording that requires reporting
- return false;
- } else {
- mRecordConfigs.put(new Integer(session),
+ final int patchHandle = recordingInfo[6];
+ final Integer sessionKey = new Integer(session);
+ if (mRecordConfigs.containsKey(sessionKey)) {
+ final AudioRecordConfiguration updatedConfig =
new AudioRecordConfiguration(session, source,
- clientFormat, deviceFormat));
+ clientFormat, deviceFormat, patchHandle);
+ if (updatedConfig.equals(mRecordConfigs.get(sessionKey))) {
+ return false;
+ } else {
+ // config exists but has been modified
+ mRecordConfigs.remove(sessionKey);
+ mRecordConfigs.put(sessionKey, updatedConfig);
+ return true;
+ }
+ } else {
+ mRecordConfigs.put(sessionKey,
+ new AudioRecordConfiguration(session, source,
+ clientFormat, deviceFormat, patchHandle));
return true;
}
default: