TIF: allow TIS to override default sink from audio policy

Bug: 16635576
Change-Id: I995cfca084950f96675aeb086c5055f76d1c4752
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
index 80ea2c8..4d375ee 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.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 @@
         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<AudioPort> devicePorts = new ArrayList<AudioPort>();
-                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<AudioPort> devicePorts = new ArrayList<AudioPort>();
+            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<AudioPort> devicePorts = new ArrayList<AudioPort>();
+            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 @@
                 }
                 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 @@
             }
         }
 
+        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 @@
                 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 {