summaryrefslogtreecommitdiffstats
path: root/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java
blob: 8c404249cfd50514e0c1ace40ff6d03eb2e37ec4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
/*
 * 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) {
            mService.maySendFeatureAbortCommand(message, mReason);
            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);
    }
}