Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7468f1db authored by Jungshik Jang's avatar Jungshik Jang Committed by Android (Google) Code Review
Browse files

Merge "Revamp HDMI-CEC system audio mode integration with audio service."

parents 355319f7 6f34f5ab
Loading
Loading
Loading
Loading
+4 −2
Original line number Diff line number Diff line
@@ -2895,13 +2895,15 @@ public class AudioManager {
     * @param device out device type to be used for system audio mode.
     *               Ignored if {@code on} is {@code false}
     * @param name name of system audio device
     * @return output device type. 0 (DEVICE_NONE) if failed to set device.
     * @hide
     */
    public void setHdmiSystemAudioSupported(boolean on, int device, String name) {
    public int setHdmiSystemAudioSupported(boolean on, int device, String name) {
        try {
            getService().setHdmiSystemAudioSupported(on, device, name);
            return getService().setHdmiSystemAudioSupported(on, device, name);
        } catch (RemoteException e) {
            Log.w(TAG, "Error setting system audio mode", e);
            return AudioSystem.DEVICE_NONE;
        }
    }

+91 −28
Original line number Diff line number Diff line
@@ -65,7 +65,6 @@ import android.os.UserHandle;
import android.os.Vibrator;
import android.provider.Settings;
import android.provider.Settings.System;

import android.telecomm.TelecommManager;
import android.text.TextUtils;
import android.util.Log;
@@ -84,6 +83,7 @@ import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.concurrent.ConcurrentHashMap;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@@ -3776,6 +3776,11 @@ public class AudioService extends IAudioService.Stub {
                                        AudioSystem.FORCE_ANALOG_DOCK : AudioSystem.FORCE_NONE);
                    }

                    if (mHdmiTvClient != null) {
                        setHdmiSystemAudioSupported(mHdmiSystemAudioSupported,
                                mHdmiSystemAudioOutputDevice, "");
                    }

                    // indicate the end of reconfiguration phase to audio HAL
                    AudioSystem.setParameters("restarting=false");
                    break;
@@ -4754,53 +4759,111 @@ public class AudioService extends IAudioService.Stub {
    private boolean mHdmiSystemAudioSupported = false;
    // Set only when device is tv.
    private HdmiTvClient mHdmiTvClient;
    private int mHdmiSystemAudioOutputDevice = AudioSystem.DEVICE_NONE;
    private int[] mSpeakerGains;

    @Override
    public void setHdmiSystemAudioSupported(boolean on, int device, String name) {
    public int setHdmiSystemAudioSupported(boolean on, int device, String name) {
        if (mHdmiTvClient == null) {
            Log.w(TAG, "Only Hdmi-Cec enabled TV device supports system audio mode.");
            return;
            return AudioSystem.DEVICE_NONE;
        }

        if ((device & AudioSystem.DEVICE_OUT_ALL_HDMI_SYSTEM_AUDIO) == 0) {
            Log.w(TAG, "Unsupported Hdmi-Cec system audio output:" + device);
            return;
        if (on && !checkHdmiSystemAudioOutput(device)) {
            return AudioSystem.DEVICE_NONE;
        }

        VolumeStreamState streamState = mStreamStates[AudioSystem.STREAM_MUSIC];
        int oldStreamDevice = getDeviceForStream(AudioSystem.STREAM_MUSIC);
        int oldIndex = streamState.getIndex(oldStreamDevice);

        synchronized (mHdmiTvClient) {
            if (on) {
                mHdmiSystemAudioOutputDevice = device;
            }
            if (mHdmiSystemAudioSupported == on) {
                return AudioSystem.getDevicesForStream(AudioSystem.STREAM_MUSIC);
            }
            mHdmiSystemAudioSupported = on;
            updateHdmiSystemAudioVolumeLocked(on);
        }
        return AudioSystem.getDevicesForStream(AudioSystem.STREAM_MUSIC);
    }

            // TODO: call AudioSystem.setForceUse(FORCE_FOR_MEDIA,
            //         on ? AudioSystem.FORCE_SYSTEM_AUDIO_XXX : AudioSystem.FORCE_NONE;
    private boolean checkHdmiSystemAudioOutput(int device) {
        if ((device & AudioSystem.DEVICE_OUT_ALL_HDMI_SYSTEM_AUDIO) == 0) {
            Log.w(TAG, "Unsupported Hdmi-Cec system audio output:" + device);
            return false;
        }

        int newStreamDevice = getDeviceForStream(AudioSystem.STREAM_MUSIC);
        boolean updateSpeakerVolume = false;
        int streamDevice = AudioSystem.getDevicesForStream(AudioSystem.STREAM_MUSIC);
        // If other devices except for system audio and speaker are available,
        // fails to start system audio mode.
        if ((streamDevice & ~AudioSystem.DEVICE_ALL_HDMI_SYSTEM_AUDIO_AND_SPEAKER) != 0) {
            Log.w(TAG, "Should turn off other devices before starting system audio:"
                    + streamDevice);
            return false;
        }
        if (AudioSystem.getDeviceConnectionState(device, "") !=
            AudioSystem.DEVICE_STATE_AVAILABLE) {
            Log.w(TAG, "Output device is not connected:" + device);
            return false;
        }
        return true;
    }

    private void updateHdmiSystemAudioVolumeLocked(boolean on) {
        AudioDevicePort speaker = findAudioDevicePort(AudioSystem.DEVICE_OUT_SPEAKER);
        if (speaker == null) {
            Log.w(TAG, "Has no speaker output.");
            return;
        }

        AudioPortConfig portConfig = speaker.activeConfig();
        AudioGainConfig gainConfig = portConfig.gain();
        int[] newGains;
        // When system audio is on, backup original gains and mute all channels of speaker by
        // setting gains to 0; otherwise, restore gains of speaker.
        if (on) {
            if ((oldStreamDevice & AudioSystem.DEVICE_OUT_SPEAKER) != 0) {
                // Mute tv speaker. Note that set volume 0 instead of call mute() method because
                // it's not possible to mute for a specific device.
                streamState.setIndex(0, AudioSystem.DEVICE_OUT_SPEAKER);
                updateSpeakerVolume = true;
            if (gainConfig == null) {
                Log.w(TAG, "Speaker has no gain control.");
                return;
            }
            // Back up original gains.
            mSpeakerGains = Arrays.copyOf(gainConfig.values(), gainConfig.values().length);
            // Set all gains to 0.
            newGains = new int[gainConfig.values().length];
        } else {
            if ((newStreamDevice & AudioSystem.DEVICE_OUT_SPEAKER) != 0) {
                // Restore speaker volume if exists. As there is no way to mute a device here,
                // load system audio's volume and set it to speaker.
                streamState.setIndex(oldIndex, AudioSystem.DEVICE_OUT_SPEAKER);
                updateSpeakerVolume = true;
            if (mSpeakerGains == null) {
                Log.w(TAG, "mSpeakerGains should not be null.");
                return;
            }
            newGains = Arrays.copyOf(mSpeakerGains, mSpeakerGains.length);
        }

        if (updateSpeakerVolume) {
            sendMsg(mAudioHandler, MSG_SET_DEVICE_VOLUME, SENDMSG_QUEUE,
                    AudioSystem.DEVICE_OUT_SPEAKER, 0,
                    streamState, 0);
        gainConfig = gainConfig.mGain.buildConfig(gainConfig.mode(),
                gainConfig.channelMask(),
                newGains,
                gainConfig.rampDurationMs());

        AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
        if (AudioSystem.SUCCESS !=  audioManager.setAudioPortGain(speaker, gainConfig)) {
            Log.w(TAG, "Failed to update audio port config.");
        }
    }

    private AudioDevicePort findAudioDevicePort(int type) {
        ArrayList<AudioPort> devicePorts = new ArrayList<>();
        AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
        int status = audioManager.listAudioDevicePorts(devicePorts);
        if (status != AudioSystem.SUCCESS)  {
            Log.w(TAG, "Failed to list up all audio ports");
            return null;
        }

        for (AudioPort port : devicePorts) {
            AudioDevicePort devicePort = (AudioDevicePort) port;
            if (devicePort.type() == type) {
                return devicePort;
            }
        }
        return null;
    }

    //==========================================================================================
+4 −4
Original line number Diff line number Diff line
@@ -290,6 +290,9 @@ public class AudioSystem
    public static final int DEVICE_OUT_ALL_HDMI_SYSTEM_AUDIO = (DEVICE_OUT_LINE |
                                                                DEVICE_OUT_HDMI_ARC |
                                                                DEVICE_OUT_SPDIF);
    public static final int DEVICE_ALL_HDMI_SYSTEM_AUDIO_AND_SPEAKER =
            (DEVICE_OUT_ALL_HDMI_SYSTEM_AUDIO |
             DEVICE_OUT_SPEAKER);

    // input devices
    public static final int DEVICE_IN_COMMUNICATION = DEVICE_BIT_IN | 0x1;
@@ -437,10 +440,7 @@ public class AudioSystem
    public static final int FORCE_DIGITAL_DOCK = 9;
    public static final int FORCE_NO_BT_A2DP = 10;
    public static final int FORCE_SYSTEM_ENFORCED = 11;
    public static final int FORCE_SYSTEM_AUDIO_HDMI_ARC = 12;
    public static final int FORCE_SYSTEM_AUDIO_SPDIF = 13;
    public static final int FORCE_SYSTEM_LINE = 14;
    private static final int NUM_FORCE_CONFIG = 15;
    private static final int NUM_FORCE_CONFIG = 12;
    public static final int FORCE_DEFAULT = FORCE_NONE;

    // usage for setForceUse, must match AudioSystem::force_use
+1 −1
Original line number Diff line number Diff line
@@ -198,5 +198,5 @@ interface IAudioService {

    void disableSafeMediaVolume();

    oneway void setHdmiSystemAudioSupported(boolean on, int device, String name);
    int setHdmiSystemAudioSupported(boolean on, int device, String name);
}
+30 −0
Original line number Diff line number Diff line
@@ -20,6 +20,9 @@ import android.content.Intent;
import android.hardware.hdmi.HdmiCecDeviceInfo;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.IHdmiControlCallback;
import android.media.AudioManager.OnAudioPortUpdateListener;
import android.media.AudioPatch;
import android.media.AudioPort;
import android.media.AudioSystem;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -101,6 +104,33 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
                mAddress, mService.getVendorId()));
        launchRoutingControl(true);
        launchDeviceDiscovery();

        registerAudioPortUpdateListener();
        // TODO: unregister audio port update listener if local device is released.
    }

    private void registerAudioPortUpdateListener() {
        mService.getAudioManager().registerAudioPortUpdateListener(
                new OnAudioPortUpdateListener() {
                    @Override
                    public void OnAudioPatchListUpdate(AudioPatch[] patchList) {}

                    @Override
                    public void OnAudioPortListUpdate(AudioPort[] portList) {
                        if (!mSystemAudioMode) {
                            return;
                        }
                        int devices = mService.getAudioManager().getDevicesForStream(
                                AudioSystem.STREAM_MUSIC);
                        if ((devices & ~AudioSystem.DEVICE_ALL_HDMI_SYSTEM_AUDIO_AND_SPEAKER)
                                != 0) {
                            // TODO: release system audio here.
                        }
                    }

                    @Override
                    public void OnServiceDied() {}
                });
    }

    /**