blob: e5f4282eefe0d1b0796959147e3acaf07528947a [file] [log] [blame]
Jinsuk Kim2918e9e2014-05-20 16:45:45 +09001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.hdmi;
18
Jungshik Jang61f4fbd2014-08-06 19:21:12 +090019import android.hardware.hdmi.HdmiDeviceInfo;
Terry Heo3e1564e2014-08-12 14:41:00 +090020import android.hardware.input.InputManager;
Jungshik Jang4fc1d102014-07-09 19:24:50 +090021import android.os.Handler;
Jungshik Jang79c58a42014-06-16 16:45:36 +090022import android.os.Looper;
Jungshik Jang4fc1d102014-07-09 19:24:50 +090023import android.os.Message;
Terry Heo3e1564e2014-08-12 14:41:00 +090024import android.os.SystemClock;
Jungshik Jang092b4452014-06-11 15:19:17 +090025import android.util.Slog;
Terry Heo3e1564e2014-08-12 14:41:00 +090026import android.view.InputDevice;
27import android.view.KeyCharacterMap;
28import android.view.KeyEvent;
Jinsuk Kim2918e9e2014-05-20 16:45:45 +090029
Jungshik Jang79c58a42014-06-16 16:45:36 +090030import com.android.internal.annotations.GuardedBy;
Terry Heo959d2db2014-08-28 16:45:41 +090031import com.android.internal.util.IndentingPrintWriter;
Jungshik Janga5b74142014-06-23 18:03:10 +090032import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
Jungshik Jang79c58a42014-06-16 16:45:36 +090033
34import java.util.ArrayList;
Jinsuk Kim43c23e22014-07-29 13:59:14 +090035import java.util.Collections;
Jungshik Jang79c58a42014-06-16 16:45:36 +090036import java.util.Iterator;
Jungshik Jang79c58a42014-06-16 16:45:36 +090037import java.util.List;
38
Jinsuk Kim2918e9e2014-05-20 16:45:45 +090039/**
40 * Class that models a logical CEC device hosted in this system. Handles initialization,
41 * CEC commands that call for actions customized per device type.
42 */
43abstract class HdmiCecLocalDevice {
Jungshik Jang092b4452014-06-11 15:19:17 +090044 private static final String TAG = "HdmiCecLocalDevice";
Jinsuk Kim2918e9e2014-05-20 16:45:45 +090045
Jungshik Jang4fc1d102014-07-09 19:24:50 +090046 private static final int MSG_DISABLE_DEVICE_TIMEOUT = 1;
Terry Heo3e1564e2014-08-12 14:41:00 +090047 private static final int MSG_USER_CONTROL_RELEASE_TIMEOUT = 2;
Jungshik Jang4fc1d102014-07-09 19:24:50 +090048 // Timeout in millisecond for device clean up (5s).
49 // Normal actions timeout is 2s but some of them would have several sequence of timeout.
50 private static final int DEVICE_CLEANUP_TIMEOUT = 5000;
Terry Heo3e1564e2014-08-12 14:41:00 +090051 // Within the timer, a received <User Control Pressed> will start "Press and Hold" behavior.
52 // When it expires, we can assume <User Control Release> is received.
53 private static final int FOLLOWER_SAFETY_TIMEOUT = 550;
Jungshik Jang4fc1d102014-07-09 19:24:50 +090054
Jungshik Jang3ee65722014-06-03 16:22:30 +090055 protected final HdmiControlService mService;
Jinsuk Kim2918e9e2014-05-20 16:45:45 +090056 protected final int mDeviceType;
57 protected int mAddress;
58 protected int mPreferredAddress;
Jungshik Jang61f4fbd2014-08-06 19:21:12 +090059 protected HdmiDeviceInfo mDeviceInfo;
Terry Heo3e1564e2014-08-12 14:41:00 +090060 protected int mLastKeycode = HdmiCecKeycode.UNSUPPORTED_KEYCODE;
61 protected int mLastKeyRepeatCount = 0;
Jinsuk Kim2918e9e2014-05-20 16:45:45 +090062
Jinsuk Kim72b7d732014-07-24 09:15:35 +090063 static class ActiveSource {
64 int logicalAddress;
65 int physicalAddress;
66
Jinsuk Kim43c23e22014-07-29 13:59:14 +090067 public ActiveSource() {
68 invalidate();
69 }
Jinsuk Kim72b7d732014-07-24 09:15:35 +090070 public ActiveSource(int logical, int physical) {
71 logicalAddress = logical;
72 physicalAddress = physical;
73 }
Jinsuk Kim04f813c2015-04-02 16:56:49 +090074 public static ActiveSource of(ActiveSource source) {
75 return new ActiveSource(source.logicalAddress, source.physicalAddress);
76 }
Jinsuk Kim72b7d732014-07-24 09:15:35 +090077 public static ActiveSource of(int logical, int physical) {
78 return new ActiveSource(logical, physical);
79 }
80 public boolean isValid() {
81 return HdmiUtils.isValidAddress(logicalAddress);
82 }
Jinsuk Kim43c23e22014-07-29 13:59:14 +090083 public void invalidate() {
84 logicalAddress = Constants.ADDR_INVALID;
85 physicalAddress = Constants.INVALID_PHYSICAL_ADDRESS;
86 }
Jinsuk Kim72b7d732014-07-24 09:15:35 +090087 public boolean equals(int logical, int physical) {
88 return logicalAddress == logical && physicalAddress == physical;
89 }
90 @Override
91 public boolean equals(Object obj) {
92 if (obj instanceof ActiveSource) {
93 ActiveSource that = (ActiveSource) obj;
94 return that.logicalAddress == logicalAddress &&
95 that.physicalAddress == physicalAddress;
96 }
97 return false;
98 }
99 @Override
100 public int hashCode() {
101 return logicalAddress * 29 + physicalAddress;
102 }
Terry Heo959d2db2014-08-28 16:45:41 +0900103 @Override
104 public String toString() {
105 StringBuffer s = new StringBuffer();
106 String logicalAddressString = (logicalAddress == Constants.ADDR_INVALID)
107 ? "invalid" : String.format("0x%02x", logicalAddress);
Jinsuk Kim04f813c2015-04-02 16:56:49 +0900108 s.append("(").append(logicalAddressString);
Terry Heo959d2db2014-08-28 16:45:41 +0900109 String physicalAddressString = (physicalAddress == Constants.INVALID_PHYSICAL_ADDRESS)
110 ? "invalid" : String.format("0x%04x", physicalAddress);
Jinsuk Kim04f813c2015-04-02 16:56:49 +0900111 s.append(", ").append(physicalAddressString).append(")");
Terry Heo959d2db2014-08-28 16:45:41 +0900112 return s.toString();
113 }
Jinsuk Kim72b7d732014-07-24 09:15:35 +0900114 }
Jungshik Jang79c58a42014-06-16 16:45:36 +0900115 // Logical address of the active source.
116 @GuardedBy("mLock")
Jinsuk Kim43c23e22014-07-29 13:59:14 +0900117 protected final ActiveSource mActiveSource = new ActiveSource();
Jungshik Jang79c58a42014-06-16 16:45:36 +0900118
119 // Active routing path. Physical address of the active source but not all the time, such as
120 // when the new active source does not claim itself to be one. Note that we don't keep
121 // the active port id (or active input) since it can be gotten by {@link #pathToPortId(int)}.
122 @GuardedBy("mLock")
123 private int mActiveRoutingPath;
124
Jungshik Jang79c58a42014-06-16 16:45:36 +0900125 protected final HdmiCecMessageCache mCecMessageCache = new HdmiCecMessageCache();
126 protected final Object mLock;
127
128 // A collection of FeatureAction.
129 // Note that access to this collection should happen in service thread.
Jungshik Jang5352081c2014-09-22 15:14:49 +0900130 private final ArrayList<HdmiCecFeatureAction> mActions = new ArrayList<>();
Jungshik Jang79c58a42014-06-16 16:45:36 +0900131
Yuncheol Heoc516d652014-07-11 18:23:24 +0900132 private final Handler mHandler = new Handler () {
Jungshik Jang4fc1d102014-07-09 19:24:50 +0900133 @Override
134 public void handleMessage(Message msg) {
135 switch (msg.what) {
136 case MSG_DISABLE_DEVICE_TIMEOUT:
137 handleDisableDeviceTimeout();
138 break;
Terry Heo3e1564e2014-08-12 14:41:00 +0900139 case MSG_USER_CONTROL_RELEASE_TIMEOUT:
140 handleUserControlReleased();
141 break;
Jungshik Jang4fc1d102014-07-09 19:24:50 +0900142 }
143 }
144 };
145
146 /**
147 * A callback interface to get notified when all pending action is cleared.
148 * It can be called when timeout happened.
149 */
150 interface PendingActionClearedCallback {
151 void onCleared(HdmiCecLocalDevice device);
152 }
153
154 protected PendingActionClearedCallback mPendingActionClearedCallback;
155
Jungshik Jang3ee65722014-06-03 16:22:30 +0900156 protected HdmiCecLocalDevice(HdmiControlService service, int deviceType) {
157 mService = service;
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900158 mDeviceType = deviceType;
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900159 mAddress = Constants.ADDR_UNREGISTERED;
Jungshik Jang79c58a42014-06-16 16:45:36 +0900160 mLock = service.getServiceLock();
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900161 }
162
163 // Factory method that returns HdmiCecLocalDevice of corresponding type.
Jungshik Jang3ee65722014-06-03 16:22:30 +0900164 static HdmiCecLocalDevice create(HdmiControlService service, int deviceType) {
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900165 switch (deviceType) {
Jungshik Jang61f4fbd2014-08-06 19:21:12 +0900166 case HdmiDeviceInfo.DEVICE_TV:
Jungshik Jang3ee65722014-06-03 16:22:30 +0900167 return new HdmiCecLocalDeviceTv(service);
Jungshik Jang61f4fbd2014-08-06 19:21:12 +0900168 case HdmiDeviceInfo.DEVICE_PLAYBACK:
Jungshik Jang3ee65722014-06-03 16:22:30 +0900169 return new HdmiCecLocalDevicePlayback(service);
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900170 default:
171 return null;
172 }
173 }
174
Jungshik Janga5b74142014-06-23 18:03:10 +0900175 @ServiceThreadOnly
Jungshik Jang3ee65722014-06-03 16:22:30 +0900176 void init() {
Jungshik Janga5b74142014-06-23 18:03:10 +0900177 assertRunOnServiceThread();
Jinsuk Kimaf2acf02014-07-11 18:43:04 +0900178 mPreferredAddress = getPreferredAddress();
Jinsuk Kim7e4b4802015-04-20 13:17:13 +0900179 mPendingActionClearedCallback = null;
Jungshik Jang3ee65722014-06-03 16:22:30 +0900180 }
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900181
Jungshik Jang8b308d92014-05-29 21:52:28 +0900182 /**
Jungshik Jang3ee65722014-06-03 16:22:30 +0900183 * Called once a logical address of the local device is allocated.
Jungshik Jang8b308d92014-05-29 21:52:28 +0900184 */
Yuncheol Heofc44e4e2014-08-04 19:41:09 +0900185 protected abstract void onAddressAllocated(int logicalAddress, int reason);
Jungshik Jang8b308d92014-05-29 21:52:28 +0900186
Jungshik Jang092b4452014-06-11 15:19:17 +0900187 /**
Jinsuk Kimaf2acf02014-07-11 18:43:04 +0900188 * Get the preferred logical address from system properties.
189 */
190 protected abstract int getPreferredAddress();
191
192 /**
193 * Set the preferred logical address to system properties.
194 */
195 protected abstract void setPreferredAddress(int addr);
196
197 /**
Jinsuk Kim6e26f7f2015-01-07 16:45:14 +0900198 * Returns true if the TV input associated with the CEC device is ready
199 * to accept further processing such as input switching. This is used
200 * to buffer certain CEC commands and process it later if the input is not
201 * ready yet. For other types of local devices(non-TV), this method returns
202 * true by default to let the commands be processed right away.
203 */
204 protected boolean isInputReady(int deviceId) {
205 return true;
206 }
207
208 /**
Jinsuk Kime26d8332015-01-09 08:55:41 +0900209 * Returns true if the local device allows the system to be put to standby.
210 * The default implementation returns true.
211 */
212 protected boolean canGoToStandby() {
213 return true;
214 }
215
216 /**
Jungshik Jang092b4452014-06-11 15:19:17 +0900217 * Dispatch incoming message.
218 *
219 * @param message incoming message
220 * @return true if consumed a message; otherwise, return false.
221 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900222 @ServiceThreadOnly
Yuncheol Heo25c20292014-07-31 17:59:39 +0900223 boolean dispatchMessage(HdmiCecMessage message) {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900224 assertRunOnServiceThread();
Jungshik Jang092b4452014-06-11 15:19:17 +0900225 int dest = message.getDestination();
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900226 if (dest != mAddress && dest != Constants.ADDR_BROADCAST) {
Jungshik Jang092b4452014-06-11 15:19:17 +0900227 return false;
228 }
Jungshik Jang79c58a42014-06-16 16:45:36 +0900229 // Cache incoming message. Note that it caches only white-listed one.
230 mCecMessageCache.cacheMessage(message);
Jungshik Jang092b4452014-06-11 15:19:17 +0900231 return onMessage(message);
232 }
233
Jungshik Janga5b74142014-06-23 18:03:10 +0900234 @ServiceThreadOnly
Jungshik Jang60cffce2014-06-12 18:03:04 +0900235 protected final boolean onMessage(HdmiCecMessage message) {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900236 assertRunOnServiceThread();
Jungshik Jang79c58a42014-06-16 16:45:36 +0900237 if (dispatchMessageToAction(message)) {
238 return true;
239 }
Jungshik Jang092b4452014-06-11 15:19:17 +0900240 switch (message.getOpcode()) {
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900241 case Constants.MESSAGE_ACTIVE_SOURCE:
Jinsuk Kim83335712014-06-24 07:57:00 +0900242 return handleActiveSource(message);
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900243 case Constants.MESSAGE_INACTIVE_SOURCE:
Jinsuk Kim83335712014-06-24 07:57:00 +0900244 return handleInactiveSource(message);
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900245 case Constants.MESSAGE_REQUEST_ACTIVE_SOURCE:
Jinsuk Kim83335712014-06-24 07:57:00 +0900246 return handleRequestActiveSource(message);
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900247 case Constants.MESSAGE_GET_MENU_LANGUAGE:
Jungshik Jang092b4452014-06-11 15:19:17 +0900248 return handleGetMenuLanguage(message);
Terry Heo795415b2014-10-01 15:03:53 +0900249 case Constants.MESSAGE_SET_MENU_LANGUAGE:
250 return handleSetMenuLanguage(message);
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900251 case Constants.MESSAGE_GIVE_PHYSICAL_ADDRESS:
Jungshik Jang092b4452014-06-11 15:19:17 +0900252 return handleGivePhysicalAddress();
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900253 case Constants.MESSAGE_GIVE_OSD_NAME:
Jungshik Jang092b4452014-06-11 15:19:17 +0900254 return handleGiveOsdName(message);
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900255 case Constants.MESSAGE_GIVE_DEVICE_VENDOR_ID:
Jungshik Jang092b4452014-06-11 15:19:17 +0900256 return handleGiveDeviceVendorId();
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900257 case Constants.MESSAGE_GET_CEC_VERSION:
Jungshik Jang092b4452014-06-11 15:19:17 +0900258 return handleGetCecVersion(message);
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900259 case Constants.MESSAGE_REPORT_PHYSICAL_ADDRESS:
Jungshik Jang60cffce2014-06-12 18:03:04 +0900260 return handleReportPhysicalAddress(message);
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900261 case Constants.MESSAGE_ROUTING_CHANGE:
Jinsuk Kim92b77cf2014-06-27 16:39:26 +0900262 return handleRoutingChange(message);
Yuncheol Heo64bafd92014-08-11 11:17:54 +0900263 case Constants.MESSAGE_ROUTING_INFORMATION:
264 return handleRoutingInformation(message);
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900265 case Constants.MESSAGE_INITIATE_ARC:
Jungshik Jang79c58a42014-06-16 16:45:36 +0900266 return handleInitiateArc(message);
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900267 case Constants.MESSAGE_TERMINATE_ARC:
Jungshik Jang79c58a42014-06-16 16:45:36 +0900268 return handleTerminateArc(message);
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900269 case Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE:
Jungshik Jang79c58a42014-06-16 16:45:36 +0900270 return handleSetSystemAudioMode(message);
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900271 case Constants.MESSAGE_SYSTEM_AUDIO_MODE_STATUS:
Jungshik Jang79c58a42014-06-16 16:45:36 +0900272 return handleSystemAudioModeStatus(message);
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900273 case Constants.MESSAGE_REPORT_AUDIO_STATUS:
Jungshik Jang8fa36b12014-06-25 15:51:36 +0900274 return handleReportAudioStatus(message);
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900275 case Constants.MESSAGE_STANDBY:
Yuncheol Heo38db6292014-07-01 14:15:14 +0900276 return handleStandby(message);
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900277 case Constants.MESSAGE_TEXT_VIEW_ON:
Yuncheol Heo38db6292014-07-01 14:15:14 +0900278 return handleTextViewOn(message);
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900279 case Constants.MESSAGE_IMAGE_VIEW_ON:
Yuncheol Heo38db6292014-07-01 14:15:14 +0900280 return handleImageViewOn(message);
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900281 case Constants.MESSAGE_USER_CONTROL_PRESSED:
Yuncheol Heo38db6292014-07-01 14:15:14 +0900282 return handleUserControlPressed(message);
Terry Heo3e1564e2014-08-12 14:41:00 +0900283 case Constants.MESSAGE_USER_CONTROL_RELEASED:
284 return handleUserControlReleased();
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900285 case Constants.MESSAGE_SET_STREAM_PATH:
Yuncheol Heo38db6292014-07-01 14:15:14 +0900286 return handleSetStreamPath(message);
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900287 case Constants.MESSAGE_GIVE_DEVICE_POWER_STATUS:
Yuncheol Heo38db6292014-07-01 14:15:14 +0900288 return handleGiveDevicePowerStatus(message);
Terry Heo3e1564e2014-08-12 14:41:00 +0900289 case Constants.MESSAGE_MENU_REQUEST:
Yuncheol Heo184b1242014-09-12 15:09:07 +0900290 return handleMenuRequest(message);
291 case Constants.MESSAGE_MENU_STATUS:
292 return handleMenuStatus(message);
Jinsuk Kim119160a2014-07-07 18:48:10 +0900293 case Constants.MESSAGE_VENDOR_COMMAND:
294 return handleVendorCommand(message);
295 case Constants.MESSAGE_VENDOR_COMMAND_WITH_ID:
296 return handleVendorCommandWithId(message);
Jungshik Jang8f2ed352014-07-07 15:02:47 +0900297 case Constants.MESSAGE_SET_OSD_NAME:
298 return handleSetOsdName(message);
Jungshik Jangb6591b82014-07-23 16:10:23 +0900299 case Constants.MESSAGE_RECORD_TV_SCREEN:
300 return handleRecordTvScreen(message);
Jungshik Jange5a93372014-07-25 13:41:14 +0900301 case Constants.MESSAGE_TIMER_CLEARED_STATUS:
302 return handleTimerClearedStatus(message);
Jungshik Jang4480efa2014-09-04 17:08:34 +0900303 case Constants.MESSAGE_REPORT_POWER_STATUS:
304 return handleReportPowerStatus(message);
305 case Constants.MESSAGE_TIMER_STATUS:
306 return handleTimerStatus(message);
307 case Constants.MESSAGE_RECORD_STATUS:
308 return handleRecordStatus(message);
Jungshik Jang092b4452014-06-11 15:19:17 +0900309 default:
310 return false;
311 }
312 }
313
Jungshik Janga5b74142014-06-23 18:03:10 +0900314 @ServiceThreadOnly
Jungshik Jang79c58a42014-06-16 16:45:36 +0900315 private boolean dispatchMessageToAction(HdmiCecMessage message) {
Jungshik Janga5b74142014-06-23 18:03:10 +0900316 assertRunOnServiceThread();
Jungshik Jang5352081c2014-09-22 15:14:49 +0900317 boolean processed = false;
318 // Use copied action list in that processCommand may remove itself.
319 for (HdmiCecFeatureAction action : new ArrayList<>(mActions)) {
320 // Iterates all actions to check whether incoming message is consumed.
321 boolean result = action.processCommand(message);
322 processed = processed || result;
Jungshik Jang79c58a42014-06-16 16:45:36 +0900323 }
Jungshik Jang5352081c2014-09-22 15:14:49 +0900324 return processed;
Jungshik Jang79c58a42014-06-16 16:45:36 +0900325 }
326
Jungshik Janga5b74142014-06-23 18:03:10 +0900327 @ServiceThreadOnly
Jungshik Jang092b4452014-06-11 15:19:17 +0900328 protected boolean handleGivePhysicalAddress() {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900329 assertRunOnServiceThread();
330
Jungshik Jang092b4452014-06-11 15:19:17 +0900331 int physicalAddress = mService.getPhysicalAddress();
332 HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
333 mAddress, physicalAddress, mDeviceType);
334 mService.sendCecCommand(cecMessage);
335 return true;
336 }
337
Jungshik Janga5b74142014-06-23 18:03:10 +0900338 @ServiceThreadOnly
Jungshik Jang092b4452014-06-11 15:19:17 +0900339 protected boolean handleGiveDeviceVendorId() {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900340 assertRunOnServiceThread();
Jungshik Jang092b4452014-06-11 15:19:17 +0900341 int vendorId = mService.getVendorId();
342 HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildDeviceVendorIdCommand(
343 mAddress, vendorId);
344 mService.sendCecCommand(cecMessage);
345 return true;
346 }
347
Jungshik Janga5b74142014-06-23 18:03:10 +0900348 @ServiceThreadOnly
Jungshik Jang092b4452014-06-11 15:19:17 +0900349 protected boolean handleGetCecVersion(HdmiCecMessage message) {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900350 assertRunOnServiceThread();
Jungshik Jang092b4452014-06-11 15:19:17 +0900351 int version = mService.getCecVersion();
352 HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildCecVersion(message.getDestination(),
353 message.getSource(), version);
354 mService.sendCecCommand(cecMessage);
355 return true;
356 }
357
Jungshik Janga5b74142014-06-23 18:03:10 +0900358 @ServiceThreadOnly
Jinsuk Kim83335712014-06-24 07:57:00 +0900359 protected boolean handleActiveSource(HdmiCecMessage message) {
360 return false;
361 }
362
363 @ServiceThreadOnly
364 protected boolean handleInactiveSource(HdmiCecMessage message) {
365 return false;
366 }
367
368 @ServiceThreadOnly
369 protected boolean handleRequestActiveSource(HdmiCecMessage message) {
370 return false;
371 }
372
373 @ServiceThreadOnly
Jungshik Jang092b4452014-06-11 15:19:17 +0900374 protected boolean handleGetMenuLanguage(HdmiCecMessage message) {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900375 assertRunOnServiceThread();
Jungshik Jang092b4452014-06-11 15:19:17 +0900376 Slog.w(TAG, "Only TV can handle <Get Menu Language>:" + message.toString());
Yuncheol Heo6aae6522014-08-05 14:48:37 +0900377 // 'return false' will cause to reply with <Feature Abort>.
378 return false;
Jungshik Jang092b4452014-06-11 15:19:17 +0900379 }
380
Jungshik Janga5b74142014-06-23 18:03:10 +0900381 @ServiceThreadOnly
Terry Heo795415b2014-10-01 15:03:53 +0900382 protected boolean handleSetMenuLanguage(HdmiCecMessage message) {
383 assertRunOnServiceThread();
384 Slog.w(TAG, "Only Playback device can handle <Set Menu Language>:" + message.toString());
385 // 'return false' will cause to reply with <Feature Abort>.
386 return false;
387 }
388
389 @ServiceThreadOnly
Jungshik Jang092b4452014-06-11 15:19:17 +0900390 protected boolean handleGiveOsdName(HdmiCecMessage message) {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900391 assertRunOnServiceThread();
Jungshik Jang092b4452014-06-11 15:19:17 +0900392 // Note that since this method is called after logical address allocation is done,
393 // mDeviceInfo should not be null.
394 HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildSetOsdNameCommand(
395 mAddress, message.getSource(), mDeviceInfo.getDisplayName());
396 if (cecMessage != null) {
397 mService.sendCecCommand(cecMessage);
398 } else {
399 Slog.w(TAG, "Failed to build <Get Osd Name>:" + mDeviceInfo.getDisplayName());
400 }
401 return true;
402 }
403
Jinsuk Kim92b77cf2014-06-27 16:39:26 +0900404 protected boolean handleRoutingChange(HdmiCecMessage message) {
405 return false;
406 }
407
Yuncheol Heo64bafd92014-08-11 11:17:54 +0900408 protected boolean handleRoutingInformation(HdmiCecMessage message) {
409 return false;
410 }
411
Jungshik Jang60cffce2014-06-12 18:03:04 +0900412 protected boolean handleReportPhysicalAddress(HdmiCecMessage message) {
413 return false;
414 }
415
Jungshik Jang79c58a42014-06-16 16:45:36 +0900416 protected boolean handleSystemAudioModeStatus(HdmiCecMessage message) {
417 return false;
418 }
419
420 protected boolean handleSetSystemAudioMode(HdmiCecMessage message) {
421 return false;
422 }
423
424 protected boolean handleTerminateArc(HdmiCecMessage message) {
425 return false;
426 }
427
428 protected boolean handleInitiateArc(HdmiCecMessage message) {
429 return false;
430 }
431
Jungshik Jang8fa36b12014-06-25 15:51:36 +0900432 protected boolean handleReportAudioStatus(HdmiCecMessage message) {
433 return false;
434 }
435
Jungshik Janga5b74142014-06-23 18:03:10 +0900436 @ServiceThreadOnly
Yuncheol Heo38db6292014-07-01 14:15:14 +0900437 protected boolean handleStandby(HdmiCecMessage message) {
438 assertRunOnServiceThread();
439 // Seq #12
Jinsuk Kim4d43d932014-07-03 16:43:58 +0900440 if (mService.isControlEnabled() && !mService.isProhibitMode()
Yuncheol Heo38db6292014-07-01 14:15:14 +0900441 && mService.isPowerOnOrTransient()) {
442 mService.standby();
443 return true;
444 }
445 return false;
446 }
447
448 @ServiceThreadOnly
449 protected boolean handleUserControlPressed(HdmiCecMessage message) {
450 assertRunOnServiceThread();
Terry Heo3e1564e2014-08-12 14:41:00 +0900451 mHandler.removeMessages(MSG_USER_CONTROL_RELEASE_TIMEOUT);
Yuncheol Heo38db6292014-07-01 14:15:14 +0900452 if (mService.isPowerOnOrTransient() && isPowerOffOrToggleCommand(message)) {
453 mService.standby();
454 return true;
455 } else if (mService.isPowerStandbyOrTransient() && isPowerOnOrToggleCommand(message)) {
456 mService.wakeUp();
457 return true;
458 }
Terry Heo3e1564e2014-08-12 14:41:00 +0900459
460 final long downTime = SystemClock.uptimeMillis();
461 final byte[] params = message.getParams();
Jungshik Jang73483b6b2014-09-26 14:00:59 +0900462 final int keycode = HdmiCecKeycode.cecKeycodeAndParamsToAndroidKey(params);
Terry Heo3e1564e2014-08-12 14:41:00 +0900463 int keyRepeatCount = 0;
464 if (mLastKeycode != HdmiCecKeycode.UNSUPPORTED_KEYCODE) {
465 if (keycode == mLastKeycode) {
466 keyRepeatCount = mLastKeyRepeatCount + 1;
467 } else {
468 injectKeyEvent(downTime, KeyEvent.ACTION_UP, mLastKeycode, 0);
469 }
470 }
471 mLastKeycode = keycode;
472 mLastKeyRepeatCount = keyRepeatCount;
473
474 if (keycode != HdmiCecKeycode.UNSUPPORTED_KEYCODE) {
475 injectKeyEvent(downTime, KeyEvent.ACTION_DOWN, keycode, keyRepeatCount);
476 mHandler.sendMessageDelayed(Message.obtain(mHandler, MSG_USER_CONTROL_RELEASE_TIMEOUT),
477 FOLLOWER_SAFETY_TIMEOUT);
478 return true;
479 }
Yuncheol Heo38db6292014-07-01 14:15:14 +0900480 return false;
481 }
482
Terry Heo3e1564e2014-08-12 14:41:00 +0900483 @ServiceThreadOnly
484 protected boolean handleUserControlReleased() {
485 assertRunOnServiceThread();
486 mHandler.removeMessages(MSG_USER_CONTROL_RELEASE_TIMEOUT);
487 mLastKeyRepeatCount = 0;
488 if (mLastKeycode != HdmiCecKeycode.UNSUPPORTED_KEYCODE) {
489 final long upTime = SystemClock.uptimeMillis();
490 injectKeyEvent(upTime, KeyEvent.ACTION_UP, mLastKeycode, 0);
491 mLastKeycode = HdmiCecKeycode.UNSUPPORTED_KEYCODE;
492 return true;
493 }
494 return false;
495 }
496
497 static void injectKeyEvent(long time, int action, int keycode, int repeat) {
498 KeyEvent keyEvent = KeyEvent.obtain(time, time, action, keycode,
499 repeat, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FROM_SYSTEM,
500 InputDevice.SOURCE_HDMI, null);
501 InputManager.getInstance().injectInputEvent(keyEvent,
502 InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
503 keyEvent.recycle();
504 }
505
Yuncheol Heo25c20292014-07-31 17:59:39 +0900506 static boolean isPowerOnOrToggleCommand(HdmiCecMessage message) {
Yuncheol Heo38db6292014-07-01 14:15:14 +0900507 byte[] params = message.getParams();
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900508 return message.getOpcode() == Constants.MESSAGE_USER_CONTROL_PRESSED
Jungshik Jang210d73d2014-07-04 11:11:29 +0900509 && (params[0] == HdmiCecKeycode.CEC_KEYCODE_POWER
510 || params[0] == HdmiCecKeycode.CEC_KEYCODE_POWER_ON_FUNCTION
511 || params[0] == HdmiCecKeycode.CEC_KEYCODE_POWER_TOGGLE_FUNCTION);
Yuncheol Heo38db6292014-07-01 14:15:14 +0900512 }
513
Yuncheol Heo25c20292014-07-31 17:59:39 +0900514 static boolean isPowerOffOrToggleCommand(HdmiCecMessage message) {
Yuncheol Heo38db6292014-07-01 14:15:14 +0900515 byte[] params = message.getParams();
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900516 return message.getOpcode() == Constants.MESSAGE_USER_CONTROL_PRESSED
Jungshik Jang210d73d2014-07-04 11:11:29 +0900517 && (params[0] == HdmiCecKeycode.CEC_KEYCODE_POWER
518 || params[0] == HdmiCecKeycode.CEC_KEYCODE_POWER_OFF_FUNCTION
519 || params[0] == HdmiCecKeycode.CEC_KEYCODE_POWER_TOGGLE_FUNCTION);
Yuncheol Heo38db6292014-07-01 14:15:14 +0900520 }
521
522 protected boolean handleTextViewOn(HdmiCecMessage message) {
523 return false;
524 }
525
526 protected boolean handleImageViewOn(HdmiCecMessage message) {
527 return false;
528 }
529
530 protected boolean handleSetStreamPath(HdmiCecMessage message) {
531 return false;
532 }
533
534 protected boolean handleGiveDevicePowerStatus(HdmiCecMessage message) {
535 mService.sendCecCommand(HdmiCecMessageBuilder.buildReportPowerStatus(
536 mAddress, message.getSource(), mService.getPowerStatus()));
537 return true;
538 }
539
Yuncheol Heo184b1242014-09-12 15:09:07 +0900540 protected boolean handleMenuRequest(HdmiCecMessage message) {
Terry Heo3e1564e2014-08-12 14:41:00 +0900541 // Always report menu active to receive Remote Control.
542 mService.sendCecCommand(HdmiCecMessageBuilder.buildReportMenuStatus(
543 mAddress, message.getSource(), Constants.MENU_STATE_ACTIVATED));
544 return true;
545 }
546
Yuncheol Heo184b1242014-09-12 15:09:07 +0900547 protected boolean handleMenuStatus(HdmiCecMessage message) {
548 return false;
549 }
550
Jinsuk Kim119160a2014-07-07 18:48:10 +0900551 protected boolean handleVendorCommand(HdmiCecMessage message) {
Yuncheol Heo0608b932014-10-13 16:39:18 +0900552 if (!mService.invokeVendorCommandListenersOnReceived(mDeviceType, message.getSource(),
553 message.getDestination(), message.getParams(), false)) {
Jinsuk Kimd4a94db2014-09-12 13:51:10 +0900554 // Vendor command listener may not have been registered yet. Respond with
555 // <Feature Abort> [NOT_IN_CORRECT_MODE] so that the sender can try again later.
556 mService.maySendFeatureAbortCommand(message, Constants.ABORT_NOT_IN_CORRECT_MODE);
557 }
Jinsuk Kim119160a2014-07-07 18:48:10 +0900558 return true;
559 }
560
561 protected boolean handleVendorCommandWithId(HdmiCecMessage message) {
562 byte[] params = message.getParams();
563 int vendorId = HdmiUtils.threeBytesToInt(params);
564 if (vendorId == mService.getVendorId()) {
Yuncheol Heo0608b932014-10-13 16:39:18 +0900565 if (!mService.invokeVendorCommandListenersOnReceived(mDeviceType, message.getSource(),
566 message.getDestination(), params, true)) {
Jinsuk Kimd4a94db2014-09-12 13:51:10 +0900567 mService.maySendFeatureAbortCommand(message, Constants.ABORT_NOT_IN_CORRECT_MODE);
568 }
Jinsuk Kim119160a2014-07-07 18:48:10 +0900569 } else if (message.getDestination() != Constants.ADDR_BROADCAST &&
570 message.getSource() != Constants.ADDR_UNREGISTERED) {
571 Slog.v(TAG, "Wrong direct vendor command. Replying with <Feature Abort>");
Yuncheol Heo6aae6522014-08-05 14:48:37 +0900572 mService.maySendFeatureAbortCommand(message, Constants.ABORT_UNRECOGNIZED_OPCODE);
Jinsuk Kim119160a2014-07-07 18:48:10 +0900573 } else {
574 Slog.v(TAG, "Wrong broadcast vendor command. Ignoring");
575 }
576 return true;
577 }
578
Jinsuk Kimd4a94db2014-09-12 13:51:10 +0900579 protected void sendStandby(int deviceId) {
580 // Do nothing.
581 }
582
Jungshik Jang8f2ed352014-07-07 15:02:47 +0900583 protected boolean handleSetOsdName(HdmiCecMessage message) {
584 // The default behavior of <Set Osd Name> is doing nothing.
585 return true;
586 }
587
Jungshik Jangb6591b82014-07-23 16:10:23 +0900588 protected boolean handleRecordTvScreen(HdmiCecMessage message) {
Yuncheol Heo25c20292014-07-31 17:59:39 +0900589 // The default behavior of <Record TV Screen> is replying <Feature Abort> with
590 // "Cannot provide source".
Yuncheol Heo6aae6522014-08-05 14:48:37 +0900591 mService.maySendFeatureAbortCommand(message, Constants.ABORT_CANNOT_PROVIDE_SOURCE);
Jungshik Jangb6591b82014-07-23 16:10:23 +0900592 return true;
593 }
594
Jungshik Jange5a93372014-07-25 13:41:14 +0900595 protected boolean handleTimerClearedStatus(HdmiCecMessage message) {
596 return false;
597 }
598
Jungshik Jang4480efa2014-09-04 17:08:34 +0900599 protected boolean handleReportPowerStatus(HdmiCecMessage message) {
600 return false;
601 }
602
603 protected boolean handleTimerStatus(HdmiCecMessage message) {
604 return false;
605 }
606
607 protected boolean handleRecordStatus(HdmiCecMessage message) {
608 return false;
609 }
610
Yuncheol Heo38db6292014-07-01 14:15:14 +0900611 @ServiceThreadOnly
Yuncheol Heofc44e4e2014-08-04 19:41:09 +0900612 final void handleAddressAllocated(int logicalAddress, int reason) {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900613 assertRunOnServiceThread();
Jungshik Jang3ee65722014-06-03 16:22:30 +0900614 mAddress = mPreferredAddress = logicalAddress;
Yuncheol Heofc44e4e2014-08-04 19:41:09 +0900615 onAddressAllocated(logicalAddress, reason);
Jinsuk Kimaf2acf02014-07-11 18:43:04 +0900616 setPreferredAddress(logicalAddress);
Jungshik Jang1a4485d2014-05-26 11:02:36 +0900617 }
618
Yuncheol Heob5021862014-09-02 10:36:04 +0900619 int getType() {
620 return mDeviceType;
621 }
622
Jungshik Janga5b74142014-06-23 18:03:10 +0900623 @ServiceThreadOnly
Jungshik Jang61f4fbd2014-08-06 19:21:12 +0900624 HdmiDeviceInfo getDeviceInfo() {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900625 assertRunOnServiceThread();
Jungshik Jang1a4485d2014-05-26 11:02:36 +0900626 return mDeviceInfo;
627 }
628
Jungshik Janga5b74142014-06-23 18:03:10 +0900629 @ServiceThreadOnly
Jungshik Jang61f4fbd2014-08-06 19:21:12 +0900630 void setDeviceInfo(HdmiDeviceInfo info) {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900631 assertRunOnServiceThread();
Jungshik Jang1a4485d2014-05-26 11:02:36 +0900632 mDeviceInfo = info;
633 }
634
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900635 // Returns true if the logical address is same as the argument.
Jungshik Janga5b74142014-06-23 18:03:10 +0900636 @ServiceThreadOnly
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900637 boolean isAddressOf(int addr) {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900638 assertRunOnServiceThread();
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900639 return addr == mAddress;
640 }
641
642 // Resets the logical address to unregistered(15), meaning the logical device is invalid.
Jungshik Janga5b74142014-06-23 18:03:10 +0900643 @ServiceThreadOnly
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900644 void clearAddress() {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900645 assertRunOnServiceThread();
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900646 mAddress = Constants.ADDR_UNREGISTERED;
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900647 }
648
Jungshik Janga5b74142014-06-23 18:03:10 +0900649 @ServiceThreadOnly
Jungshik Jangb509c2e2014-08-07 13:45:01 +0900650 void addAndStartAction(final HdmiCecFeatureAction action) {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900651 assertRunOnServiceThread();
Jinsuk Kim6f87b4e2014-10-10 14:40:29 +0900652 mActions.add(action);
Donghyun Chofc462b92016-05-13 21:06:02 +0900653 if (mService.isPowerStandby() || !mService.isAddressAllocated()) {
Jinsuk Kim6f87b4e2014-10-10 14:40:29 +0900654 Slog.i(TAG, "Not ready to start action. Queued for deferred start:" + action);
Yuncheol Heo38db6292014-07-01 14:15:14 +0900655 return;
656 }
Jungshik Jang79c58a42014-06-16 16:45:36 +0900657 action.start();
658 }
659
Jinsuk Kim6f87b4e2014-10-10 14:40:29 +0900660 @ServiceThreadOnly
661 void startQueuedActions() {
662 assertRunOnServiceThread();
663 for (HdmiCecFeatureAction action : mActions) {
664 if (!action.started()) {
665 Slog.i(TAG, "Starting queued action:" + action);
666 action.start();
667 }
668 }
669 }
670
Jungshik Jang79c58a42014-06-16 16:45:36 +0900671 // See if we have an action of a given type in progress.
Jungshik Janga5b74142014-06-23 18:03:10 +0900672 @ServiceThreadOnly
Jungshik Jangb509c2e2014-08-07 13:45:01 +0900673 <T extends HdmiCecFeatureAction> boolean hasAction(final Class<T> clazz) {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900674 assertRunOnServiceThread();
Jungshik Jangb509c2e2014-08-07 13:45:01 +0900675 for (HdmiCecFeatureAction action : mActions) {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900676 if (action.getClass().equals(clazz)) {
677 return true;
678 }
679 }
680 return false;
681 }
682
683 // Returns all actions matched with given class type.
Jungshik Janga5b74142014-06-23 18:03:10 +0900684 @ServiceThreadOnly
Jungshik Jangb509c2e2014-08-07 13:45:01 +0900685 <T extends HdmiCecFeatureAction> List<T> getActions(final Class<T> clazz) {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900686 assertRunOnServiceThread();
Jinsuk Kim43c23e22014-07-29 13:59:14 +0900687 List<T> actions = Collections.<T>emptyList();
Jungshik Jangb509c2e2014-08-07 13:45:01 +0900688 for (HdmiCecFeatureAction action : mActions) {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900689 if (action.getClass().equals(clazz)) {
Jinsuk Kim43c23e22014-07-29 13:59:14 +0900690 if (actions.isEmpty()) {
691 actions = new ArrayList<T>();
692 }
Jungshik Jang79c58a42014-06-16 16:45:36 +0900693 actions.add((T) action);
694 }
695 }
696 return actions;
697 }
698
699 /**
Jungshik Jangb509c2e2014-08-07 13:45:01 +0900700 * Remove the given {@link HdmiCecFeatureAction} object from the action queue.
Jungshik Jang79c58a42014-06-16 16:45:36 +0900701 *
Jungshik Jangb509c2e2014-08-07 13:45:01 +0900702 * @param action {@link HdmiCecFeatureAction} to remove
Jungshik Jang79c58a42014-06-16 16:45:36 +0900703 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900704 @ServiceThreadOnly
Jungshik Jangb509c2e2014-08-07 13:45:01 +0900705 void removeAction(final HdmiCecFeatureAction action) {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900706 assertRunOnServiceThread();
Yuncheol Heoc516d652014-07-11 18:23:24 +0900707 action.finish(false);
Jungshik Jang79c58a42014-06-16 16:45:36 +0900708 mActions.remove(action);
Yuncheol Heo38db6292014-07-01 14:15:14 +0900709 checkIfPendingActionsCleared();
Jungshik Jang79c58a42014-06-16 16:45:36 +0900710 }
711
712 // Remove all actions matched with the given Class type.
Jungshik Janga5b74142014-06-23 18:03:10 +0900713 @ServiceThreadOnly
Jungshik Jangb509c2e2014-08-07 13:45:01 +0900714 <T extends HdmiCecFeatureAction> void removeAction(final Class<T> clazz) {
Jungshik Janga5b74142014-06-23 18:03:10 +0900715 assertRunOnServiceThread();
Jungshik Jang79c58a42014-06-16 16:45:36 +0900716 removeActionExcept(clazz, null);
717 }
718
719 // Remove all actions matched with the given Class type besides |exception|.
Jungshik Janga5b74142014-06-23 18:03:10 +0900720 @ServiceThreadOnly
Jungshik Jangb509c2e2014-08-07 13:45:01 +0900721 <T extends HdmiCecFeatureAction> void removeActionExcept(final Class<T> clazz,
722 final HdmiCecFeatureAction exception) {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900723 assertRunOnServiceThread();
Jungshik Jangb509c2e2014-08-07 13:45:01 +0900724 Iterator<HdmiCecFeatureAction> iter = mActions.iterator();
Jungshik Jang79c58a42014-06-16 16:45:36 +0900725 while (iter.hasNext()) {
Jungshik Jangb509c2e2014-08-07 13:45:01 +0900726 HdmiCecFeatureAction action = iter.next();
Jungshik Jang79c58a42014-06-16 16:45:36 +0900727 if (action != exception && action.getClass().equals(clazz)) {
Yuncheol Heoc516d652014-07-11 18:23:24 +0900728 action.finish(false);
729 iter.remove();
Jungshik Jang79c58a42014-06-16 16:45:36 +0900730 }
731 }
Yuncheol Heo38db6292014-07-01 14:15:14 +0900732 checkIfPendingActionsCleared();
Jungshik Jang79c58a42014-06-16 16:45:36 +0900733 }
734
Yuncheol Heo38db6292014-07-01 14:15:14 +0900735 protected void checkIfPendingActionsCleared() {
Jungshik Jang4fc1d102014-07-09 19:24:50 +0900736 if (mActions.isEmpty() && mPendingActionClearedCallback != null) {
Yuncheol Heo26ba7fd2014-07-29 18:21:25 +0900737 PendingActionClearedCallback callback = mPendingActionClearedCallback;
738 // To prevent from calling the callback again during handling the callback itself.
739 mPendingActionClearedCallback = null;
740 callback.onCleared(this);
Yuncheol Heo38db6292014-07-01 14:15:14 +0900741 }
742 }
Jungshik Jang4fc1d102014-07-09 19:24:50 +0900743
Jungshik Jang79c58a42014-06-16 16:45:36 +0900744 protected void assertRunOnServiceThread() {
745 if (Looper.myLooper() != mService.getServiceLooper()) {
746 throw new IllegalStateException("Should run on service thread.");
747 }
748 }
749
Jinsuk Kime6e8f3d2015-05-11 14:17:04 +0900750 void setAutoDeviceOff(boolean enabled) {
751 }
752
Jungshik Jang79c58a42014-06-16 16:45:36 +0900753 /**
754 * Called when a hot-plug event issued.
755 *
756 * @param portId id of port where a hot-plug event happened
757 * @param connected whether to connected or not on the event
758 */
759 void onHotplug(int portId, boolean connected) {
760 }
761
762 final HdmiControlService getService() {
763 return mService;
764 }
765
Jungshik Janga5b74142014-06-23 18:03:10 +0900766 @ServiceThreadOnly
Jungshik Jang79c58a42014-06-16 16:45:36 +0900767 final boolean isConnectedToArcPort(int path) {
Jungshik Janga5b74142014-06-23 18:03:10 +0900768 assertRunOnServiceThread();
Jungshik Jang79c58a42014-06-16 16:45:36 +0900769 return mService.isConnectedToArcPort(path);
770 }
771
Jinsuk Kim72b7d732014-07-24 09:15:35 +0900772 ActiveSource getActiveSource() {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900773 synchronized (mLock) {
774 return mActiveSource;
775 }
776 }
777
Jinsuk Kim72b7d732014-07-24 09:15:35 +0900778 void setActiveSource(ActiveSource newActive) {
779 setActiveSource(newActive.logicalAddress, newActive.physicalAddress);
780 }
781
Jungshik Jang61f4fbd2014-08-06 19:21:12 +0900782 void setActiveSource(HdmiDeviceInfo info) {
Jinsuk Kim72b7d732014-07-24 09:15:35 +0900783 setActiveSource(info.getLogicalAddress(), info.getPhysicalAddress());
784 }
785
786 void setActiveSource(int logicalAddress, int physicalAddress) {
Jinsuk Kim83335712014-06-24 07:57:00 +0900787 synchronized (mLock) {
Jinsuk Kim72b7d732014-07-24 09:15:35 +0900788 mActiveSource.logicalAddress = logicalAddress;
789 mActiveSource.physicalAddress = physicalAddress;
Jinsuk Kim83335712014-06-24 07:57:00 +0900790 }
Jinsuk Kime9f6ed32014-08-20 17:45:22 +0900791 mService.setLastInputForMhl(Constants.INVALID_PORT_ID);
Jinsuk Kim83335712014-06-24 07:57:00 +0900792 }
793
Jungshik Jang79c58a42014-06-16 16:45:36 +0900794 int getActivePath() {
795 synchronized (mLock) {
796 return mActiveRoutingPath;
797 }
798 }
799
Jinsuk Kim83335712014-06-24 07:57:00 +0900800 void setActivePath(int path) {
801 synchronized (mLock) {
802 mActiveRoutingPath = path;
803 }
Jungshik Jang867b4e02014-08-12 13:41:30 +0900804 mService.setActivePortId(pathToPortId(path));
Jinsuk Kim83335712014-06-24 07:57:00 +0900805 }
806
Jungshik Jang79c58a42014-06-16 16:45:36 +0900807 /**
Jinsuk Kima062a932014-06-18 10:00:39 +0900808 * Returns the ID of the active HDMI port. The active port is the one that has the active
809 * routing path connected to it directly or indirectly under the device hierarchy.
Jungshik Jang79c58a42014-06-16 16:45:36 +0900810 */
Jinsuk Kima062a932014-06-18 10:00:39 +0900811 int getActivePortId() {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900812 synchronized (mLock) {
813 return mService.pathToPortId(mActiveRoutingPath);
814 }
815 }
816
Jinsuk Kima062a932014-06-18 10:00:39 +0900817 /**
818 * Update the active port.
819 *
820 * @param portId the new active port id
821 */
822 void setActivePortId(int portId) {
Jungshik Jang867b4e02014-08-12 13:41:30 +0900823 // We update active routing path instead, since we get the active port id from
824 // the active routing path.
825 setActivePath(mService.portIdToPath(portId));
Jinsuk Kima062a932014-06-18 10:00:39 +0900826 }
827
Jungshik Janga5b74142014-06-23 18:03:10 +0900828 @ServiceThreadOnly
Jungshik Jang79c58a42014-06-16 16:45:36 +0900829 HdmiCecMessageCache getCecMessageCache() {
830 assertRunOnServiceThread();
831 return mCecMessageCache;
832 }
833
Jungshik Janga5b74142014-06-23 18:03:10 +0900834 @ServiceThreadOnly
Jungshik Jang79c58a42014-06-16 16:45:36 +0900835 int pathToPortId(int newPath) {
836 assertRunOnServiceThread();
837 return mService.pathToPortId(newPath);
838 }
Yuncheol Heo38db6292014-07-01 14:15:14 +0900839
840 /**
Yuncheol Heo38db6292014-07-01 14:15:14 +0900841 * Called when the system goes to standby mode.
842 *
843 * @param initiatedByCec true if this power sequence is initiated
Jungshik Jangb3e114a2014-07-11 17:50:05 +0900844 * by the reception the CEC messages like &lt;Standby&gt;
Jinsuk Kime6e8f3d2015-05-11 14:17:04 +0900845 * @param standbyAction Intent action that drives the standby process,
846 * either {@link HdmiControlService#STANDBY_SCREEN_OFF} or
847 * {@link HdmiControlService#STANDBY_SHUTDOWN}
Yuncheol Heo38db6292014-07-01 14:15:14 +0900848 */
Jinsuk Kime6e8f3d2015-05-11 14:17:04 +0900849 protected void onStandby(boolean initiatedByCec, int standbyAction) {}
Jungshik Jang4fc1d102014-07-09 19:24:50 +0900850
851 /**
852 * Disable device. {@code callback} is used to get notified when all pending
853 * actions are completed or timeout is issued.
854 *
855 * @param initiatedByCec true if this sequence is initiated
Jungshik Jangb3e114a2014-07-11 17:50:05 +0900856 * by the reception the CEC messages like &lt;Standby&gt;
Jinsuk Kim7e4b4802015-04-20 13:17:13 +0900857 * @param originalCallback callback interface to get notified when all pending actions are
Jungshik Jangb3e114a2014-07-11 17:50:05 +0900858 * cleared
Jungshik Jang4fc1d102014-07-09 19:24:50 +0900859 */
Jungshik Jangb3e114a2014-07-11 17:50:05 +0900860 protected void disableDevice(boolean initiatedByCec,
Jinsuk Kim7e4b4802015-04-20 13:17:13 +0900861 final PendingActionClearedCallback originalCallback) {
Jungshik Jangb3e114a2014-07-11 17:50:05 +0900862 mPendingActionClearedCallback = new PendingActionClearedCallback() {
863 @Override
864 public void onCleared(HdmiCecLocalDevice device) {
865 mHandler.removeMessages(MSG_DISABLE_DEVICE_TIMEOUT);
Jinsuk Kim7e4b4802015-04-20 13:17:13 +0900866 originalCallback.onCleared(device);
Jungshik Jangb3e114a2014-07-11 17:50:05 +0900867 }
868 };
Jungshik Jang4fc1d102014-07-09 19:24:50 +0900869 mHandler.sendMessageDelayed(Message.obtain(mHandler, MSG_DISABLE_DEVICE_TIMEOUT),
870 DEVICE_CLEANUP_TIMEOUT);
871 }
872
873 @ServiceThreadOnly
874 private void handleDisableDeviceTimeout() {
875 assertRunOnServiceThread();
876
877 // If all actions are not cleared in DEVICE_CLEANUP_TIMEOUT, enforce to finish them.
878 // onCleard will be called at the last action's finish method.
Jungshik Jangb509c2e2014-08-07 13:45:01 +0900879 Iterator<HdmiCecFeatureAction> iter = mActions.iterator();
Jungshik Jang4fc1d102014-07-09 19:24:50 +0900880 while (iter.hasNext()) {
Jungshik Jangb509c2e2014-08-07 13:45:01 +0900881 HdmiCecFeatureAction action = iter.next();
Yuncheol Heoc516d652014-07-11 18:23:24 +0900882 action.finish(false);
Jungshik Jang4fc1d102014-07-09 19:24:50 +0900883 iter.remove();
884 }
Jinsuk Kim7e4b4802015-04-20 13:17:13 +0900885 if (mPendingActionClearedCallback != null) {
886 mPendingActionClearedCallback.onCleared(this);
887 }
Jungshik Jang4fc1d102014-07-09 19:24:50 +0900888 }
Jinsuk Kimc068bb52014-07-07 16:59:20 +0900889
890 /**
Donghyun Cho7609bc32016-12-02 15:31:52 +0900891 * Send a key event to other CEC device. The logical address of target device will be given by
892 * {@link #findKeyReceiverAddress}.
Jinsuk Kimc068bb52014-07-07 16:59:20 +0900893 *
Jungshik Jang4fc1d102014-07-09 19:24:50 +0900894 * @param keyCode key code defined in {@link android.view.KeyEvent}
Jinsuk Kimc068bb52014-07-07 16:59:20 +0900895 * @param isPressed {@code true} for key down event
Donghyun Cho7609bc32016-12-02 15:31:52 +0900896 * @see #findKeyReceiverAddress()
Jinsuk Kimc068bb52014-07-07 16:59:20 +0900897 */
Donghyun Cho7609bc32016-12-02 15:31:52 +0900898 @ServiceThreadOnly
Jinsuk Kimc068bb52014-07-07 16:59:20 +0900899 protected void sendKeyEvent(int keyCode, boolean isPressed) {
Donghyun Cho7609bc32016-12-02 15:31:52 +0900900 assertRunOnServiceThread();
901 if (!HdmiCecKeycode.isSupportedKeycode(keyCode)) {
902 Slog.w(TAG, "Unsupported key: " + keyCode);
903 return;
904 }
905 List<SendKeyAction> action = getActions(SendKeyAction.class);
906 int logicalAddress = findKeyReceiverAddress();
907 if (logicalAddress == Constants.ADDR_INVALID || logicalAddress == mAddress) {
908 // Don't send key event to invalid device or itself.
909 Slog.w(TAG, "Discard key event: " + keyCode + ", pressed:" + isPressed
910 + ", receiverAddr=" + logicalAddress);
911 } else if (!action.isEmpty()) {
912 action.get(0).processKeyEvent(keyCode, isPressed);
913 } else if (isPressed) {
914 addAndStartAction(new SendKeyAction(this, logicalAddress, keyCode));
915 }
916 }
917
918 /**
919 * Returns the logical address of the device which will receive key events via
920 * {@link #sendKeyEvent}.
921 *
922 * @see #sendKeyEvent(int, boolean)
923 */
924 protected int findKeyReceiverAddress() {
925 Slog.w(TAG, "findKeyReceiverAddress is not implemented");
926 return Constants.ADDR_INVALID;
Jinsuk Kimc068bb52014-07-07 16:59:20 +0900927 }
Terry Heo959d2db2014-08-28 16:45:41 +0900928
Jungshik Jang2e8f1b62014-09-03 08:28:02 +0900929 void sendUserControlPressedAndReleased(int targetAddress, int cecKeycode) {
930 mService.sendCecCommand(HdmiCecMessageBuilder.buildUserControlPressed(
931 mAddress, targetAddress, cecKeycode));
932 mService.sendCecCommand(HdmiCecMessageBuilder.buildUserControlReleased(
933 mAddress, targetAddress));
934 }
935
Terry Heo959d2db2014-08-28 16:45:41 +0900936 /**
937 * Dump internal status of HdmiCecLocalDevice object.
938 */
939 protected void dump(final IndentingPrintWriter pw) {
940 pw.println("mDeviceType: " + mDeviceType);
941 pw.println("mAddress: " + mAddress);
942 pw.println("mPreferredAddress: " + mPreferredAddress);
943 pw.println("mDeviceInfo: " + mDeviceInfo);
944 pw.println("mActiveSource: " + mActiveSource);
945 pw.println(String.format("mActiveRoutingPath: 0x%04x", mActiveRoutingPath));
946 }
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900947}