blob: f329c6907eca72dd809e8e1932798cade8346980 [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 Jang3ee65722014-06-03 16:22:30 +090050 /**
51 * Interface to report allocated logical address.
52 */
53 interface AllocateAddressCallback {
54 /**
55 * Called when a new logical address is allocated.
56 *
57 * @param deviceType requested device type to allocate logical address
58 * @param logicalAddress allocated logical address. If it is
59 * {@link HdmiCec#ADDR_UNREGISTERED}, it means that
60 * it failed to allocate logical address for the given device type
61 */
62 void onAllocated(int deviceType, int logicalAddress);
63 }
64
Jungshik Jang3f74ab02014-04-30 14:31:02 +090065 private static final byte[] EMPTY_BODY = EmptyArray.BYTE;
66
Jungshik Jange9c77c82014-04-24 20:30:09 +090067 // A message to pass cec send command to IO looper.
68 private static final int MSG_SEND_CEC_COMMAND = 1;
Jungshik Jang3f74ab02014-04-30 14:31:02 +090069 // A message to delegate logical allocation to IO looper.
70 private static final int MSG_ALLOCATE_LOGICAL_ADDRESS = 2;
Jungshik Jange9c77c82014-04-24 20:30:09 +090071
72 // Message types to handle incoming message in main service looper.
73 private final static int MSG_RECEIVE_CEC_COMMAND = 1;
Jungshik Jang3f74ab02014-04-30 14:31:02 +090074 // A message to report allocated logical address to main control looper.
75 private final static int MSG_REPORT_LOGICAL_ADDRESS = 2;
Jungshik Jange9c77c82014-04-24 20:30:09 +090076
Jungshik Jang3f74ab02014-04-30 14:31:02 +090077 private static final int NUM_LOGICAL_ADDRESS = 16;
78
Jungshik Jang02bb4262014-05-23 16:48:31 +090079 private static final int RETRY_COUNT_FOR_LOGICAL_ADDRESS_ALLOCATION = 3;
80
Jungshik Jang0792d372014-04-23 17:57:26 +090081 // Handler instance to process synchronous I/O (mainly send) message.
82 private Handler mIoHandler;
83
84 // Handler instance to process various messages coming from other CEC
85 // device or issued by internal state change.
Jungshik Jange9c77c82014-04-24 20:30:09 +090086 private Handler mControlHandler;
Jungshik Jang0792d372014-04-23 17:57:26 +090087
88 // Stores the pointer to the native implementation of the service that
89 // interacts with HAL.
Jungshik Jang02bb4262014-05-23 16:48:31 +090090 private volatile long mNativePtr;
Jungshik Jang0792d372014-04-23 17:57:26 +090091
Jungshik Janga1fa91f2014-05-08 20:56:41 +090092 private HdmiControlService mService;
93
Jungshik Jang1a4485d2014-05-26 11:02:36 +090094 // Map-like container of all cec devices including local ones.
95 // A logical address of device is used as key of container.
Jungshik Jang02bb4262014-05-23 16:48:31 +090096 private final SparseArray<HdmiCecDeviceInfo> mDeviceInfos = new SparseArray<>();
Jinsuk Kim2918e9e2014-05-20 16:45:45 +090097
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +090098 // Stores the local CEC devices in the system. Device type is used for key.
99 private final SparseArray<HdmiCecLocalDevice> mLocalDevices = new SparseArray<>();
Jungshik Jang7d9a8432014-04-29 15:12:43 +0900100
Jungshik Jang0792d372014-04-23 17:57:26 +0900101 // Private constructor. Use HdmiCecController.create().
102 private HdmiCecController() {
103 }
104
105 /**
106 * A factory method to get {@link HdmiCecController}. If it fails to initialize
107 * inner device or has no device it will return {@code null}.
108 *
109 * <p>Declared as package-private, accessed by {@link HdmiControlService} only.
Jungshik Jange9c77c82014-04-24 20:30:09 +0900110 * @param service {@link HdmiControlService} instance used to create internal handler
111 * and to pass callback for incoming message or event.
Jungshik Jang0792d372014-04-23 17:57:26 +0900112 * @return {@link HdmiCecController} if device is initialized successfully. Otherwise,
113 * returns {@code null}.
114 */
Jungshik Jange9c77c82014-04-24 20:30:09 +0900115 static HdmiCecController create(HdmiControlService service) {
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900116 HdmiCecController controller = new HdmiCecController();
Jungshik Jang4085d0e2014-05-27 19:52:39 +0900117 long nativePtr = nativeInit(controller, service.getServiceLooper().getQueue());
Jungshik Jang0792d372014-04-23 17:57:26 +0900118 if (nativePtr == 0L) {
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900119 controller = null;
Jungshik Jang0792d372014-04-23 17:57:26 +0900120 return null;
121 }
122
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900123 controller.init(service, nativePtr);
124 return controller;
125 }
126
127 private void init(HdmiControlService service, long nativePtr) {
128 mService = service;
129 mIoHandler = new Handler(service.getServiceLooper());
130 mControlHandler = new Handler(service.getServiceLooper());
131 mNativePtr = nativePtr;
Jungshik Jang0792d372014-04-23 17:57:26 +0900132 }
133
Jungshik Jang3ee65722014-06-03 16:22:30 +0900134 void addLocalDevice(int deviceType, HdmiCecLocalDevice device) {
135 mLocalDevices.put(deviceType, device);
Jungshik Jang3f74ab02014-04-30 14:31:02 +0900136 }
137
138 /**
139 * Allocate a new logical address of the given device type. Allocated
Jungshik Jang3ee65722014-06-03 16:22:30 +0900140 * address will be reported through {@link AllocateAddressCallback}.
Jungshik Jang3f74ab02014-04-30 14:31:02 +0900141 *
142 * <p> Declared as package-private, accessed by {@link HdmiControlService} only.
143 *
144 * @param deviceType type of device to used to determine logical address
145 * @param preferredAddress a logical address preferred to be allocated.
146 * If sets {@link HdmiCec#ADDR_UNREGISTERED}, scans
147 * the smallest logical address matched with the given device type.
148 * Otherwise, scan address will start from {@code preferredAddress}
149 * @param callback callback interface to report allocated logical address to caller
150 */
Jungshik Jangd643f762014-05-22 19:28:09 +0900151 void allocateLogicalAddress(final int deviceType, final int preferredAddress,
Jungshik Jang3ee65722014-06-03 16:22:30 +0900152 final AllocateAddressCallback callback) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900153 assertRunOnServiceThread();
154
Jungshik Jangd643f762014-05-22 19:28:09 +0900155 runOnIoThread(new Runnable() {
156 @Override
157 public void run() {
158 handleAllocateLogicalAddress(deviceType, preferredAddress, callback);
159 }
160 });
161 }
162
163 private void handleAllocateLogicalAddress(final int deviceType, int preferredAddress,
Jungshik Jang3ee65722014-06-03 16:22:30 +0900164 final AllocateAddressCallback callback) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900165 assertRunOnIoThread();
Jungshik Jangd643f762014-05-22 19:28:09 +0900166 int startAddress = preferredAddress;
167 // If preferred address is "unregistered", start address will be the smallest
168 // address matched with the given device type.
169 if (preferredAddress == HdmiCec.ADDR_UNREGISTERED) {
170 for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) {
171 if (deviceType == HdmiCec.getTypeFromAddress(i)) {
172 startAddress = i;
173 break;
174 }
175 }
176 }
177
178 int logicalAddress = HdmiCec.ADDR_UNREGISTERED;
179 // Iterates all possible addresses which has the same device type.
180 for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) {
181 int curAddress = (startAddress + i) % NUM_LOGICAL_ADDRESS;
182 if (curAddress != HdmiCec.ADDR_UNREGISTERED
Jinsuk Kim0bc8b072014-05-27 17:23:27 +0900183 && deviceType == HdmiCec.getTypeFromAddress(curAddress)) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900184 if (!sendPollMessage(curAddress, RETRY_COUNT_FOR_LOGICAL_ADDRESS_ALLOCATION)) {
Jungshik Jangd643f762014-05-22 19:28:09 +0900185 logicalAddress = curAddress;
186 break;
187 }
188 }
189 }
190
191 final int assignedAddress = logicalAddress;
192 if (callback != null) {
193 runOnServiceThread(new Runnable() {
194 @Override
195 public void run() {
196 callback.onAllocated(deviceType, assignedAddress);
197 }
198 });
199 }
Jungshik Jang3f74ab02014-04-30 14:31:02 +0900200 }
201
Jungshik Jange9c77c82014-04-24 20:30:09 +0900202 private static byte[] buildBody(int opcode, byte[] params) {
203 byte[] body = new byte[params.length + 1];
204 body[0] = (byte) opcode;
205 System.arraycopy(params, 0, body, 1, params.length);
206 return body;
207 }
Jungshik Jang0792d372014-04-23 17:57:26 +0900208
Jungshik Jang7d9a8432014-04-29 15:12:43 +0900209 /**
210 * Add a new {@link HdmiCecDeviceInfo}. It returns old device info which has the same
211 * logical address as new device info's.
212 *
213 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
214 *
215 * @param deviceInfo a new {@link HdmiCecDeviceInfo} to be added.
216 * @return {@code null} if it is new device. Otherwise, returns old {@HdmiCecDeviceInfo}
217 * that has the same logical address as new one has.
218 */
219 HdmiCecDeviceInfo addDeviceInfo(HdmiCecDeviceInfo deviceInfo) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900220 assertRunOnServiceThread();
Jungshik Jang7d9a8432014-04-29 15:12:43 +0900221 HdmiCecDeviceInfo oldDeviceInfo = getDeviceInfo(deviceInfo.getLogicalAddress());
222 if (oldDeviceInfo != null) {
223 removeDeviceInfo(deviceInfo.getLogicalAddress());
224 }
225 mDeviceInfos.append(deviceInfo.getLogicalAddress(), deviceInfo);
226 return oldDeviceInfo;
227 }
228
229 /**
230 * Remove a device info corresponding to the given {@code logicalAddress}.
231 * It returns removed {@link HdmiCecDeviceInfo} if exists.
232 *
233 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
234 *
235 * @param logicalAddress logical address of device to be removed
236 * @return removed {@link HdmiCecDeviceInfo} it exists. Otherwise, returns {@code null}
237 */
238 HdmiCecDeviceInfo removeDeviceInfo(int logicalAddress) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900239 assertRunOnServiceThread();
Jungshik Jang7d9a8432014-04-29 15:12:43 +0900240 HdmiCecDeviceInfo deviceInfo = mDeviceInfos.get(logicalAddress);
241 if (deviceInfo != null) {
242 mDeviceInfos.remove(logicalAddress);
243 }
244 return deviceInfo;
245 }
246
247 /**
Jungshik Jangcc5ef8c2014-05-27 13:27:36 +0900248 * Clear all device info.
249 *
250 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
251 */
252 void clearDeviceInfoList() {
253 assertRunOnServiceThread();
254 mDeviceInfos.clear();
255 }
256
257 /**
Jungshik Jang02bb4262014-05-23 16:48:31 +0900258 * Return a list of all {@link HdmiCecDeviceInfo}.
Jungshik Jang7d9a8432014-04-29 15:12:43 +0900259 *
260 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
261 */
262 List<HdmiCecDeviceInfo> getDeviceInfoList() {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900263 assertRunOnServiceThread();
Jungshik Jangcc5ef8c2014-05-27 13:27:36 +0900264 return sparseArrayToList(mDeviceInfos);
Jungshik Jang7d9a8432014-04-29 15:12:43 +0900265 }
266
267 /**
268 * Return a {@link HdmiCecDeviceInfo} corresponding to the given {@code logicalAddress}.
269 *
270 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
271 *
272 * @param logicalAddress logical address to be retrieved
273 * @return {@link HdmiCecDeviceInfo} matched with the given {@code logicalAddress}.
274 * Returns null if no logical address matched
275 */
276 HdmiCecDeviceInfo getDeviceInfo(int logicalAddress) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900277 assertRunOnServiceThread();
Jungshik Jang7d9a8432014-04-29 15:12:43 +0900278 return mDeviceInfos.get(logicalAddress);
279 }
280
Jungshik Janga9095ba2014-05-02 13:06:22 +0900281 /**
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +0900282 * Return the locally hosted logical device of a given type.
283 *
284 * @param deviceType logical device type
285 * @return {@link HdmiCecLocalDevice} instance if the instance of the type is available;
286 * otherwise null.
287 */
288 HdmiCecLocalDevice getLocalDevice(int deviceType) {
289 return mLocalDevices.get(deviceType);
290 }
291
292 /**
Jungshik Janga9095ba2014-05-02 13:06:22 +0900293 * Add a new logical address to the device. Device's HW should be notified
294 * when a new logical address is assigned to a device, so that it can accept
295 * a command having available destinations.
296 *
297 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
298 *
299 * @param newLogicalAddress a logical address to be added
300 * @return 0 on success. Otherwise, returns negative value
301 */
302 int addLogicalAddress(int newLogicalAddress) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900303 assertRunOnServiceThread();
Jungshik Janga9095ba2014-05-02 13:06:22 +0900304 if (HdmiCec.isValidAddress(newLogicalAddress)) {
305 return nativeAddLogicalAddress(mNativePtr, newLogicalAddress);
306 } else {
307 return -1;
308 }
309 }
310
311 /**
312 * Clear all logical addresses registered in the device.
313 *
314 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
315 */
316 void clearLogicalAddress() {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900317 assertRunOnServiceThread();
Jungshik Janga1fa91f2014-05-08 20:56:41 +0900318 // TODO: consider to backup logical address so that new logical address
319 // allocation can use it as preferred address.
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +0900320 for (int i = 0; i < mLocalDevices.size(); ++i) {
321 mLocalDevices.valueAt(i).clearAddress();
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900322 }
Jungshik Janga9095ba2014-05-02 13:06:22 +0900323 nativeClearLogicalAddress(mNativePtr);
324 }
325
326 /**
327 * Return the physical address of the device.
328 *
329 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
330 *
331 * @return CEC physical address of the device. The range of success address
332 * is between 0x0000 and 0xFFFF. If failed it returns -1
333 */
334 int getPhysicalAddress() {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900335 assertRunOnServiceThread();
Jungshik Janga9095ba2014-05-02 13:06:22 +0900336 return nativeGetPhysicalAddress(mNativePtr);
337 }
338
339 /**
340 * Return CEC version of the device.
341 *
342 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
343 */
344 int getVersion() {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900345 assertRunOnServiceThread();
Jungshik Janga9095ba2014-05-02 13:06:22 +0900346 return nativeGetVersion(mNativePtr);
347 }
348
349 /**
350 * Return vendor id of the device.
351 *
352 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
353 */
354 int getVendorId() {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900355 assertRunOnServiceThread();
Jungshik Janga9095ba2014-05-02 13:06:22 +0900356 return nativeGetVendorId(mNativePtr);
357 }
358
Jungshik Jang02bb4262014-05-23 16:48:31 +0900359 /**
360 * Poll all remote devices. It sends &lt;Polling Message&gt; to all remote
361 * devices.
362 *
363 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
364 *
365 * @param callback an interface used to get a list of all remote devices' address
366 * @param retryCount the number of retry used to send polling message to remote devices
367 */
368 void pollDevices(DevicePollingCallback callback, int retryCount) {
369 assertRunOnServiceThread();
370 // Extract polling candidates. No need to poll against local devices.
371 ArrayList<Integer> pollingCandidates = new ArrayList<>();
372 for (int i = HdmiCec.ADDR_SPECIFIC_USE; i >= HdmiCec.ADDR_TV; --i) {
373 if (!isAllocatedLocalDeviceAddress(i)) {
374 pollingCandidates.add(i);
375 }
376 }
377
378 runDevicePolling(pollingCandidates, retryCount, callback);
379 }
380
Jungshik Jangcc5ef8c2014-05-27 13:27:36 +0900381 /**
382 * Return a list of all {@link HdmiCecLocalDevice}s.
383 *
384 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
385 */
386 List<HdmiCecLocalDevice> getLocalDeviceList() {
387 assertRunOnServiceThread();
388 return sparseArrayToList(mLocalDevices);
389 }
390
391 private static <T> List<T> sparseArrayToList(SparseArray<T> array) {
392 ArrayList<T> list = new ArrayList<>();
393 for (int i = 0; i < array.size(); ++i) {
394 list.add(array.valueAt(i));
395 }
396 return list;
397 }
398
Jungshik Jang02bb4262014-05-23 16:48:31 +0900399 private boolean isAllocatedLocalDeviceAddress(int address) {
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +0900400 for (int i = 0; i < mLocalDevices.size(); ++i) {
401 if (mLocalDevices.valueAt(i).isAddressOf(address)) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900402 return true;
403 }
404 }
405 return false;
406 }
407
408 private void runDevicePolling(final List<Integer> candidates, final int retryCount,
409 final DevicePollingCallback callback) {
410 assertRunOnServiceThread();
411 runOnIoThread(new Runnable() {
412 @Override
413 public void run() {
414 final ArrayList<Integer> allocated = new ArrayList<>();
415 for (Integer address : candidates) {
416 if (sendPollMessage(address, retryCount)) {
417 allocated.add(address);
418 }
419 }
420 if (callback != null) {
421 runOnServiceThread(new Runnable() {
422 @Override
423 public void run() {
424 callback.onPollingFinished(allocated);
425 }
426 });
427 }
428 }
429 });
430 }
431
432 private boolean sendPollMessage(int address, int retryCount) {
433 assertRunOnIoThread();
434 for (int i = 0; i < retryCount; ++i) {
435 // <Polling Message> is a message which has empty body and
436 // uses same address for both source and destination address.
437 // If sending <Polling Message> failed (NAK), it becomes
438 // new logical address for the device because no device uses
439 // it as logical address of the device.
440 if (nativeSendCecCommand(mNativePtr, address, address, EMPTY_BODY)
441 == HdmiControlService.SEND_RESULT_SUCCESS) {
442 return true;
443 }
444 }
445 return false;
446 }
447
448 private void assertRunOnIoThread() {
449 if (Looper.myLooper() != mIoHandler.getLooper()) {
450 throw new IllegalStateException("Should run on io thread.");
451 }
452 }
453
454 private void assertRunOnServiceThread() {
455 if (Looper.myLooper() != mControlHandler.getLooper()) {
456 throw new IllegalStateException("Should run on service thread.");
457 }
458 }
459
460 // Run a Runnable on IO thread.
461 // It should be careful to access member variables on IO thread because
462 // it can be accessed from system thread as well.
Jungshik Jangd643f762014-05-22 19:28:09 +0900463 private void runOnIoThread(Runnable runnable) {
464 mIoHandler.post(runnable);
465 }
466
467 private void runOnServiceThread(Runnable runnable) {
468 mControlHandler.post(runnable);
469 }
470
Jungshik Janga1fa91f2014-05-08 20:56:41 +0900471 private boolean isAcceptableAddress(int address) {
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900472 // Can access command targeting devices available in local device or broadcast command.
473 if (address == HdmiCec.ADDR_BROADCAST) {
474 return true;
475 }
Jungshik Jang02bb4262014-05-23 16:48:31 +0900476 return isAllocatedLocalDeviceAddress(address);
Jungshik Janga1fa91f2014-05-08 20:56:41 +0900477 }
478
Jungshik Jange9c77c82014-04-24 20:30:09 +0900479 private void onReceiveCommand(HdmiCecMessage message) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900480 assertRunOnServiceThread();
Jungshik Jang4085d0e2014-05-27 19:52:39 +0900481 if (isAcceptableAddress(message.getDestination())
482 && mService.handleCecCommand(message)) {
Jungshik Janga1fa91f2014-05-08 20:56:41 +0900483 return;
484 }
Jungshik Jange9c77c82014-04-24 20:30:09 +0900485
Jinsuk Kim3a2f7432014-05-29 06:52:45 +0900486 if (message.getDestination() != HdmiCec.ADDR_BROADCAST) {
487 int sourceAddress = message.getDestination();
488 // Reply <Feature Abort> to initiator (source) for all requests.
489 HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildFeatureAbortCommand(
490 sourceAddress, message.getSource(), message.getOpcode(),
Yuncheol Heo63a2e062014-05-27 23:06:01 +0900491 HdmiConstants.ABORT_REFUSED);
Jinsuk Kim3a2f7432014-05-29 06:52:45 +0900492 sendCommand(cecMessage, null);
493 }
Jungshik Jange9c77c82014-04-24 20:30:09 +0900494 }
495
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900496 void sendCommand(HdmiCecMessage cecMessage) {
497 sendCommand(cecMessage, null);
498 }
499
Jungshik Jangd643f762014-05-22 19:28:09 +0900500 void sendCommand(final HdmiCecMessage cecMessage,
501 final HdmiControlService.SendMessageCallback callback) {
502 runOnIoThread(new Runnable() {
503 @Override
504 public void run() {
505 byte[] body = buildBody(cecMessage.getOpcode(), cecMessage.getParams());
506 final int error = nativeSendCecCommand(mNativePtr, cecMessage.getSource(),
507 cecMessage.getDestination(), body);
Yuncheol Heoece603b2014-05-23 20:10:19 +0900508 if (error != HdmiControlService.SEND_RESULT_SUCCESS) {
509 Slog.w(TAG, "Failed to send " + cecMessage);
510 }
Jungshik Jangd643f762014-05-22 19:28:09 +0900511 if (callback != null) {
512 runOnServiceThread(new Runnable() {
513 @Override
514 public void run() {
515 callback.onSendCompleted(error);
516 }
517 });
518 }
519 }
520 });
Jungshik Jange9c77c82014-04-24 20:30:09 +0900521 }
522
Jungshik Jang0792d372014-04-23 17:57:26 +0900523 /**
Jungshik Jange9c77c82014-04-24 20:30:09 +0900524 * Called by native when incoming CEC message arrived.
Jungshik Jang0792d372014-04-23 17:57:26 +0900525 */
Jungshik Jange9c77c82014-04-24 20:30:09 +0900526 private void handleIncomingCecCommand(int srcAddress, int dstAddress, byte[] body) {
Jungshik Jang4085d0e2014-05-27 19:52:39 +0900527 assertRunOnServiceThread();
528 onReceiveCommand(HdmiCecMessageBuilder.of(srcAddress, dstAddress, body));
Jungshik Jange9c77c82014-04-24 20:30:09 +0900529 }
530
531 /**
532 * Called by native when a hotplug event issues.
533 */
534 private void handleHotplug(boolean connected) {
Jungshik Jang67ea5212014-05-15 14:05:24 +0900535 // TODO: once add port number to cec HAL interface, pass port number
536 // to the service.
537 mService.onHotplug(0, connected);
Jungshik Jang0792d372014-04-23 17:57:26 +0900538 }
539
Jungshik Jang4085d0e2014-05-27 19:52:39 +0900540 private static native long nativeInit(HdmiCecController handler, MessageQueue messageQueue);
Jungshik Janga9095ba2014-05-02 13:06:22 +0900541 private static native int nativeSendCecCommand(long controllerPtr, int srcAddress,
Jungshik Jange9c77c82014-04-24 20:30:09 +0900542 int dstAddress, byte[] body);
Jungshik Janga9095ba2014-05-02 13:06:22 +0900543 private static native int nativeAddLogicalAddress(long controllerPtr, int logicalAddress);
544 private static native void nativeClearLogicalAddress(long controllerPtr);
545 private static native int nativeGetPhysicalAddress(long controllerPtr);
546 private static native int nativeGetVersion(long controllerPtr);
547 private static native int nativeGetVendorId(long controllerPtr);
Jungshik Jang0792d372014-04-23 17:57:26 +0900548}