blob: 292f1cd49ee680f26a4a13b35a0b0c042f0fbe28 [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
Jungshik Jange9c77c82014-04-24 20:30:09 +090019import android.hardware.hdmi.HdmiCec;
Jungshik Jang7d9a8432014-04-29 15:12:43 +090020import android.hardware.hdmi.HdmiCecDeviceInfo;
Jungshik Jange9c77c82014-04-24 20:30:09 +090021import android.hardware.hdmi.HdmiCecMessage;
Jungshik Jang0792d372014-04-23 17:57:26 +090022import android.os.Handler;
Jungshik Jang02bb4262014-05-23 16:48:31 +090023import android.os.Looper;
Jungshik Jang4085d0e2014-05-27 19:52:39 +090024import android.os.MessageQueue;
Yuncheol Heoece603b2014-05-23 20:10:19 +090025import android.util.Slog;
Jungshik Jang7d9a8432014-04-29 15:12:43 +090026import android.util.SparseArray;
Jungshik Jange9c77c82014-04-24 20:30:09 +090027
Jungshik Jang02bb4262014-05-23 16:48:31 +090028import com.android.server.hdmi.HdmiControlService.DevicePollingCallback;
29
Jungshik Jang3f74ab02014-04-30 14:31:02 +090030import libcore.util.EmptyArray;
31
Jungshik Jang7d9a8432014-04-29 15:12:43 +090032import java.util.ArrayList;
Jungshik Jang7d9a8432014-04-29 15:12:43 +090033import java.util.List;
Jungshik Jang0792d372014-04-23 17:57:26 +090034
35/**
36 * Manages HDMI-CEC command and behaviors. It converts user's command into CEC command
37 * and pass it to CEC HAL so that it sends message to other device. For incoming
38 * message it translates the message and delegates it to proper module.
39 *
Jungshik Jang02bb4262014-05-23 16:48:31 +090040 * <p>It should be careful to access member variables on IO thread because
41 * it can be accessed from system thread as well.
42 *
Jungshik Jang0792d372014-04-23 17:57:26 +090043 * <p>It can be created only by {@link HdmiCecController#create}
44 *
45 * <p>Declared as package-private, accessed by {@link HdmiControlService} only.
46 */
Jungshik Janga9095ba2014-05-02 13:06:22 +090047final class HdmiCecController {
Jungshik Jang0792d372014-04-23 17:57:26 +090048 private static final String TAG = "HdmiCecController";
49
Jungshik Jang3f74ab02014-04-30 14:31:02 +090050 private static final byte[] EMPTY_BODY = EmptyArray.BYTE;
51
Jungshik Jange9c77c82014-04-24 20:30:09 +090052 // A message to pass cec send command to IO looper.
53 private static final int MSG_SEND_CEC_COMMAND = 1;
Jungshik Jang3f74ab02014-04-30 14:31:02 +090054 // A message to delegate logical allocation to IO looper.
55 private static final int MSG_ALLOCATE_LOGICAL_ADDRESS = 2;
Jungshik Jange9c77c82014-04-24 20:30:09 +090056
57 // Message types to handle incoming message in main service looper.
58 private final static int MSG_RECEIVE_CEC_COMMAND = 1;
Jungshik Jang3f74ab02014-04-30 14:31:02 +090059 // A message to report allocated logical address to main control looper.
60 private final static int MSG_REPORT_LOGICAL_ADDRESS = 2;
Jungshik Jange9c77c82014-04-24 20:30:09 +090061
Jungshik Jang3f74ab02014-04-30 14:31:02 +090062 private static final int NUM_LOGICAL_ADDRESS = 16;
63
Jungshik Jang02bb4262014-05-23 16:48:31 +090064 private static final int RETRY_COUNT_FOR_LOGICAL_ADDRESS_ALLOCATION = 3;
65
Jungshik Jang0792d372014-04-23 17:57:26 +090066 // Handler instance to process synchronous I/O (mainly send) message.
67 private Handler mIoHandler;
68
69 // Handler instance to process various messages coming from other CEC
70 // device or issued by internal state change.
Jungshik Jange9c77c82014-04-24 20:30:09 +090071 private Handler mControlHandler;
Jungshik Jang0792d372014-04-23 17:57:26 +090072
73 // Stores the pointer to the native implementation of the service that
74 // interacts with HAL.
Jungshik Jang02bb4262014-05-23 16:48:31 +090075 private volatile long mNativePtr;
Jungshik Jang0792d372014-04-23 17:57:26 +090076
Jungshik Janga1fa91f2014-05-08 20:56:41 +090077 private HdmiControlService mService;
78
Jungshik Jang1a4485d2014-05-26 11:02:36 +090079 // Map-like container of all cec devices including local ones.
80 // A logical address of device is used as key of container.
Jungshik Jang02bb4262014-05-23 16:48:31 +090081 private final SparseArray<HdmiCecDeviceInfo> mDeviceInfos = new SparseArray<>();
Jinsuk Kim2918e9e2014-05-20 16:45:45 +090082
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +090083 // Stores the local CEC devices in the system. Device type is used for key.
84 private final SparseArray<HdmiCecLocalDevice> mLocalDevices = new SparseArray<>();
Jungshik Jang7d9a8432014-04-29 15:12:43 +090085
Jungshik Jang0792d372014-04-23 17:57:26 +090086 // Private constructor. Use HdmiCecController.create().
87 private HdmiCecController() {
88 }
89
90 /**
91 * A factory method to get {@link HdmiCecController}. If it fails to initialize
92 * inner device or has no device it will return {@code null}.
93 *
94 * <p>Declared as package-private, accessed by {@link HdmiControlService} only.
Jungshik Jange9c77c82014-04-24 20:30:09 +090095 * @param service {@link HdmiControlService} instance used to create internal handler
96 * and to pass callback for incoming message or event.
Jungshik Jang0792d372014-04-23 17:57:26 +090097 * @return {@link HdmiCecController} if device is initialized successfully. Otherwise,
98 * returns {@code null}.
99 */
Jungshik Jange9c77c82014-04-24 20:30:09 +0900100 static HdmiCecController create(HdmiControlService service) {
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900101 HdmiCecController controller = new HdmiCecController();
Jungshik Jang4085d0e2014-05-27 19:52:39 +0900102 long nativePtr = nativeInit(controller, service.getServiceLooper().getQueue());
Jungshik Jang0792d372014-04-23 17:57:26 +0900103 if (nativePtr == 0L) {
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900104 controller = null;
Jungshik Jang0792d372014-04-23 17:57:26 +0900105 return null;
106 }
107
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900108 controller.init(service, nativePtr);
109 return controller;
110 }
111
112 private void init(HdmiControlService service, long nativePtr) {
113 mService = service;
114 mIoHandler = new Handler(service.getServiceLooper());
115 mControlHandler = new Handler(service.getServiceLooper());
116 mNativePtr = nativePtr;
Jungshik Jang0792d372014-04-23 17:57:26 +0900117 }
118
Jungshik Jang3f74ab02014-04-30 14:31:02 +0900119 /**
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900120 * Perform initialization for each hosted device.
Jinsuk Kima8a5e502014-05-15 16:51:49 +0900121 *
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900122 * @param deviceTypes array of device types
Jinsuk Kima8a5e502014-05-15 16:51:49 +0900123 */
124 void initializeLocalDevices(int[] deviceTypes) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900125 assertRunOnServiceThread();
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900126 for (int type : deviceTypes) {
127 HdmiCecLocalDevice device = HdmiCecLocalDevice.create(this, type);
128 if (device == null) {
129 continue;
Jinsuk Kima8a5e502014-05-15 16:51:49 +0900130 }
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900131 // TODO: Consider restoring the local device addresses from persistent storage
132 // to allocate the same addresses again if possible.
133 device.setPreferredAddress(HdmiCec.ADDR_UNREGISTERED);
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +0900134 mLocalDevices.put(type, device);
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900135 device.init();
Jinsuk Kima8a5e502014-05-15 16:51:49 +0900136 }
Jinsuk Kima8a5e502014-05-15 16:51:49 +0900137 }
138
139 /**
Jungshik Jang3f74ab02014-04-30 14:31:02 +0900140 * Interface to report allocated logical address.
141 */
142 interface AllocateLogicalAddressCallback {
143 /**
144 * Called when a new logical address is allocated.
145 *
146 * @param deviceType requested device type to allocate logical address
147 * @param logicalAddress allocated logical address. If it is
148 * {@link HdmiCec#ADDR_UNREGISTERED}, it means that
149 * it failed to allocate logical address for the given device type
150 */
151 void onAllocated(int deviceType, int logicalAddress);
152 }
153
154 /**
155 * Allocate a new logical address of the given device type. Allocated
156 * address will be reported through {@link AllocateLogicalAddressCallback}.
157 *
158 * <p> Declared as package-private, accessed by {@link HdmiControlService} only.
159 *
160 * @param deviceType type of device to used to determine logical address
161 * @param preferredAddress a logical address preferred to be allocated.
162 * If sets {@link HdmiCec#ADDR_UNREGISTERED}, scans
163 * the smallest logical address matched with the given device type.
164 * Otherwise, scan address will start from {@code preferredAddress}
165 * @param callback callback interface to report allocated logical address to caller
166 */
Jungshik Jangd643f762014-05-22 19:28:09 +0900167 void allocateLogicalAddress(final int deviceType, final int preferredAddress,
168 final AllocateLogicalAddressCallback callback) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900169 assertRunOnServiceThread();
170
Jungshik Jangd643f762014-05-22 19:28:09 +0900171 runOnIoThread(new Runnable() {
172 @Override
173 public void run() {
174 handleAllocateLogicalAddress(deviceType, preferredAddress, callback);
175 }
176 });
177 }
178
179 private void handleAllocateLogicalAddress(final int deviceType, int preferredAddress,
180 final AllocateLogicalAddressCallback callback) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900181 assertRunOnIoThread();
Jungshik Jangd643f762014-05-22 19:28:09 +0900182 int startAddress = preferredAddress;
183 // If preferred address is "unregistered", start address will be the smallest
184 // address matched with the given device type.
185 if (preferredAddress == HdmiCec.ADDR_UNREGISTERED) {
186 for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) {
187 if (deviceType == HdmiCec.getTypeFromAddress(i)) {
188 startAddress = i;
189 break;
190 }
191 }
192 }
193
194 int logicalAddress = HdmiCec.ADDR_UNREGISTERED;
195 // Iterates all possible addresses which has the same device type.
196 for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) {
197 int curAddress = (startAddress + i) % NUM_LOGICAL_ADDRESS;
198 if (curAddress != HdmiCec.ADDR_UNREGISTERED
Jinsuk Kim0bc8b072014-05-27 17:23:27 +0900199 && deviceType == HdmiCec.getTypeFromAddress(curAddress)) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900200 if (!sendPollMessage(curAddress, RETRY_COUNT_FOR_LOGICAL_ADDRESS_ALLOCATION)) {
Jungshik Jangd643f762014-05-22 19:28:09 +0900201 logicalAddress = curAddress;
202 break;
203 }
204 }
205 }
206
207 final int assignedAddress = logicalAddress;
208 if (callback != null) {
209 runOnServiceThread(new Runnable() {
210 @Override
211 public void run() {
212 callback.onAllocated(deviceType, assignedAddress);
213 }
214 });
215 }
Jungshik Jang3f74ab02014-04-30 14:31:02 +0900216 }
217
Jungshik Jange9c77c82014-04-24 20:30:09 +0900218 private static byte[] buildBody(int opcode, byte[] params) {
219 byte[] body = new byte[params.length + 1];
220 body[0] = (byte) opcode;
221 System.arraycopy(params, 0, body, 1, params.length);
222 return body;
223 }
Jungshik Jang0792d372014-04-23 17:57:26 +0900224
Jungshik Jang7d9a8432014-04-29 15:12:43 +0900225 /**
226 * Add a new {@link HdmiCecDeviceInfo}. It returns old device info which has the same
227 * logical address as new device info's.
228 *
229 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
230 *
231 * @param deviceInfo a new {@link HdmiCecDeviceInfo} to be added.
232 * @return {@code null} if it is new device. Otherwise, returns old {@HdmiCecDeviceInfo}
233 * that has the same logical address as new one has.
234 */
235 HdmiCecDeviceInfo addDeviceInfo(HdmiCecDeviceInfo deviceInfo) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900236 assertRunOnServiceThread();
Jungshik Jang7d9a8432014-04-29 15:12:43 +0900237 HdmiCecDeviceInfo oldDeviceInfo = getDeviceInfo(deviceInfo.getLogicalAddress());
238 if (oldDeviceInfo != null) {
239 removeDeviceInfo(deviceInfo.getLogicalAddress());
240 }
241 mDeviceInfos.append(deviceInfo.getLogicalAddress(), deviceInfo);
242 return oldDeviceInfo;
243 }
244
245 /**
246 * Remove a device info corresponding to the given {@code logicalAddress}.
247 * It returns removed {@link HdmiCecDeviceInfo} if exists.
248 *
249 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
250 *
251 * @param logicalAddress logical address of device to be removed
252 * @return removed {@link HdmiCecDeviceInfo} it exists. Otherwise, returns {@code null}
253 */
254 HdmiCecDeviceInfo removeDeviceInfo(int logicalAddress) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900255 assertRunOnServiceThread();
Jungshik Jang7d9a8432014-04-29 15:12:43 +0900256 HdmiCecDeviceInfo deviceInfo = mDeviceInfos.get(logicalAddress);
257 if (deviceInfo != null) {
258 mDeviceInfos.remove(logicalAddress);
259 }
260 return deviceInfo;
261 }
262
263 /**
Jungshik Jangcc5ef8c2014-05-27 13:27:36 +0900264 * Clear all device info.
265 *
266 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
267 */
268 void clearDeviceInfoList() {
269 assertRunOnServiceThread();
270 mDeviceInfos.clear();
271 }
272
273 /**
Jungshik Jang02bb4262014-05-23 16:48:31 +0900274 * Return a list of all {@link HdmiCecDeviceInfo}.
Jungshik Jang7d9a8432014-04-29 15:12:43 +0900275 *
276 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
277 */
278 List<HdmiCecDeviceInfo> getDeviceInfoList() {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900279 assertRunOnServiceThread();
Jungshik Jangcc5ef8c2014-05-27 13:27:36 +0900280 return sparseArrayToList(mDeviceInfos);
Jungshik Jang7d9a8432014-04-29 15:12:43 +0900281 }
282
283 /**
284 * Return a {@link HdmiCecDeviceInfo} corresponding to the given {@code logicalAddress}.
285 *
286 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
287 *
288 * @param logicalAddress logical address to be retrieved
289 * @return {@link HdmiCecDeviceInfo} matched with the given {@code logicalAddress}.
290 * Returns null if no logical address matched
291 */
292 HdmiCecDeviceInfo getDeviceInfo(int logicalAddress) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900293 assertRunOnServiceThread();
Jungshik Jang7d9a8432014-04-29 15:12:43 +0900294 return mDeviceInfos.get(logicalAddress);
295 }
296
Jungshik Janga9095ba2014-05-02 13:06:22 +0900297 /**
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +0900298 * Return the locally hosted logical device of a given type.
299 *
300 * @param deviceType logical device type
301 * @return {@link HdmiCecLocalDevice} instance if the instance of the type is available;
302 * otherwise null.
303 */
304 HdmiCecLocalDevice getLocalDevice(int deviceType) {
305 return mLocalDevices.get(deviceType);
306 }
307
308 /**
Jungshik Janga9095ba2014-05-02 13:06:22 +0900309 * Add a new logical address to the device. Device's HW should be notified
310 * when a new logical address is assigned to a device, so that it can accept
311 * a command having available destinations.
312 *
313 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
314 *
315 * @param newLogicalAddress a logical address to be added
316 * @return 0 on success. Otherwise, returns negative value
317 */
318 int addLogicalAddress(int newLogicalAddress) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900319 assertRunOnServiceThread();
Jungshik Janga9095ba2014-05-02 13:06:22 +0900320 if (HdmiCec.isValidAddress(newLogicalAddress)) {
321 return nativeAddLogicalAddress(mNativePtr, newLogicalAddress);
322 } else {
323 return -1;
324 }
325 }
326
327 /**
328 * Clear all logical addresses registered in the device.
329 *
330 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
331 */
332 void clearLogicalAddress() {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900333 assertRunOnServiceThread();
Jungshik Janga1fa91f2014-05-08 20:56:41 +0900334 // TODO: consider to backup logical address so that new logical address
335 // allocation can use it as preferred address.
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +0900336 for (int i = 0; i < mLocalDevices.size(); ++i) {
337 mLocalDevices.valueAt(i).clearAddress();
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900338 }
Jungshik Janga9095ba2014-05-02 13:06:22 +0900339 nativeClearLogicalAddress(mNativePtr);
340 }
341
342 /**
343 * Return the physical address of the device.
344 *
345 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
346 *
347 * @return CEC physical address of the device. The range of success address
348 * is between 0x0000 and 0xFFFF. If failed it returns -1
349 */
350 int getPhysicalAddress() {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900351 assertRunOnServiceThread();
Jungshik Janga9095ba2014-05-02 13:06:22 +0900352 return nativeGetPhysicalAddress(mNativePtr);
353 }
354
355 /**
356 * Return CEC version of the device.
357 *
358 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
359 */
360 int getVersion() {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900361 assertRunOnServiceThread();
Jungshik Janga9095ba2014-05-02 13:06:22 +0900362 return nativeGetVersion(mNativePtr);
363 }
364
365 /**
366 * Return vendor id of the device.
367 *
368 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
369 */
370 int getVendorId() {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900371 assertRunOnServiceThread();
Jungshik Janga9095ba2014-05-02 13:06:22 +0900372 return nativeGetVendorId(mNativePtr);
373 }
374
Jungshik Jang02bb4262014-05-23 16:48:31 +0900375 /**
376 * Poll all remote devices. It sends &lt;Polling Message&gt; to all remote
377 * devices.
378 *
379 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
380 *
381 * @param callback an interface used to get a list of all remote devices' address
382 * @param retryCount the number of retry used to send polling message to remote devices
383 */
384 void pollDevices(DevicePollingCallback callback, int retryCount) {
385 assertRunOnServiceThread();
386 // Extract polling candidates. No need to poll against local devices.
387 ArrayList<Integer> pollingCandidates = new ArrayList<>();
388 for (int i = HdmiCec.ADDR_SPECIFIC_USE; i >= HdmiCec.ADDR_TV; --i) {
389 if (!isAllocatedLocalDeviceAddress(i)) {
390 pollingCandidates.add(i);
391 }
392 }
393
394 runDevicePolling(pollingCandidates, retryCount, callback);
395 }
396
Jungshik Jangcc5ef8c2014-05-27 13:27:36 +0900397 /**
398 * Return a list of all {@link HdmiCecLocalDevice}s.
399 *
400 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
401 */
402 List<HdmiCecLocalDevice> getLocalDeviceList() {
403 assertRunOnServiceThread();
404 return sparseArrayToList(mLocalDevices);
405 }
406
407 private static <T> List<T> sparseArrayToList(SparseArray<T> array) {
408 ArrayList<T> list = new ArrayList<>();
409 for (int i = 0; i < array.size(); ++i) {
410 list.add(array.valueAt(i));
411 }
412 return list;
413 }
414
Jungshik Jang02bb4262014-05-23 16:48:31 +0900415 private boolean isAllocatedLocalDeviceAddress(int address) {
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +0900416 for (int i = 0; i < mLocalDevices.size(); ++i) {
417 if (mLocalDevices.valueAt(i).isAddressOf(address)) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900418 return true;
419 }
420 }
421 return false;
422 }
423
424 private void runDevicePolling(final List<Integer> candidates, final int retryCount,
425 final DevicePollingCallback callback) {
426 assertRunOnServiceThread();
427 runOnIoThread(new Runnable() {
428 @Override
429 public void run() {
430 final ArrayList<Integer> allocated = new ArrayList<>();
431 for (Integer address : candidates) {
432 if (sendPollMessage(address, retryCount)) {
433 allocated.add(address);
434 }
435 }
436 if (callback != null) {
437 runOnServiceThread(new Runnable() {
438 @Override
439 public void run() {
440 callback.onPollingFinished(allocated);
441 }
442 });
443 }
444 }
445 });
446 }
447
448 private boolean sendPollMessage(int address, int retryCount) {
449 assertRunOnIoThread();
450 for (int i = 0; i < retryCount; ++i) {
451 // <Polling Message> is a message which has empty body and
452 // uses same address for both source and destination address.
453 // If sending <Polling Message> failed (NAK), it becomes
454 // new logical address for the device because no device uses
455 // it as logical address of the device.
456 if (nativeSendCecCommand(mNativePtr, address, address, EMPTY_BODY)
457 == HdmiControlService.SEND_RESULT_SUCCESS) {
458 return true;
459 }
460 }
461 return false;
462 }
463
464 private void assertRunOnIoThread() {
465 if (Looper.myLooper() != mIoHandler.getLooper()) {
466 throw new IllegalStateException("Should run on io thread.");
467 }
468 }
469
470 private void assertRunOnServiceThread() {
471 if (Looper.myLooper() != mControlHandler.getLooper()) {
472 throw new IllegalStateException("Should run on service thread.");
473 }
474 }
475
476 // Run a Runnable on IO thread.
477 // It should be careful to access member variables on IO thread because
478 // it can be accessed from system thread as well.
Jungshik Jangd643f762014-05-22 19:28:09 +0900479 private void runOnIoThread(Runnable runnable) {
480 mIoHandler.post(runnable);
481 }
482
483 private void runOnServiceThread(Runnable runnable) {
484 mControlHandler.post(runnable);
485 }
486
Jungshik Janga1fa91f2014-05-08 20:56:41 +0900487 private boolean isAcceptableAddress(int address) {
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900488 // Can access command targeting devices available in local device or broadcast command.
489 if (address == HdmiCec.ADDR_BROADCAST) {
490 return true;
491 }
Jungshik Jang02bb4262014-05-23 16:48:31 +0900492 return isAllocatedLocalDeviceAddress(address);
Jungshik Janga1fa91f2014-05-08 20:56:41 +0900493 }
494
Jungshik Jange9c77c82014-04-24 20:30:09 +0900495 private void onReceiveCommand(HdmiCecMessage message) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900496 assertRunOnServiceThread();
Jungshik Jang4085d0e2014-05-27 19:52:39 +0900497 if (isAcceptableAddress(message.getDestination())
498 && mService.handleCecCommand(message)) {
Jungshik Janga1fa91f2014-05-08 20:56:41 +0900499 return;
500 }
Jungshik Jange9c77c82014-04-24 20:30:09 +0900501
502 // TODO: Use device's source address for broadcast message.
503 int sourceAddress = message.getDestination() != HdmiCec.ADDR_BROADCAST ?
504 message.getDestination() : 0;
505 // Reply <Feature Abort> to initiator (source) for all requests.
Jungshik Janga1fa91f2014-05-08 20:56:41 +0900506 HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildFeatureAbortCommand
507 (sourceAddress, message.getSource(), message.getOpcode(),
508 HdmiCecMessageBuilder.ABORT_REFUSED);
Jungshik Jangd643f762014-05-22 19:28:09 +0900509 sendCommand(cecMessage, null);
Jungshik Jange9c77c82014-04-24 20:30:09 +0900510 }
511
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900512 void sendCommand(HdmiCecMessage cecMessage) {
513 sendCommand(cecMessage, null);
514 }
515
Jungshik Jangd643f762014-05-22 19:28:09 +0900516 void sendCommand(final HdmiCecMessage cecMessage,
517 final HdmiControlService.SendMessageCallback callback) {
518 runOnIoThread(new Runnable() {
519 @Override
520 public void run() {
521 byte[] body = buildBody(cecMessage.getOpcode(), cecMessage.getParams());
522 final int error = nativeSendCecCommand(mNativePtr, cecMessage.getSource(),
523 cecMessage.getDestination(), body);
Yuncheol Heoece603b2014-05-23 20:10:19 +0900524 if (error != HdmiControlService.SEND_RESULT_SUCCESS) {
525 Slog.w(TAG, "Failed to send " + cecMessage);
526 }
Jungshik Jangd643f762014-05-22 19:28:09 +0900527 if (callback != null) {
528 runOnServiceThread(new Runnable() {
529 @Override
530 public void run() {
531 callback.onSendCompleted(error);
532 }
533 });
534 }
535 }
536 });
Jungshik Jange9c77c82014-04-24 20:30:09 +0900537 }
538
Jungshik Jang0792d372014-04-23 17:57:26 +0900539 /**
Jungshik Jange9c77c82014-04-24 20:30:09 +0900540 * Called by native when incoming CEC message arrived.
Jungshik Jang0792d372014-04-23 17:57:26 +0900541 */
Jungshik Jange9c77c82014-04-24 20:30:09 +0900542 private void handleIncomingCecCommand(int srcAddress, int dstAddress, byte[] body) {
Jungshik Jang4085d0e2014-05-27 19:52:39 +0900543 assertRunOnServiceThread();
544 onReceiveCommand(HdmiCecMessageBuilder.of(srcAddress, dstAddress, body));
Jungshik Jange9c77c82014-04-24 20:30:09 +0900545 }
546
547 /**
548 * Called by native when a hotplug event issues.
549 */
550 private void handleHotplug(boolean connected) {
Jungshik Jang67ea5212014-05-15 14:05:24 +0900551 // TODO: once add port number to cec HAL interface, pass port number
552 // to the service.
553 mService.onHotplug(0, connected);
Jungshik Jang0792d372014-04-23 17:57:26 +0900554 }
555
Jungshik Jang4085d0e2014-05-27 19:52:39 +0900556 private static native long nativeInit(HdmiCecController handler, MessageQueue messageQueue);
Jungshik Janga9095ba2014-05-02 13:06:22 +0900557 private static native int nativeSendCecCommand(long controllerPtr, int srcAddress,
Jungshik Jange9c77c82014-04-24 20:30:09 +0900558 int dstAddress, byte[] body);
Jungshik Janga9095ba2014-05-02 13:06:22 +0900559 private static native int nativeAddLogicalAddress(long controllerPtr, int logicalAddress);
560 private static native void nativeClearLogicalAddress(long controllerPtr);
561 private static native int nativeGetPhysicalAddress(long controllerPtr);
562 private static native int nativeGetVersion(long controllerPtr);
563 private static native int nativeGetVendorId(long controllerPtr);
Jungshik Jang0792d372014-04-23 17:57:26 +0900564}