diff --git a/media/java/android/media/tv/ITvInputHardware.aidl b/media/java/android/media/tv/ITvInputHardware.aidl index f35e8f3adf225..3a041613ee1b9 100644 --- a/media/java/android/media/tv/ITvInputHardware.aidl +++ b/media/java/android/media/tv/ITvInputHardware.aidl @@ -33,6 +33,7 @@ interface ITvInputHardware { * trigger CEC commands for adjusting active HDMI source. Returns true on success. */ boolean setSurface(in Surface surface, in TvStreamConfig config); + /** * Set volume for this stream via AudioGain. (TBD) */ @@ -43,4 +44,21 @@ interface ITvInputHardware { * HDMI CEC commands. If the hardware is not representing an HDMI port, this method will fail. */ boolean dispatchKeyEventToHdmi(in KeyEvent event); + + /** + * Override default audio sink from audio policy. When override is on, it is + * TvInputService's responsibility to adjust to audio configuration change + * (for example, when the audio sink becomes unavailable or more desirable + * audio sink is detected). + * + * @param audioType one of AudioManager.DEVICE_* values. When it's * DEVICE_NONE, override + * becomes off. + * @param audioAddress audio address of the overriding device. + * @param samplingRate desired sampling rate. Use default when it's 0. + * @param channelMask desired channel mask. Use default when it's + * AudioFormat.CHANNEL_OUT_DEFAULT. + * @param format desired format. Use default when it's AudioFormat.ENCODING_DEFAULT. + */ + void overrideAudioSink(int audioType, String audioAddress, int samplingRate, int channelMask, + int format); } diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java index 80ea2c8ef0fae..4d375eed70868 100644 --- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java +++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java @@ -28,6 +28,7 @@ import android.hardware.hdmi.IHdmiDeviceEventListener; import android.hardware.hdmi.IHdmiHotplugEventListener; import android.hardware.hdmi.IHdmiInputChangeListener; import android.media.AudioDevicePort; +import android.media.AudioFormat; import android.media.AudioManager; import android.media.AudioPatch; import android.media.AudioPort; @@ -556,41 +557,54 @@ class TvInputHardwareManager implements TvInputHal.Callback { private final Object mImplLock = new Object(); private final AudioDevicePort mAudioSource; - private final AudioDevicePort mAudioSink; + private AudioDevicePort mAudioSink; private AudioPatch mAudioPatch = null; private TvStreamConfig mActiveConfig = null; + private int mDesiredSamplingRate = 0; + private int mDesiredChannelMask = AudioFormat.CHANNEL_OUT_DEFAULT; + private int mDesiredFormat = AudioFormat.ENCODING_DEFAULT; + public TvInputHardwareImpl(TvInputHardwareInfo info) { mInfo = info; AudioDevicePort audioSource = null; - AudioDevicePort audioSink = null; if (mInfo.getAudioType() != AudioManager.DEVICE_NONE) { - ArrayList devicePorts = new ArrayList(); - if (mAudioManager.listAudioDevicePorts(devicePorts) == AudioManager.SUCCESS) { - // Find source - for (AudioPort port : devicePorts) { - AudioDevicePort devicePort = (AudioDevicePort) port; - if (devicePort.type() == mInfo.getAudioType() && - devicePort.address().equals(mInfo.getAudioAddress())) { - audioSource = devicePort; - break; - } - } - // Find sink - // TODO: App may want to specify sink device? - int sinkDevices = mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC); - for (AudioPort port : devicePorts) { - AudioDevicePort devicePort = (AudioDevicePort) port; - if (devicePort.type() == sinkDevices) { - audioSink = devicePort; - break; - } + audioSource = findAudioDevicePort(mInfo.getAudioType(), mInfo.getAudioAddress()); + mAudioSink = findAudioSinkFromAudioPolicy(); + } + mAudioSource = audioSource; + } + + private AudioDevicePort findAudioSinkFromAudioPolicy() { + ArrayList devicePorts = new ArrayList(); + if (mAudioManager.listAudioDevicePorts(devicePorts) == AudioManager.SUCCESS) { + int sinkDevice = mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC); + for (AudioPort port : devicePorts) { + AudioDevicePort devicePort = (AudioDevicePort) port; + if (devicePort.type() == sinkDevice) { + return devicePort; } } } - mAudioSource = audioSource; - mAudioSink = audioSink; + return null; + } + + private AudioDevicePort findAudioDevicePort(int type, String address) { + if (type == AudioManager.DEVICE_NONE) { + return null; + } + ArrayList devicePorts = new ArrayList(); + if (mAudioManager.listAudioDevicePorts(devicePorts) != AudioManager.SUCCESS) { + return null; + } + for (AudioPort port : devicePorts) { + AudioDevicePort devicePort = (AudioDevicePort) port; + if (devicePort.type() == type && devicePort.address().equals(address)) { + return devicePort; + } + } + return null; } public void release() { @@ -621,15 +635,7 @@ class TvInputHardwareManager implements TvInputHal.Callback { } if (mAudioSource != null && mAudioSink != null) { if (surface != null) { - AudioPortConfig sourceConfig = mAudioSource.activeConfig(); - AudioPortConfig sinkConfig = mAudioSink.activeConfig(); - AudioPatch[] audioPatchArray = new AudioPatch[] { mAudioPatch }; - // TODO: build config if activeConfig() == null - mAudioManager.createAudioPatch( - audioPatchArray, - new AudioPortConfig[] { sourceConfig }, - new AudioPortConfig[] { sinkConfig }); - mAudioPatch = audioPatchArray[0]; + updateAudioPatchLocked(); } else { mAudioManager.releaseAudioPatch(mAudioPatch); mAudioPatch = null; @@ -656,6 +662,31 @@ class TvInputHardwareManager implements TvInputHal.Callback { } } + private void updateAudioPatchLocked() { + AudioPortConfig sourceConfig = mAudioSource.activeConfig(); + AudioPortConfig sinkConfig = mAudioSink.activeConfig(); + AudioPatch[] audioPatchArray = new AudioPatch[] { mAudioPatch }; + if (sinkConfig == null + || (mDesiredSamplingRate != 0 + && sinkConfig.samplingRate() != mDesiredSamplingRate) + || (mDesiredChannelMask != AudioFormat.CHANNEL_OUT_DEFAULT + && sinkConfig.channelMask() != mDesiredChannelMask) + || (mDesiredFormat != AudioFormat.ENCODING_DEFAULT + && sinkConfig.format() != mDesiredFormat)) { + sinkConfig = mAudioSource.buildConfig(mDesiredSamplingRate, mDesiredChannelMask, + mDesiredFormat, null); + } + if (sourceConfig == null) { + sourceConfig = mAudioSource.buildConfig(sinkConfig.samplingRate(), + sinkConfig.channelMask(), sinkConfig.format(), null); + } + mAudioManager.createAudioPatch( + audioPatchArray, + new AudioPortConfig[] { sourceConfig }, + new AudioPortConfig[] { sinkConfig }); + mAudioPatch = audioPatchArray[0]; + } + @Override public void setVolume(float volume) throws RemoteException { synchronized (mImplLock) { @@ -710,6 +741,28 @@ class TvInputHardwareManager implements TvInputHal.Callback { return result == TvInputHal.SUCCESS; } } + + @Override + public void overrideAudioSink(int audioType, String audioAddress, int samplingRate, + int channelMask, int format) { + synchronized (mImplLock) { + if (audioType == AudioManager.DEVICE_NONE) { + mAudioSink = findAudioSinkFromAudioPolicy(); + } else { + AudioDevicePort audioSink = findAudioDevicePort(audioType, audioAddress); + if (audioSink != null) { + mAudioSink = audioSink; + } + } + mDesiredSamplingRate = samplingRate; + mDesiredChannelMask = channelMask; + mDesiredFormat = format; + + if (mAudioPatch != null) { + updateAudioPatchLocked(); + } + } + } } interface Listener {