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

Commit 25c20298 authored by Yuncheol Heo's avatar Yuncheol Heo
Browse files

Add the CEC message handler for the standby mode.

- Associate the state of AutoWakeUp with the pref value.

Bug: 16661406
Change-Id: I1299c6ca287aac4127e397a08b5af00190aa5b0b
parent 5b6a5e4b
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -162,7 +162,7 @@ final class Constants {
    // Constants related to operands of HDMI CEC commands.
    // Refer to CEC Table 29 in HDMI Spec v1.4b.
    // [Abort Reason]
    static final int ABORT_UNRECOGNIZED_MODE = 0;
    static final int ABORT_UNRECOGNIZED_OPCODE = 0;
    static final int ABORT_NOT_IN_CORRECT_MODE = 1;
    static final int ABORT_CANNOT_PROVIDE_SOURCE = 2;
    static final int ABORT_INVALID_OPERAND = 3;
+8 −7
Original line number Diff line number Diff line
@@ -172,7 +172,7 @@ abstract class HdmiCecLocalDevice {
     * @return true if consumed a message; otherwise, return false.
     */
    @ServiceThreadOnly
    final boolean dispatchMessage(HdmiCecMessage message) {
    boolean dispatchMessage(HdmiCecMessage message) {
        assertRunOnServiceThread();
        int dest = message.getDestination();
        if (dest != mAddress && dest != Constants.ADDR_BROADCAST) {
@@ -309,7 +309,7 @@ abstract class HdmiCecLocalDevice {
        mService.sendCecCommand(
                HdmiCecMessageBuilder.buildFeatureAbortCommand(mAddress,
                        message.getSource(), Constants.MESSAGE_GET_MENU_LANGUAGE,
                        Constants.ABORT_UNRECOGNIZED_MODE));
                        Constants.ABORT_UNRECOGNIZED_OPCODE));
        return true;
    }

@@ -381,7 +381,7 @@ abstract class HdmiCecLocalDevice {
        return false;
    }

    private static boolean isPowerOnOrToggleCommand(HdmiCecMessage message) {
    static boolean isPowerOnOrToggleCommand(HdmiCecMessage message) {
        byte[] params = message.getParams();
        return message.getOpcode() == Constants.MESSAGE_USER_CONTROL_PRESSED
                && (params[0] == HdmiCecKeycode.CEC_KEYCODE_POWER
@@ -389,7 +389,7 @@ abstract class HdmiCecLocalDevice {
                        || params[0] == HdmiCecKeycode.CEC_KEYCODE_POWER_TOGGLE_FUNCTION);
    }

    private static boolean isPowerOffOrToggleCommand(HdmiCecMessage message) {
    static boolean isPowerOffOrToggleCommand(HdmiCecMessage message) {
        byte[] params = message.getParams();
        return message.getOpcode() == Constants.MESSAGE_USER_CONTROL_PRESSED
                && (params[0] == HdmiCecKeycode.CEC_KEYCODE_POWER
@@ -431,7 +431,7 @@ abstract class HdmiCecLocalDevice {
            Slog.v(TAG, "Wrong direct vendor command. Replying with <Feature Abort>");
            mService.sendCecCommand(HdmiCecMessageBuilder.buildFeatureAbortCommand(mAddress,
                    message.getSource(), Constants.MESSAGE_VENDOR_COMMAND_WITH_ID,
                    Constants.ABORT_UNRECOGNIZED_MODE));
                    Constants.ABORT_UNRECOGNIZED_OPCODE));
        } else {
            Slog.v(TAG, "Wrong broadcast vendor command. Ignoring");
        }
@@ -444,9 +444,10 @@ abstract class HdmiCecLocalDevice {
    }

    protected boolean handleRecordTvScreen(HdmiCecMessage message) {
        // The default behavior of <Record TV Screen> is replying <Feature Abort> with "Refused".
        // The default behavior of <Record TV Screen> is replying <Feature Abort> with
        // "Cannot provide source".
        mService.sendCecCommand(HdmiCecMessageBuilder.buildFeatureAbortCommand(mAddress,
                message.getSource(), message.getOpcode(), Constants.ABORT_REFUSED));
                message.getSource(), message.getOpcode(), Constants.ABORT_CANNOT_PROVIDE_SOURCE));
        return true;
    }

+19 −2
Original line number Diff line number Diff line
@@ -100,12 +100,15 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
    // If true, TV wakes itself up when receiving <Text/Image View On>.
    private boolean mAutoWakeup;

    private final HdmiCecStandbyModeHandler mStandbyHandler;

    HdmiCecLocalDeviceTv(HdmiControlService service) {
        super(service, HdmiCecDeviceInfo.DEVICE_TV);
        mPrevPortId = Constants.INVALID_PORT_ID;
        mAutoDeviceOff = mService.readBooleanSetting(Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED,
                true);
        mAutoWakeup = mService.readBooleanSetting(Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED, true);
        mStandbyHandler = new HdmiCecStandbyModeHandler(service, this);
    }

    @Override
@@ -135,6 +138,16 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
        SystemProperties.set(Constants.PROPERTY_PREFERRED_ADDRESS_TV, String.valueOf(addr));
    }

    @Override
    @ServiceThreadOnly
    boolean dispatchMessage(HdmiCecMessage message) {
        assertRunOnServiceThread();
        if (mService.isPowerStandby() && mStandbyHandler.handleCommand(message)) {
            return true;
        }
        return super.onMessage(message);
    }

    /**
     * Performs the action 'device select', or 'one touch play' initiated by TV.
     *
@@ -787,8 +800,6 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
    }

    private boolean isSystemAudioOn() {


        synchronized (mLock) {
            return mSystemAudioActivated;
        }
@@ -1183,6 +1194,12 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
        mService.writeBooleanSetting(Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED, enabled);
    }

    @ServiceThreadOnly
    boolean getAutoWakeup() {
        assertRunOnServiceThread();
        return mAutoWakeup;
    }

    @Override
    @ServiceThreadOnly
    protected void disableDevice(boolean initiatedByCec, PendingActionClearedCallback callback) {
+174 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.hdmi;

import android.util.SparseArray;

/**
 * This class handles the incoming messages when HdmiCecService is in the standby mode.
 */
public final class HdmiCecStandbyModeHandler {

    private interface CecMessageHandler {
        boolean handle(HdmiCecMessage message);
    }

    private static final class Bystander implements CecMessageHandler {
        @Override
        public boolean handle(HdmiCecMessage message) {
            return true;
        }
    }

    private static final class Bypasser implements CecMessageHandler {
        @Override
        public boolean handle(HdmiCecMessage message) {
            return false;
        }
    }

    private final class Aborter implements CecMessageHandler {
        private final int mReason;
        public Aborter(int reason) {
            mReason = reason;
        }
        @Override
        public boolean handle(HdmiCecMessage message) {
            int src = message.getSource();
            int dest = message.getDestination();
            if (src == Constants.ADDR_BROADCAST || dest == Constants.ADDR_BROADCAST) {
                // Do not send <Feature Abort> on the message from the unassigned device
                // or the broadcasted message.
                return true;
            }
            HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildFeatureAbortCommand(
                    dest, src, message.getOpcode(), mReason);
            mService.sendCecCommand(cecMessage);
            return true;
        }
    }

    private final class AutoOnHandler implements CecMessageHandler {
        @Override
        public boolean handle(HdmiCecMessage message) {
            if (!mTv.getAutoWakeup()) {
                mAborterRefused.handle(message);
                return true;
            }
            return false;
        }
    }

    private final class UserControlProcessedHandler implements CecMessageHandler {
        @Override
        public boolean handle(HdmiCecMessage message) {
            // The power status here is always standby.
            if (HdmiCecLocalDevice.isPowerOnOrToggleCommand(message)) {
                return false;
            } else if (HdmiCecLocalDevice.isPowerOffOrToggleCommand(message)) {
                return true;
            }
            return mAborterIncorrectMode.handle(message);
        }
    }

    private final HdmiControlService mService;
    private final HdmiCecLocalDeviceTv mTv;

    private final SparseArray<CecMessageHandler> mCecMessageHandlers = new SparseArray<>();
    private final CecMessageHandler mDefaultHandler = new Aborter(
            Constants.ABORT_UNRECOGNIZED_OPCODE);
    private final CecMessageHandler mAborterIncorrectMode = new Aborter(
            Constants.ABORT_NOT_IN_CORRECT_MODE);
    private final CecMessageHandler mAborterRefused = new Aborter(Constants.ABORT_REFUSED);
    private final CecMessageHandler mAutoOnHandler = new AutoOnHandler();
    private final CecMessageHandler mBypasser = new Bypasser();
    private final CecMessageHandler mBystander = new Bystander();
    private final UserControlProcessedHandler
            mUserControlProcessedHandler = new UserControlProcessedHandler();

    public HdmiCecStandbyModeHandler(HdmiControlService service, HdmiCecLocalDeviceTv tv) {
        mService = service;
        mTv = tv;

        addHandler(Constants.MESSAGE_IMAGE_VIEW_ON, mAutoOnHandler);
        addHandler(Constants.MESSAGE_TEXT_VIEW_ON, mAutoOnHandler);

        addHandler(Constants.MESSAGE_ACTIVE_SOURCE, mBystander);
        addHandler(Constants.MESSAGE_REQUEST_ACTIVE_SOURCE, mBystander);
        addHandler(Constants.MESSAGE_ROUTING_CHANGE, mBystander);
        addHandler(Constants.MESSAGE_ROUTING_INFORMATION, mBystander);
        addHandler(Constants.MESSAGE_SET_STREAM_PATH, mBystander);
        addHandler(Constants.MESSAGE_STANDBY, mBystander);
        addHandler(Constants.MESSAGE_SET_MENU_LANGUAGE, mBystander);
        addHandler(Constants.MESSAGE_DEVICE_VENDOR_ID, mBystander);
        addHandler(Constants.MESSAGE_USER_CONTROL_RELEASED, mBystander);
        addHandler(Constants.MESSAGE_REPORT_POWER_STATUS, mBystander);
        addHandler(Constants.MESSAGE_FEATURE_ABORT, mBystander);
        addHandler(Constants.MESSAGE_INACTIVE_SOURCE, mBystander);
        addHandler(Constants.MESSAGE_SYSTEM_AUDIO_MODE_STATUS, mBystander);
        addHandler(Constants.MESSAGE_REPORT_AUDIO_STATUS, mBystander);

        // If TV supports the following messages during power-on, ignore them and do nothing,
        // else reply with <Feature Abort>["Unrecognized Opcode"]
        // <Deck Status>, <Tuner Device Status>, <Tuner Cleared Status>, <Timer Status>
        addHandler(Constants.MESSAGE_RECORD_STATUS, mBystander);

        // If TV supports the following messages during power-on, reply with <Feature Abort>["Not
        // in correct mode to respond"], else reply with <Feature Abort>["Unrecognized Opcode"]
        // <Give Tuner Device Status>, <Select Digital Service>, <Tuner Step Decrement>,
        // <Tuner Stem Increment>, <Menu Status>.
        addHandler(Constants.MESSAGE_RECORD_TV_SCREEN, mAborterIncorrectMode);
        addHandler(Constants.MESSAGE_INITIATE_ARC, mAborterIncorrectMode);
        addHandler(Constants.MESSAGE_TERMINATE_ARC, mAborterIncorrectMode);

        addHandler(Constants.MESSAGE_GIVE_PHYSICAL_ADDRESS, mBypasser);
        addHandler(Constants.MESSAGE_GET_MENU_LANGUAGE, mBypasser);
        addHandler(Constants.MESSAGE_REPORT_PHYSICAL_ADDRESS, mBypasser);
        addHandler(Constants.MESSAGE_GIVE_DEVICE_VENDOR_ID, mBypasser);
        addHandler(Constants.MESSAGE_GIVE_OSD_NAME, mBypasser);
        addHandler(Constants.MESSAGE_SET_OSD_NAME, mBypasser);

        addHandler(Constants.MESSAGE_USER_CONTROL_PRESSED, mUserControlProcessedHandler);

        addHandler(Constants.MESSAGE_GIVE_DEVICE_POWER_STATUS, mBypasser);
        addHandler(Constants.MESSAGE_ABORT, mBypasser);
        addHandler(Constants.MESSAGE_GET_CEC_VERSION, mBypasser);

        addHandler(Constants.MESSAGE_VENDOR_COMMAND_WITH_ID, mAborterIncorrectMode);
        addHandler(Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE, mAborterIncorrectMode);
    }

    private void addHandler(int opcode, CecMessageHandler handler) {
        mCecMessageHandlers.put(opcode, handler);
    }

    /**
     * Handles the CEC message in the standby mode.
     *
     * @param message {@link HdmiCecMessage} to be processed
     * @return true if the message is handled in the handler, false means that the message is need
     *         to be dispatched to the local device.
     */
    boolean handleCommand(HdmiCecMessage message) {
        CecMessageHandler handler = mCecMessageHandlers.get(message.getOpcode());
        if (handler != null) {
            return handler.handle(message);
        }
        return mDefaultHandler.handle(message);
    }
}
+12 −0
Original line number Diff line number Diff line
@@ -260,6 +260,16 @@ public final class HdmiControlService extends SystemService {
        }
    }

    /**
     * Called when the initialization of local devices is complete.
     */
    private void onInitializeCecComplete() {
        if (isTvDevice()) {
            mCecController.setOption(HdmiTvClient.OPTION_CEC_AUTO_WAKEUP,
                    tv().getAutoWakeup() ? HdmiTvClient.ENABLED : HdmiTvClient.DISABLED);
        }
    }

    boolean readBooleanSetting(String key, boolean defVal) {
        ContentResolver cr = getContext().getContentResolver();
        return Global.getInt(cr, key, defVal ? Constants.TRUE : Constants.FALSE) == Constants.TRUE;
@@ -322,6 +332,7 @@ public final class HdmiControlService extends SystemService {
            HdmiCecLocalDevice device = devices.valueAt(i);
            device.handleAddressAllocated(address, fromBootup);
        }
        onInitializeCecComplete();
    }

    // Initialize HDMI port information. Combine the information from CEC and MHL HAL and
@@ -996,6 +1007,7 @@ public final class HdmiControlService extends SystemService {
            }
            switch (key) {
                case HdmiTvClient.OPTION_CEC_AUTO_WAKEUP:
                    tv().setAutoWakeup(value == HdmiTvClient.ENABLED);
                    mCecController.setOption(key, value);
                    break;
                case HdmiTvClient.OPTION_CEC_AUTO_DEVICE_OFF: