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

Commit 7566a0b9 authored by winnichang's avatar winnichang Committed by Winni Chang
Browse files

Decouple ARC and SAD

Couple ARC and SAD causes several issues.
1.  Initiating ARC can lead to an extended delay in reporting the "ARC initiated" status, potentially causing failures in HDMI 1.4 certification testing. The specification mandates a response time between 200ms and 1s, but the process of binding with the SAD can exceed this limit.
2. Certain ARC devices might interpret the lack of a timely "ARC initiated" report as an error and consequently produce no audio output.
3. Repeatedly initiating ARC can trigger multiple "request SAD action" commands. This can result in duplicate SAD reports, potentially leading to some SADs being missed or ignored by the receiving device.
4. Devices like the Onkyo TX-NR696, which respond with a "feature abort" message before eventually sending the actual supported SAD, can create a situation where the TV fails to receive the subsequent, correct SAD information.

Bug: 366350787
Flag: EXEMPT decouple ARC and SAD
Test: adb -s CAB36A2EE24208000724 shell dumpsys media.audio_flinger | grep AUDIO_DEVICE_OUT_HDMI_ -A 100 | grep AUDIO_FORMAT| grep -v 'AUDIO_FORMAT_PCM'
or
check the Profiles under Port Id: xx "HDMI ARC" under Available Out Devices in dumpsys
and
local test the audio output
Change-Id: Ic9bafd856260c15e441895d69fb1a86468889b0c
parent 9735f28d
Loading
Loading
Loading
Loading
+41 −2
Original line number Original line Diff line number Diff line
@@ -79,6 +79,9 @@ public class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
    // True by default for all the ARC-enabled ports.
    // True by default for all the ARC-enabled ports.
    private final SparseBooleanArray mArcFeatureEnabled = new SparseBooleanArray();
    private final SparseBooleanArray mArcFeatureEnabled = new SparseBooleanArray();


    @GuardedBy("mLock")
    private List<byte[]> mSupportedSads = new ArrayList<>();

    // Whether the System Audio Control feature is enabled or not. True by default.
    // Whether the System Audio Control feature is enabled or not. True by default.
    @GuardedBy("mLock")
    @GuardedBy("mLock")
    private boolean mSystemAudioControlFeatureEnabled;
    private boolean mSystemAudioControlFeatureEnabled;
@@ -856,6 +859,13 @@ public class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
                new SystemAudioActionFromTv(this, avr.getLogicalAddress(), enabled, callback));
                new SystemAudioActionFromTv(this, avr.getLogicalAddress(), enabled, callback));
    }
    }


    void clearSads() {
        synchronized (mLock) {
            mSupportedSads.clear();
        }
    }


    // # Seq 25
    // # Seq 25
    void setSystemAudioMode(boolean on) {
    void setSystemAudioMode(boolean on) {
        if (!isSystemAudioControlFeatureEnabled() && on) {
        if (!isSystemAudioControlFeatureEnabled() && on) {
@@ -909,13 +919,41 @@ public class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
    }
    }


    @ServiceThreadOnly
    @ServiceThreadOnly
    void enableArc(List<byte[]> supportedSads) {
    void enableArc() {
        assertRunOnServiceThread();
        assertRunOnServiceThread();
        HdmiLogger.debug("Set Arc Status[old:%b new:true]", mArcEstablished);
        HdmiLogger.debug("Set Arc Status[old:%b new:true]", mArcEstablished);


        enableAudioReturnChannel(true);
        enableAudioReturnChannel(true);
        notifyArcStatusToAudioService(true, supportedSads);
        //Ensure mSupportedSads is empty before fetching SADs
        synchronized (mLock) {
            mSupportedSads.clear();
        }
        notifyArcStatusToAudioService(true, mSupportedSads);
        mArcEstablished = true;
        mArcEstablished = true;

        // Avoid triggering duplicate RequestSadAction events.
        // This could lead to unexpected responses from the AVR and cause the TV to receive data
        // out of order. The SAD report does not provide information about the order of events.
        if (hasAction(RequestSadAction.class)) {
            return;
        }

        // Send Request SAD to get real SAD instead of default empty
        RequestSadAction action = new RequestSadAction(
                this, Constants.ADDR_AUDIO_SYSTEM,
                new RequestSadAction.RequestSadCallback() {
                    @Override
                    public void onRequestSadDone(List<byte[]> supportedSadsDone) {
                        synchronized (mLock) {
                            mSupportedSads = supportedSadsDone;
                        }
                        notifyArcStatusToAudioService(false, new ArrayList<>());
                        synchronized (mLock) {
                            notifyArcStatusToAudioService(true, mSupportedSads);
                        }
                    }
                });
        addAndStartAction(action);
    }
    }


    @ServiceThreadOnly
    @ServiceThreadOnly
@@ -926,6 +964,7 @@ public class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
        enableAudioReturnChannel(false);
        enableAudioReturnChannel(false);
        notifyArcStatusToAudioService(false, new ArrayList<>());
        notifyArcStatusToAudioService(false, new ArrayList<>());
        mArcEstablished = false;
        mArcEstablished = false;
        clearSads();
    }
    }


    /**
    /**
+14 −29
Original line number Original line Diff line number Diff line
@@ -60,18 +60,6 @@ final class SetArcTransmissionStateAction extends HdmiCecFeatureAction {
    boolean start() {
    boolean start() {
        // Seq #37.
        // Seq #37.
        if (mEnabled) {
        if (mEnabled) {
            // Avoid triggering duplicate RequestSadAction events.
            // This could lead to unexpected responses from the AVR and cause the TV to receive data
            // out of order. The SAD report does not provide information about the order of events.
            if ((tv().hasAction(RequestSadAction.class))) {
                return true;
            }
            // Request SADs before enabling ARC
            RequestSadAction action = new RequestSadAction(
                    localDevice(), Constants.ADDR_AUDIO_SYSTEM,
                    new RequestSadAction.RequestSadCallback() {
                        @Override
                        public void onRequestSadDone(List<byte[]> supportedSads) {
            // Enable ARC status immediately before sending <Report Arc Initiated>.
            // Enable ARC status immediately before sending <Report Arc Initiated>.
            // If AVR responds with <Feature Abort>, disable ARC status again.
            // If AVR responds with <Feature Abort>, disable ARC status again.
            // This is different from spec that says that turns ARC status to
            // This is different from spec that says that turns ARC status to
@@ -80,15 +68,12 @@ final class SetArcTransmissionStateAction extends HdmiCecFeatureAction {
            // But implemented this way to save the time having to wait for
            // But implemented this way to save the time having to wait for
            // <Feature Abort>.
            // <Feature Abort>.
            Slog.i(TAG, "Enabling ARC");
            Slog.i(TAG, "Enabling ARC");
                            tv().enableArc(supportedSads);
            tv().enableArc();
            // If succeeds to send <Report ARC Initiated>, wait general timeout to
            // If succeeds to send <Report ARC Initiated>, wait general timeout to
            // check whether there is no <Feature Abort> for <Report ARC Initiated>.
            // check whether there is no <Feature Abort> for <Report ARC Initiated>.
            mState = STATE_WAITING_TIMEOUT;
            mState = STATE_WAITING_TIMEOUT;
            addTimer(mState, HdmiConfig.TIMEOUT_MS);
            addTimer(mState, HdmiConfig.TIMEOUT_MS);
            sendReportArcInitiated();
            sendReportArcInitiated();
                        }
                    });
            addAndStartAction(action);
        } else {
        } else {
            disableArc();
            disableArc();
            finish();
            finish();
+2 −53
Original line number Original line Diff line number Diff line
@@ -341,18 +341,9 @@ public class HdmiCecLocalDeviceTvTest {
        HdmiCecMessage reportArcInitiated = HdmiCecMessageBuilder.buildReportArcInitiated(
        HdmiCecMessage reportArcInitiated = HdmiCecMessageBuilder.buildReportArcInitiated(
                ADDR_TV,
                ADDR_TV,
                ADDR_AUDIO_SYSTEM);
                ADDR_AUDIO_SYSTEM);
        // <Report ARC Initiated> should only be sent after SAD querying is done
        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(reportArcInitiated);

        // Finish querying SADs
        for (int i = 0; i <= RETRY_COUNTER_MAX; ++i) {
            assertThat(mNativeWrapper.getResultMessages()).contains(SAD_QUERY);
            mNativeWrapper.clearResultMessages();
            mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
            mTestLooper.dispatchAll();
        }

        assertThat(mNativeWrapper.getResultMessages()).contains(reportArcInitiated);
        assertThat(mNativeWrapper.getResultMessages()).contains(reportArcInitiated);
        // But we need to check SADs started to be queried at this time
        assertThat(mNativeWrapper.getResultMessages()).contains(SAD_QUERY);
        mNativeWrapper.clearResultMessages();
        mNativeWrapper.clearResultMessages();
    }
    }


@@ -752,17 +743,6 @@ public class HdmiCecLocalDeviceTvTest {
        HdmiCecMessage reportArcInitiated = HdmiCecMessageBuilder.buildReportArcInitiated(
        HdmiCecMessage reportArcInitiated = HdmiCecMessageBuilder.buildReportArcInitiated(
                ADDR_TV,
                ADDR_TV,
                ADDR_AUDIO_SYSTEM);
                ADDR_AUDIO_SYSTEM);
        // <Report ARC Initiated> should only be sent after SAD querying is done
        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(reportArcInitiated);

        // Finish querying SADs
        for (int i = 0; i <= RETRY_COUNTER_MAX; ++i) {
            assertThat(mNativeWrapper.getResultMessages()).contains(SAD_QUERY);
            mNativeWrapper.clearResultMessages();
            mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
            mTestLooper.dispatchAll();
        }

        assertThat(mNativeWrapper.getResultMessages()).contains(reportArcInitiated);
        assertThat(mNativeWrapper.getResultMessages()).contains(reportArcInitiated);
    }
    }


@@ -1067,16 +1047,6 @@ public class HdmiCecLocalDeviceTvTest {
        HdmiCecMessage reportArcInitiated = HdmiCecMessageBuilder.buildReportArcInitiated(
        HdmiCecMessage reportArcInitiated = HdmiCecMessageBuilder.buildReportArcInitiated(
            ADDR_TV,
            ADDR_TV,
            ADDR_AUDIO_SYSTEM);
            ADDR_AUDIO_SYSTEM);
        // <Report ARC Initiated> should only be sent after SAD querying is done
        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(reportArcInitiated);
        // Finish querying SADs
        for (int i = 0; i <= RETRY_COUNTER_MAX; ++i) {
            assertThat(mNativeWrapper.getResultMessages()).contains(SAD_QUERY);
            mNativeWrapper.clearResultMessages();
            mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
            mTestLooper.dispatchAll();
        }

        assertThat(mNativeWrapper.getResultMessages()).contains(reportArcInitiated);
        assertThat(mNativeWrapper.getResultMessages()).contains(reportArcInitiated);
        mNativeWrapper.clearResultMessages();
        mNativeWrapper.clearResultMessages();


@@ -1268,16 +1238,6 @@ public class HdmiCecLocalDeviceTvTest {


        mNativeWrapper.onCecMessage(initiateArc);
        mNativeWrapper.onCecMessage(initiateArc);
        mTestLooper.dispatchAll();
        mTestLooper.dispatchAll();

        // Finish querying SADs
        for (int i = 0; i <= RETRY_COUNTER_MAX; ++i) {
            assertThat(mNativeWrapper.getResultMessages()).contains(SAD_QUERY);
            mNativeWrapper.clearResultMessages();
            mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
            mTestLooper.dispatchAll();
        }

        // ARC should be established after RequestSadAction is finished
        assertThat(mNativeWrapper.getResultMessages()).contains(reportArcInitiated);
        assertThat(mNativeWrapper.getResultMessages()).contains(reportArcInitiated);


        mHdmiControlService.onStandby(HdmiControlService.STANDBY_SCREEN_OFF);
        mHdmiControlService.onStandby(HdmiControlService.STANDBY_SCREEN_OFF);
@@ -1421,17 +1381,6 @@ public class HdmiCecLocalDeviceTvTest {
        HdmiCecMessage reportArcInitiated = HdmiCecMessageBuilder.buildReportArcInitiated(
        HdmiCecMessage reportArcInitiated = HdmiCecMessageBuilder.buildReportArcInitiated(
                ADDR_TV,
                ADDR_TV,
                ADDR_AUDIO_SYSTEM);
                ADDR_AUDIO_SYSTEM);
        // <Report ARC Initiated> should only be sent after SAD querying is done
        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(reportArcInitiated);

        // Finish querying SADs
        for (int i = 0; i <= RETRY_COUNTER_MAX; ++i) {
            assertThat(mNativeWrapper.getResultMessages()).contains(SAD_QUERY);
            mNativeWrapper.clearResultMessages();
            mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
            mTestLooper.dispatchAll();
        }

        assertThat(mNativeWrapper.getResultMessages()).contains(reportArcInitiated);
        assertThat(mNativeWrapper.getResultMessages()).contains(reportArcInitiated);
    }
    }