blob: bc6a299bbb4932de979da84ae4e6dc2c76654370 [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;
37import java.util.LinkedList;
38import java.util.List;
39
Jinsuk Kim2918e9e2014-05-20 16:45:45 +090040/**
41 * Class that models a logical CEC device hosted in this system. Handles initialization,
42 * CEC commands that call for actions customized per device type.
43 */
44abstract class HdmiCecLocalDevice {
Jungshik Jang092b4452014-06-11 15:19:17 +090045 private static final String TAG = "HdmiCecLocalDevice";
Jinsuk Kim2918e9e2014-05-20 16:45:45 +090046
Jungshik Jang4fc1d102014-07-09 19:24:50 +090047 private static final int MSG_DISABLE_DEVICE_TIMEOUT = 1;
Terry Heo3e1564e2014-08-12 14:41:00 +090048 private static final int MSG_USER_CONTROL_RELEASE_TIMEOUT = 2;
Jungshik Jang4fc1d102014-07-09 19:24:50 +090049 // Timeout in millisecond for device clean up (5s).
50 // Normal actions timeout is 2s but some of them would have several sequence of timeout.
51 private static final int DEVICE_CLEANUP_TIMEOUT = 5000;
Terry Heo3e1564e2014-08-12 14:41:00 +090052 // Within the timer, a received <User Control Pressed> will start "Press and Hold" behavior.
53 // When it expires, we can assume <User Control Release> is received.
54 private static final int FOLLOWER_SAFETY_TIMEOUT = 550;
Jungshik Jang4fc1d102014-07-09 19:24:50 +090055
Jungshik Jang3ee65722014-06-03 16:22:30 +090056 protected final HdmiControlService mService;
Jinsuk Kim2918e9e2014-05-20 16:45:45 +090057 protected final int mDeviceType;
58 protected int mAddress;
59 protected int mPreferredAddress;
Jungshik Jang61f4fbd2014-08-06 19:21:12 +090060 protected HdmiDeviceInfo mDeviceInfo;
Terry Heo3e1564e2014-08-12 14:41:00 +090061 protected int mLastKeycode = HdmiCecKeycode.UNSUPPORTED_KEYCODE;
62 protected int mLastKeyRepeatCount = 0;
Jinsuk Kim2918e9e2014-05-20 16:45:45 +090063
Jinsuk Kim72b7d732014-07-24 09:15:35 +090064 static class ActiveSource {
65 int logicalAddress;
66 int physicalAddress;
67
Jinsuk Kim43c23e22014-07-29 13:59:14 +090068 public ActiveSource() {
69 invalidate();
70 }
Jinsuk Kim72b7d732014-07-24 09:15:35 +090071 public ActiveSource(int logical, int physical) {
72 logicalAddress = logical;
73 physicalAddress = physical;
74 }
75 public static ActiveSource of(int logical, int physical) {
76 return new ActiveSource(logical, physical);
77 }
78 public boolean isValid() {
79 return HdmiUtils.isValidAddress(logicalAddress);
80 }
Jinsuk Kim43c23e22014-07-29 13:59:14 +090081 public void invalidate() {
82 logicalAddress = Constants.ADDR_INVALID;
83 physicalAddress = Constants.INVALID_PHYSICAL_ADDRESS;
84 }
Jinsuk Kim72b7d732014-07-24 09:15:35 +090085 public boolean equals(int logical, int physical) {
86 return logicalAddress == logical && physicalAddress == physical;
87 }
88 @Override
89 public boolean equals(Object obj) {
90 if (obj instanceof ActiveSource) {
91 ActiveSource that = (ActiveSource) obj;
92 return that.logicalAddress == logicalAddress &&
93 that.physicalAddress == physicalAddress;
94 }
95 return false;
96 }
97 @Override
98 public int hashCode() {
99 return logicalAddress * 29 + physicalAddress;
100 }
Terry Heo959d2db2014-08-28 16:45:41 +0900101 @Override
102 public String toString() {
103 StringBuffer s = new StringBuffer();
104 String logicalAddressString = (logicalAddress == Constants.ADDR_INVALID)
105 ? "invalid" : String.format("0x%02x", logicalAddress);
106 s.append("logical_address: ").append(logicalAddressString);
107 String physicalAddressString = (physicalAddress == Constants.INVALID_PHYSICAL_ADDRESS)
108 ? "invalid" : String.format("0x%04x", physicalAddress);
109 s.append(", physical_address: ").append(physicalAddressString);
110 return s.toString();
111 }
Jinsuk Kim72b7d732014-07-24 09:15:35 +0900112 }
Jungshik Jang79c58a42014-06-16 16:45:36 +0900113 // Logical address of the active source.
114 @GuardedBy("mLock")
Jinsuk Kim43c23e22014-07-29 13:59:14 +0900115 protected final ActiveSource mActiveSource = new ActiveSource();
Jungshik Jang79c58a42014-06-16 16:45:36 +0900116
117 // Active routing path. Physical address of the active source but not all the time, such as
118 // when the new active source does not claim itself to be one. Note that we don't keep
119 // the active port id (or active input) since it can be gotten by {@link #pathToPortId(int)}.
120 @GuardedBy("mLock")
121 private int mActiveRoutingPath;
122
Jungshik Jang79c58a42014-06-16 16:45:36 +0900123 protected final HdmiCecMessageCache mCecMessageCache = new HdmiCecMessageCache();
124 protected final Object mLock;
125
126 // A collection of FeatureAction.
127 // Note that access to this collection should happen in service thread.
Jungshik Jangb509c2e2014-08-07 13:45:01 +0900128 private final LinkedList<HdmiCecFeatureAction> mActions = new LinkedList<>();
Jungshik Jang79c58a42014-06-16 16:45:36 +0900129
Yuncheol Heoc516d652014-07-11 18:23:24 +0900130 private final Handler mHandler = new Handler () {
Jungshik Jang4fc1d102014-07-09 19:24:50 +0900131 @Override
132 public void handleMessage(Message msg) {
133 switch (msg.what) {
134 case MSG_DISABLE_DEVICE_TIMEOUT:
135 handleDisableDeviceTimeout();
136 break;
Terry Heo3e1564e2014-08-12 14:41:00 +0900137 case MSG_USER_CONTROL_RELEASE_TIMEOUT:
138 handleUserControlReleased();
139 break;
Jungshik Jang4fc1d102014-07-09 19:24:50 +0900140 }
141 }
142 };
143
144 /**
145 * A callback interface to get notified when all pending action is cleared.
146 * It can be called when timeout happened.
147 */
148 interface PendingActionClearedCallback {
149 void onCleared(HdmiCecLocalDevice device);
150 }
151
152 protected PendingActionClearedCallback mPendingActionClearedCallback;
153
Jungshik Jang3ee65722014-06-03 16:22:30 +0900154 protected HdmiCecLocalDevice(HdmiControlService service, int deviceType) {
155 mService = service;
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900156 mDeviceType = deviceType;
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900157 mAddress = Constants.ADDR_UNREGISTERED;
Jungshik Jang79c58a42014-06-16 16:45:36 +0900158 mLock = service.getServiceLock();
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900159 }
160
161 // Factory method that returns HdmiCecLocalDevice of corresponding type.
Jungshik Jang3ee65722014-06-03 16:22:30 +0900162 static HdmiCecLocalDevice create(HdmiControlService service, int deviceType) {
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900163 switch (deviceType) {
Jungshik Jang61f4fbd2014-08-06 19:21:12 +0900164 case HdmiDeviceInfo.DEVICE_TV:
Jungshik Jang3ee65722014-06-03 16:22:30 +0900165 return new HdmiCecLocalDeviceTv(service);
Jungshik Jang61f4fbd2014-08-06 19:21:12 +0900166 case HdmiDeviceInfo.DEVICE_PLAYBACK:
Jungshik Jang3ee65722014-06-03 16:22:30 +0900167 return new HdmiCecLocalDevicePlayback(service);
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900168 default:
169 return null;
170 }
171 }
172
Jungshik Janga5b74142014-06-23 18:03:10 +0900173 @ServiceThreadOnly
Jungshik Jang3ee65722014-06-03 16:22:30 +0900174 void init() {
Jungshik Janga5b74142014-06-23 18:03:10 +0900175 assertRunOnServiceThread();
Jinsuk Kimaf2acf02014-07-11 18:43:04 +0900176 mPreferredAddress = getPreferredAddress();
Jungshik Jang3ee65722014-06-03 16:22:30 +0900177 }
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900178
Jungshik Jang8b308d92014-05-29 21:52:28 +0900179 /**
Jungshik Jang3ee65722014-06-03 16:22:30 +0900180 * Called once a logical address of the local device is allocated.
Jungshik Jang8b308d92014-05-29 21:52:28 +0900181 */
Yuncheol Heofc44e4e2014-08-04 19:41:09 +0900182 protected abstract void onAddressAllocated(int logicalAddress, int reason);
Jungshik Jang8b308d92014-05-29 21:52:28 +0900183
Jungshik Jang092b4452014-06-11 15:19:17 +0900184 /**
Jinsuk Kimaf2acf02014-07-11 18:43:04 +0900185 * Get the preferred logical address from system properties.
186 */
187 protected abstract int getPreferredAddress();
188
189 /**
190 * Set the preferred logical address to system properties.
191 */
192 protected abstract void setPreferredAddress(int addr);
193
194 /**
Jungshik Jang092b4452014-06-11 15:19:17 +0900195 * Dispatch incoming message.
196 *
197 * @param message incoming message
198 * @return true if consumed a message; otherwise, return false.
199 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900200 @ServiceThreadOnly
Yuncheol Heo25c20292014-07-31 17:59:39 +0900201 boolean dispatchMessage(HdmiCecMessage message) {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900202 assertRunOnServiceThread();
Jungshik Jang092b4452014-06-11 15:19:17 +0900203 int dest = message.getDestination();
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900204 if (dest != mAddress && dest != Constants.ADDR_BROADCAST) {
Jungshik Jang092b4452014-06-11 15:19:17 +0900205 return false;
206 }
Jungshik Jang79c58a42014-06-16 16:45:36 +0900207 // Cache incoming message. Note that it caches only white-listed one.
208 mCecMessageCache.cacheMessage(message);
Jungshik Jang092b4452014-06-11 15:19:17 +0900209 return onMessage(message);
210 }
211
Jungshik Janga5b74142014-06-23 18:03:10 +0900212 @ServiceThreadOnly
Jungshik Jang60cffce2014-06-12 18:03:04 +0900213 protected final boolean onMessage(HdmiCecMessage message) {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900214 assertRunOnServiceThread();
Jungshik Jang79c58a42014-06-16 16:45:36 +0900215 if (dispatchMessageToAction(message)) {
216 return true;
217 }
Jungshik Jang092b4452014-06-11 15:19:17 +0900218 switch (message.getOpcode()) {
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900219 case Constants.MESSAGE_ACTIVE_SOURCE:
Jinsuk Kim83335712014-06-24 07:57:00 +0900220 return handleActiveSource(message);
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900221 case Constants.MESSAGE_INACTIVE_SOURCE:
Jinsuk Kim83335712014-06-24 07:57:00 +0900222 return handleInactiveSource(message);
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900223 case Constants.MESSAGE_REQUEST_ACTIVE_SOURCE:
Jinsuk Kim83335712014-06-24 07:57:00 +0900224 return handleRequestActiveSource(message);
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900225 case Constants.MESSAGE_GET_MENU_LANGUAGE:
Jungshik Jang092b4452014-06-11 15:19:17 +0900226 return handleGetMenuLanguage(message);
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900227 case Constants.MESSAGE_GIVE_PHYSICAL_ADDRESS:
Jungshik Jang092b4452014-06-11 15:19:17 +0900228 return handleGivePhysicalAddress();
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900229 case Constants.MESSAGE_GIVE_OSD_NAME:
Jungshik Jang092b4452014-06-11 15:19:17 +0900230 return handleGiveOsdName(message);
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900231 case Constants.MESSAGE_GIVE_DEVICE_VENDOR_ID:
Jungshik Jang092b4452014-06-11 15:19:17 +0900232 return handleGiveDeviceVendorId();
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900233 case Constants.MESSAGE_GET_CEC_VERSION:
Jungshik Jang092b4452014-06-11 15:19:17 +0900234 return handleGetCecVersion(message);
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900235 case Constants.MESSAGE_REPORT_PHYSICAL_ADDRESS:
Jungshik Jang60cffce2014-06-12 18:03:04 +0900236 return handleReportPhysicalAddress(message);
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900237 case Constants.MESSAGE_ROUTING_CHANGE:
Jinsuk Kim92b77cf2014-06-27 16:39:26 +0900238 return handleRoutingChange(message);
Yuncheol Heo64bafd92014-08-11 11:17:54 +0900239 case Constants.MESSAGE_ROUTING_INFORMATION:
240 return handleRoutingInformation(message);
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900241 case Constants.MESSAGE_INITIATE_ARC:
Jungshik Jang79c58a42014-06-16 16:45:36 +0900242 return handleInitiateArc(message);
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900243 case Constants.MESSAGE_TERMINATE_ARC:
Jungshik Jang79c58a42014-06-16 16:45:36 +0900244 return handleTerminateArc(message);
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900245 case Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE:
Jungshik Jang79c58a42014-06-16 16:45:36 +0900246 return handleSetSystemAudioMode(message);
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900247 case Constants.MESSAGE_SYSTEM_AUDIO_MODE_STATUS:
Jungshik Jang79c58a42014-06-16 16:45:36 +0900248 return handleSystemAudioModeStatus(message);
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900249 case Constants.MESSAGE_REPORT_AUDIO_STATUS:
Jungshik Jang8fa36b12014-06-25 15:51:36 +0900250 return handleReportAudioStatus(message);
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900251 case Constants.MESSAGE_STANDBY:
Yuncheol Heo38db6292014-07-01 14:15:14 +0900252 return handleStandby(message);
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900253 case Constants.MESSAGE_TEXT_VIEW_ON:
Yuncheol Heo38db6292014-07-01 14:15:14 +0900254 return handleTextViewOn(message);
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900255 case Constants.MESSAGE_IMAGE_VIEW_ON:
Yuncheol Heo38db6292014-07-01 14:15:14 +0900256 return handleImageViewOn(message);
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900257 case Constants.MESSAGE_USER_CONTROL_PRESSED:
Yuncheol Heo38db6292014-07-01 14:15:14 +0900258 return handleUserControlPressed(message);
Terry Heo3e1564e2014-08-12 14:41:00 +0900259 case Constants.MESSAGE_USER_CONTROL_RELEASED:
260 return handleUserControlReleased();
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900261 case Constants.MESSAGE_SET_STREAM_PATH:
Yuncheol Heo38db6292014-07-01 14:15:14 +0900262 return handleSetStreamPath(message);
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900263 case Constants.MESSAGE_GIVE_DEVICE_POWER_STATUS:
Yuncheol Heo38db6292014-07-01 14:15:14 +0900264 return handleGiveDevicePowerStatus(message);
Terry Heo3e1564e2014-08-12 14:41:00 +0900265 case Constants.MESSAGE_MENU_REQUEST:
266 return handleGiveDeviceMenuStatus(message);
Jinsuk Kim119160a2014-07-07 18:48:10 +0900267 case Constants.MESSAGE_VENDOR_COMMAND:
268 return handleVendorCommand(message);
269 case Constants.MESSAGE_VENDOR_COMMAND_WITH_ID:
270 return handleVendorCommandWithId(message);
Jungshik Jang8f2ed352014-07-07 15:02:47 +0900271 case Constants.MESSAGE_SET_OSD_NAME:
272 return handleSetOsdName(message);
Jungshik Jangb6591b82014-07-23 16:10:23 +0900273 case Constants.MESSAGE_RECORD_TV_SCREEN:
274 return handleRecordTvScreen(message);
Jungshik Jange5a93372014-07-25 13:41:14 +0900275 case Constants.MESSAGE_TIMER_CLEARED_STATUS:
276 return handleTimerClearedStatus(message);
Jungshik Jang092b4452014-06-11 15:19:17 +0900277 default:
278 return false;
279 }
280 }
281
Jungshik Janga5b74142014-06-23 18:03:10 +0900282 @ServiceThreadOnly
Jungshik Jang79c58a42014-06-16 16:45:36 +0900283 private boolean dispatchMessageToAction(HdmiCecMessage message) {
Jungshik Janga5b74142014-06-23 18:03:10 +0900284 assertRunOnServiceThread();
Jungshik Jangb509c2e2014-08-07 13:45:01 +0900285 for (HdmiCecFeatureAction action : mActions) {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900286 if (action.processCommand(message)) {
287 return true;
288 }
289 }
290 return false;
291 }
292
Jungshik Janga5b74142014-06-23 18:03:10 +0900293 @ServiceThreadOnly
Jungshik Jang092b4452014-06-11 15:19:17 +0900294 protected boolean handleGivePhysicalAddress() {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900295 assertRunOnServiceThread();
296
Jungshik Jang092b4452014-06-11 15:19:17 +0900297 int physicalAddress = mService.getPhysicalAddress();
298 HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
299 mAddress, physicalAddress, mDeviceType);
300 mService.sendCecCommand(cecMessage);
301 return true;
302 }
303
Jungshik Janga5b74142014-06-23 18:03:10 +0900304 @ServiceThreadOnly
Jungshik Jang092b4452014-06-11 15:19:17 +0900305 protected boolean handleGiveDeviceVendorId() {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900306 assertRunOnServiceThread();
Jungshik Jang092b4452014-06-11 15:19:17 +0900307 int vendorId = mService.getVendorId();
308 HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildDeviceVendorIdCommand(
309 mAddress, vendorId);
310 mService.sendCecCommand(cecMessage);
311 return true;
312 }
313
Jungshik Janga5b74142014-06-23 18:03:10 +0900314 @ServiceThreadOnly
Jungshik Jang092b4452014-06-11 15:19:17 +0900315 protected boolean handleGetCecVersion(HdmiCecMessage message) {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900316 assertRunOnServiceThread();
Jungshik Jang092b4452014-06-11 15:19:17 +0900317 int version = mService.getCecVersion();
318 HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildCecVersion(message.getDestination(),
319 message.getSource(), version);
320 mService.sendCecCommand(cecMessage);
321 return true;
322 }
323
Jungshik Janga5b74142014-06-23 18:03:10 +0900324 @ServiceThreadOnly
Jinsuk Kim83335712014-06-24 07:57:00 +0900325 protected boolean handleActiveSource(HdmiCecMessage message) {
326 return false;
327 }
328
329 @ServiceThreadOnly
330 protected boolean handleInactiveSource(HdmiCecMessage message) {
331 return false;
332 }
333
334 @ServiceThreadOnly
335 protected boolean handleRequestActiveSource(HdmiCecMessage message) {
336 return false;
337 }
338
339 @ServiceThreadOnly
Jungshik Jang092b4452014-06-11 15:19:17 +0900340 protected boolean handleGetMenuLanguage(HdmiCecMessage message) {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900341 assertRunOnServiceThread();
Jungshik Jang092b4452014-06-11 15:19:17 +0900342 Slog.w(TAG, "Only TV can handle <Get Menu Language>:" + message.toString());
Yuncheol Heo6aae6522014-08-05 14:48:37 +0900343 // 'return false' will cause to reply with <Feature Abort>.
344 return false;
Jungshik Jang092b4452014-06-11 15:19:17 +0900345 }
346
Jungshik Janga5b74142014-06-23 18:03:10 +0900347 @ServiceThreadOnly
Jungshik Jang092b4452014-06-11 15:19:17 +0900348 protected boolean handleGiveOsdName(HdmiCecMessage message) {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900349 assertRunOnServiceThread();
Jungshik Jang092b4452014-06-11 15:19:17 +0900350 // Note that since this method is called after logical address allocation is done,
351 // mDeviceInfo should not be null.
352 HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildSetOsdNameCommand(
353 mAddress, message.getSource(), mDeviceInfo.getDisplayName());
354 if (cecMessage != null) {
355 mService.sendCecCommand(cecMessage);
356 } else {
357 Slog.w(TAG, "Failed to build <Get Osd Name>:" + mDeviceInfo.getDisplayName());
358 }
359 return true;
360 }
361
Jinsuk Kim92b77cf2014-06-27 16:39:26 +0900362 protected boolean handleRoutingChange(HdmiCecMessage message) {
363 return false;
364 }
365
Yuncheol Heo64bafd92014-08-11 11:17:54 +0900366 protected boolean handleRoutingInformation(HdmiCecMessage message) {
367 return false;
368 }
369
Jungshik Jang60cffce2014-06-12 18:03:04 +0900370 protected boolean handleReportPhysicalAddress(HdmiCecMessage message) {
371 return false;
372 }
373
Jungshik Jang79c58a42014-06-16 16:45:36 +0900374 protected boolean handleSystemAudioModeStatus(HdmiCecMessage message) {
375 return false;
376 }
377
378 protected boolean handleSetSystemAudioMode(HdmiCecMessage message) {
379 return false;
380 }
381
382 protected boolean handleTerminateArc(HdmiCecMessage message) {
383 return false;
384 }
385
386 protected boolean handleInitiateArc(HdmiCecMessage message) {
387 return false;
388 }
389
Jungshik Jang8fa36b12014-06-25 15:51:36 +0900390 protected boolean handleReportAudioStatus(HdmiCecMessage message) {
391 return false;
392 }
393
Jungshik Janga5b74142014-06-23 18:03:10 +0900394 @ServiceThreadOnly
Yuncheol Heo38db6292014-07-01 14:15:14 +0900395 protected boolean handleStandby(HdmiCecMessage message) {
396 assertRunOnServiceThread();
397 // Seq #12
Jinsuk Kim4d43d932014-07-03 16:43:58 +0900398 if (mService.isControlEnabled() && !mService.isProhibitMode()
Yuncheol Heo38db6292014-07-01 14:15:14 +0900399 && mService.isPowerOnOrTransient()) {
400 mService.standby();
401 return true;
402 }
403 return false;
404 }
405
406 @ServiceThreadOnly
407 protected boolean handleUserControlPressed(HdmiCecMessage message) {
408 assertRunOnServiceThread();
Terry Heo3e1564e2014-08-12 14:41:00 +0900409 mHandler.removeMessages(MSG_USER_CONTROL_RELEASE_TIMEOUT);
Yuncheol Heo38db6292014-07-01 14:15:14 +0900410 if (mService.isPowerOnOrTransient() && isPowerOffOrToggleCommand(message)) {
411 mService.standby();
412 return true;
413 } else if (mService.isPowerStandbyOrTransient() && isPowerOnOrToggleCommand(message)) {
414 mService.wakeUp();
415 return true;
416 }
Terry Heo3e1564e2014-08-12 14:41:00 +0900417
418 final long downTime = SystemClock.uptimeMillis();
419 final byte[] params = message.getParams();
Jungshik Jang5691b2f2014-08-18 16:50:12 +0900420 // Note that we don't support parameterized keycode now.
421 // TODO: translate parameterized keycode as well.
422 final int keycode = HdmiCecKeycode.cecKeyToAndroidKey(params[0]);
Terry Heo3e1564e2014-08-12 14:41:00 +0900423 int keyRepeatCount = 0;
424 if (mLastKeycode != HdmiCecKeycode.UNSUPPORTED_KEYCODE) {
425 if (keycode == mLastKeycode) {
426 keyRepeatCount = mLastKeyRepeatCount + 1;
427 } else {
428 injectKeyEvent(downTime, KeyEvent.ACTION_UP, mLastKeycode, 0);
429 }
430 }
431 mLastKeycode = keycode;
432 mLastKeyRepeatCount = keyRepeatCount;
433
434 if (keycode != HdmiCecKeycode.UNSUPPORTED_KEYCODE) {
435 injectKeyEvent(downTime, KeyEvent.ACTION_DOWN, keycode, keyRepeatCount);
436 mHandler.sendMessageDelayed(Message.obtain(mHandler, MSG_USER_CONTROL_RELEASE_TIMEOUT),
437 FOLLOWER_SAFETY_TIMEOUT);
438 return true;
439 }
Yuncheol Heo38db6292014-07-01 14:15:14 +0900440 return false;
441 }
442
Terry Heo3e1564e2014-08-12 14:41:00 +0900443 @ServiceThreadOnly
444 protected boolean handleUserControlReleased() {
445 assertRunOnServiceThread();
446 mHandler.removeMessages(MSG_USER_CONTROL_RELEASE_TIMEOUT);
447 mLastKeyRepeatCount = 0;
448 if (mLastKeycode != HdmiCecKeycode.UNSUPPORTED_KEYCODE) {
449 final long upTime = SystemClock.uptimeMillis();
450 injectKeyEvent(upTime, KeyEvent.ACTION_UP, mLastKeycode, 0);
451 mLastKeycode = HdmiCecKeycode.UNSUPPORTED_KEYCODE;
452 return true;
453 }
454 return false;
455 }
456
457 static void injectKeyEvent(long time, int action, int keycode, int repeat) {
458 KeyEvent keyEvent = KeyEvent.obtain(time, time, action, keycode,
459 repeat, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FROM_SYSTEM,
460 InputDevice.SOURCE_HDMI, null);
461 InputManager.getInstance().injectInputEvent(keyEvent,
462 InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
463 keyEvent.recycle();
464 }
465
Yuncheol Heo25c20292014-07-31 17:59:39 +0900466 static boolean isPowerOnOrToggleCommand(HdmiCecMessage message) {
Yuncheol Heo38db6292014-07-01 14:15:14 +0900467 byte[] params = message.getParams();
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900468 return message.getOpcode() == Constants.MESSAGE_USER_CONTROL_PRESSED
Jungshik Jang210d73d2014-07-04 11:11:29 +0900469 && (params[0] == HdmiCecKeycode.CEC_KEYCODE_POWER
470 || params[0] == HdmiCecKeycode.CEC_KEYCODE_POWER_ON_FUNCTION
471 || params[0] == HdmiCecKeycode.CEC_KEYCODE_POWER_TOGGLE_FUNCTION);
Yuncheol Heo38db6292014-07-01 14:15:14 +0900472 }
473
Yuncheol Heo25c20292014-07-31 17:59:39 +0900474 static boolean isPowerOffOrToggleCommand(HdmiCecMessage message) {
Yuncheol Heo38db6292014-07-01 14:15:14 +0900475 byte[] params = message.getParams();
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900476 return message.getOpcode() == Constants.MESSAGE_USER_CONTROL_PRESSED
Jungshik Jang210d73d2014-07-04 11:11:29 +0900477 && (params[0] == HdmiCecKeycode.CEC_KEYCODE_POWER
478 || params[0] == HdmiCecKeycode.CEC_KEYCODE_POWER_OFF_FUNCTION
479 || params[0] == HdmiCecKeycode.CEC_KEYCODE_POWER_TOGGLE_FUNCTION);
Yuncheol Heo38db6292014-07-01 14:15:14 +0900480 }
481
482 protected boolean handleTextViewOn(HdmiCecMessage message) {
483 return false;
484 }
485
486 protected boolean handleImageViewOn(HdmiCecMessage message) {
487 return false;
488 }
489
490 protected boolean handleSetStreamPath(HdmiCecMessage message) {
491 return false;
492 }
493
494 protected boolean handleGiveDevicePowerStatus(HdmiCecMessage message) {
495 mService.sendCecCommand(HdmiCecMessageBuilder.buildReportPowerStatus(
496 mAddress, message.getSource(), mService.getPowerStatus()));
497 return true;
498 }
499
Terry Heo3e1564e2014-08-12 14:41:00 +0900500 protected boolean handleGiveDeviceMenuStatus(HdmiCecMessage message) {
501 // Always report menu active to receive Remote Control.
502 mService.sendCecCommand(HdmiCecMessageBuilder.buildReportMenuStatus(
503 mAddress, message.getSource(), Constants.MENU_STATE_ACTIVATED));
504 return true;
505 }
506
Jinsuk Kim119160a2014-07-07 18:48:10 +0900507 protected boolean handleVendorCommand(HdmiCecMessage message) {
508 mService.invokeVendorCommandListeners(mDeviceType, message.getSource(),
509 message.getParams(), false);
510 return true;
511 }
512
513 protected boolean handleVendorCommandWithId(HdmiCecMessage message) {
514 byte[] params = message.getParams();
515 int vendorId = HdmiUtils.threeBytesToInt(params);
516 if (vendorId == mService.getVendorId()) {
517 mService.invokeVendorCommandListeners(mDeviceType, message.getSource(), params, true);
518 } else if (message.getDestination() != Constants.ADDR_BROADCAST &&
519 message.getSource() != Constants.ADDR_UNREGISTERED) {
520 Slog.v(TAG, "Wrong direct vendor command. Replying with <Feature Abort>");
Yuncheol Heo6aae6522014-08-05 14:48:37 +0900521 mService.maySendFeatureAbortCommand(message, Constants.ABORT_UNRECOGNIZED_OPCODE);
Jinsuk Kim119160a2014-07-07 18:48:10 +0900522 } else {
523 Slog.v(TAG, "Wrong broadcast vendor command. Ignoring");
524 }
525 return true;
526 }
527
Jungshik Jang8f2ed352014-07-07 15:02:47 +0900528 protected boolean handleSetOsdName(HdmiCecMessage message) {
529 // The default behavior of <Set Osd Name> is doing nothing.
530 return true;
531 }
532
Jungshik Jangb6591b82014-07-23 16:10:23 +0900533 protected boolean handleRecordTvScreen(HdmiCecMessage message) {
Yuncheol Heo25c20292014-07-31 17:59:39 +0900534 // The default behavior of <Record TV Screen> is replying <Feature Abort> with
535 // "Cannot provide source".
Yuncheol Heo6aae6522014-08-05 14:48:37 +0900536 mService.maySendFeatureAbortCommand(message, Constants.ABORT_CANNOT_PROVIDE_SOURCE);
Jungshik Jangb6591b82014-07-23 16:10:23 +0900537 return true;
538 }
539
Jungshik Jange5a93372014-07-25 13:41:14 +0900540 protected boolean handleTimerClearedStatus(HdmiCecMessage message) {
541 return false;
542 }
543
Yuncheol Heo38db6292014-07-01 14:15:14 +0900544 @ServiceThreadOnly
Yuncheol Heofc44e4e2014-08-04 19:41:09 +0900545 final void handleAddressAllocated(int logicalAddress, int reason) {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900546 assertRunOnServiceThread();
Jungshik Jang3ee65722014-06-03 16:22:30 +0900547 mAddress = mPreferredAddress = logicalAddress;
Yuncheol Heofc44e4e2014-08-04 19:41:09 +0900548 onAddressAllocated(logicalAddress, reason);
Jinsuk Kimaf2acf02014-07-11 18:43:04 +0900549 setPreferredAddress(logicalAddress);
Jungshik Jang1a4485d2014-05-26 11:02:36 +0900550 }
551
Yuncheol Heob5021862014-09-02 10:36:04 +0900552 int getType() {
553 return mDeviceType;
554 }
555
Jungshik Janga5b74142014-06-23 18:03:10 +0900556 @ServiceThreadOnly
Jungshik Jang61f4fbd2014-08-06 19:21:12 +0900557 HdmiDeviceInfo getDeviceInfo() {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900558 assertRunOnServiceThread();
Jungshik Jang1a4485d2014-05-26 11:02:36 +0900559 return mDeviceInfo;
560 }
561
Jungshik Janga5b74142014-06-23 18:03:10 +0900562 @ServiceThreadOnly
Jungshik Jang61f4fbd2014-08-06 19:21:12 +0900563 void setDeviceInfo(HdmiDeviceInfo info) {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900564 assertRunOnServiceThread();
Jungshik Jang1a4485d2014-05-26 11:02:36 +0900565 mDeviceInfo = info;
566 }
567
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900568 // Returns true if the logical address is same as the argument.
Jungshik Janga5b74142014-06-23 18:03:10 +0900569 @ServiceThreadOnly
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900570 boolean isAddressOf(int addr) {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900571 assertRunOnServiceThread();
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900572 return addr == mAddress;
573 }
574
575 // Resets the logical address to unregistered(15), meaning the logical device is invalid.
Jungshik Janga5b74142014-06-23 18:03:10 +0900576 @ServiceThreadOnly
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900577 void clearAddress() {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900578 assertRunOnServiceThread();
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900579 mAddress = Constants.ADDR_UNREGISTERED;
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900580 }
581
Jungshik Janga5b74142014-06-23 18:03:10 +0900582 @ServiceThreadOnly
Jungshik Jangb509c2e2014-08-07 13:45:01 +0900583 void addAndStartAction(final HdmiCecFeatureAction action) {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900584 assertRunOnServiceThread();
Yuncheol Heo38db6292014-07-01 14:15:14 +0900585 if (mService.isPowerStandbyOrTransient()) {
586 Slog.w(TAG, "Skip the action during Standby: " + action);
587 return;
588 }
Jungshik Jang79c58a42014-06-16 16:45:36 +0900589 mActions.add(action);
590 action.start();
591 }
592
593 // See if we have an action of a given type in progress.
Jungshik Janga5b74142014-06-23 18:03:10 +0900594 @ServiceThreadOnly
Jungshik Jangb509c2e2014-08-07 13:45:01 +0900595 <T extends HdmiCecFeatureAction> boolean hasAction(final Class<T> clazz) {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900596 assertRunOnServiceThread();
Jungshik Jangb509c2e2014-08-07 13:45:01 +0900597 for (HdmiCecFeatureAction action : mActions) {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900598 if (action.getClass().equals(clazz)) {
599 return true;
600 }
601 }
602 return false;
603 }
604
605 // Returns all actions matched with given class type.
Jungshik Janga5b74142014-06-23 18:03:10 +0900606 @ServiceThreadOnly
Jungshik Jangb509c2e2014-08-07 13:45:01 +0900607 <T extends HdmiCecFeatureAction> List<T> getActions(final Class<T> clazz) {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900608 assertRunOnServiceThread();
Jinsuk Kim43c23e22014-07-29 13:59:14 +0900609 List<T> actions = Collections.<T>emptyList();
Jungshik Jangb509c2e2014-08-07 13:45:01 +0900610 for (HdmiCecFeatureAction action : mActions) {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900611 if (action.getClass().equals(clazz)) {
Jinsuk Kim43c23e22014-07-29 13:59:14 +0900612 if (actions.isEmpty()) {
613 actions = new ArrayList<T>();
614 }
Jungshik Jang79c58a42014-06-16 16:45:36 +0900615 actions.add((T) action);
616 }
617 }
618 return actions;
619 }
620
621 /**
Jungshik Jangb509c2e2014-08-07 13:45:01 +0900622 * Remove the given {@link HdmiCecFeatureAction} object from the action queue.
Jungshik Jang79c58a42014-06-16 16:45:36 +0900623 *
Jungshik Jangb509c2e2014-08-07 13:45:01 +0900624 * @param action {@link HdmiCecFeatureAction} to remove
Jungshik Jang79c58a42014-06-16 16:45:36 +0900625 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900626 @ServiceThreadOnly
Jungshik Jangb509c2e2014-08-07 13:45:01 +0900627 void removeAction(final HdmiCecFeatureAction action) {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900628 assertRunOnServiceThread();
Yuncheol Heoc516d652014-07-11 18:23:24 +0900629 action.finish(false);
Jungshik Jang79c58a42014-06-16 16:45:36 +0900630 mActions.remove(action);
Yuncheol Heo38db6292014-07-01 14:15:14 +0900631 checkIfPendingActionsCleared();
Jungshik Jang79c58a42014-06-16 16:45:36 +0900632 }
633
634 // Remove all actions matched with the given Class type.
Jungshik Janga5b74142014-06-23 18:03:10 +0900635 @ServiceThreadOnly
Jungshik Jangb509c2e2014-08-07 13:45:01 +0900636 <T extends HdmiCecFeatureAction> void removeAction(final Class<T> clazz) {
Jungshik Janga5b74142014-06-23 18:03:10 +0900637 assertRunOnServiceThread();
Jungshik Jang79c58a42014-06-16 16:45:36 +0900638 removeActionExcept(clazz, null);
639 }
640
641 // Remove all actions matched with the given Class type besides |exception|.
Jungshik Janga5b74142014-06-23 18:03:10 +0900642 @ServiceThreadOnly
Jungshik Jangb509c2e2014-08-07 13:45:01 +0900643 <T extends HdmiCecFeatureAction> void removeActionExcept(final Class<T> clazz,
644 final HdmiCecFeatureAction exception) {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900645 assertRunOnServiceThread();
Jungshik Jangb509c2e2014-08-07 13:45:01 +0900646 Iterator<HdmiCecFeatureAction> iter = mActions.iterator();
Jungshik Jang79c58a42014-06-16 16:45:36 +0900647 while (iter.hasNext()) {
Jungshik Jangb509c2e2014-08-07 13:45:01 +0900648 HdmiCecFeatureAction action = iter.next();
Jungshik Jang79c58a42014-06-16 16:45:36 +0900649 if (action != exception && action.getClass().equals(clazz)) {
Yuncheol Heoc516d652014-07-11 18:23:24 +0900650 action.finish(false);
651 iter.remove();
Jungshik Jang79c58a42014-06-16 16:45:36 +0900652 }
653 }
Yuncheol Heo38db6292014-07-01 14:15:14 +0900654 checkIfPendingActionsCleared();
Jungshik Jang79c58a42014-06-16 16:45:36 +0900655 }
656
Yuncheol Heo38db6292014-07-01 14:15:14 +0900657 protected void checkIfPendingActionsCleared() {
Jungshik Jang4fc1d102014-07-09 19:24:50 +0900658 if (mActions.isEmpty() && mPendingActionClearedCallback != null) {
Yuncheol Heo26ba7fd2014-07-29 18:21:25 +0900659 PendingActionClearedCallback callback = mPendingActionClearedCallback;
660 // To prevent from calling the callback again during handling the callback itself.
661 mPendingActionClearedCallback = null;
662 callback.onCleared(this);
Yuncheol Heo38db6292014-07-01 14:15:14 +0900663 }
664 }
Jungshik Jang4fc1d102014-07-09 19:24:50 +0900665
Jungshik Jang79c58a42014-06-16 16:45:36 +0900666 protected void assertRunOnServiceThread() {
667 if (Looper.myLooper() != mService.getServiceLooper()) {
668 throw new IllegalStateException("Should run on service thread.");
669 }
670 }
671
672 /**
673 * Called when a hot-plug event issued.
674 *
675 * @param portId id of port where a hot-plug event happened
676 * @param connected whether to connected or not on the event
677 */
678 void onHotplug(int portId, boolean connected) {
679 }
680
681 final HdmiControlService getService() {
682 return mService;
683 }
684
Jungshik Janga5b74142014-06-23 18:03:10 +0900685 @ServiceThreadOnly
Jungshik Jang79c58a42014-06-16 16:45:36 +0900686 final boolean isConnectedToArcPort(int path) {
Jungshik Janga5b74142014-06-23 18:03:10 +0900687 assertRunOnServiceThread();
Jungshik Jang79c58a42014-06-16 16:45:36 +0900688 return mService.isConnectedToArcPort(path);
689 }
690
Jinsuk Kim72b7d732014-07-24 09:15:35 +0900691 ActiveSource getActiveSource() {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900692 synchronized (mLock) {
693 return mActiveSource;
694 }
695 }
696
Jinsuk Kim72b7d732014-07-24 09:15:35 +0900697 void setActiveSource(ActiveSource newActive) {
698 setActiveSource(newActive.logicalAddress, newActive.physicalAddress);
699 }
700
Jungshik Jang61f4fbd2014-08-06 19:21:12 +0900701 void setActiveSource(HdmiDeviceInfo info) {
Jinsuk Kim72b7d732014-07-24 09:15:35 +0900702 setActiveSource(info.getLogicalAddress(), info.getPhysicalAddress());
703 }
704
705 void setActiveSource(int logicalAddress, int physicalAddress) {
Jinsuk Kim83335712014-06-24 07:57:00 +0900706 synchronized (mLock) {
Jinsuk Kim72b7d732014-07-24 09:15:35 +0900707 mActiveSource.logicalAddress = logicalAddress;
708 mActiveSource.physicalAddress = physicalAddress;
Jinsuk Kim83335712014-06-24 07:57:00 +0900709 }
Jinsuk Kime9f6ed32014-08-20 17:45:22 +0900710 mService.setLastInputForMhl(Constants.INVALID_PORT_ID);
Jinsuk Kim83335712014-06-24 07:57:00 +0900711 }
712
Jungshik Jang79c58a42014-06-16 16:45:36 +0900713 int getActivePath() {
714 synchronized (mLock) {
715 return mActiveRoutingPath;
716 }
717 }
718
Jinsuk Kim83335712014-06-24 07:57:00 +0900719 void setActivePath(int path) {
720 synchronized (mLock) {
721 mActiveRoutingPath = path;
722 }
Jungshik Jang867b4e02014-08-12 13:41:30 +0900723 mService.setActivePortId(pathToPortId(path));
Jinsuk Kim83335712014-06-24 07:57:00 +0900724 }
725
Jungshik Jang79c58a42014-06-16 16:45:36 +0900726 /**
Jinsuk Kima062a932014-06-18 10:00:39 +0900727 * Returns the ID of the active HDMI port. The active port is the one that has the active
728 * routing path connected to it directly or indirectly under the device hierarchy.
Jungshik Jang79c58a42014-06-16 16:45:36 +0900729 */
Jinsuk Kima062a932014-06-18 10:00:39 +0900730 int getActivePortId() {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900731 synchronized (mLock) {
732 return mService.pathToPortId(mActiveRoutingPath);
733 }
734 }
735
Jinsuk Kima062a932014-06-18 10:00:39 +0900736 /**
737 * Update the active port.
738 *
739 * @param portId the new active port id
740 */
741 void setActivePortId(int portId) {
Jungshik Jang867b4e02014-08-12 13:41:30 +0900742 // We update active routing path instead, since we get the active port id from
743 // the active routing path.
744 setActivePath(mService.portIdToPath(portId));
Jinsuk Kima062a932014-06-18 10:00:39 +0900745 }
746
Jungshik Janga5b74142014-06-23 18:03:10 +0900747 @ServiceThreadOnly
Jungshik Jang79c58a42014-06-16 16:45:36 +0900748 HdmiCecMessageCache getCecMessageCache() {
749 assertRunOnServiceThread();
750 return mCecMessageCache;
751 }
752
Jungshik Janga5b74142014-06-23 18:03:10 +0900753 @ServiceThreadOnly
Jungshik Jang79c58a42014-06-16 16:45:36 +0900754 int pathToPortId(int newPath) {
755 assertRunOnServiceThread();
756 return mService.pathToPortId(newPath);
757 }
Yuncheol Heo38db6292014-07-01 14:15:14 +0900758
759 /**
Yuncheol Heo38db6292014-07-01 14:15:14 +0900760 * Called when the system goes to standby mode.
761 *
762 * @param initiatedByCec true if this power sequence is initiated
Jungshik Jangb3e114a2014-07-11 17:50:05 +0900763 * by the reception the CEC messages like &lt;Standby&gt;
Yuncheol Heo38db6292014-07-01 14:15:14 +0900764 */
Jungshik Jang4fc1d102014-07-09 19:24:50 +0900765 protected void onStandby(boolean initiatedByCec) {}
766
767 /**
768 * Disable device. {@code callback} is used to get notified when all pending
769 * actions are completed or timeout is issued.
770 *
771 * @param initiatedByCec true if this sequence is initiated
Jungshik Jangb3e114a2014-07-11 17:50:05 +0900772 * by the reception the CEC messages like &lt;Standby&gt;
773 * @param origialCallback callback interface to get notified when all pending actions are
774 * cleared
Jungshik Jang4fc1d102014-07-09 19:24:50 +0900775 */
Jungshik Jangb3e114a2014-07-11 17:50:05 +0900776 protected void disableDevice(boolean initiatedByCec,
777 final PendingActionClearedCallback origialCallback) {
778 mPendingActionClearedCallback = new PendingActionClearedCallback() {
779 @Override
780 public void onCleared(HdmiCecLocalDevice device) {
781 mHandler.removeMessages(MSG_DISABLE_DEVICE_TIMEOUT);
782 origialCallback.onCleared(device);
783 }
784 };
Jungshik Jang4fc1d102014-07-09 19:24:50 +0900785 mHandler.sendMessageDelayed(Message.obtain(mHandler, MSG_DISABLE_DEVICE_TIMEOUT),
786 DEVICE_CLEANUP_TIMEOUT);
787 }
788
789 @ServiceThreadOnly
790 private void handleDisableDeviceTimeout() {
791 assertRunOnServiceThread();
792
793 // If all actions are not cleared in DEVICE_CLEANUP_TIMEOUT, enforce to finish them.
794 // onCleard will be called at the last action's finish method.
Jungshik Jangb509c2e2014-08-07 13:45:01 +0900795 Iterator<HdmiCecFeatureAction> iter = mActions.iterator();
Jungshik Jang4fc1d102014-07-09 19:24:50 +0900796 while (iter.hasNext()) {
Jungshik Jangb509c2e2014-08-07 13:45:01 +0900797 HdmiCecFeatureAction action = iter.next();
Yuncheol Heoc516d652014-07-11 18:23:24 +0900798 action.finish(false);
Jungshik Jang4fc1d102014-07-09 19:24:50 +0900799 iter.remove();
800 }
801 }
Jinsuk Kimc068bb52014-07-07 16:59:20 +0900802
803 /**
804 * Send a key event to other device.
805 *
Jungshik Jang4fc1d102014-07-09 19:24:50 +0900806 * @param keyCode key code defined in {@link android.view.KeyEvent}
Jinsuk Kimc068bb52014-07-07 16:59:20 +0900807 * @param isPressed {@code true} for key down event
808 */
809 protected void sendKeyEvent(int keyCode, boolean isPressed) {
810 Slog.w(TAG, "sendKeyEvent not implemented");
811 }
Terry Heo959d2db2014-08-28 16:45:41 +0900812
813 /**
814 * Dump internal status of HdmiCecLocalDevice object.
815 */
816 protected void dump(final IndentingPrintWriter pw) {
817 pw.println("mDeviceType: " + mDeviceType);
818 pw.println("mAddress: " + mAddress);
819 pw.println("mPreferredAddress: " + mPreferredAddress);
820 pw.println("mDeviceInfo: " + mDeviceInfo);
821 pw.println("mActiveSource: " + mActiveSource);
822 pw.println(String.format("mActiveRoutingPath: 0x%04x", mActiveRoutingPath));
823 }
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900824}