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

Commit 8fa36b11 authored by Jungshik Jang's avatar Jungshik Jang
Browse files

Implement volume control action in cec service.

Hdmi CEC's volume control is based on key event handling
but in android we can only get delta of volume change.
VolumeControlAction simulates key event action from
delta of volume change. However, it's highly dependent
on <Report Audio Status> message coming from AVR.
This implementation waits 900ms for <Report Audio Status>
message and if no message arrives it finishes action.
Instead, HdmiCecLocalDeviceTv consumes it after action
termination so that Tv can reflect system audio's
volume all the time.

Change-Id: I0442d31721365acdc009c8fa1c1e0a4361e4a1cc
parent 0c9b72b0
Loading
Loading
Loading
Loading
+4 −9
Original line number Diff line number Diff line
@@ -164,8 +164,10 @@ final class DeviceSelectAction extends FeatureAction {
    }

    private void turnOnDevice() {
        sendRemoteKeyCommand(HdmiConstants.UI_COMMAND_POWER);
        sendRemoteKeyCommand(HdmiConstants.UI_COMMAND_POWER_ON_FUNCTION);
        sendUserControlPressedAndReleased(mTarget.getLogicalAddress(),
                HdmiConstants.UI_COMMAND_POWER);
        sendUserControlPressedAndReleased(mTarget.getLogicalAddress(),
                HdmiConstants.UI_COMMAND_POWER_ON_FUNCTION);
        mState = STATE_WAIT_FOR_DEVICE_POWER_ON;
        addTimer(mState, TIMEOUT_POWER_ON_MS);
    }
@@ -177,13 +179,6 @@ final class DeviceSelectAction extends FeatureAction {
        addTimer(mState, TIMEOUT_ACTIVE_SOURCE_MS);
    }

    private void sendRemoteKeyCommand(int keyCode) {
        sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(getSourceAddress(),
                mTarget.getLogicalAddress(), keyCode));
        sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(getSourceAddress(),
                mTarget.getLogicalAddress()));
    }

    @Override
    public void handleTimerEvent(int timeoutState) {
        if (mState != timeoutState) {
+7 −0
Original line number Diff line number Diff line
@@ -248,4 +248,11 @@ abstract class FeatureAction {
    protected final int getSourcePath() {
        return mSource.getDeviceInfo().getPhysicalAddress();
    }

    protected void sendUserControlPressedAndReleased(int targetAddress, int uiCommand) {
        sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(
                getSourceAddress(), targetAddress, uiCommand));
        sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(
                getSourceAddress(), targetAddress));
    }
}
+6 −0
Original line number Diff line number Diff line
@@ -151,6 +151,8 @@ abstract class HdmiCecLocalDevice {
                return handleSetSystemAudioMode(message);
            case HdmiCec.MESSAGE_SYSTEM_AUDIO_MODE_STATUS:
                return handleSystemAudioModeStatus(message);
            case HdmiCec.MESSAGE_REPORT_AUDIO_STATUS:
                return handleReportAudioStatus(message);
            default:
                return false;
        }
@@ -263,6 +265,10 @@ abstract class HdmiCecLocalDevice {
        return false;
    }

    protected boolean handleReportAudioStatus(HdmiCecMessage message) {
        return false;
    }

    @ServiceThreadOnly
    final void handleAddressAllocated(int logicalAddress) {
        assertRunOnServiceThread();
+79 −2
Original line number Diff line number Diff line
@@ -56,6 +56,12 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
    @GuardedBy("mLock")
    private int mPrevPortId;

    @GuardedBy("mLock")
    private int mSystemAudioVolume = HdmiConstants.UNKNOWN_VOLUME;

    @GuardedBy("mLock")
    private boolean mSystemAudioMute = false;

    // Copy of mDeviceInfos to guarantee thread-safety.
    @GuardedBy("mLock")
    private List<HdmiCecDeviceInfo> mSafeAllDeviceInfos = Collections.emptyList();
@@ -353,6 +359,22 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
        return true;
    }

    @Override
    @ServiceThreadOnly
    protected boolean handleReportAudioStatus(HdmiCecMessage message) {
        assertRunOnServiceThread();

        byte params[] = message.getParams();
        if (params.length < 1) {
            Slog.w(TAG, "Invalide <Report Audio Status> message:" + message);
            return true;
        }
        int mute = params[0] & 0x80;
        int volume = params[0] & 0x7F;
        setAudioStatus(mute == 0x80, volume);
        return true;
    }

    @ServiceThreadOnly
    private void launchDeviceDiscovery() {
        assertRunOnServiceThread();
@@ -458,10 +480,65 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
        }
    }

    @ServiceThreadOnly
    void setAudioStatus(boolean mute, int volume) {
        synchronized (mLock) {
            mSystemAudioMute = mute;
            mSystemAudioVolume = volume;
            // TODO: pass volume to service (audio service) after scale it to local volume level.
            mService.setAudioStatus(mute, volume);
        }
    }

    @ServiceThreadOnly
    void changeVolume(int curVolume, int delta, int maxVolume) {
        assertRunOnServiceThread();
        if (delta == 0 || !isSystemAudioOn()) {
            return;
        }

        int targetVolume = curVolume + delta;
        int cecVolume = VolumeControlAction.scaleToCecVolume(targetVolume, maxVolume);
        synchronized (mLock) {
            // If new volume is the same as current system audio volume, just ignore it.
            // Note that UNKNOWN_VOLUME is not in range of cec volume scale.
            if (cecVolume == mSystemAudioVolume) {
                // Update tv volume with system volume value.
                mService.setAudioStatus(false,
                        VolumeControlAction.scaleToCustomVolume(mSystemAudioVolume, maxVolume));
                return;
            }
        }

        // Remove existing volume action.
        removeAction(VolumeControlAction.class);

        HdmiCecDeviceInfo avr = getAvrDeviceInfo();
        addAndStartAction(VolumeControlAction.ofVolumeChange(this, avr.getLogicalAddress(),
                cecVolume, delta > 0));
    }

    @ServiceThreadOnly
    void changeMute(boolean mute) {
        assertRunOnServiceThread();
        if (!isSystemAudioOn()) {
            return;
        }

        // Remove existing volume action.
        removeAction(VolumeControlAction.class);
        HdmiCecDeviceInfo avr = getAvrDeviceInfo();
        addAndStartAction(VolumeControlAction.ofMute(this, avr.getLogicalAddress(), mute));
    }

    private boolean isSystemAudioOn() {
        if (getAvrDeviceInfo() == null) {
            return false;
        }

        synchronized (mLock) {
            return mSystemAudioMode;
        }
    }

    @Override
    @ServiceThreadOnly
+7 −0
Original line number Diff line number Diff line
@@ -97,5 +97,12 @@ final class HdmiConstants {

    static final int UNKNOWN_VOLUME = -1;

    // IRT(Initiator Repetition Time) in millisecond as recommended in the standard.
    // Outgoing UCP commands, when in 'Press and Hold' mode, should be this much apart
    // from the adjacent one so as not to place unnecessarily heavy load on the CEC line.
    // TODO: This value might need tweaking per product basis. Consider putting it
    //       in config.xml to allow customization.
    static final int IRT_MS = 300;

    private HdmiConstants() { /* cannot be instantiated */ }
}
Loading