am 986468a8: am dce7a427: Merge "Full volume on remote submix for apps that need it" into lmp-dev

* commit '986468a8ab77ce1e89247078d4e93a3aaa5d294b':
  Full volume on remote submix for apps that need it
This commit is contained in:
Jean-Michel Trivi
2014-10-07 17:42:58 +00:00
committed by Android Git Automerger
3 changed files with 154 additions and 6 deletions

View File

@@ -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)
@@ -420,7 +440,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();
}
@@ -587,6 +608,7 @@ public class AudioRecord
// start recording
synchronized(mRecordingStateLock) {
if (native_start(MediaSyncEvent.SYNC_EVENT_NONE, 0) == SUCCESS) {
handleFullVolumeRec(true);
mRecordingState = RECORDSTATE_RECORDING;
}
}
@@ -609,6 +631,7 @@ public class AudioRecord
// start recording
synchronized(mRecordingStateLock) {
if (native_start(syncEvent.getType(), syncEvent.getAudioSessionId()) == SUCCESS) {
handleFullVolumeRec(true);
mRecordingState = RECORDSTATE_RECORDING;
}
}
@@ -626,11 +649,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
@@ -881,6 +918,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();

View File

@@ -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);

View File

@@ -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();