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

Commit 10cc447a authored by Paul Colta's avatar Paul Colta
Browse files

HDMI: Keep active source on <Set Stream Path> to ancestor

If <Set Stream Path> targeting an ancestor address is received while this device is the
active source, wait to see if a new Active Source is declared, as a parent switch might
be changing paths.

E.g. TV [0.0.0.0] -- AVR [3.0.0.0] -- Playback [3.1.0.0] (Active Source)
TV sends <Set Stream Path> [3.0.0.0]

Possible outcomes:
 - AVR sends <Routing Information> [3.2.0.0] -> Playback [3.1.0.0] is no longer AS.
 - AVR sends <Routing Information> [3.1.0.0] -> Playback [3.1.0.0] stays <AS>.
 - AVR sends no relevant CEC message -> Playback [3.1.0.0] stays <AS>.

Test: atest HdmiCecLocalDevicePlayback
Flag: EXEMPT bugfix
Bug: 405800336
Bug: 406374736
Change-Id: Ic1602f525d23a8e8da5ccd05a2421e6bf96261ea
parent e2e048ae
Loading
Loading
Loading
Loading
+22 −4
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import android.annotation.CallSuper;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiPortInfo;
import android.hardware.hdmi.IHdmiControlCallback;
import android.os.Handler;
import android.sysprop.HdmiProperties;
import android.util.Slog;

@@ -65,8 +66,11 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice {
    @GuardedBy("mLock")
    protected boolean mRoutingControlFeatureEnabled;

    private Handler mHandler;

    protected HdmiCecLocalDeviceSource(HdmiControlService service, int deviceType) {
        super(service, deviceType);
        mHandler = new Handler(service.getServiceLooper());
    }

    @ServiceThreadOnly
@@ -243,12 +247,26 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice {
    protected int handleSetStreamPath(HdmiCecMessage message) {
        assertRunOnServiceThread();
        int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
        int localDevicePhysicalAddress = mService.getPhysicalAddress();
        // If <Set Stream Path> targeting an ancestor address is received while this device is
        // the active source, wait to see if a new Active Source is declared, as a parent switch
        // might be changing paths.
        // E.g. TV [0.0.0.0] ------ AVR [3.0.0.0] ------ Playback [3.1.0.0] (Active Source)
        // TV sends <Set Stream Path> [3.0.0.0]
        //   AVR sends <Routing Information> [3.2.0.0] -> Playback [3.1.0.0] is no longer AS.
        //   AVR sends <Routing Information> [3.1.0.0] -> Playback [3.1.0.0] stays <AS>.
        //   AVR sends no relevant CEC message         -> Playback [3.1.0.0] stays <AS>.
        if (isActiveSource()
                && HdmiUtils.isTailOfActivePath(localDevicePhysicalAddress, physicalAddress)) {
            return Constants.HANDLED;
        }
        if (physicalAddress == localDevicePhysicalAddress
                && mService.isPlaybackDevice()) {
            // If current device is the target path, set to Active Source.
            // If the path is under the current device, should switch
        if (physicalAddress == mService.getPhysicalAddress() && mService.isPlaybackDevice()) {
            setAndBroadcastActiveSource(message, physicalAddress,
                    "HdmiCecLocalDeviceSource#handleSetStreamPath()");
        } else if (physicalAddress != mService.getPhysicalAddress() || !isActiveSource()) {
        } else if (physicalAddress != localDevicePhysicalAddress || !isActiveSource()) {
            // Invalidate the active source if stream path is set to other physical address or
            // our physical address while not active source
            setActiveSource(physicalAddress, "HdmiCecLocalDeviceSource#handleSetStreamPath()");
+2 −29
Original line number Diff line number Diff line
@@ -690,7 +690,7 @@ public class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {

    private boolean handleNewDeviceAtTheTailOfActivePath(int path) {
        // Seq #22
        if (isTailOfActivePath(path, getActivePath())) {
        if (HdmiUtils.isTailOfActivePath(path, getActivePath())) {
            int newPath = mService.portIdToPath(getActivePortId());
            setActivePath(newPath);
            startRoutingControl(getActivePath(), newPath, null);
@@ -699,33 +699,6 @@ public class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
        return false;
    }

    /**
     * Whether the given path is located in the tail of current active path.
     *
     * @param path to be tested
     * @param activePath current active path
     * @return true if the given path is located in the tail of current active path; otherwise,
     *         false
     */
    static boolean isTailOfActivePath(int path, int activePath) {
        // If active routing path is internal source, return false.
        if (activePath == 0) {
            return false;
        }
        for (int i = 12; i >= 0; i -= 4) {
            int curActivePath = (activePath >> i) & 0xF;
            if (curActivePath == 0) {
                return true;
            } else {
                int curPath = (path >> i) & 0xF;
                if (curPath != curActivePath) {
                    return false;
                }
            }
        }
        return false;
    }

    @Override
    @ServiceThreadOnly
    @Constants.HandleMessageResult
@@ -1391,7 +1364,7 @@ public class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
    void handleRemoveActiveRoutingPath(int path) {
        assertRunOnServiceThread();
        // Seq #23
        if (isTailOfActivePath(path, getActivePath())) {
        if (HdmiUtils.isTailOfActivePath(path, getActivePath())) {
            int newPath = mService.portIdToPath(getActivePortId());
            startRoutingControl(getActivePath(), newPath, null);
        }
+5 −0
Original line number Diff line number Diff line
@@ -872,6 +872,11 @@ public class HdmiCecNetwork {
        return mPhysicalAddress;
    }

    @VisibleForTesting
    protected void invalidatePhysicalAddress() {
        mPhysicalAddress = Constants.INVALID_PHYSICAL_ADDRESS;
    }

    @ServiceThreadOnly
    public void clear() {
        assertRunOnServiceThread();
+27 −0
Original line number Diff line number Diff line
@@ -724,6 +724,33 @@ final class HdmiUtils {
        }
    }

    /**
     * Whether the given path is located in the tail of current active path.
     *
     * @param path to be tested
     * @param activePath current active path
     * @return true if the given path is located in the tail of current active path; otherwise,
     *         false
     */
    static boolean isTailOfActivePath(int path, int activePath) {
        // If active routing path is internal source, return false.
        if (activePath == 0) {
            return false;
        }
        for (int i = 12; i >= 0; i -= 4) {
            int curActivePath = (activePath >> i) & 0xF;
            if (curActivePath == 0) {
                return true;
            } else {
                int curPath = (path >> i) & 0xF;
                if (curPath != curActivePath) {
                    return false;
                }
            }
        }
        return false;
    }

    // Device configuration of its supported Codecs and their Short Audio Descriptors.
    public static class DeviceConfig {
        /** Name of the device. Should be {@link Constants.AudioDevice}. **/
+55 −0
Original line number Diff line number Diff line
@@ -47,6 +47,7 @@ import android.os.RemoteException;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
import android.sysprop.HdmiProperties;
import android.util.Log;
import android.view.KeyEvent;

import androidx.test.InstrumentationRegistry;
@@ -1804,6 +1805,60 @@ public class HdmiCecLocalDevicePlaybackTest {
        assertThat(mActiveMediaSessionsPaused).isFalse();
    }

    @Test
    public void handleSetStreamPath_ancestorPathReceivedWhileActive_dutStillActiveSource() {
        int playbackPhysicalAddress = 0x3100;
        mNativeWrapper.setPhysicalAddress(playbackPhysicalAddress);
        mHdmiControlService.getHdmiCecNetwork().invalidatePhysicalAddress();
        mTestLooper.dispatchAll();

        mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
        mTestLooper.dispatchAll();

        mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
                playbackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
        mTestLooper.dispatchAll();

        HdmiCecMessage message =
                HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV, 0x3000);
        assertThat(mHdmiCecLocalDevicePlayback.handleSetStreamPath(message))
                .isEqualTo(Constants.HANDLED);
        mTestLooper.dispatchAll();

        assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isTrue();
        assertThat(mPowerManager.isInteractive()).isTrue();

    }

    @Test
    public void handleSetStreamPath_ancestorPathReceivedWhileActive_dutNotActiveSource() {
        int playbackPhysicalAddress = 0x3100;
        mNativeWrapper.setPhysicalAddress(playbackPhysicalAddress);
        mHdmiControlService.getHdmiCecNetwork().invalidatePhysicalAddress();
        mTestLooper.dispatchAll();

        mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
        mTestLooper.dispatchAll();

        mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
                playbackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
        mTestLooper.dispatchAll();

        HdmiCecMessage setStreamPath =
                HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV, 0x3000);
        HdmiCecMessage routingInformation =
                HdmiCecMessageBuilder.buildRoutingInformation(ADDR_AUDIO_SYSTEM, 0x3200);
        assertThat(mHdmiCecLocalDevicePlayback.handleSetStreamPath(setStreamPath))
                .isEqualTo(Constants.HANDLED);
        mTestLooper.dispatchAll();

        assertThat(mHdmiCecLocalDevicePlayback.handleRoutingInformation(routingInformation))
                .isEqualTo(Constants.HANDLED);
        mTestLooper.dispatchAll();

        assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isFalse();

    }
    @Test
    public void oneTouchPlay_PowerControlModeToTv() {
        mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(