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

Commit 92b77cf9 authored by Jinsuk Kim's avatar Jinsuk Kim
Browse files

Refactor handling sequences in HdmiControlService

- Rewrote some methods to match more closely to the handling
  sequences in guideline
- Added setControlEnabled() API in the aidl
- Handled <Routing Control> command
- Handled some scenarios invoking RoutingControlAction

Change-Id: I5db0c6fc775cef709c2e0f338b85937deebf2a54
parent 92222a00
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -48,4 +48,5 @@ interface IHdmiControlService {
    void setSystemAudioMode(boolean enabled, IHdmiControlCallback callback);
    void addSystemAudioModeChangeListener(IHdmiSystemAudioModeChangeListener listener);
    void removeSystemAudioModeChangeListener(IHdmiSystemAudioModeChangeListener listener);
    void setControlEnabled(boolean enabled);
}
+1 −3
Original line number Diff line number Diff line
@@ -67,9 +67,7 @@ final class ActiveSourceHandler {
        }
        HdmiCecDeviceInfo device = mService.getDeviceInfo(activeAddress);
        if (device == null) {
            // "New device action" initiated by <Active Source> does not require
            // "Routing change action".
            tv.addAndStartAction(new NewDeviceAction(tv, activeAddress, activePath, false));
            tv.addAndStartAction(new NewDeviceAction(tv, activeAddress, activePath));
        }

        int currentActive = tv.getActiveSource();
+6 −35
Original line number Diff line number Diff line
@@ -143,6 +143,8 @@ abstract class HdmiCecLocalDevice {
                return handleGetCecVersion(message);
            case HdmiCec.MESSAGE_REPORT_PHYSICAL_ADDRESS:
                return handleReportPhysicalAddress(message);
            case HdmiCec.MESSAGE_ROUTING_CHANGE:
                return handleRoutingChange(message);
            case HdmiCec.MESSAGE_INITIATE_ARC:
                return handleInitiateArc(message);
            case HdmiCec.MESSAGE_TERMINATE_ARC:
@@ -245,6 +247,10 @@ abstract class HdmiCecLocalDevice {
        return false;
    }

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

    protected boolean handleReportPhysicalAddress(HdmiCecMessage message) {
        return false;
    }
@@ -471,41 +477,6 @@ abstract class HdmiCecLocalDevice {
        }
    }

    boolean isHdmiControlEnabled() {
        synchronized (mLock) {
            return !mInputChangeEnabled;
        }
    }

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

    @ServiceThreadOnly
    HdmiCecMessageCache getCecMessageCache() {
        assertRunOnServiceThread();
+110 −18
Original line number Diff line number Diff line
@@ -121,7 +121,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
    private void handleSelectInternalSource(IHdmiControlCallback callback) {
        assertRunOnServiceThread();
        // Seq #18
        if (isHdmiControlEnabled() && getActiveSource() != mAddress) {
        if (mService.isControlEnabled() && getActiveSource() != mAddress) {
            updateActiveSource(mAddress, mService.getPhysicalAddress());
            // TODO: Check if this comes from <Text/Image View On> - if true, do nothing.
            HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource(
@@ -185,7 +185,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
    void doManualPortSwitching(int portId, IHdmiControlCallback callback) {
        assertRunOnServiceThread();
        // Seq #20
        if (!isHdmiControlEnabled() || portId == getActivePortId()) {
        if (!mService.isControlEnabled() || portId == getActivePortId()) {
            invokeCallback(callback, HdmiCec.RESULT_INCORRECT_MODE);
            return;
        }
@@ -243,8 +243,13 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
    @ServiceThreadOnly
    protected boolean handleActiveSource(HdmiCecMessage message) {
        assertRunOnServiceThread();
        int activePath = HdmiUtils.twoBytesToInt(message.getParams());
        ActiveSourceHandler.create(this, null).process(message.getSource(), activePath);
        int address = message.getSource();
        int path = HdmiUtils.twoBytesToInt(message.getParams());
        if (getDeviceInfo(address) == null) {
            handleNewDeviceAtTheTailOfActivePath(address, path);
        } else {
            ActiveSourceHandler.create(this, null).process(address, path);
        }
        return true;
    }

@@ -286,10 +291,9 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
    protected boolean handleRequestActiveSource(HdmiCecMessage message) {
        assertRunOnServiceThread();
        // Seq #19
        int address = getDeviceInfo().getLogicalAddress();
        if (address == getActiveSource()) {
        if (mAddress == getActiveSource()) {
            mService.sendCecCommand(
                    HdmiCecMessageBuilder.buildActiveSource(address, getActivePath()));
                    HdmiCecMessageBuilder.buildActiveSource(mAddress, getActivePath()));
        }
        return true;
    }
@@ -320,15 +324,70 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
            return true;
        }

        int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
        int logicalAddress = message.getSource();
        int path = HdmiUtils.twoBytesToInt(message.getParams());
        int address = message.getSource();
        if (!isInDeviceList(path, address)) {
            handleNewDeviceAtTheTailOfActivePath(address, path);
        }
        addAndStartAction(new NewDeviceAction(this, address, path));
        return true;
    }

    private void handleNewDeviceAtTheTailOfActivePath(int address, int path) {
        // Seq #22
        if (isTailOfActivePath(path, getActivePath())) {
            removeAction(RoutingControlAction.class);
            int newPath = mService.portIdToPath(getActivePortId());
            mService.sendCecCommand(HdmiCecMessageBuilder.buildRoutingChange(
                    mAddress, getActivePath(), newPath));
            addAndStartAction(new RoutingControlAction(this, getActivePortId(), null));
        }
    }

        // If it is a new device and connected to the tail of active path,
        // it's required to change routing path.
        boolean requireRoutingChange = !isInDeviceList(physicalAddress, logicalAddress)
                && isTailOfActivePath(physicalAddress);
        addAndStartAction(new NewDeviceAction(this, message.getSource(), physicalAddress,
                requireRoutingChange));
    /**
     * 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
    protected boolean handleRoutingChange(HdmiCecMessage message) {
        assertRunOnServiceThread();
        // Seq #21
        byte[] params = message.getParams();
        if (params.length != 4) {
            Slog.w(TAG, "Wrong parameter: " + message);
            return true;
        }
        int currentPath = HdmiUtils.twoBytesToInt(params);
        if (HdmiUtils.isAffectingActiveRoutingPath(getActivePath(), currentPath)) {
            int newPath = HdmiUtils.twoBytesToInt(params, 2);
            setActivePath(newPath);
            removeAction(RoutingControlAction.class);
            addAndStartAction(new RoutingControlAction(this, newPath, null));
        }
        return true;
    }

@@ -776,10 +835,43 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
    final void removeCecDevice(int address) {
        assertRunOnServiceThread();
        HdmiCecDeviceInfo info = removeDeviceInfo(address);
        handleRemoveActiveRoutingPath(info.getPhysicalAddress());
        mCecMessageCache.flushMessagesFrom(address);
        mService.invokeDeviceEventListeners(info, false);
    }

    private void handleRemoveActiveRoutingPath(int path) {
        // Seq #23
        if (isTailOfActivePath(path, getActivePath())) {
            removeAction(RoutingControlAction.class);
            int newPath = mService.portIdToPath(getActivePortId());
            mService.sendCecCommand(HdmiCecMessageBuilder.buildRoutingChange(
                    mAddress, getActivePath(), newPath));
            addAndStartAction(new RoutingControlAction(this, getActivePortId(), null));
        }
    }

    @ServiceThreadOnly
    void routingAtEnableTime() {
        assertRunOnServiceThread();
        // Seq #24
        if (getActivePortId() != HdmiConstants.INVALID_PORT_ID) {
            // TODO: Check if TV was not powered on due to <Text/Image View On>,
            //       TV is not in Preset Installation mode, not in initial setup mode, not
            //       in Software updating mode, not in service mode, for following actions.
            removeAction(RoutingControlAction.class);
            int newPath = mService.portIdToPath(getActivePortId());
            mService.sendCecCommand(
                    HdmiCecMessageBuilder.buildRoutingChange(mAddress, getActivePath(), newPath));
            addAndStartAction(new RoutingControlAction(this, getActivePortId(), null));
        } else {
            int activePath = mService.getPhysicalAddress();
            setActivePath(activePath);
            // TODO: Do following only when TV was not powered on due to <Text/Image View On>.
            mService.sendCecCommand(HdmiCecMessageBuilder.buildActiveSource(mAddress, activePath));
        }
    }

    /**
     * Returns the {@link HdmiCecDeviceInfo} instance whose physical address matches
     * the given routing path. CEC devices use routing path for its physical address to
@@ -804,12 +896,12 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
     * in a device info list. However, both are minimal condition and it could
     * be different device from the original one.
     *
     * @param physicalAddress physical address of a device to be searched
     * @param logicalAddress logical address of a device to be searched
     * @param physicalAddress physical address of a device to be searched
     * @return true if exist; otherwise false
     */
    @ServiceThreadOnly
    boolean isInDeviceList(int physicalAddress, int logicalAddress) {
    boolean isInDeviceList(int logicalAddress, int physicalAddress) {
        assertRunOnServiceThread();
        HdmiCecDeviceInfo device = getDeviceInfo(logicalAddress);
        if (device == null) {
@@ -820,7 +912,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {

    @Override
    @ServiceThreadOnly
    void onHotplug(int portNo, boolean connected) {
    void onHotplug(int portId, boolean connected) {
        assertRunOnServiceThread();

        // Tv device will have permanent HotplugDetectionAction.
+35 −0
Original line number Diff line number Diff line
@@ -116,6 +116,11 @@ public final class HdmiControlService extends SystemService {
    private final ArrayList<DeviceEventListenerRecord> mDeviceEventListenerRecords =
            new ArrayList<>();

    // Set to true while HDMI control is enabled. If set to false, HDMI-CEC/MHL protocol
    // handling will be disabled and no request will be handled.
    @GuardedBy("mLock")
    private boolean mHdmiControlEnabled;

    // List of listeners registered by callers that want to get notified of
    // system audio mode changes.
    private final ArrayList<IHdmiSystemAudioModeChangeListener>
@@ -163,6 +168,7 @@ public final class HdmiControlService extends SystemService {

        // TODO: Read the preference for SystemAudioMode and initialize mSystemAudioMode and
        // start to monitor the preference value and invoke SystemAudioActionFromTv if needed.
        mHdmiControlEnabled = true;
    }

    @ServiceThreadOnly
@@ -716,6 +722,29 @@ public final class HdmiControlService extends SystemService {
            enforceAccessPermission();
            HdmiControlService.this.removeSystemAudioModeChangeListener(listener);
        }

        @Override
        public void setControlEnabled(boolean enabled) {
            enforceAccessPermission();
            synchronized (mLock) {
                mHdmiControlEnabled = enabled;
            }
            // TODO: Stop the running actions when disabled, and start
            //       address allocation/device discovery when enabled.
            if (!enabled) {
                return;
            }
            runOnServiceThread(new Runnable() {
                @Override
                public void run() {
                    HdmiCecLocalDeviceTv tv = tv();
                    if (tv == null) {
                        return;
                    }
                    tv.routingAtEnableTime();
                }
            });
        }
    }

    @ServiceThreadOnly
@@ -875,4 +904,10 @@ public final class HdmiControlService extends SystemService {
    AudioManager getAudioManager() {
        return (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
    }

    boolean isControlEnabled() {
        synchronized (mLock) {
            return mHdmiControlEnabled;
        }
    }
}
Loading