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

Commit 38db629d authored by Yuncheol Heo's avatar Yuncheol Heo
Browse files

Handle the power state change.

- Add BrocastIntent listener to catch SCREEN_ON/OFF intents.
- Add callbacks onTransitionToStandBy(), onStandBy() in HdmiCecLocalDevice.java.
- When it gets the SCREEN_OFF intent, the state will be TRANSITION_TO_STANDBY,
  then when there is no outstanding actions, the state will be STANDBY.
- When the state is STANDBY, the TV device will broadcast <StandBy>.
- When it gets the SCREEN_ON intent, the state will be TRANSITION_TO_ON,
  then when the initialization is done, the state will be ON.
- When the state is ON and it receives <StandBy> CEC message, it will force
  the system to go to sleep.
- When the state is STANDBY and it receives the cec messages to wake-up the
  system, it will force the system to wake up from sleep.
- Manage the active source state for the playback device.

Change-Id: Ib473219a0b0deb5224df89386db5a83720000411
parent 2bed9563
Loading
Loading
Loading
Loading
+103 −0
Original line number Diff line number Diff line
@@ -155,6 +155,18 @@ abstract class HdmiCecLocalDevice {
                return handleSystemAudioModeStatus(message);
            case HdmiCec.MESSAGE_REPORT_AUDIO_STATUS:
                return handleReportAudioStatus(message);
            case HdmiCec.MESSAGE_STANDBY:
                return handleStandby(message);
            case HdmiCec.MESSAGE_TEXT_VIEW_ON:
                return handleTextViewOn(message);
            case HdmiCec.MESSAGE_IMAGE_VIEW_ON:
                return handleImageViewOn(message);
            case HdmiCec.MESSAGE_USER_CONTROL_PRESSED:
                return handleUserControlPressed(message);
            case HdmiCec.MESSAGE_SET_STREAM_PATH:
                return handleSetStreamPath(message);
            case HdmiCec.MESSAGE_GIVE_DEVICE_POWER_STATUS:
                return handleGiveDevicePowerStatus(message);
            default:
                return false;
        }
@@ -275,6 +287,67 @@ abstract class HdmiCecLocalDevice {
        return false;
    }

    @ServiceThreadOnly
    protected boolean handleStandby(HdmiCecMessage message) {
        assertRunOnServiceThread();
        // Seq #12
        if (mService.isControlEnabled() && !isInPresetInstallationMode()
                && mService.isPowerOnOrTransient()) {
            mService.standby();
            return true;
        }
        return false;
    }

    @ServiceThreadOnly
    protected boolean handleUserControlPressed(HdmiCecMessage message) {
        assertRunOnServiceThread();
        final int opCode = message.getOpcode();
        final byte[] params = message.getParams();
        if (mService.isPowerOnOrTransient() && isPowerOffOrToggleCommand(message)) {
            mService.standby();
            return true;
        } else if (mService.isPowerStandbyOrTransient() && isPowerOnOrToggleCommand(message)) {
            mService.wakeUp();
            return true;
        }
        return false;
    }

    private static boolean isPowerOnOrToggleCommand(HdmiCecMessage message) {
        byte[] params = message.getParams();
        return message.getOpcode() == HdmiCec.MESSAGE_USER_CONTROL_PRESSED && params.length == 1
                && (params[0] == HdmiConstants.UI_COMMAND_POWER
                        || params[0] == HdmiConstants.UI_COMMAND_POWER_ON_FUNCTION
                        || params[0] == HdmiConstants.UI_COMMAND_POWER_TOGGLE_FUNCTION);
    }

    private static boolean isPowerOffOrToggleCommand(HdmiCecMessage message) {
        byte[] params = message.getParams();
        return message.getOpcode() == HdmiCec.MESSAGE_USER_CONTROL_PRESSED && params.length == 1
                && (params[0] == HdmiConstants.UI_COMMAND_POWER
                        || params[0] == HdmiConstants.UI_COMMAND_POWER_OFF_FUNCTION
                        || params[0] == HdmiConstants.UI_COMMAND_POWER_TOGGLE_FUNCTION);
    }

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

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

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

    protected boolean handleGiveDevicePowerStatus(HdmiCecMessage message) {
        mService.sendCecCommand(HdmiCecMessageBuilder.buildReportPowerStatus(
                mAddress, message.getSource(), mService.getPowerStatus()));
        return true;
    }

    @ServiceThreadOnly
    final void handleAddressAllocated(int logicalAddress) {
        assertRunOnServiceThread();
@@ -323,6 +396,10 @@ abstract class HdmiCecLocalDevice {
    @ServiceThreadOnly
    void addAndStartAction(final FeatureAction action) {
        assertRunOnServiceThread();
        if (mService.isPowerStandbyOrTransient()) {
            Slog.w(TAG, "Skip the action during Standby: " + action);
            return;
        }
        mActions.add(action);
        action.start();
    }
@@ -361,6 +438,7 @@ abstract class HdmiCecLocalDevice {
    void removeAction(final FeatureAction action) {
        assertRunOnServiceThread();
        mActions.remove(action);
        checkIfPendingActionsCleared();
    }

    // Remove all actions matched with the given Class type.
@@ -383,8 +461,14 @@ abstract class HdmiCecLocalDevice {
                mActions.remove(action);
            }
        }
        checkIfPendingActionsCleared();
    }

    protected void checkIfPendingActionsCleared() {
        if (mActions.isEmpty()) {
            mService.onPendingActionsCleared();
        }
    }
    protected void assertRunOnServiceThread() {
        if (Looper.myLooper() != mService.getServiceLooper()) {
            throw new IllegalStateException("Should run on service thread.");
@@ -488,4 +572,23 @@ abstract class HdmiCecLocalDevice {
        assertRunOnServiceThread();
        return mService.pathToPortId(newPath);
    }

    /**
     * Called when the system started transition to standby mode.
     *
     * @param initiatedByCec true if this power sequence is initiated
     *         by the reception the CEC messages like <StandBy>
     */
    protected void onTransitionToStandby(boolean initiatedByCec) {
        // If there are no outstanding actions, we'll go to STANDBY state.
        checkIfPendingActionsCleared();
    }

    /**
     * Called when the system goes to standby mode.
     *
     * @param initiatedByCec true if this power sequence is initiated
     *         by the reception the CEC messages like <StandBy>
     */
    protected void onStandBy(boolean initiatedByCec) {}
}
+54 −1
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.server.hdmi;

import android.hardware.hdmi.HdmiCec;
import android.hardware.hdmi.HdmiCecMessage;
import android.hardware.hdmi.IHdmiControlCallback;
import android.os.RemoteException;
import android.util.Slog;
@@ -29,6 +30,8 @@ import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice {
    private static final String TAG = "HdmiCecLocalDevicePlayback";

    private boolean mIsActiveSource = false;

    HdmiCecLocalDevicePlayback(HdmiControlService service) {
        super(service, HdmiCec.DEVICE_PLAYBACK);
    }
@@ -93,7 +96,57 @@ final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice {
    @ServiceThreadOnly
    void onHotplug(int portId, boolean connected) {
        assertRunOnServiceThread();
        // TODO: clear devices connected to the given port id.
        mCecMessageCache.flushAll();
        mIsActiveSource = false;
        if (connected && mService.isPowerStandbyOrTransient()) {
            mService.wakeUp();
        }
    }

    @ServiceThreadOnly
    void markActiveSource() {
        assertRunOnServiceThread();
        mIsActiveSource = true;
    }

    @Override
    @ServiceThreadOnly
    protected boolean handleActiveSource(HdmiCecMessage message) {
        assertRunOnServiceThread();
        int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
        if (physicalAddress != mService.getPhysicalAddress()) {
            mIsActiveSource = false;
            if (mService.isPowerOnOrTransient()) {
                mService.standby();
            }
            return true;
        }
        return false;
    }

    @Override
    @ServiceThreadOnly
    protected boolean handleSetStreamPath(HdmiCecMessage message) {
        assertRunOnServiceThread();
        int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
        if (physicalAddress == mService.getPhysicalAddress()) {
            if (mService.isPowerStandbyOrTransient()) {
                mService.wakeUp();
            }
            return true;
        }
        return false;
    }

    @Override
    @ServiceThreadOnly
    protected void onTransitionToStandby(boolean initiatedByCec) {
        assertRunOnServiceThread();
        if (!initiatedByCec && mIsActiveSource) {
            mService.sendCecCommand(HdmiCecMessageBuilder.buildInactiveSource(
                    mAddress, mService.getPhysicalAddress()));
        }
        mIsActiveSource = false;
        checkIfPendingActionsCleared();
    }
}
+58 −0
Original line number Diff line number Diff line
@@ -441,6 +441,26 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
        return true;
    }

    @Override
    @ServiceThreadOnly
    protected boolean handleTextViewOn(HdmiCecMessage message) {
        assertRunOnServiceThread();
        if (mService.isPowerStandbyOrTransient()) {
            mService.wakeUp();
        }
        // TODO: Connect to Hardware input manager to invoke TV App with the appropriate channel
        //       that represents the source device.
        return true;
    }

    @Override
    @ServiceThreadOnly
    protected boolean handleImageViewOn(HdmiCecMessage message) {
        assertRunOnServiceThread();
        // Currently, it's the same as <Text View On>.
        return handleTextViewOn(message);
    }

    @ServiceThreadOnly
    private void launchDeviceDiscovery() {
        assertRunOnServiceThread();
@@ -985,4 +1005,42 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
        assertRunOnServiceThread();
        mAutoDeviceOff = enabled;
    }

    @Override
    @ServiceThreadOnly
    protected void onTransitionToStandby(boolean initiatedByCec) {
        assertRunOnServiceThread();
        // Remove any repeated working actions.
        // HotplugDetectionAction will be reinstated during the wake up process.
        // HdmiControlService.onWakeUp() -> initializeLocalDevices() ->
        //     LocalDeviceTv.onAddressAllocated() -> launchDeviceDiscovery().
        removeAction(HotplugDetectionAction.class);
        checkIfPendingActionsCleared();
    }

    @Override
    @ServiceThreadOnly
    protected void onStandBy(boolean initiatedByCec) {
        assertRunOnServiceThread();
        // Seq #11
        if (!mService.isControlEnabled()) {
            return;
        }
        if (!initiatedByCec) {
            mService.sendCecCommand(HdmiCecMessageBuilder.buildStandby(
                    mAddress, HdmiCec.ADDR_BROADCAST));
        }
    }

    @Override
    @ServiceThreadOnly
    protected boolean handleStandby(HdmiCecMessage message) {
        assertRunOnServiceThread();
        // Seq #12
        // Tv accepts directly addressed <Standby> only.
        if (message.getDestination() == mAddress) {
            super.handleStandby(message);
        }
        return false;
    }
}
+38 −0
Original line number Diff line number Diff line
@@ -269,6 +269,18 @@ public class HdmiCecMessageBuilder {
                physicalAddressToParam(physicalAddress));
    }

    /**
     * Build &lt;Inactive Source&gt; command.
     *
     * @param src source address of command
     * @param physicalAddress physical address of the device to become inactive
     * @return newly created {@link HdmiCecMessage}
     */
    static HdmiCecMessage buildInactiveSource(int src, int physicalAddress) {
        return buildCommand(src, HdmiCec.ADDR_BROADCAST, HdmiCec.MESSAGE_INACTIVE_SOURCE,
                physicalAddressToParam(physicalAddress));
    }

    /**
     * Build &lt;Set Stream Path&gt; command.
     *
@@ -312,6 +324,21 @@ public class HdmiCecMessageBuilder {
        return buildCommand(src, dest, HdmiCec.MESSAGE_GIVE_DEVICE_POWER_STATUS);
    }

    /**
     * Build &lt;Report Power Status&gt; command.
     *
     * @param src source address of command
     * @param dest destination address of command
     * @param powerStatus power status of the device
     * @return newly created {@link HdmiCecMessage}
     */
    static HdmiCecMessage buildReportPowerStatus(int src, int dest, int powerStatus) {
        byte[] param = new byte[] {
                (byte) (powerStatus)
        };
        return buildCommand(src, dest, HdmiCec.MESSAGE_REPORT_POWER_STATUS, param);
    }

    /**
     * Build &lt;System Audio Mode Request&gt; command.
     *
@@ -388,6 +415,17 @@ public class HdmiCecMessageBuilder {
        return buildCommand(src, dest, HdmiCec.MESSAGE_GIVE_SYSTEM_AUDIO_MODE_STATUS);
    }

    /**
     * Build &lt;Standby&gt; command.
     *
     * @param src source address of command
     * @param dest destination address of command
     * @return newly created {@link HdmiCecMessage}
     */
    public static HdmiCecMessage buildStandby(int src, int dest) {
        return buildCommand(src, dest, HdmiCec.MESSAGE_STANDBY);
    }

    /***** Please ADD new buildXXX() methods above. ******/

    /**
+2 −0
Original line number Diff line number Diff line
@@ -45,6 +45,8 @@ final class HdmiConstants {
    static final int UI_COMMAND_MUTE = 0x43;
    static final int UI_COMMAND_MUTE_FUNCTION = 0x65;
    static final int UI_COMMAND_RESTORE_VOLUME_FUNCTION = 0x66;
    static final int UI_COMMAND_POWER_TOGGLE_FUNCTION = 0x6B;
    static final int UI_COMMAND_POWER_OFF_FUNCTION = 0x6C;
    static final int UI_COMMAND_POWER_ON_FUNCTION = 0x6D;

    // Bit mask used to get the routing path of the top level device.
Loading