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

Commit a09489c3 authored by Jinsuk Kim's avatar Jinsuk Kim Committed by Android (Google) Code Review
Browse files

Merge "Handle incoming active source-related commands for HdmiControlService"

parents 1f2b57db 8333571b
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -101,6 +101,9 @@ public final class HdmiCec {
    /** Logical address used to indicate it is not initialized or invalid. */
    public static final int ADDR_INVALID = -1;

    /** Logical address used to indicate the source comes from internal device. */
    public static final int ADDR_INTERNAL = 0xFFFF;

    // TODO: Complete the list of CEC messages definition.
    public static final int MESSAGE_FEATURE_ABORT = 0x00;
    public static final int MESSAGE_IMAGE_VIEW_ON = 0x04;
+26 −28
Original line number Diff line number Diff line
@@ -27,18 +27,18 @@ import android.util.Slog;
/**
 * Handles CEC command <Active Source>.
 * <p>
 * Used by feature actions that need to handle the command in their flow.
 * Used by feature actions that need to handle the command in their flow. Only for TV
 * local device.
 */
final class ActiveSourceHandler {
    private static final String TAG = "ActiveSourceHandler";

    private final HdmiCecLocalDevice mSource;
    private final HdmiCecLocalDeviceTv mSource;
    private final HdmiControlService mService;
    @Nullable
    private final IHdmiControlCallback mCallback;

    static ActiveSourceHandler create(HdmiCecLocalDevice source,
            IHdmiControlCallback callback) {
    static ActiveSourceHandler create(HdmiCecLocalDeviceTv source, IHdmiControlCallback callback) {
        if (source == null) {
            Slog.e(TAG, "Wrong arguments");
            return null;
@@ -46,7 +46,7 @@ final class ActiveSourceHandler {
        return new ActiveSourceHandler(source, callback);
    }

    private ActiveSourceHandler(HdmiCecLocalDevice source, IHdmiControlCallback callback) {
    private ActiveSourceHandler(HdmiCecLocalDeviceTv source, IHdmiControlCallback callback) {
        mSource = source;
        mService = mSource.getService();
        mCallback = callback;
@@ -55,48 +55,46 @@ final class ActiveSourceHandler {
    /**
     * Handles the incoming active source command.
     *
     * @param deviceLogicalAddress logical address of the device to be the active source
     * @param routingPath routing path of the device to be the active source
     * @param activeAddress logical address of the device to be the active source
     * @param activePath routing path of the device to be the active source
     */
    void process(int deviceLogicalAddress, int routingPath) {
        if (getSourcePath() == routingPath && mSource.getActiveSource() == getSourceAddress()) {
    void process(int activeAddress, int activePath) {
        // Seq #17
        HdmiCecLocalDeviceTv tv = mSource;
        if (getSourcePath() == activePath && tv.getActiveSource() == getSourceAddress()) {
            invokeCallback(HdmiCec.RESULT_SUCCESS);
            return;
        }
        HdmiCecDeviceInfo device = mService.getDeviceInfo(deviceLogicalAddress);
        HdmiCecDeviceInfo device = mService.getDeviceInfo(activeAddress);
        if (device == null) {
            // "New device action" initiated by <Active Source> does not require
            // "Routing change action".
            mSource.addAndStartAction(new NewDeviceAction(mSource, deviceLogicalAddress,
                    routingPath, false));
            tv.addAndStartAction(new NewDeviceAction(tv, activeAddress, activePath, false));
        }

        if (!mSource.isInPresetInstallationMode()) {
            int prevActiveInput = mSource.getActivePortId();
            mSource.updateActiveDevice(deviceLogicalAddress, routingPath);
            if (prevActiveInput != mSource.getActivePortId()) {
                // TODO: change port input here.
        int currentActive = tv.getActiveSource();
        int currentPath = tv.getActivePath();
        if (!tv.isInPresetInstallationMode()) {
            tv.updateActiveSource(activeAddress, activePath);
            if (currentActive != activeAddress && currentPath != activePath) {
                tv.updateActivePortId(mService.pathToPortId(activePath));
            }
            invokeCallback(HdmiCec.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 (mSource.getActiveSource() == getSourceAddress()) {
            if (currentActive == getSourceAddress()) {
                HdmiCecMessage activeSource =
                        HdmiCecMessageBuilder.buildActiveSource(getSourceAddress(),
                                getSourcePath());
                        HdmiCecMessageBuilder.buildActiveSource(currentActive, currentPath);
                mService.sendCecCommand(activeSource);
                mSource.updateActiveDevice(deviceLogicalAddress, routingPath);
                tv.updateActiveSource(currentActive, currentPath);
                invokeCallback(HdmiCec.RESULT_SUCCESS);
            } else {
                int activePath = mSource.getActivePath();
                mService.sendCecCommand(HdmiCecMessageBuilder.buildRoutingChange(getSourceAddress(),
                        routingPath, activePath));
                // TODO: Start port select action here
                // PortSelectAction action = new PortSelectAction(mService, getSourceAddress(),
                // activePath, mCallback);
                // mService.addActionAndStart(action);
                HdmiCecMessage routingChange = HdmiCecMessageBuilder.buildRoutingChange(
                        getSourceAddress(), activePath, currentPath);
                mService.sendCecCommand(routingChange);
                tv.addAndStartAction(new RoutingControlAction(tv, currentPath, mCallback));
            }
        }
    }
+2 −2
Original line number Diff line number Diff line
@@ -77,7 +77,7 @@ final class DeviceSelectAction extends FeatureAction {
     * @param target target logical device that will be a new active source
     * @param callback callback object
     */
    public DeviceSelectAction(HdmiCecLocalDevice source,
    public DeviceSelectAction(HdmiCecLocalDeviceTv source,
            HdmiCecDeviceInfo target, IHdmiControlCallback callback) {
        super(source);
        mCallback = callback;
@@ -116,7 +116,7 @@ final class DeviceSelectAction extends FeatureAction {
                if (opcode == HdmiCec.MESSAGE_ACTIVE_SOURCE && params.length == 2) {
                    int activePath = HdmiUtils.twoBytesToInt(params);
                    ActiveSourceHandler
                            .create(localDevice(), mCallback)
                            .create((HdmiCecLocalDeviceTv) localDevice(), mCallback)
                            .process(cmd.getSource(), activePath);
                    finish();
                    return true;
+40 −3
Original line number Diff line number Diff line
@@ -125,6 +125,12 @@ abstract class HdmiCecLocalDevice {
            return true;
        }
        switch (message.getOpcode()) {
            case HdmiCec.MESSAGE_ACTIVE_SOURCE:
                return handleActiveSource(message);
            case HdmiCec.MESSAGE_INACTIVE_SOURCE:
                return handleInactiveSource(message);
            case HdmiCec.MESSAGE_REQUEST_ACTIVE_SOURCE:
                return handleRequestActiveSource(message);
            case HdmiCec.MESSAGE_GET_MENU_LANGUAGE:
                return handleGetMenuLanguage(message);
            case HdmiCec.MESSAGE_GIVE_PHYSICAL_ADDRESS:
@@ -192,6 +198,21 @@ abstract class HdmiCecLocalDevice {
        return true;
    }

    @ServiceThreadOnly
    protected boolean handleActiveSource(HdmiCecMessage message) {
        return false;
    }

    @ServiceThreadOnly
    protected boolean handleInactiveSource(HdmiCecMessage message) {
        return false;
    }

    @ServiceThreadOnly
    protected boolean handleRequestActiveSource(HdmiCecMessage message) {
        return false;
    }

    @ServiceThreadOnly
    protected boolean handleGetMenuLanguage(HdmiCecMessage message) {
        assertRunOnServiceThread();
@@ -383,15 +404,24 @@ abstract class HdmiCecLocalDevice {
        }
    }

    /**
     * Returns the active routing path.
     */
    void setActiveSource(int source) {
        synchronized (mLock) {
            mActiveSource = source;
        }
    }

    int getActivePath() {
        synchronized (mLock) {
            return mActiveRoutingPath;
        }
    }

    void setActivePath(int path) {
        synchronized (mLock) {
            mActiveRoutingPath = path;
        }
    }

    /**
     * Returns the ID of the active HDMI port. The active port is the one that has the active
     * routing path connected to it directly or indirectly under the device hierarchy.
@@ -429,6 +459,13 @@ abstract class HdmiCecLocalDevice {
    }

    boolean isInPresetInstallationMode() {
        // TODO: Change this to check the right flag.
        synchronized (mLock) {
            return !mInputChangeEnabled;
        }
    }

    boolean isHdmiControlEnabled() {
        synchronized (mLock) {
            return !mInputChangeEnabled;
        }
+136 −14
Original line number Diff line number Diff line
@@ -48,6 +48,14 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
    @GuardedBy("mLock")
    private boolean mSystemAudioMode;

    // The previous port id (input) before switching to the new one. This is remembered in order to
    // be able to switch to it upon receiving <Inactive Source> from currently active source.
    // This remains valid only when the active source was switched via one touch play operation
    // (either by TV or source device). Manual port switching invalidates this value to
    // HdmiConstants.PORT_INVALID, for which case <Inactive Source> does not do anything.
    @GuardedBy("mLock")
    private int mPrevPortId;

    // Copy of mDeviceInfos to guarantee thread-safety.
    @GuardedBy("mLock")
    private List<HdmiCecDeviceInfo> mSafeAllDeviceInfos = Collections.emptyList();
@@ -62,7 +70,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {

    HdmiCecLocalDeviceTv(HdmiControlService service) {
        super(service, HdmiCec.DEVICE_TV);

        mPrevPortId = HdmiConstants.INVALID_PORT_ID;
        // TODO: load system audio mode and set it to mSystemAudioMode.
    }

@@ -90,6 +98,10 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
    @ServiceThreadOnly
    void deviceSelect(int targetAddress, IHdmiControlCallback callback) {
        assertRunOnServiceThread();
        if (targetAddress == HdmiCec.ADDR_INTERNAL) {
            handleSelectInternalSource(callback);
            return;
        }
        HdmiCecDeviceInfo targetDevice = getDeviceInfo(targetAddress);
        if (targetDevice == null) {
            invokeCallback(callback, HdmiCec.RESULT_TARGET_NOT_AVAILABLE);
@@ -99,28 +111,83 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
        addAndStartAction(new DeviceSelectAction(this, targetDevice, callback));
    }

    @ServiceThreadOnly
    private void handleSelectInternalSource(IHdmiControlCallback callback) {
        assertRunOnServiceThread();
        // Seq #18
        if (isHdmiControlEnabled() && getActiveSource() != mAddress) {
            updateActiveSource(mAddress, mService.getPhysicalAddress());
            // TODO: Check if this comes from <Text/Image View On> - if true, do nothing.
            HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource(
                    mAddress, mService.getPhysicalAddress());
            mService.sendCecCommand(activeSource);
        }
    }

    @ServiceThreadOnly
    void updateActiveSource(int activeSource, int activePath) {
        assertRunOnServiceThread();
        // Seq #14
        if (activeSource == getActiveSource() && activePath == getActivePath()) {
            return;
        }
        setActiveSource(activeSource);
        setActivePath(activePath);
        if (getDeviceInfo(activeSource) != null && activeSource != mAddress) {
            if (mService.pathToPortId(activePath) == getActivePortId()) {
                setPrevPortId(getActivePortId());
            }
            // TODO: Show the OSD banner related to the new active source device.
        } else {
            // TODO: If displayed, remove the OSD banner related to the previous
            //       active source device.
        }
    }

    /**
     * Performs the action routing control.
     *
     * @param portId new HDMI port to route to
     * @param callback callback object to report the result with
     * Returns the previous port id kept to handle input switching on <Inactive Source>.
     */
    int getPrevPortId() {
        synchronized (mLock) {
            return mPrevPortId;
        }
    }

    /**
     * Sets the previous port id. INVALID_PORT_ID invalidates it, hence no actions will be
     * taken for <Inactive Source>.
     */
    void setPrevPortId(int portId) {
        synchronized (mLock) {
            mPrevPortId = portId;
        }
    }

    @ServiceThreadOnly
    void portSelect(int portId, IHdmiControlCallback callback) {
    void updateActivePortId(int portId) {
        assertRunOnServiceThread();
        if (isInPresetInstallationMode()) {
            invokeCallback(callback, HdmiCec.RESULT_INCORRECT_MODE);
        // Seq #15
        if (portId == getActivePortId()) {
            return;
        }
        // Make sure this call does not stem from <Active Source> message reception, in
        // which case the two ports will be the same.
        if (portId == getActivePortId()) {
            invokeCallback(callback, HdmiCec.RESULT_SUCCESS);
        setPrevPortId(portId);
        // TODO: Actually switch the physical port here. Handle PAP/PIP as well.
        //       Show OSD port change banner
    }

    @ServiceThreadOnly
    void doManualPortSwitching(int portId, IHdmiControlCallback callback) {
        assertRunOnServiceThread();
        // Seq #20
        if (!isHdmiControlEnabled() || portId == getActivePortId()) {
            invokeCallback(callback, HdmiCec.RESULT_INCORRECT_MODE);
            return;
        }
        setActivePortId(portId);
        // TODO: Make sure this call does not stem from <Active Source> message reception.

        setActivePortId(portId);
        // 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.
        // TODO: Handle invalid port id / active input which should be treated as an
        //       internal tuner.

@@ -166,6 +233,61 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
        }
    }

    @Override
    @ServiceThreadOnly
    protected boolean handleActiveSource(HdmiCecMessage message) {
        assertRunOnServiceThread();
        int activePath = HdmiUtils.twoBytesToInt(message.getParams());
        ActiveSourceHandler.create(this, null).process(message.getSource(), activePath);
        return true;
    }

    @Override
    @ServiceThreadOnly
    protected boolean handleInactiveSource(HdmiCecMessage message) {
        assertRunOnServiceThread();
        // Seq #10

        // Ignore <Inactive Source> from non-active source device.
        if (getActiveSource() != message.getSource()) {
            return true;
        }
        if (isInPresetInstallationMode()) {
            return true;
        }
        int portId = getPrevPortId();
        if (portId != HdmiConstants.INVALID_PORT_ID) {
            // TODO: Do this only if TV is not showing multiview like PIP/PAP.

            HdmiCecDeviceInfo inactiveSource = getDeviceInfo(message.getSource());
            if (inactiveSource == null) {
                return true;
            }
            if (mService.pathToPortId(inactiveSource.getPhysicalAddress()) == portId) {
                return true;
            }
            // TODO: Switch the TV freeze mode off

            setActivePortId(portId);
            doManualPortSwitching(portId, null);
            setPrevPortId(HdmiConstants.INVALID_PORT_ID);
        }
        return true;
    }

    @Override
    @ServiceThreadOnly
    protected boolean handleRequestActiveSource(HdmiCecMessage message) {
        assertRunOnServiceThread();
        // Seq #19
        int address = getDeviceInfo().getLogicalAddress();
        if (address == getActiveSource()) {
            mService.sendCecCommand(
                    HdmiCecMessageBuilder.buildActiveSource(address, getActivePath()));
        }
        return true;
    }

    @Override
    @ServiceThreadOnly
    protected boolean handleGetMenuLanguage(HdmiCecMessage message) {
Loading