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

Commit 79c58a4b authored by Jungshik Jang's avatar Jungshik Jang
Browse files

Rearrange ownership between Hdmi control modules.

Here is a list of changes on this. (R: rationale)
1. HdmiCecLocalDeviceTv takes over responsibilty of device info
management.
 R: All devices infos are added or removed by only Tv's
device discovery and hot plug detection mecanism

2. Each HdmiCecLocalDevice manages FeatureAction and Cec
 message cache.
 R: There is no direct connection between actions that
 are created in different device action. If there is
 an same actions created from different local device,
 they should be managed independently.

3. Active path and logical address is managed by
   HdmiCecLocalDevice.
 R: All device should know active path of current source.

4. All system audio & ARC features are handled by
   HdmiCecLocalDeviceTv
 R: In terms of ARC, theoretically, any device can be transmiter of
   ARC but TV is the de facto device
   On other hands, for system audio Tv is the recipeint
    of request.

Change-Id: Iac9ff43fb41798ed4f94c61d23345fe5fe777fbb
parent 7237cd81
Loading
Loading
Loading
Loading
+35 −28
Original line number Diff line number Diff line
@@ -26,31 +26,29 @@ import android.util.Slog;

/**
 * Handles CEC command <Active Source>.
 *
 * <p>Used by feature actions that need to handle the command in their flow.
 * <p>
 * Used by feature actions that need to handle the command in their flow.
 */
final class ActiveSourceHandler {
    private static final String TAG = "ActiveSourceHandler";

    private final HdmiCecLocalDevice mSource;
    private final HdmiControlService mService;
    private final int mSourceAddress;
    private final int mSourcePath;
    @Nullable private final IHdmiControlCallback mCallback;
    @Nullable
    private final IHdmiControlCallback mCallback;

    static ActiveSourceHandler create(HdmiControlService service, int sourceAddress,
            int sourcePath, IHdmiControlCallback callback) {
        if (service == null) {
    static ActiveSourceHandler create(HdmiCecLocalDevice source,
            IHdmiControlCallback callback) {
        if (source == null) {
            Slog.e(TAG, "Wrong arguments");
            return null;
        }
        return new ActiveSourceHandler(service, sourceAddress, sourcePath, callback);
        return new ActiveSourceHandler(source, callback);
    }

    private ActiveSourceHandler(HdmiControlService service, int sourceAddress, int sourcePath,
            IHdmiControlCallback callback) {
        mService = service;
        mSourceAddress = sourceAddress;
        mSourcePath = sourcePath;
    private ActiveSourceHandler(HdmiCecLocalDevice source, IHdmiControlCallback callback) {
        mSource = source;
        mService = mSource.getService();
        mCallback = callback;
    }

@@ -61,7 +59,7 @@ final class ActiveSourceHandler {
     * @param routingPath routing path of the device to be the active source
     */
    void process(int deviceLogicalAddress, int routingPath) {
        if (mSourcePath == routingPath && mService.getActiveSource() == mSourceAddress) {
        if (getSourcePath() == routingPath && mSource.getActiveSource() == getSourceAddress()) {
            invokeCallback(HdmiCec.RESULT_SUCCESS);
            return;
        }
@@ -69,14 +67,14 @@ final class ActiveSourceHandler {
        if (device == null) {
            // "New device action" initiated by <Active Source> does not require
            // "Routing change action".
            mService.addAndStartAction(new NewDeviceAction(mService, mSourceAddress,
                    deviceLogicalAddress, routingPath, false));
            mSource.addAndStartAction(new NewDeviceAction(mSource, deviceLogicalAddress,
                    routingPath, false));
        }

        if (!mService.isInPresetInstallationMode()) {
            int prevActiveInput = mService.getActiveInput();
            mService.updateActiveDevice(deviceLogicalAddress, routingPath);
            if (prevActiveInput != mService.getActiveInput()) {
        if (!mSource.isInPresetInstallationMode()) {
            int prevActiveInput = mSource.getActiveInput();
            mSource.updateActiveDevice(deviceLogicalAddress, routingPath);
            if (prevActiveInput != mSource.getActiveInput()) {
                // TODO: change port input here.
            }
            invokeCallback(HdmiCec.RESULT_SUCCESS);
@@ -84,24 +82,33 @@ final class ActiveSourceHandler {
            // 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 (mService.getActiveSource() == mSourceAddress) {
            if (mSource.getActiveSource() == getSourceAddress()) {
                HdmiCecMessage activeSource =
                        HdmiCecMessageBuilder.buildActiveSource(mSourceAddress, mSourcePath);
                        HdmiCecMessageBuilder.buildActiveSource(getSourceAddress(),
                                getSourcePath());
                mService.sendCecCommand(activeSource);
                mService.updateActiveDevice(deviceLogicalAddress, routingPath);
                mSource.updateActiveDevice(deviceLogicalAddress, routingPath);
                invokeCallback(HdmiCec.RESULT_SUCCESS);
            } else {
                int activePath = mService.getActivePath();
                mService.sendCecCommand(HdmiCecMessageBuilder.buildRoutingChange(mSourceAddress,
                int activePath = mSource.getActivePath();
                mService.sendCecCommand(HdmiCecMessageBuilder.buildRoutingChange(getSourceAddress(),
                        routingPath, activePath));
                // TODO: Start port select action here
                // PortSelectAction action = new PortSelectAction(mService, mSourceAddress,
                // PortSelectAction action = new PortSelectAction(mService, getSourceAddress(),
                // activePath, mCallback);
                // mService.addActionAndStart(action);
            }
        }
    }

    private final int getSourceAddress() {
        return mSource.getDeviceInfo().getLogicalAddress();
    }

    private final int getSourcePath() {
        return mSource.getDeviceInfo().getPhysicalAddress();
    }

    private void invokeCallback(int result) {
        if (mCallback == null) {
            return;
+9 −10
Original line number Diff line number Diff line
@@ -94,12 +94,10 @@ final class DeviceDiscoveryAction extends FeatureAction {
    /**
     * Constructor.
     *
     * @param service an instance of {@link HdmiControlService}.
     * @param sourceAddress a logical address which initiates this action
     * @param source an instance of {@link HdmiCecLocalDevice}.
     */
    DeviceDiscoveryAction(HdmiControlService service, int sourceAddress,
            DeviceDiscoveryCallback callback) {
        super(service, sourceAddress);
    DeviceDiscoveryAction(HdmiCecLocalDevice source, DeviceDiscoveryCallback callback) {
        super(source);
        mCallback = Preconditions.checkNotNull(callback);
    }

@@ -108,7 +106,7 @@ final class DeviceDiscoveryAction extends FeatureAction {
        mDevices.clear();
        mState = STATE_WAITING_FOR_DEVICE_POLLING;

        mService.pollDevices(new DevicePollingCallback() {
        pollDevices(new DevicePollingCallback() {
            @Override
            public void onPollingFinished(List<Integer> ackedAddress) {
                if (ackedAddress.isEmpty()) {
@@ -156,7 +154,7 @@ final class DeviceDiscoveryAction extends FeatureAction {
        if (mayProcessMessageIfCached(address, HdmiCec.MESSAGE_REPORT_PHYSICAL_ADDRESS)) {
            return;
        }
        sendCommand(HdmiCecMessageBuilder.buildGivePhysicalAddress(mSourceAddress, address));
        sendCommand(HdmiCecMessageBuilder.buildGivePhysicalAddress(getSourceAddress(), address));
        addTimer(mState, TIMEOUT_MS);
    }

@@ -179,7 +177,7 @@ final class DeviceDiscoveryAction extends FeatureAction {
        if (mayProcessMessageIfCached(address, HdmiCec.MESSAGE_SET_OSD_NAME)) {
            return;
        }
        sendCommand(HdmiCecMessageBuilder.buildGiveOsdNameCommand(mSourceAddress, address));
        sendCommand(HdmiCecMessageBuilder.buildGiveOsdNameCommand(getSourceAddress(), address));
        addTimer(mState, TIMEOUT_MS);
    }

@@ -203,12 +201,13 @@ final class DeviceDiscoveryAction extends FeatureAction {
        if (mayProcessMessageIfCached(address, HdmiCec.MESSAGE_DEVICE_VENDOR_ID)) {
            return;
        }
        sendCommand(HdmiCecMessageBuilder.buildGiveDeviceVendorIdCommand(mSourceAddress, address));
        sendCommand(
                HdmiCecMessageBuilder.buildGiveDeviceVendorIdCommand(getSourceAddress(), address));
        addTimer(mState, TIMEOUT_MS);
    }

    private boolean mayProcessMessageIfCached(int address, int opcode) {
        HdmiCecMessage message = mService.getCecMessageCache().getMessage(address, opcode);
        HdmiCecMessage message = getCecMessageCache().getMessage(address, opcode);
        if (message != null) {
            processCommand(message);
            return true;
+9 −8
Original line number Diff line number Diff line
@@ -16,9 +16,10 @@ package com.android.server.hdmi;
 * limitations under the License.
 */

import android.hardware.hdmi.IHdmiControlCallback;
import android.hardware.hdmi.HdmiCec;
import android.hardware.hdmi.HdmiCecMessage;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.IHdmiControlCallback;
import android.os.RemoteException;
import android.util.Slog;

@@ -40,18 +41,18 @@ final class DevicePowerStatusAction extends FeatureAction {
    private final int mTargetAddress;
    private final IHdmiControlCallback mCallback;

    static DevicePowerStatusAction create(HdmiControlService service, int sourceAddress,
    static DevicePowerStatusAction create(HdmiCecLocalDevice source,
            int targetAddress, IHdmiControlCallback callback) {
        if (service == null || callback == null) {
        if (source == null || callback == null) {
            Slog.e(TAG, "Wrong arguments");
            return null;
        }
        return new DevicePowerStatusAction(service, sourceAddress, targetAddress, callback);
        return new DevicePowerStatusAction(source, targetAddress, callback);
    }

    private DevicePowerStatusAction(HdmiControlService service, int sourceAddress,
    private DevicePowerStatusAction(HdmiCecLocalDevice localDevice,
            int targetAddress, IHdmiControlCallback callback) {
        super(service, sourceAddress);
        super(localDevice);
        mTargetAddress = targetAddress;
        mCallback = callback;
    }
@@ -65,8 +66,8 @@ final class DevicePowerStatusAction extends FeatureAction {
    }

    private void queryDevicePowerStatus() {
        mService.sendCecCommand(
                HdmiCecMessageBuilder.buildGiveDevicePowerStatus(mSourceAddress, mTargetAddress));
        sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(getSourceAddress(),
                mTargetAddress));
    }

    @Override
+12 −14
Original line number Diff line number Diff line
@@ -16,10 +16,11 @@

package com.android.server.hdmi;

import android.hardware.hdmi.IHdmiControlCallback;
import android.hardware.hdmi.HdmiCecDeviceInfo;
import android.hardware.hdmi.HdmiCec;
import android.hardware.hdmi.HdmiCecDeviceInfo;
import android.hardware.hdmi.HdmiCecMessage;
import android.hardware.hdmi.HdmiTvClient;
import android.hardware.hdmi.IHdmiControlCallback;
import android.os.RemoteException;
import android.util.Slog;

@@ -66,24 +67,20 @@ final class DeviceSelectAction extends FeatureAction {

    private final HdmiCecDeviceInfo mTarget;
    private final IHdmiControlCallback mCallback;
    private final int mSourcePath;

    private int mPowerStatusCounter = 0;

    /**
     * Constructor.
     *
     * @param service {@link HdmiControlService} instance
     * @param sourceAddress logical address of TV initiating this action
     * @param sourcePath physical address of TV
     * @param source {@link HdmiCecLocalDevice} instance
     * @param target target logical device that will be a new active source
     * @param callback callback object
     */
    public DeviceSelectAction(HdmiControlService service, int sourceAddress, int sourcePath,
    public DeviceSelectAction(HdmiCecLocalDevice source,
            HdmiCecDeviceInfo target, IHdmiControlCallback callback) {
        super(service, sourceAddress);
        super(source);
        mCallback = callback;
        mSourcePath = sourcePath;
        mTarget = target;
    }

@@ -96,7 +93,7 @@ final class DeviceSelectAction extends FeatureAction {

    private void queryDevicePowerStatus() {
        sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
                mSourceAddress, mTarget.getLogicalAddress()));
                getSourceAddress(), mTarget.getLogicalAddress()));
        mState = STATE_WAIT_FOR_REPORT_POWER_STATUS;
        addTimer(mState, TIMEOUT_MS);
    }
@@ -118,7 +115,8 @@ final class DeviceSelectAction extends FeatureAction {
            case STATE_WAIT_FOR_ACTIVE_SOURCE:
                if (opcode == HdmiCec.MESSAGE_ACTIVE_SOURCE && params.length == 2) {
                    int activePath = HdmiUtils.twoBytesToInt(params);
                    ActiveSourceHandler.create(mService, mSourceAddress, mSourcePath, mCallback)
                    ActiveSourceHandler
                            .create(localDevice(), mCallback)
                            .process(cmd.getSource(), activePath);
                    finish();
                    return true;
@@ -174,15 +172,15 @@ final class DeviceSelectAction extends FeatureAction {

    private void sendSetStreamPath() {
        sendCommand(HdmiCecMessageBuilder.buildSetStreamPath(
                mSourceAddress, mTarget.getPhysicalAddress()));
                getSourceAddress(), mTarget.getPhysicalAddress()));
        mState = STATE_WAIT_FOR_ACTIVE_SOURCE;
        addTimer(mState, TIMEOUT_ACTIVE_SOURCE_MS);
    }

    private void sendRemoteKeyCommand(int keyCode) {
        sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(mSourceAddress,
        sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(getSourceAddress(),
                mTarget.getLogicalAddress(), keyCode));
        sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(mSourceAddress,
        sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(getSourceAddress(),
                mTarget.getLogicalAddress()));
    }

+64 −19
Original line number Diff line number Diff line
@@ -22,6 +22,9 @@ import android.os.Message;
import android.util.Slog;

import com.android.internal.annotations.VisibleForTesting;
import com.android.server.hdmi.HdmiControlService.DevicePollingCallback;

import java.util.List;

/**
 * Encapsulates a sequence of CEC/MHL command exchange for a certain feature.
@@ -33,14 +36,13 @@ import com.android.internal.annotations.VisibleForTesting;
 * of the object. All the actual action classes inherit FeatureAction.
 *
 * <p>More than one FeatureAction objects can be up and running simultaneously,
 * maintained by {@link HdmiControlService}. Each action is passed a new command
 * maintained by {@link HdmiCecLocalDevice}. Each action is passed a new command
 * arriving from the bus, and either consumes it if the command is what the action expects,
 * or yields it to other action.
 *
 * Declared as package private, accessed by {@link HdmiControlService} only.
 */
abstract class FeatureAction {

    private static final String TAG = "FeatureAction";

    // Timer handler message used for timeout event
@@ -56,19 +58,16 @@ abstract class FeatureAction {
    // Internal state indicating the progress of action.
    protected int mState = STATE_NONE;

    protected final HdmiControlService mService;

    // Logical address of the device for which the feature action is taken. The commands
    // generated in an action all use this field as source address.
    protected final int mSourceAddress;
    private final HdmiControlService mService;
    private final HdmiCecLocalDevice mSource;

    // Timer that manages timeout events.
    protected ActionTimer mActionTimer;

    FeatureAction(HdmiControlService service, int sourceAddress) {
        mService = service;
        mSourceAddress = sourceAddress;
        mActionTimer = createActionTimer(service.getServiceLooper());
    FeatureAction(HdmiCecLocalDevice source) {
        mSource = source;
        mService = mSource.getService();
        mActionTimer = createActionTimer(mService.getServiceLooper());
    }

    @VisibleForTesting
@@ -175,6 +174,42 @@ abstract class FeatureAction {
        mService.sendCecCommand(cmd, callback);
    }

    protected final void addAndStartAction(FeatureAction action) {
        mSource.addAndStartAction(action);
    }

    protected final <T extends FeatureAction> List<T> getActions(final Class<T> clazz) {
        return mSource.getActions(clazz);
    }

    protected final HdmiCecMessageCache getCecMessageCache() {
        return mSource.getCecMessageCache();
    }

    /**
     * Remove the action from the action queue. This is called after the action finishes
     * its role.
     *
     * @param action
     */
    protected final void removeAction(FeatureAction action) {
        mSource.removeAction(action);
    }

    protected final <T extends FeatureAction> void removeAction(final Class<T> clazz) {
        mSource.removeActionExcept(clazz, null);
    }

    protected final <T extends FeatureAction> void removeActionExcept(final Class<T> clazz,
            final FeatureAction exception) {
        mSource.removeActionExcept(clazz, exception);
    }

    protected final void pollDevices(DevicePollingCallback callback, int pickStrategy,
            int retryCount) {
        mService.pollDevices(callback, pickStrategy, retryCount);
    }

    /**
     * Clean up action's state.
     *
@@ -194,13 +229,23 @@ abstract class FeatureAction {
        removeAction(this);
    }

    /**
     * Remove the action from the action queue. This is called after the action finishes
     * its role.
     *
     * @param action
     */
    private void removeAction(FeatureAction action) {
        mService.removeAction(action);
    protected final HdmiCecLocalDevice localDevice() {
        return mSource;
    }

    protected final HdmiCecLocalDevicePlayback playback() {
        return (HdmiCecLocalDevicePlayback) mSource;
    }

    protected final HdmiCecLocalDeviceTv tv() {
        return (HdmiCecLocalDeviceTv) mSource;
    }

    protected final int getSourceAddress() {
        return mSource.getDeviceInfo().getLogicalAddress();
    }

    protected final int getSourcePath() {
        return mSource.getDeviceInfo().getPhysicalAddress();
    }
}
Loading