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

Commit 72b7d738 authored by Jinsuk Kim's avatar Jinsuk Kim
Browse files

Give clear distinction between active source/active routing path

Previously in HdmiControlSerivce, active routing path was mixed with
the physical address of the active source, but these two concepts needs
clearer distinction.

Defined a new variable for active source that contains both logical/physical
address to keep active routing path separate. This change makes it possible
to write the flow more closely to the guideline.

Also added to this CL is flag notifyInputChange, which allows the device/
routing change triggered by external event to trigger InputChangeListener
to get TIF notified. For routing control, the device info passed to TIF
has the path information in the field mPhysicalAddress (other fields are
invalid).

Bug: 16519939
Change-Id: I8b400bc48e874b0866500655773aea38ab945fe4
parent 5438e4e1
Loading
Loading
Loading
Loading
+21 −18
Original line number Diff line number Diff line
@@ -23,6 +23,8 @@ import android.hardware.hdmi.HdmiControlManager;
import android.os.RemoteException;
import android.util.Slog;

import com.android.server.hdmi.HdmiCecLocalDevice.ActiveSource;

/**
 * Handles CEC command <Active Source>.
 * <p>
@@ -54,44 +56,45 @@ final class ActiveSourceHandler {
    /**
     * Handles the incoming active source command.
     *
     * @param activeAddress logical address of the device to be the active source
     * @param activePath routing path of the device to be the active source
     * @param newActive new active source information
     */
    void process(int activeAddress, int activePath) {
    void process(ActiveSource newActive) {
        // Seq #17
        HdmiCecLocalDeviceTv tv = mSource;
        if (getSourcePath() == activePath && tv.getActiveSource() == getSourceAddress()) {
        ActiveSource activeSource = tv.getActiveSource();
        if (activeSource.equals(newActive)) {
            invokeCallback(HdmiControlManager.RESULT_SUCCESS);
            return;
        }
        HdmiCecDeviceInfo device = mService.getDeviceInfo(activeAddress);
        HdmiCecDeviceInfo device = mService.getDeviceInfo(newActive.logicalAddress);
        if (device == null) {
            tv.startNewDeviceAction(activeAddress, activePath);
            tv.startNewDeviceAction(newActive);
        }

        int currentActive = tv.getActiveSource();
        int currentPath = tv.getActivePath();
        ActiveSource current = tv.getActiveSource();
        if (!tv.isProhibitMode()) {
            tv.updateActiveSource(activeAddress, activePath);
            if (currentActive != activeAddress && currentPath != activePath) {
                tv.updateActivePortId(mService.pathToPortId(activePath));
            tv.updateActiveSource(newActive);
            if (!current.equals(newActive)) {
                boolean notifyInputChange = (mCallback == null);
                tv.updateActiveInput(newActive.physicalAddress, notifyInputChange);
            }
            invokeCallback(HdmiControlManager.RESULT_SUCCESS);
        } else {
            // TV is in a mode that should keep its current source/input from
            // being changed for its operation. Reclaim the active source
            // or switch the port back to the one used for the current mode.
            if (currentActive == getSourceAddress()) {
                HdmiCecMessage activeSource =
                        HdmiCecMessageBuilder.buildActiveSource(currentActive, currentPath);
                mService.sendCecCommand(activeSource);
                tv.updateActiveSource(currentActive, currentPath);
            if (current.logicalAddress == getSourceAddress()) {
                HdmiCecMessage activeSourceCommand = HdmiCecMessageBuilder.buildActiveSource(
                        current.logicalAddress, current.physicalAddress);
                mService.sendCecCommand(activeSourceCommand);
                tv.updateActiveSource(current);
                invokeCallback(HdmiControlManager.RESULT_SUCCESS);
            } else {
                HdmiCecMessage routingChange = HdmiCecMessageBuilder.buildRoutingChange(
                        getSourceAddress(), activePath, currentPath);
                        getSourceAddress(), newActive.physicalAddress, current.physicalAddress);
                mService.sendCecCommand(routingChange);
                tv.addAndStartAction(new RoutingControlAction(tv, currentPath, true, mCallback));
                tv.addAndStartAction(
                        new RoutingControlAction(tv, current.physicalAddress, true, mCallback));
            }
        }
    }
+3 −2
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import android.hardware.hdmi.IHdmiControlCallback;
import android.os.RemoteException;
import android.util.Slog;

import com.android.server.hdmi.HdmiCecLocalDevice.ActiveSource;
import com.android.server.hdmi.HdmiControlService.SendMessageCallback;

/**
@@ -130,10 +131,10 @@ final class DeviceSelectAction extends FeatureAction {
                return false;
            case STATE_WAIT_FOR_ACTIVE_SOURCE:
                if (opcode == Constants.MESSAGE_ACTIVE_SOURCE) {
                    int activePath = HdmiUtils.twoBytesToInt(params);
                    int physicalAddress = HdmiUtils.twoBytesToInt(params);
                    ActiveSourceHandler
                            .create((HdmiCecLocalDeviceTv) localDevice(), mCallback)
                            .process(cmd.getSource(), activePath);
                            .process(ActiveSource.of(cmd.getSource(), physicalAddress));
                    finish();
                    return true;
                }
+45 −11
Original line number Diff line number Diff line
@@ -49,9 +49,41 @@ abstract class HdmiCecLocalDevice {
    protected int mPreferredAddress;
    protected HdmiCecDeviceInfo mDeviceInfo;

    static class ActiveSource {
        int logicalAddress;
        int physicalAddress;

        public ActiveSource(int logical, int physical) {
            logicalAddress = logical;
            physicalAddress = physical;
        }
        public static ActiveSource of(int logical, int physical) {
            return new ActiveSource(logical, physical);
        }
        public boolean isValid() {
            return HdmiUtils.isValidAddress(logicalAddress);
        }
        public boolean equals(int logical, int physical) {
            return logicalAddress == logical && physicalAddress == physical;
        }
        @Override
        public boolean equals(Object obj) {
            if (obj instanceof ActiveSource) {
                ActiveSource that = (ActiveSource) obj;
                return that.logicalAddress == logicalAddress &&
                       that.physicalAddress == physicalAddress;
            }
            return false;
        }
        @Override
        public int hashCode() {
            return logicalAddress * 29 + physicalAddress;
        }
    }
    // Logical address of the active source.
    @GuardedBy("mLock")
    private int mActiveSource;
    protected final ActiveSource mActiveSource =
            new ActiveSource(-1, Constants.INVALID_PHYSICAL_ADDRESS);

    // Active routing path. Physical address of the active source but not all the time, such as
    // when the new active source does not claim itself to be one. Note that we don't keep
@@ -549,15 +581,24 @@ abstract class HdmiCecLocalDevice {
        return mService.isConnectedToArcPort(path);
    }

    int getActiveSource() {
    ActiveSource getActiveSource() {
        synchronized (mLock) {
            return mActiveSource;
        }
    }

    void setActiveSource(int source) {
    void setActiveSource(ActiveSource newActive) {
        setActiveSource(newActive.logicalAddress, newActive.physicalAddress);
    }

    void setActiveSource(HdmiCecDeviceInfo info) {
        setActiveSource(info.getLogicalAddress(), info.getPhysicalAddress());
    }

    void setActiveSource(int logicalAddress, int physicalAddress) {
        synchronized (mLock) {
            mActiveSource = source;
            mActiveSource.logicalAddress = logicalAddress;
            mActiveSource.physicalAddress = physicalAddress;
        }
    }

@@ -596,13 +637,6 @@ abstract class HdmiCecLocalDevice {
        }
    }

    void updateActiveDevice(int logicalAddress, int physicalAddress) {
        synchronized (mLock) {
            mActiveSource = logicalAddress;
            mActiveRoutingPath = physicalAddress;
        }
    }

    @ServiceThreadOnly
    HdmiCecMessageCache getCecMessageCache() {
        assertRunOnServiceThread();
+56 −36
Original line number Diff line number Diff line
@@ -147,12 +147,15 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
        if (targetAddress == Constants.ADDR_INTERNAL) {
            handleSelectInternalSource();
            // Switching to internal source is always successful even when CEC control is disabled.
            setActiveSource(targetAddress);
            setActiveSource(targetAddress, mService.getPhysicalAddress());
            invokeCallback(callback, HdmiControlManager.RESULT_SUCCESS);
            return;
        }
        if (!mService.isControlEnabled()) {
            setActiveSource(targetAddress);
            HdmiCecDeviceInfo info = getDeviceInfo(targetAddress);
            if (info != null) {
                setActiveSource(info);
            }
            invokeCallback(callback, HdmiControlManager.RESULT_INCORRECT_MODE);
            return;
        }
@@ -169,7 +172,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
    private void handleSelectInternalSource() {
        assertRunOnServiceThread();
        // Seq #18
        if (mService.isControlEnabled() && getActiveSource() != mAddress) {
        if (mService.isControlEnabled() && mActiveSource.logicalAddress != mAddress) {
            updateActiveSource(mAddress, mService.getPhysicalAddress());
            // TODO: Check if this comes from <Text/Image View On> - if true, do nothing.
            HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource(
@@ -179,16 +182,22 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
    }

    @ServiceThreadOnly
    void updateActiveSource(int activeSource, int activePath) {
    void updateActiveSource(int logicalAddress, int physicalAddress) {
        assertRunOnServiceThread();
        updateActiveSource(ActiveSource.of(logicalAddress, physicalAddress));
    }

    @ServiceThreadOnly
    void updateActiveSource(ActiveSource newActive) {
        assertRunOnServiceThread();
        // Seq #14
        if (activeSource == getActiveSource() && activePath == getActivePath()) {
        if (mActiveSource.equals(newActive)) {
            return;
        }
        setActiveSource(activeSource);
        setActivePath(activePath);
        if (getDeviceInfo(activeSource) != null && activeSource != mAddress) {
            if (mService.pathToPortId(activePath) == getActivePortId()) {
        setActiveSource(newActive);
        int logicalAddress = newActive.logicalAddress;
        if (getDeviceInfo(logicalAddress) != null && logicalAddress != mAddress) {
            if (mService.pathToPortId(newActive.physicalAddress) == getActivePortId()) {
                setPrevPortId(getActivePortId());
            }
            // TODO: Show the OSD banner related to the new active source device.
@@ -222,16 +231,26 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
    }

    @ServiceThreadOnly
    void updateActivePortId(int portId) {
    void updateActiveInput(int path, boolean notifyInputChange) {
        assertRunOnServiceThread();
        // Seq #15
        int portId = mService.pathToPortId(path);
        if (portId == getActivePortId()) {
            return;
        }
        setActivePath(path);
        setPrevPortId(portId);
        // TODO: Actually switch the physical port here. Handle PAP/PIP as well.
        // TODO: Handle PAP/PIP case.
        // Show OSD port change banner
        mService.invokeInputChangeListener(getActiveSource());
        if (notifyInputChange) {
            ActiveSource activeSource = getActiveSource();
            HdmiCecDeviceInfo info = getDeviceInfo(activeSource.logicalAddress);
            if (info == null) {
                info = new HdmiCecDeviceInfo(Constants.ADDR_INVALID, path, portId,
                        HdmiCecDeviceInfo.DEVICE_RESERVED, 0, null);
            }
            mService.invokeInputChangeListener(info);
        }
    }

    @ServiceThreadOnly
@@ -242,26 +261,25 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
            invokeCallback(callback, HdmiControlManager.RESULT_INCORRECT_MODE);
            return;
        }
        if (!mService.isControlEnabled()) {
            setActivePortId(portId);
            invokeCallback(callback, HdmiControlManager.RESULT_INCORRECT_MODE);
            return;
        }
        if (portId == getActivePortId()) {
            invokeCallback(callback, HdmiControlManager.RESULT_SUCCESS);
            return;
        }
        setActiveSource(Constants.ADDR_INVALID, Constants.INVALID_PHYSICAL_ADDRESS);
        if (!mService.isControlEnabled()) {
            setActivePortId(portId);
            invokeCallback(callback, HdmiControlManager.RESULT_INCORRECT_MODE);
            return;
        }
        // TODO: Return immediately if the operation is triggered by <Text/Image View On>
        // and this is the first notification about the active input after power-on
        // (switch to HDMI didn't happen so far but is expected to happen soon).
        removeAction(RoutingControlAction.class);

        int oldPath = mService.portIdToPath(mService.portIdToPath(getActivePortId()));
        int oldPath = mService.portIdToPath(getActivePortId());
        int newPath = mService.portIdToPath(portId);
        HdmiCecMessage routingChange =
                HdmiCecMessageBuilder.buildRoutingChange(mAddress, oldPath, newPath);
        mService.sendCecCommand(routingChange);
        removeAction(RoutingControlAction.class);
        addAndStartAction(new RoutingControlAction(this, newPath, false, callback));
    }

@@ -284,7 +302,8 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
            action.get(0).processKeyEvent(keyCode, isPressed);
        } else {
            if (isPressed) {
                addAndStartAction(new SendKeyAction(this, getActiveSource(), keyCode));
                int logicalAddress = getActiveSource().logicalAddress;
                addAndStartAction(new SendKeyAction(this, logicalAddress, keyCode));
            } else {
                Slog.w(TAG, "Discard key release event");
            }
@@ -306,12 +325,13 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
    @ServiceThreadOnly
    protected boolean handleActiveSource(HdmiCecMessage message) {
        assertRunOnServiceThread();
        int address = message.getSource();
        int path = HdmiUtils.twoBytesToInt(message.getParams());
        if (getDeviceInfo(address) == null) {
            handleNewDeviceAtTheTailOfActivePath(path);
        int logicalAddress = message.getSource();
        int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
        if (getDeviceInfo(logicalAddress) == null) {
            handleNewDeviceAtTheTailOfActivePath(physicalAddress);
        } else {
            ActiveSourceHandler.create(this, null).process(address, path);
            ActiveSource activeSource = ActiveSource.of(logicalAddress, physicalAddress);
            ActiveSourceHandler.create(this, null).process(activeSource);
        }
        return true;
    }
@@ -323,7 +343,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
        // Seq #10

        // Ignore <Inactive Source> from non-active source device.
        if (getActiveSource() != message.getSource()) {
        if (getActiveSource().logicalAddress != message.getSource()) {
            return true;
        }
        if (isProhibitMode()) {
@@ -342,7 +362,6 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
            }
            // TODO: Switch the TV freeze mode off

            setActivePortId(portId);
            doManualPortSwitching(portId, null);
            setPrevPortId(Constants.INVALID_PORT_ID);
        }
@@ -354,7 +373,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
    protected boolean handleRequestActiveSource(HdmiCecMessage message) {
        assertRunOnServiceThread();
        // Seq #19
        if (mAddress == getActiveSource()) {
        if (mAddress == getActiveSource().logicalAddress) {
            mService.sendCecCommand(
                    HdmiCecMessageBuilder.buildActiveSource(mAddress, getActivePath()));
        }
@@ -392,11 +411,11 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
        if (!isInDeviceList(path, address)) {
            handleNewDeviceAtTheTailOfActivePath(path);
        }
        startNewDeviceAction(address, path);
        startNewDeviceAction(ActiveSource.of(address, path));
        return true;
    }

    void startNewDeviceAction(int address, int path) {
    void startNewDeviceAction(ActiveSource activeSource) {
        for (NewDeviceAction action : getActions(NewDeviceAction.class)) {
            // If there is new device action which has the same logical address and path
            // ignore new request.
@@ -406,12 +425,13 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
            // in. However, TV can detect a new device from HotPlugDetectionAction,
            // which sends <Give Physical Address> to the source for newly detected
            // device.
            if (action.isActionOf(address, path)) {
            if (action.isActionOf(activeSource)) {
                return;
            }
        }

        addAndStartAction(new NewDeviceAction(this, address, path));
        addAndStartAction(new NewDeviceAction(this, activeSource.logicalAddress,
                activeSource.physicalAddress));
    }

    private void handleNewDeviceAtTheTailOfActivePath(int path) {
+10 −3
Original line number Diff line number Diff line
@@ -724,6 +724,10 @@ public final class HdmiControlService extends SystemService {
            runOnServiceThread(new Runnable() {
                @Override
                public void run() {
                    if (callback == null) {
                        Slog.e(TAG, "Callback cannot be null");
                        return;
                    }
                    HdmiCecLocalDeviceTv tv = tv();
                    if (tv == null) {
                        Slog.w(TAG, "Local tv device not available");
@@ -741,6 +745,10 @@ public final class HdmiControlService extends SystemService {
            runOnServiceThread(new Runnable() {
                @Override
                public void run() {
                    if (callback == null) {
                        Slog.e(TAG, "Callback cannot be null");
                        return;
                    }
                    HdmiCecLocalDeviceTv tv = tv();
                    if (tv == null) {
                        Slog.w(TAG, "Local tv device not available");
@@ -1218,12 +1226,11 @@ public final class HdmiControlService extends SystemService {
        }
    }

    void invokeInputChangeListener(int activeAddress) {
    void invokeInputChangeListener(HdmiCecDeviceInfo info) {
        synchronized (mLock) {
            if (mInputChangeListener != null) {
                HdmiCecDeviceInfo activeSource = getDeviceInfo(activeAddress);
                try {
                    mInputChangeListener.onChanged(activeSource);
                    mInputChangeListener.onChanged(info);
                } catch (RemoteException e) {
                    Slog.w(TAG, "Exception thrown by IHdmiInputChangeListener: " + e);
                }
Loading