blob: 63df9a20191022890011acddf11165a1f562d344 [file] [log] [blame]
Jungshik Jang0792d372014-04-23 17:57:26 +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
19import android.annotation.Nullable;
20import android.content.Context;
Jungshik Janga1fa91f2014-05-08 20:56:41 +090021import android.hardware.hdmi.HdmiCec;
Jinsuk Kimc70d2292014-04-30 15:43:16 +090022import android.hardware.hdmi.HdmiCecDeviceInfo;
23import android.hardware.hdmi.HdmiCecMessage;
Jungshik Jang60cffce2014-06-12 18:03:04 +090024import android.hardware.hdmi.HdmiHotplugEvent;
Jinsuk Kim0340bbc2014-06-05 11:07:47 +090025import android.hardware.hdmi.HdmiPortInfo;
Jungshik Jangd643f762014-05-22 19:28:09 +090026import android.hardware.hdmi.IHdmiControlCallback;
27import android.hardware.hdmi.IHdmiControlService;
Jinsuk Kim6d97f5b2014-06-16 11:41:42 +090028import android.hardware.hdmi.IHdmiDeviceEventListener;
Jungshik Jangd643f762014-05-22 19:28:09 +090029import android.hardware.hdmi.IHdmiHotplugEventListener;
Jungshik Jangea67c182014-06-19 22:19:20 +090030import android.hardware.hdmi.IHdmiSystemAudioModeChangeListener;
Jungshik Janga858d222014-06-23 17:17:47 +090031import android.media.AudioManager;
Jungshik Jang42c98002014-06-12 13:17:44 +090032import android.os.Build;
Jungshik Jang67ea5212014-05-15 14:05:24 +090033import android.os.Handler;
Jungshik Jang0792d372014-04-23 17:57:26 +090034import android.os.HandlerThread;
Jinsuk Kim78d695d2014-05-13 16:36:15 +090035import android.os.IBinder;
Jungshik Jange9c77c82014-04-24 20:30:09 +090036import android.os.Looper;
Jinsuk Kim78d695d2014-05-13 16:36:15 +090037import android.os.RemoteException;
Jungshik Jang41d97462014-06-30 22:26:29 +090038import android.util.Log;
Jungshik Jang0792d372014-04-23 17:57:26 +090039import android.util.Slog;
Jungshik Jang3ee65722014-06-03 16:22:30 +090040import android.util.SparseArray;
Jungshik Jang8b308d92014-05-29 21:52:28 +090041import android.util.SparseIntArray;
Jinsuk Kim78d695d2014-05-13 16:36:15 +090042
Jinsuk Kim4893c7e2014-06-19 14:13:22 +090043import com.android.internal.annotations.GuardedBy;
Jungshik Jang0792d372014-04-23 17:57:26 +090044import com.android.server.SystemService;
Jungshik Janga5b74142014-06-23 18:03:10 +090045import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
Jungshik Jang3ee65722014-06-03 16:22:30 +090046import com.android.server.hdmi.HdmiCecController.AllocateAddressCallback;
Jungshik Jang0792d372014-04-23 17:57:26 +090047
Jinsuk Kim78d695d2014-05-13 16:36:15 +090048import java.util.ArrayList;
Jinsuk Kim0340bbc2014-06-05 11:07:47 +090049import java.util.Collections;
Jungshik Jang02bb4262014-05-23 16:48:31 +090050import java.util.List;
Jungshik Janga1fa91f2014-05-08 20:56:41 +090051
Jungshik Jang0792d372014-04-23 17:57:26 +090052/**
53 * Provides a service for sending and processing HDMI control messages,
54 * HDMI-CEC and MHL control command, and providing the information on both standard.
55 */
56public final class HdmiControlService extends SystemService {
57 private static final String TAG = "HdmiControlService";
58
Jinsuk Kim78d695d2014-05-13 16:36:15 +090059 // TODO: Rename the permission to HDMI_CONTROL.
60 private static final String PERMISSION = "android.permission.HDMI_CEC";
61
Jungshik Jangd643f762014-05-22 19:28:09 +090062 /**
63 * Interface to report send result.
64 */
65 interface SendMessageCallback {
66 /**
67 * Called when {@link HdmiControlService#sendCecCommand} is completed.
68 *
Yuncheol Heoece603b2014-05-23 20:10:19 +090069 * @param error result of send request.
70 * @see {@link #SEND_RESULT_SUCCESS}
71 * @see {@link #SEND_RESULT_NAK}
72 * @see {@link #SEND_RESULT_FAILURE}
Jungshik Jangd643f762014-05-22 19:28:09 +090073 */
Jungshik Jangd643f762014-05-22 19:28:09 +090074 void onSendCompleted(int error);
75 }
76
Jungshik Jang02bb4262014-05-23 16:48:31 +090077 /**
78 * Interface to get a list of available logical devices.
79 */
80 interface DevicePollingCallback {
81 /**
82 * Called when device polling is finished.
83 *
84 * @param ackedAddress a list of logical addresses of available devices
85 */
86 void onPollingFinished(List<Integer> ackedAddress);
87 }
88
Jungshik Jang0792d372014-04-23 17:57:26 +090089 // A thread to handle synchronous IO of CEC and MHL control service.
90 // Since all of CEC and MHL HAL interfaces processed in short time (< 200ms)
91 // and sparse call it shares a thread to handle IO operations.
92 private final HandlerThread mIoThread = new HandlerThread("Hdmi Control Io Thread");
93
Jinsuk Kim78d695d2014-05-13 16:36:15 +090094 // Used to synchronize the access to the service.
95 private final Object mLock = new Object();
96
Jinsuk Kim0340bbc2014-06-05 11:07:47 +090097 // Type of logical devices hosted in the system. Stored in the unmodifiable list.
98 private final List<Integer> mLocalDevices;
Jinsuk Kim78d695d2014-05-13 16:36:15 +090099
100 // List of listeners registered by callers that want to get notified of
101 // hotplug events.
Jinsuk Kim4893c7e2014-06-19 14:13:22 +0900102 @GuardedBy("mLock")
Jinsuk Kim78d695d2014-05-13 16:36:15 +0900103 private final ArrayList<IHdmiHotplugEventListener> mHotplugEventListeners = new ArrayList<>();
104
105 // List of records for hotplug event listener to handle the the caller killed in action.
Jinsuk Kim4893c7e2014-06-19 14:13:22 +0900106 @GuardedBy("mLock")
Jinsuk Kim78d695d2014-05-13 16:36:15 +0900107 private final ArrayList<HotplugEventListenerRecord> mHotplugEventListenerRecords =
108 new ArrayList<>();
109
Jinsuk Kim6d97f5b2014-06-16 11:41:42 +0900110 // List of listeners registered by callers that want to get notified of
111 // device status events.
Jinsuk Kim4893c7e2014-06-19 14:13:22 +0900112 @GuardedBy("mLock")
Jinsuk Kim6d97f5b2014-06-16 11:41:42 +0900113 private final ArrayList<IHdmiDeviceEventListener> mDeviceEventListeners = new ArrayList<>();
114
115 // List of records for device event listener to handle the the caller killed in action.
Jinsuk Kim4893c7e2014-06-19 14:13:22 +0900116 @GuardedBy("mLock")
Jinsuk Kim6d97f5b2014-06-16 11:41:42 +0900117 private final ArrayList<DeviceEventListenerRecord> mDeviceEventListenerRecords =
118 new ArrayList<>();
119
Jinsuk Kim92b77cf2014-06-27 16:39:26 +0900120 // Set to true while HDMI control is enabled. If set to false, HDMI-CEC/MHL protocol
121 // handling will be disabled and no request will be handled.
122 @GuardedBy("mLock")
123 private boolean mHdmiControlEnabled;
124
Jungshik Jangea67c182014-06-19 22:19:20 +0900125 // List of listeners registered by callers that want to get notified of
126 // system audio mode changes.
127 private final ArrayList<IHdmiSystemAudioModeChangeListener>
128 mSystemAudioModeChangeListeners = new ArrayList<>();
129 // List of records for system audio mode change to handle the the caller killed in action.
130 private final ArrayList<SystemAudioModeChangeListenerRecord>
131 mSystemAudioModeChangeListenerRecords = new ArrayList<>();
132
Jinsuk Kim4893c7e2014-06-19 14:13:22 +0900133 // Handler used to run a task in service thread.
Jinsuk Kim0340bbc2014-06-05 11:07:47 +0900134 private final Handler mHandler = new Handler();
135
Jungshik Jang0792d372014-04-23 17:57:26 +0900136 @Nullable
137 private HdmiCecController mCecController;
138
139 @Nullable
140 private HdmiMhlController mMhlController;
141
Jinsuk Kim0340bbc2014-06-05 11:07:47 +0900142 // HDMI port information. Stored in the unmodifiable list to keep the static information
143 // from being modified.
144 private List<HdmiPortInfo> mPortInfo;
145
Jungshik Jang0792d372014-04-23 17:57:26 +0900146 public HdmiControlService(Context context) {
147 super(context);
Jinsuk Kim0340bbc2014-06-05 11:07:47 +0900148 mLocalDevices = HdmiUtils.asImmutableList(getContext().getResources().getIntArray(
149 com.android.internal.R.array.config_hdmiCecLogicalDeviceType));
Jungshik Jang0792d372014-04-23 17:57:26 +0900150 }
151
152 @Override
153 public void onStart() {
Jungshik Jang2f51aec2014-05-20 14:37:38 +0900154 mIoThread.start();
Jungshik Jange9c77c82014-04-24 20:30:09 +0900155 mCecController = HdmiCecController.create(this);
Jungshik Jang3ee65722014-06-03 16:22:30 +0900156
Jinsuk Kima8a5e502014-05-15 16:51:49 +0900157 if (mCecController != null) {
Jungshik Jang3ee65722014-06-03 16:22:30 +0900158 initializeLocalDevices(mLocalDevices);
Jinsuk Kima8a5e502014-05-15 16:51:49 +0900159 } else {
Jungshik Jang0792d372014-04-23 17:57:26 +0900160 Slog.i(TAG, "Device does not support HDMI-CEC.");
161 }
162
Jungshik Jange9c77c82014-04-24 20:30:09 +0900163 mMhlController = HdmiMhlController.create(this);
Jungshik Jang0792d372014-04-23 17:57:26 +0900164 if (mMhlController == null) {
165 Slog.i(TAG, "Device does not support MHL-control.");
166 }
Jinsuk Kim0340bbc2014-06-05 11:07:47 +0900167 mPortInfo = initPortInfo();
Jinsuk Kim8692fc62014-05-29 07:39:22 +0900168 publishBinderService(Context.HDMI_CONTROL_SERVICE, new BinderService());
Yuncheol Heo63a2e062014-05-27 23:06:01 +0900169
170 // TODO: Read the preference for SystemAudioMode and initialize mSystemAudioMode and
171 // start to monitor the preference value and invoke SystemAudioActionFromTv if needed.
Jinsuk Kim92b77cf2014-06-27 16:39:26 +0900172 mHdmiControlEnabled = true;
Jungshik Jang0792d372014-04-23 17:57:26 +0900173 }
Jungshik Jange9c77c82014-04-24 20:30:09 +0900174
Jungshik Janga5b74142014-06-23 18:03:10 +0900175 @ServiceThreadOnly
Jinsuk Kim0340bbc2014-06-05 11:07:47 +0900176 private void initializeLocalDevices(final List<Integer> deviceTypes) {
Jungshik Janga5b74142014-06-23 18:03:10 +0900177 assertRunOnServiceThread();
Jungshik Jang3ee65722014-06-03 16:22:30 +0900178 // A container for [Logical Address, Local device info].
179 final SparseArray<HdmiCecLocalDevice> devices = new SparseArray<>();
180 final SparseIntArray finished = new SparseIntArray();
Jinsuk Kim13c030e2014-06-20 13:25:17 +0900181 mCecController.clearLogicalAddress();
Jungshik Jang3ee65722014-06-03 16:22:30 +0900182 for (int type : deviceTypes) {
183 final HdmiCecLocalDevice localDevice = HdmiCecLocalDevice.create(this, type);
184 localDevice.init();
185 mCecController.allocateLogicalAddress(type,
186 localDevice.getPreferredAddress(), new AllocateAddressCallback() {
187 @Override
188 public void onAllocated(int deviceType, int logicalAddress) {
189 if (logicalAddress == HdmiCec.ADDR_UNREGISTERED) {
190 Slog.e(TAG, "Failed to allocate address:[device_type:" + deviceType + "]");
191 } else {
192 HdmiCecDeviceInfo deviceInfo = createDeviceInfo(logicalAddress, deviceType);
193 localDevice.setDeviceInfo(deviceInfo);
194 mCecController.addLocalDevice(deviceType, localDevice);
195 mCecController.addLogicalAddress(logicalAddress);
196 devices.append(logicalAddress, localDevice);
197 }
198 finished.append(deviceType, logicalAddress);
199
Jinsuk Kim4893c7e2014-06-19 14:13:22 +0900200 // Address allocation completed for all devices. Notify each device.
Jinsuk Kim0340bbc2014-06-05 11:07:47 +0900201 if (deviceTypes.size() == finished.size()) {
Jungshik Jang3ee65722014-06-03 16:22:30 +0900202 notifyAddressAllocated(devices);
203 }
204 }
205 });
206 }
207 }
208
Jungshik Janga5b74142014-06-23 18:03:10 +0900209 @ServiceThreadOnly
Jungshik Jang3ee65722014-06-03 16:22:30 +0900210 private void notifyAddressAllocated(SparseArray<HdmiCecLocalDevice> devices) {
Jungshik Janga5b74142014-06-23 18:03:10 +0900211 assertRunOnServiceThread();
Jungshik Jang3ee65722014-06-03 16:22:30 +0900212 for (int i = 0; i < devices.size(); ++i) {
213 int address = devices.keyAt(i);
214 HdmiCecLocalDevice device = devices.valueAt(i);
Jinsuk Kim13c030e2014-06-20 13:25:17 +0900215 device.handleAddressAllocated(address);
Jungshik Jang3ee65722014-06-03 16:22:30 +0900216 }
217 }
218
Jinsuk Kim0340bbc2014-06-05 11:07:47 +0900219 // Initialize HDMI port information. Combine the information from CEC and MHL HAL and
220 // keep them in one place.
Jungshik Janga5b74142014-06-23 18:03:10 +0900221 @ServiceThreadOnly
Jinsuk Kim0340bbc2014-06-05 11:07:47 +0900222 private List<HdmiPortInfo> initPortInfo() {
Jungshik Janga5b74142014-06-23 18:03:10 +0900223 assertRunOnServiceThread();
Jinsuk Kim0340bbc2014-06-05 11:07:47 +0900224 HdmiPortInfo[] cecPortInfo = null;
225
226 // CEC HAL provides majority of the info while MHL does only MHL support flag for
227 // each port. Return empty array if CEC HAL didn't provide the info.
228 if (mCecController != null) {
229 cecPortInfo = mCecController.getPortInfos();
230 }
231 if (cecPortInfo == null) {
232 return Collections.emptyList();
233 }
234
235 HdmiPortInfo[] mhlPortInfo = new HdmiPortInfo[0];
236 if (mMhlController != null) {
237 // TODO: Implement plumbing logic to get MHL port information.
238 // mhlPortInfo = mMhlController.getPortInfos();
239 }
240
241 // Use the id (port number) to find the matched info between CEC and MHL to combine them
242 // into one. Leave the field `mhlSupported` to false if matched MHL entry is not found.
243 ArrayList<HdmiPortInfo> result = new ArrayList<>(cecPortInfo.length);
244 for (int i = 0; i < cecPortInfo.length; ++i) {
245 HdmiPortInfo cec = cecPortInfo[i];
246 int id = cec.getId();
247 boolean mhlInfoFound = false;
248 for (HdmiPortInfo mhl : mhlPortInfo) {
249 if (id == mhl.getId()) {
250 result.add(new HdmiPortInfo(id, cec.getType(), cec.getAddress(),
251 cec.isCecSupported(), mhl.isMhlSupported(), cec.isArcSupported()));
252 mhlInfoFound = true;
253 break;
254 }
255 }
256 if (!mhlInfoFound) {
257 result.add(cec);
258 }
259 }
260
261 return Collections.unmodifiableList(result);
262 }
263
264 /**
265 * Returns HDMI port information for the given port id.
266 *
267 * @param portId HDMI port id
268 * @return {@link HdmiPortInfo} for the given port
269 */
270 HdmiPortInfo getPortInfo(int portId) {
271 // mPortInfo is an unmodifiable list and the only reference to its inner list.
272 // No lock is necessary.
273 for (HdmiPortInfo info : mPortInfo) {
274 if (portId == info.getId()) {
275 return info;
276 }
277 }
278 return null;
279 }
280
Jungshik Jange9c77c82014-04-24 20:30:09 +0900281 /**
Jinsuk Kim401e3de2014-06-14 07:47:39 +0900282 * Returns the routing path (physical address) of the HDMI port for the given
283 * port id.
284 */
285 int portIdToPath(int portId) {
286 HdmiPortInfo portInfo = getPortInfo(portId);
287 if (portInfo == null) {
288 Slog.e(TAG, "Cannot find the port info: " + portId);
Jungshik Jang60cffce2014-06-12 18:03:04 +0900289 return HdmiConstants.INVALID_PHYSICAL_ADDRESS;
Jinsuk Kim401e3de2014-06-14 07:47:39 +0900290 }
291 return portInfo.getAddress();
292 }
293
294 /**
295 * Returns the id of HDMI port located at the top of the hierarchy of
296 * the specified routing path. For the routing path 0x1220 (1.2.2.0), for instance,
297 * the port id to be returned is the ID associated with the port address
298 * 0x1000 (1.0.0.0) which is the topmost path of the given routing path.
299 */
300 int pathToPortId(int path) {
301 int portAddress = path & HdmiConstants.ROUTING_PATH_TOP_MASK;
302 for (HdmiPortInfo info : mPortInfo) {
303 if (portAddress == info.getAddress()) {
304 return info.getId();
305 }
306 }
Jungshik Jang60cffce2014-06-12 18:03:04 +0900307 return HdmiConstants.INVALID_PORT_ID;
Jinsuk Kim401e3de2014-06-14 07:47:39 +0900308 }
309
310 /**
Jungshik Jange9c77c82014-04-24 20:30:09 +0900311 * Returns {@link Looper} for IO operation.
312 *
313 * <p>Declared as package-private.
314 */
315 Looper getIoLooper() {
316 return mIoThread.getLooper();
317 }
318
319 /**
320 * Returns {@link Looper} of main thread. Use this {@link Looper} instance
321 * for tasks that are running on main service thread.
322 *
323 * <p>Declared as package-private.
324 */
325 Looper getServiceLooper() {
Jungshik Jang67ea5212014-05-15 14:05:24 +0900326 return mHandler.getLooper();
Jungshik Jange9c77c82014-04-24 20:30:09 +0900327 }
Jinsuk Kimc70d2292014-04-30 15:43:16 +0900328
329 /**
Jungshik Jang3ee65722014-06-03 16:22:30 +0900330 * Returns physical address of the device.
331 */
332 int getPhysicalAddress() {
333 return mCecController.getPhysicalAddress();
334 }
335
336 /**
337 * Returns vendor id of CEC service.
338 */
339 int getVendorId() {
340 return mCecController.getVendorId();
341 }
342
Jungshik Janga5b74142014-06-23 18:03:10 +0900343 @ServiceThreadOnly
Jinsuk Kima6ce7702014-05-11 06:54:49 +0900344 HdmiCecDeviceInfo getDeviceInfo(int logicalAddress) {
Jinsuk Kim0340bbc2014-06-05 11:07:47 +0900345 assertRunOnServiceThread();
Jungshik Jang79c58a42014-06-16 16:45:36 +0900346 HdmiCecLocalDeviceTv tv = tv();
347 if (tv == null) {
348 return null;
349 }
350 return tv.getDeviceInfo(logicalAddress);
Jinsuk Kima6ce7702014-05-11 06:54:49 +0900351 }
352
Jungshik Jang3ee65722014-06-03 16:22:30 +0900353 /**
Jungshik Jang092b4452014-06-11 15:19:17 +0900354 * Returns version of CEC.
355 */
356 int getCecVersion() {
357 return mCecController.getVersion();
358 }
359
360 /**
Jungshik Jang60cffce2014-06-12 18:03:04 +0900361 * Whether a device of the specified physical address is connected to ARC enabled port.
362 */
363 boolean isConnectedToArcPort(int physicalAddress) {
364 for (HdmiPortInfo portInfo : mPortInfo) {
365 if (hasSameTopPort(portInfo.getAddress(), physicalAddress)
366 && portInfo.isArcSupported()) {
367 return true;
368 }
369 }
370 return false;
371 }
372
Jungshik Jang79c58a42014-06-16 16:45:36 +0900373 void runOnServiceThread(Runnable runnable) {
Jungshik Jang67ea5212014-05-15 14:05:24 +0900374 mHandler.post(runnable);
375 }
376
Yuncheol Heo63a2e062014-05-27 23:06:01 +0900377 void runOnServiceThreadAtFrontOfQueue(Runnable runnable) {
378 mHandler.postAtFrontOfQueue(runnable);
379 }
380
381 private void assertRunOnServiceThread() {
382 if (Looper.myLooper() != mHandler.getLooper()) {
383 throw new IllegalStateException("Should run on service thread.");
384 }
385 }
386
Jungshik Jang67ea5212014-05-15 14:05:24 +0900387 /**
Jinsuk Kimc70d2292014-04-30 15:43:16 +0900388 * Transmit a CEC command to CEC bus.
389 *
390 * @param command CEC command to send out
Jungshik Jangd643f762014-05-22 19:28:09 +0900391 * @param callback interface used to the result of send command
Jinsuk Kimc70d2292014-04-30 15:43:16 +0900392 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900393 @ServiceThreadOnly
Jungshik Jangd643f762014-05-22 19:28:09 +0900394 void sendCecCommand(HdmiCecMessage command, @Nullable SendMessageCallback callback) {
Jungshik Janga5b74142014-06-23 18:03:10 +0900395 assertRunOnServiceThread();
Jungshik Jangd643f762014-05-22 19:28:09 +0900396 mCecController.sendCommand(command, callback);
397 }
398
Jungshik Janga5b74142014-06-23 18:03:10 +0900399 @ServiceThreadOnly
Jungshik Jangd643f762014-05-22 19:28:09 +0900400 void sendCecCommand(HdmiCecMessage command) {
Jungshik Janga5b74142014-06-23 18:03:10 +0900401 assertRunOnServiceThread();
Jungshik Jangd643f762014-05-22 19:28:09 +0900402 mCecController.sendCommand(command, null);
Jinsuk Kimc70d2292014-04-30 15:43:16 +0900403 }
404
Jungshik Janga5b74142014-06-23 18:03:10 +0900405 @ServiceThreadOnly
Jungshik Janga1fa91f2014-05-08 20:56:41 +0900406 boolean handleCecCommand(HdmiCecMessage message) {
Jungshik Janga5b74142014-06-23 18:03:10 +0900407 assertRunOnServiceThread();
Jungshik Jang092b4452014-06-11 15:19:17 +0900408 return dispatchMessageToLocalDevice(message);
409 }
410
Jungshik Jang79c58a42014-06-16 16:45:36 +0900411 void setAudioReturnChannel(boolean enabled) {
412 mCecController.setAudioReturnChannel(enabled);
Jungshik Jang60cffce2014-06-12 18:03:04 +0900413 }
414
Jungshik Janga5b74142014-06-23 18:03:10 +0900415 @ServiceThreadOnly
Jungshik Jang092b4452014-06-11 15:19:17 +0900416 private boolean dispatchMessageToLocalDevice(HdmiCecMessage message) {
Jungshik Janga5b74142014-06-23 18:03:10 +0900417 assertRunOnServiceThread();
Jungshik Jang092b4452014-06-11 15:19:17 +0900418 for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900419 if (device.dispatchMessage(message)
420 && message.getDestination() != HdmiCec.ADDR_BROADCAST) {
Jungshik Jang092b4452014-06-11 15:19:17 +0900421 return true;
422 }
423 }
Jungshik Jang60cffce2014-06-12 18:03:04 +0900424
Jungshik Jang3a959fc2014-07-03 09:34:05 +0900425 if (message.getDestination() != HdmiCec.ADDR_BROADCAST) {
426 Slog.w(TAG, "Unhandled cec command:" + message);
427 }
Jungshik Jang092b4452014-06-11 15:19:17 +0900428 return false;
Jungshik Janga1fa91f2014-05-08 20:56:41 +0900429 }
430
Jungshik Jang67ea5212014-05-15 14:05:24 +0900431 /**
432 * Called when a new hotplug event is issued.
433 *
Jungshik Jang8b308d92014-05-29 21:52:28 +0900434 * @param portNo hdmi port number where hot plug event issued.
Jungshik Jang67ea5212014-05-15 14:05:24 +0900435 * @param connected whether to be plugged in or not
436 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900437 @ServiceThreadOnly
Jungshik Jang67ea5212014-05-15 14:05:24 +0900438 void onHotplug(int portNo, boolean connected) {
Jungshik Jang60cffce2014-06-12 18:03:04 +0900439 assertRunOnServiceThread();
Jungshik Jang79c58a42014-06-16 16:45:36 +0900440 for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
441 device.onHotplug(portNo, connected);
Jungshik Jang60cffce2014-06-12 18:03:04 +0900442 }
Jungshik Jang60cffce2014-06-12 18:03:04 +0900443 announceHotplugEvent(portNo, connected);
Jungshik Jang67ea5212014-05-15 14:05:24 +0900444 }
445
Jungshik Jang02bb4262014-05-23 16:48:31 +0900446 /**
447 * Poll all remote devices. It sends &lt;Polling Message&gt; to all remote
448 * devices.
449 *
450 * @param callback an interface used to get a list of all remote devices' address
Jungshik Jang1de51422014-07-03 11:14:26 +0900451 * @param sourceAddress a logical address of source device where sends polling message
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900452 * @param pickStrategy strategy how to pick polling candidates
Jungshik Jang02bb4262014-05-23 16:48:31 +0900453 * @param retryCount the number of retry used to send polling message to remote devices
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900454 * @throw IllegalArgumentException if {@code pickStrategy} is invalid value
Jungshik Jang02bb4262014-05-23 16:48:31 +0900455 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900456 @ServiceThreadOnly
Jungshik Jang1de51422014-07-03 11:14:26 +0900457 void pollDevices(DevicePollingCallback callback, int sourceAddress, int pickStrategy,
458 int retryCount) {
Jungshik Janga5b74142014-06-23 18:03:10 +0900459 assertRunOnServiceThread();
Jungshik Jang1de51422014-07-03 11:14:26 +0900460 mCecController.pollDevices(callback, sourceAddress, checkPollStrategy(pickStrategy),
461 retryCount);
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900462 }
463
464 private int checkPollStrategy(int pickStrategy) {
Jungshik Jang3ecdd8322014-06-17 14:04:38 +0900465 int strategy = pickStrategy & HdmiConstants.POLL_STRATEGY_MASK;
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900466 if (strategy == 0) {
467 throw new IllegalArgumentException("Invalid poll strategy:" + pickStrategy);
468 }
Jungshik Jang3ecdd8322014-06-17 14:04:38 +0900469 int iterationStrategy = pickStrategy & HdmiConstants.POLL_ITERATION_STRATEGY_MASK;
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900470 if (iterationStrategy == 0) {
471 throw new IllegalArgumentException("Invalid iteration strategy:" + pickStrategy);
472 }
473 return strategy | iterationStrategy;
Jungshik Jang02bb4262014-05-23 16:48:31 +0900474 }
475
Jungshik Jang60cffce2014-06-12 18:03:04 +0900476 List<HdmiCecLocalDevice> getAllLocalDevices() {
477 assertRunOnServiceThread();
478 return mCecController.getLocalDeviceList();
479 }
Jungshik Jang3ee65722014-06-03 16:22:30 +0900480
Jungshik Jang79c58a42014-06-16 16:45:36 +0900481 Object getServiceLock() {
482 return mLock;
483 }
484
485 void setAudioStatus(boolean mute, int volume) {
486 // TODO: Hook up with AudioManager.
Jungshik Jang3ee65722014-06-03 16:22:30 +0900487 }
488
Jungshik Jangea67c182014-06-19 22:19:20 +0900489 void announceSystemAudioModeChange(boolean enabled) {
490 for (IHdmiSystemAudioModeChangeListener listener : mSystemAudioModeChangeListeners) {
491 invokeSystemAudioModeChange(listener, enabled);
492 }
493 }
494
Jungshik Jang3ee65722014-06-03 16:22:30 +0900495 private HdmiCecDeviceInfo createDeviceInfo(int logicalAddress, int deviceType) {
Jungshik Jang42c98002014-06-12 13:17:44 +0900496 // TODO: find better name instead of model name.
497 String displayName = Build.MODEL;
Jungshik Jang3ee65722014-06-03 16:22:30 +0900498 return new HdmiCecDeviceInfo(logicalAddress,
499 getPhysicalAddress(), deviceType, getVendorId(), displayName);
500 }
501
Jinsuk Kim78d695d2014-05-13 16:36:15 +0900502 // Record class that monitors the event of the caller of being killed. Used to clean up
503 // the listener list and record list accordingly.
504 private final class HotplugEventListenerRecord implements IBinder.DeathRecipient {
505 private final IHdmiHotplugEventListener mListener;
506
507 public HotplugEventListenerRecord(IHdmiHotplugEventListener listener) {
508 mListener = listener;
509 }
510
511 @Override
512 public void binderDied() {
513 synchronized (mLock) {
514 mHotplugEventListenerRecords.remove(this);
515 mHotplugEventListeners.remove(mListener);
516 }
517 }
518 }
519
Jinsuk Kim6d97f5b2014-06-16 11:41:42 +0900520 private final class DeviceEventListenerRecord implements IBinder.DeathRecipient {
521 private final IHdmiDeviceEventListener mListener;
522
523 public DeviceEventListenerRecord(IHdmiDeviceEventListener listener) {
524 mListener = listener;
525 }
526
527 @Override
Jungshik Jangea67c182014-06-19 22:19:20 +0900528 public void binderDied() {
Jinsuk Kim6d97f5b2014-06-16 11:41:42 +0900529 synchronized (mLock) {
530 mDeviceEventListenerRecords.remove(this);
531 mDeviceEventListeners.remove(mListener);
532 }
533 }
534 }
535
Jungshik Jangea67c182014-06-19 22:19:20 +0900536 private final class SystemAudioModeChangeListenerRecord implements IBinder.DeathRecipient {
537 private IHdmiSystemAudioModeChangeListener mListener;
538
539 public SystemAudioModeChangeListenerRecord(IHdmiSystemAudioModeChangeListener listener) {
540 mListener = listener;
541 }
542
543 @Override
544 public void binderDied() {
545 synchronized (mLock) {
546 mSystemAudioModeChangeListenerRecords.remove(this);
547 mSystemAudioModeChangeListeners.remove(mListener);
548 }
549 }
550 }
551
Jinsuk Kim78d695d2014-05-13 16:36:15 +0900552 private void enforceAccessPermission() {
553 getContext().enforceCallingOrSelfPermission(PERMISSION, TAG);
554 }
555
556 private final class BinderService extends IHdmiControlService.Stub {
557 @Override
558 public int[] getSupportedTypes() {
559 enforceAccessPermission();
Jinsuk Kim0340bbc2014-06-05 11:07:47 +0900560 // mLocalDevices is an unmodifiable list - no lock necesary.
561 int[] localDevices = new int[mLocalDevices.size()];
562 for (int i = 0; i < localDevices.length; ++i) {
563 localDevices[i] = mLocalDevices.get(i);
Jinsuk Kim78d695d2014-05-13 16:36:15 +0900564 }
Jinsuk Kim0340bbc2014-06-05 11:07:47 +0900565 return localDevices;
Jinsuk Kim78d695d2014-05-13 16:36:15 +0900566 }
567
568 @Override
Jinsuk Kima6ce7702014-05-11 06:54:49 +0900569 public void deviceSelect(final int logicalAddress, final IHdmiControlCallback callback) {
570 enforceAccessPermission();
571 runOnServiceThread(new Runnable() {
572 @Override
573 public void run() {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900574 HdmiCecLocalDeviceTv tv = tv();
Jinsuk Kima6ce7702014-05-11 06:54:49 +0900575 if (tv == null) {
Jinsuk Kima062a932014-06-18 10:00:39 +0900576 Slog.w(TAG, "Local tv device not available");
Jinsuk Kima6ce7702014-05-11 06:54:49 +0900577 invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE);
578 return;
579 }
580 tv.deviceSelect(logicalAddress, callback);
581 }
582 });
583 }
584
Jinsuk Kima6ce7702014-05-11 06:54:49 +0900585 @Override
Jinsuk Kima062a932014-06-18 10:00:39 +0900586 public void portSelect(final int portId, final IHdmiControlCallback callback) {
587 enforceAccessPermission();
588 runOnServiceThread(new Runnable() {
589 @Override
590 public void run() {
591 HdmiCecLocalDeviceTv tv = tv();
592 if (tv == null) {
593 Slog.w(TAG, "Local tv device not available");
594 invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE);
595 return;
596 }
Jinsuk Kim83335712014-06-24 07:57:00 +0900597 tv.doManualPortSwitching(portId, callback);
Jinsuk Kima062a932014-06-18 10:00:39 +0900598 }
599 });
600 }
601
602 @Override
603 public void sendKeyEvent(final int keyCode, final boolean isPressed) {
604 enforceAccessPermission();
605 runOnServiceThread(new Runnable() {
606 @Override
607 public void run() {
608 // TODO: sendKeyEvent is for TV device only for now. Allow other
609 // local devices of different types to use this as well.
610 HdmiCecLocalDeviceTv tv = tv();
611 if (tv == null) {
612 Slog.w(TAG, "Local tv device not available");
613 return;
614 }
615 tv.sendKeyEvent(keyCode, isPressed);
616 }
617 });
618 }
619
620 @Override
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +0900621 public void oneTouchPlay(final IHdmiControlCallback callback) {
Jinsuk Kim78d695d2014-05-13 16:36:15 +0900622 enforceAccessPermission();
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +0900623 runOnServiceThread(new Runnable() {
624 @Override
625 public void run() {
626 HdmiControlService.this.oneTouchPlay(callback);
627 }
628 });
Jinsuk Kim78d695d2014-05-13 16:36:15 +0900629 }
630
631 @Override
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +0900632 public void queryDisplayStatus(final IHdmiControlCallback callback) {
Jinsuk Kim78d695d2014-05-13 16:36:15 +0900633 enforceAccessPermission();
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +0900634 runOnServiceThread(new Runnable() {
635 @Override
636 public void run() {
637 HdmiControlService.this.queryDisplayStatus(callback);
638 }
639 });
Jinsuk Kim78d695d2014-05-13 16:36:15 +0900640 }
641
642 @Override
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +0900643 public void addHotplugEventListener(final IHdmiHotplugEventListener listener) {
Jinsuk Kim78d695d2014-05-13 16:36:15 +0900644 enforceAccessPermission();
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +0900645 runOnServiceThread(new Runnable() {
646 @Override
647 public void run() {
648 HdmiControlService.this.addHotplugEventListener(listener);
649 }
650 });
Jinsuk Kim78d695d2014-05-13 16:36:15 +0900651 }
652
653 @Override
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +0900654 public void removeHotplugEventListener(final IHdmiHotplugEventListener listener) {
Jinsuk Kim78d695d2014-05-13 16:36:15 +0900655 enforceAccessPermission();
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +0900656 runOnServiceThread(new Runnable() {
657 @Override
658 public void run() {
659 HdmiControlService.this.removeHotplugEventListener(listener);
660 }
661 });
Jinsuk Kim78d695d2014-05-13 16:36:15 +0900662 }
Jinsuk Kim6d97f5b2014-06-16 11:41:42 +0900663
664 @Override
665 public void addDeviceEventListener(final IHdmiDeviceEventListener listener) {
666 enforceAccessPermission();
667 runOnServiceThread(new Runnable() {
668 @Override
Jungshik Jangea67c182014-06-19 22:19:20 +0900669 public void run() {
Jinsuk Kim6d97f5b2014-06-16 11:41:42 +0900670 HdmiControlService.this.addDeviceEventListener(listener);
671 }
672 });
673 }
674
675 @Override
Jinsuk Kim6d97f5b2014-06-16 11:41:42 +0900676 public List<HdmiPortInfo> getPortInfo() {
677 enforceAccessPermission();
678 return mPortInfo;
679 }
Jungshik Jangea67c182014-06-19 22:19:20 +0900680
681 @Override
682 public boolean canChangeSystemAudioMode() {
683 enforceAccessPermission();
684 HdmiCecLocalDeviceTv tv = tv();
685 if (tv == null) {
686 return false;
687 }
Jungshik Jange9cf1582014-06-23 17:28:58 +0900688 return tv.hasSystemAudioDevice();
Jungshik Jangea67c182014-06-19 22:19:20 +0900689 }
690
691 @Override
692 public boolean getSystemAudioMode() {
693 enforceAccessPermission();
694 HdmiCecLocalDeviceTv tv = tv();
695 if (tv == null) {
696 return false;
697 }
698 return tv.getSystemAudioMode();
699 }
700
701 @Override
702 public void setSystemAudioMode(final boolean enabled, final IHdmiControlCallback callback) {
703 enforceAccessPermission();
704 runOnServiceThread(new Runnable() {
705 @Override
706 public void run() {
707 HdmiCecLocalDeviceTv tv = tv();
708 if (tv == null) {
709 Slog.w(TAG, "Local tv device not available");
710 invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE);
711 return;
712 }
713 tv.changeSystemAudioMode(enabled, callback);
714 }
715 });
716 }
717
718 @Override
719 public void addSystemAudioModeChangeListener(
720 final IHdmiSystemAudioModeChangeListener listener) {
721 enforceAccessPermission();
722 HdmiControlService.this.addSystemAudioModeChangeListner(listener);
723 }
724
725 @Override
726 public void removeSystemAudioModeChangeListener(
727 final IHdmiSystemAudioModeChangeListener listener) {
728 enforceAccessPermission();
729 HdmiControlService.this.removeSystemAudioModeChangeListener(listener);
730 }
Jinsuk Kim92b77cf2014-06-27 16:39:26 +0900731
732 @Override
Jinsuk Kim160a6e52014-07-02 06:16:36 +0900733 public void setControlEnabled(final boolean enabled) {
Jinsuk Kim92b77cf2014-06-27 16:39:26 +0900734 enforceAccessPermission();
735 synchronized (mLock) {
736 mHdmiControlEnabled = enabled;
737 }
738 // TODO: Stop the running actions when disabled, and start
739 // address allocation/device discovery when enabled.
740 if (!enabled) {
741 return;
742 }
743 runOnServiceThread(new Runnable() {
744 @Override
745 public void run() {
746 HdmiCecLocalDeviceTv tv = tv();
747 if (tv == null) {
748 return;
749 }
Jinsuk Kim160a6e52014-07-02 06:16:36 +0900750 int value = enabled ? HdmiCec.ENABLED : HdmiCec.DISABLED;
751 mCecController.setOption(HdmiCec.OPTION_CEC_ENABLE, value);
752 if (mMhlController != null) {
753 mMhlController.setOption(HdmiCec.OPTION_MHL_ENABLE, value);
754 }
Jinsuk Kim92b77cf2014-06-27 16:39:26 +0900755 tv.routingAtEnableTime();
756 }
757 });
758 }
Jungshik Janga13da0d2014-06-30 16:26:06 +0900759
760 @Override
Jungshik Jang41d97462014-06-30 22:26:29 +0900761 public void setSystemAudioVolume(final int oldIndex, final int newIndex,
762 final int maxIndex) {
763 enforceAccessPermission();
764 runOnServiceThread(new Runnable() {
765 @Override
766 public void run() {
767 HdmiCecLocalDeviceTv tv = tv();
768 if (tv == null) {
769 Slog.w(TAG, "Local tv device not available");
770 return;
771 }
772 tv.changeVolume(oldIndex, newIndex - oldIndex, maxIndex);
773 }
774 });
775 }
776
777 @Override
778 public void setSystemAudioMute(final boolean mute) {
779 enforceAccessPermission();
780 runOnServiceThread(new Runnable() {
781 @Override
782 public void run() {
783 HdmiCecLocalDeviceTv tv = tv();
784 if (tv == null) {
785 Slog.w(TAG, "Local tv device not available");
786 return;
787 }
788 tv.changeMute(mute);
789 }
790 });
791 }
792
793 @Override
Jungshik Janga13da0d2014-06-30 16:26:06 +0900794 public void setArcMode(final boolean enabled) {
795 enforceAccessPermission();
796 runOnServiceThread(new Runnable() {
797 @Override
798 public void run() {
799 HdmiCecLocalDeviceTv tv = tv();
800 if (tv == null) {
Jungshik Jang41d97462014-06-30 22:26:29 +0900801 Log.w(TAG, "Local tv device not available to change arc mode.");
Jungshik Janga13da0d2014-06-30 16:26:06 +0900802 return;
803 }
804 }
805 });
806 }
Jinsuk Kim160a6e52014-07-02 06:16:36 +0900807
808 @Override
809 public void setOption(final int key, final int value) {
810 if (!isTvDevice()) {
811 return;
812 }
813 switch (key) {
814 case HdmiCec.OPTION_CEC_AUTO_WAKEUP:
815 mCecController.setOption(key, value);
816 break;
817 case HdmiCec.OPTION_CEC_AUTO_DEVICE_OFF:
818 // No need to pass this option to HAL.
819 tv().setAutoDeviceOff(value == HdmiCec.ENABLED);
820 break;
821 case HdmiCec.OPTION_MHL_INPUT_SWITCHING: // Fall through
822 case HdmiCec.OPTION_MHL_POWER_CHARGE:
823 if (mMhlController != null) {
824 mMhlController.setOption(key, value);
825 }
826 break;
827 }
828 }
829
830 private boolean isTvDevice() {
831 return tv() != null;
832 }
Jinsuk Kim78d695d2014-05-13 16:36:15 +0900833 }
834
Jungshik Janga5b74142014-06-23 18:03:10 +0900835 @ServiceThreadOnly
Jungshik Jang79c58a42014-06-16 16:45:36 +0900836 private void oneTouchPlay(final IHdmiControlCallback callback) {
837 assertRunOnServiceThread();
838 HdmiCecLocalDevicePlayback source = playback();
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +0900839 if (source == null) {
840 Slog.w(TAG, "Local playback device not available");
841 invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE);
842 return;
843 }
Jungshik Jang79c58a42014-06-16 16:45:36 +0900844 source.oneTouchPlay(callback);
Jinsuk Kim78d695d2014-05-13 16:36:15 +0900845 }
846
Jungshik Janga5b74142014-06-23 18:03:10 +0900847 @ServiceThreadOnly
Jungshik Jang79c58a42014-06-16 16:45:36 +0900848 private void queryDisplayStatus(final IHdmiControlCallback callback) {
849 assertRunOnServiceThread();
850 HdmiCecLocalDevicePlayback source = playback();
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +0900851 if (source == null) {
852 Slog.w(TAG, "Local playback device not available");
853 invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE);
854 return;
855 }
Jungshik Jang79c58a42014-06-16 16:45:36 +0900856 source.queryDisplayStatus(callback);
Jinsuk Kim78d695d2014-05-13 16:36:15 +0900857 }
858
859 private void addHotplugEventListener(IHdmiHotplugEventListener listener) {
860 HotplugEventListenerRecord record = new HotplugEventListenerRecord(listener);
861 try {
862 listener.asBinder().linkToDeath(record, 0);
863 } catch (RemoteException e) {
864 Slog.w(TAG, "Listener already died");
865 return;
866 }
867 synchronized (mLock) {
868 mHotplugEventListenerRecords.add(record);
869 mHotplugEventListeners.add(listener);
870 }
871 }
872
873 private void removeHotplugEventListener(IHdmiHotplugEventListener listener) {
874 synchronized (mLock) {
875 for (HotplugEventListenerRecord record : mHotplugEventListenerRecords) {
876 if (record.mListener.asBinder() == listener.asBinder()) {
877 listener.asBinder().unlinkToDeath(record, 0);
878 mHotplugEventListenerRecords.remove(record);
879 break;
880 }
881 }
882 mHotplugEventListeners.remove(listener);
883 }
884 }
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +0900885
Jinsuk Kim6d97f5b2014-06-16 11:41:42 +0900886 private void addDeviceEventListener(IHdmiDeviceEventListener listener) {
Jinsuk Kim4893c7e2014-06-19 14:13:22 +0900887 DeviceEventListenerRecord record = new DeviceEventListenerRecord(listener);
888 try {
889 listener.asBinder().linkToDeath(record, 0);
890 } catch (RemoteException e) {
891 Slog.w(TAG, "Listener already died");
892 return;
893 }
Jinsuk Kim6d97f5b2014-06-16 11:41:42 +0900894 synchronized (mLock) {
Jinsuk Kim4893c7e2014-06-19 14:13:22 +0900895 mDeviceEventListeners.add(listener);
896 mDeviceEventListenerRecords.add(record);
897 }
898 }
899
900 void invokeDeviceEventListeners(HdmiCecDeviceInfo device, boolean activated) {
901 synchronized (mLock) {
902 for (IHdmiDeviceEventListener listener : mDeviceEventListeners) {
903 try {
904 listener.onStatusChanged(device, activated);
905 } catch (RemoteException e) {
906 Slog.e(TAG, "Failed to report device event:" + e);
Jinsuk Kim6d97f5b2014-06-16 11:41:42 +0900907 }
908 }
Jinsuk Kim6d97f5b2014-06-16 11:41:42 +0900909 }
910 }
911
Jungshik Jangea67c182014-06-19 22:19:20 +0900912 private void addSystemAudioModeChangeListner(IHdmiSystemAudioModeChangeListener listener) {
913 SystemAudioModeChangeListenerRecord record = new SystemAudioModeChangeListenerRecord(
914 listener);
915 try {
916 listener.asBinder().linkToDeath(record, 0);
917 } catch (RemoteException e) {
918 Slog.w(TAG, "Listener already died");
919 return;
920 }
921 synchronized (mLock) {
922 mSystemAudioModeChangeListeners.add(listener);
923 mSystemAudioModeChangeListenerRecords.add(record);
924 }
925 }
926
927 private void removeSystemAudioModeChangeListener(IHdmiSystemAudioModeChangeListener listener) {
928 synchronized (mLock) {
929 for (SystemAudioModeChangeListenerRecord record :
930 mSystemAudioModeChangeListenerRecords) {
931 if (record.mListener.asBinder() == listener) {
932 listener.asBinder().unlinkToDeath(record, 0);
933 mSystemAudioModeChangeListenerRecords.remove(record);
934 break;
935 }
936 }
937 mSystemAudioModeChangeListeners.remove(listener);
938 }
939 }
940
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +0900941 private void invokeCallback(IHdmiControlCallback callback, int result) {
942 try {
943 callback.onComplete(result);
944 } catch (RemoteException e) {
945 Slog.e(TAG, "Invoking callback failed:" + e);
946 }
947 }
Yuncheol Heo63a2e062014-05-27 23:06:01 +0900948
Jungshik Jangea67c182014-06-19 22:19:20 +0900949 private void invokeSystemAudioModeChange(IHdmiSystemAudioModeChangeListener listener,
950 boolean enabled) {
951 try {
952 listener.onStatusChanged(enabled);
953 } catch (RemoteException e) {
954 Slog.e(TAG, "Invoking callback failed:" + e);
955 }
956 }
957
Jinsuk Kim4893c7e2014-06-19 14:13:22 +0900958 private void announceHotplugEvent(int portId, boolean connected) {
959 HdmiHotplugEvent event = new HdmiHotplugEvent(portId, connected);
Jungshik Jang60cffce2014-06-12 18:03:04 +0900960 synchronized (mLock) {
961 for (IHdmiHotplugEventListener listener : mHotplugEventListeners) {
Jinsuk Kim4893c7e2014-06-19 14:13:22 +0900962 invokeHotplugEventListenerLocked(listener, event);
Jungshik Jang60cffce2014-06-12 18:03:04 +0900963 }
964 }
965 }
966
Jinsuk Kim4893c7e2014-06-19 14:13:22 +0900967 private void invokeHotplugEventListenerLocked(IHdmiHotplugEventListener listener,
Jungshik Jang60cffce2014-06-12 18:03:04 +0900968 HdmiHotplugEvent event) {
969 try {
970 listener.onReceived(event);
971 } catch (RemoteException e) {
972 Slog.e(TAG, "Failed to report hotplug event:" + event.toString(), e);
973 }
Jungshik Jange81e1082014-06-05 15:37:59 +0900974 }
975
Jungshik Jang60cffce2014-06-12 18:03:04 +0900976 private static boolean hasSameTopPort(int path1, int path2) {
977 return (path1 & HdmiConstants.ROUTING_PATH_TOP_MASK)
978 == (path2 & HdmiConstants.ROUTING_PATH_TOP_MASK);
979 }
980
Jungshik Jang79c58a42014-06-16 16:45:36 +0900981 private HdmiCecLocalDeviceTv tv() {
982 return (HdmiCecLocalDeviceTv) mCecController.getLocalDevice(HdmiCec.DEVICE_TV);
983 }
984
985 private HdmiCecLocalDevicePlayback playback() {
986 return (HdmiCecLocalDevicePlayback) mCecController.getLocalDevice(HdmiCec.DEVICE_PLAYBACK);
Jungshik Jang60cffce2014-06-12 18:03:04 +0900987 }
Jungshik Janga858d222014-06-23 17:17:47 +0900988
989 AudioManager getAudioManager() {
990 return (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
991 }
Jinsuk Kim92b77cf2014-06-27 16:39:26 +0900992
993 boolean isControlEnabled() {
994 synchronized (mLock) {
995 return mHdmiControlEnabled;
996 }
997 }
Jungshik Jang0792d372014-04-23 17:57:26 +0900998}