diff options
author | Jean-Michel Trivi <jmtrivi@google.com> | 2014-10-01 17:49:29 -0700 |
---|---|---|
committer | Jean-Michel Trivi <jmtrivi@google.com> | 2014-10-07 09:09:41 -0700 |
commit | ba5270b88798c66fefc17a1b25b27894e4fb7862 (patch) | |
tree | 325c18b65f5dac9d02ae6965e44e2f2c19303113 /media | |
parent | 4c334f3def076d2940b53641c932050b1e4ce286 (diff) | |
download | frameworks_base-ba5270b88798c66fefc17a1b25b27894e4fb7862.zip frameworks_base-ba5270b88798c66fefc17a1b25b27894e4fb7862.tar.gz frameworks_base-ba5270b88798c66fefc17a1b25b27894e4fb7862.tar.bz2 |
Full volume on remote submix for apps that need it
If an AudioRecord is created with the "fixedVolume" tag
when recording from REMOTE_SUBMIX, treat the
device DEVICE_OUT_REMOTE_SUBMIX as a fixed full volume
device during the recording. Also register a death
handler during the recording.
Otherwise this is a no-op.
Bug 17635294
Change-Id: I8d26fe777047126f34308e1e1b7ac28ba269ad89
Diffstat (limited to 'media')
-rw-r--r-- | media/java/android/media/AudioRecord.java | 40 | ||||
-rw-r--r-- | media/java/android/media/AudioService.java | 118 | ||||
-rw-r--r-- | media/java/android/media/IAudioService.aidl | 2 |
3 files changed, 154 insertions, 6 deletions
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java index 5be6371..52b4649 100644 --- a/media/java/android/media/AudioRecord.java +++ b/media/java/android/media/AudioRecord.java @@ -18,10 +18,15 @@ package android.media; import java.lang.ref.WeakReference; import java.nio.ByteBuffer; +import java.util.Iterator; +import android.os.Binder; import android.os.Handler; +import android.os.IBinder; import android.os.Looper; import android.os.Message; +import android.os.RemoteException; +import android.os.ServiceManager; import android.util.Log; /** @@ -99,6 +104,8 @@ public class AudioRecord private final static String TAG = "android.media.AudioRecord"; + /** @hide */ + public final static String SUBMIX_FIXED_VOLUME = "fixedVolume"; //--------------------------------------------------------- // Used exclusively by native code @@ -184,6 +191,7 @@ public class AudioRecord * AudioAttributes */ private AudioAttributes mAudioAttributes; + private boolean mIsSubmixFullVolume = false; //--------------------------------------------------------- // Constructor, Finalize @@ -267,6 +275,18 @@ public class AudioRecord mAudioAttributes = attributes; + // is this AudioRecord using REMOTE_SUBMIX at full volume? + if (mAudioAttributes.getCapturePreset() == MediaRecorder.AudioSource.REMOTE_SUBMIX) { + final Iterator<String> tagsIter = mAudioAttributes.getTags().iterator(); + while (tagsIter.hasNext()) { + if (tagsIter.next().equalsIgnoreCase(SUBMIX_FIXED_VOLUME)) { + mIsSubmixFullVolume = true; + Log.v(TAG, "Will record from REMOTE_SUBMIX at full fixed volume"); + break; + } + } + } + int rate = 0; if ((format.getPropertySetMask() & AudioFormat.AUDIO_FORMAT_HAS_PROPERTY_SAMPLE_RATE) != 0) @@ -419,7 +439,8 @@ public class AudioRecord @Override protected void finalize() { - native_finalize(); + // will cause stop() to be called, and if appropriate, will handle fixed volume recording + release(); } @@ -586,6 +607,7 @@ public class AudioRecord // start recording synchronized(mRecordingStateLock) { if (native_start(MediaSyncEvent.SYNC_EVENT_NONE, 0) == SUCCESS) { + handleFullVolumeRec(true); mRecordingState = RECORDSTATE_RECORDING; } } @@ -608,6 +630,7 @@ public class AudioRecord // start recording synchronized(mRecordingStateLock) { if (native_start(syncEvent.getType(), syncEvent.getAudioSessionId()) == SUCCESS) { + handleFullVolumeRec(true); mRecordingState = RECORDSTATE_RECORDING; } } @@ -625,11 +648,25 @@ public class AudioRecord // stop recording synchronized(mRecordingStateLock) { + handleFullVolumeRec(false); native_stop(); mRecordingState = RECORDSTATE_STOPPED; } } + private final IBinder mICallBack = new Binder(); + private void handleFullVolumeRec(boolean starting) { + if (!mIsSubmixFullVolume) { + return; + } + final IBinder b = ServiceManager.getService(android.content.Context.AUDIO_SERVICE); + final IAudioService ias = IAudioService.Stub.asInterface(b); + try { + ias.forceRemoteSubmixFullVolume(starting, mICallBack); + } catch (RemoteException e) { + Log.e(TAG, "Error talking to AudioService when handling full submix volume", e); + } + } //--------------------------------------------------------- // Audio data supply @@ -880,6 +917,7 @@ public class AudioRecord int sampleRate, int channelMask, int audioFormat, int buffSizeInBytes, int[] sessionId); + // TODO remove: implementation calls directly into implementation of native_release() private native final void native_finalize(); private native final void native_release(); diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java index 5c2abc5..96ce2c1 100644 --- a/media/java/android/media/AudioService.java +++ b/media/java/android/media/AudioService.java @@ -485,6 +485,7 @@ public class AudioService extends IAudioService.Stub { AudioSystem.DEVICE_OUT_HDMI_ARC | AudioSystem.DEVICE_OUT_SPDIF | AudioSystem.DEVICE_OUT_AUX_LINE; + int mFullVolumeDevices = 0; // TODO merge orientation and rotation private final boolean mMonitorOrientation; @@ -727,6 +728,10 @@ public class AudioService extends IAudioService.Stub { } } + private void checkAllFixedVolumeDevices(int streamType) { + mStreamStates[streamType].checkFixedVolumeDevices(); + } + private void createStreamStates() { int numStreamTypes = AudioSystem.getNumStreamTypes(); VolumeStreamState[] streams = mStreamStates = new VolumeStreamState[numStreamTypes]; @@ -1466,6 +1471,106 @@ public class AudioService extends IAudioService.Stub { return mStreamStates[streamType].isMuted(); } + private class RmtSbmxFullVolDeathHandler implements IBinder.DeathRecipient { + private IBinder mICallback; // To be notified of client's death + + RmtSbmxFullVolDeathHandler(IBinder cb) { + mICallback = cb; + try { + cb.linkToDeath(this, 0/*flags*/); + } catch (RemoteException e) { + Log.e(TAG, "can't link to death", e); + } + } + + boolean isHandlerFor(IBinder cb) { + return mICallback.equals(cb); + } + + void forget() { + try { + mICallback.unlinkToDeath(this, 0/*flags*/); + } catch (NoSuchElementException e) { + Log.e(TAG, "error unlinking to death", e); + } + } + + public void binderDied() { + Log.w(TAG, "Recorder with remote submix at full volume died " + mICallback); + forceRemoteSubmixFullVolume(false, mICallback); + } + } + + /** + * call must be synchronized on mRmtSbmxFullVolDeathHandlers + * @return true if there is a registered death handler, false otherwise */ + private boolean discardRmtSbmxFullVolDeathHandlerFor(IBinder cb) { + Iterator<RmtSbmxFullVolDeathHandler> it = mRmtSbmxFullVolDeathHandlers.iterator(); + while (it.hasNext()) { + final RmtSbmxFullVolDeathHandler handler = it.next(); + if (handler.isHandlerFor(cb)) { + handler.forget(); + mRmtSbmxFullVolDeathHandlers.remove(handler); + return true; + } + } + return false; + } + + /** call synchronized on mRmtSbmxFullVolDeathHandlers */ + private boolean hasRmtSbmxFullVolDeathHandlerFor(IBinder cb) { + Iterator<RmtSbmxFullVolDeathHandler> it = mRmtSbmxFullVolDeathHandlers.iterator(); + while (it.hasNext()) { + if (it.next().isHandlerFor(cb)) { + return true; + } + } + return false; + } + + private int mRmtSbmxFullVolRefCount = 0; + private ArrayList<RmtSbmxFullVolDeathHandler> mRmtSbmxFullVolDeathHandlers = + new ArrayList<RmtSbmxFullVolDeathHandler>(); + + public void forceRemoteSubmixFullVolume(boolean startForcing, IBinder cb) { + if (cb == null) { + return; + } + if ((PackageManager.PERMISSION_GRANTED != mContext.checkCallingOrSelfPermission( + android.Manifest.permission.CAPTURE_AUDIO_OUTPUT))) { + Log.w(TAG, "Trying to call forceRemoteSubmixFullVolume() without CAPTURE_AUDIO_OUTPUT"); + return; + } + synchronized(mRmtSbmxFullVolDeathHandlers) { + boolean applyRequired = false; + if (startForcing) { + if (!hasRmtSbmxFullVolDeathHandlerFor(cb)) { + mRmtSbmxFullVolDeathHandlers.add(new RmtSbmxFullVolDeathHandler(cb)); + if (mRmtSbmxFullVolRefCount == 0) { + mFullVolumeDevices |= AudioSystem.DEVICE_OUT_REMOTE_SUBMIX; + mFixedVolumeDevices |= AudioSystem.DEVICE_OUT_REMOTE_SUBMIX; + applyRequired = true; + } + mRmtSbmxFullVolRefCount++; + } + } else { + if (discardRmtSbmxFullVolDeathHandlerFor(cb) && (mRmtSbmxFullVolRefCount > 0)) { + mRmtSbmxFullVolRefCount--; + if (mRmtSbmxFullVolRefCount == 0) { + mFullVolumeDevices &= ~AudioSystem.DEVICE_OUT_REMOTE_SUBMIX; + mFixedVolumeDevices &= ~AudioSystem.DEVICE_OUT_REMOTE_SUBMIX; + applyRequired = true; + } + } + } + if (applyRequired) { + // Assumes only STREAM_MUSIC going through DEVICE_OUT_REMOTE_SUBMIX + checkAllFixedVolumeDevices(AudioSystem.STREAM_MUSIC); + mStreamStates[AudioSystem.STREAM_MUSIC].applyAllVolumes(); + } + } + } + /** @see AudioManager#setMasterMute(boolean, int) */ public void setMasterMute(boolean state, int flags, String callingPackage, IBinder cb) { if (mUseFixedVolume) { @@ -3243,8 +3348,8 @@ public class AudioService extends IAudioService.Stub { int index; if (isMuted()) { index = 0; - } else if ((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 && - mAvrcpAbsVolSupported) { + } else if (((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 && mAvrcpAbsVolSupported) + || ((device & mFullVolumeDevices) != 0)) { index = (mIndexMax + 5)/10; } else { index = (getIndex(device) + 5)/10; @@ -3272,8 +3377,10 @@ public class AudioService extends IAudioService.Stub { if (device != AudioSystem.DEVICE_OUT_DEFAULT) { if (isMuted()) { index = 0; - } else if ((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 && - mAvrcpAbsVolSupported) { + } else if (((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 && + mAvrcpAbsVolSupported) + || ((device & mFullVolumeDevices) != 0)) + { index = (mIndexMax + 5)/10; } else { index = ((Integer)entry.getValue() + 5)/10; @@ -3403,7 +3510,8 @@ public class AudioService extends IAudioService.Stub { Map.Entry entry = (Map.Entry)i.next(); int device = ((Integer)entry.getKey()).intValue(); int index = ((Integer)entry.getValue()).intValue(); - if (((device & mFixedVolumeDevices) != 0) && index != 0) { + if (((device & mFullVolumeDevices) != 0) + || (((device & mFixedVolumeDevices) != 0) && index != 0)) { entry.setValue(mIndexMax); } applyDeviceVolume(device); diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index 75fc03c..1c41432 100644 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -56,6 +56,8 @@ interface IAudioService { boolean isStreamMute(int streamType); + void forceRemoteSubmixFullVolume(boolean startForcing, IBinder cb); + void setMasterMute(boolean state, int flags, String callingPackage, IBinder cb); boolean isMasterMute(); |