blob: fe1686945aad128e39e2f30fb4ab586757dca43e [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;
20import android.hardware.hdmi.HdmiCecMessage;
Jinsuk Kim0340bbc2014-06-05 11:07:47 +090021import android.hardware.hdmi.HdmiPortInfo;
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 Jang0f8b4b72014-05-28 17:58:58 +090028import com.android.internal.util.Predicate;
Jungshik Jang02bb4262014-05-23 16:48:31 +090029import com.android.server.hdmi.HdmiControlService.DevicePollingCallback;
30
Jungshik Jang3f74ab02014-04-30 14:31:02 +090031import libcore.util.EmptyArray;
32
Jungshik Jang7d9a8432014-04-29 15:12:43 +090033import java.util.ArrayList;
Jungshik Jang7d9a8432014-04-29 15:12:43 +090034import java.util.List;
Jungshik Jang0792d372014-04-23 17:57:26 +090035
36/**
37 * Manages HDMI-CEC command and behaviors. It converts user's command into CEC command
38 * and pass it to CEC HAL so that it sends message to other device. For incoming
39 * message it translates the message and delegates it to proper module.
40 *
Jungshik Jang02bb4262014-05-23 16:48:31 +090041 * <p>It should be careful to access member variables on IO thread because
42 * it can be accessed from system thread as well.
43 *
Jungshik Jang0792d372014-04-23 17:57:26 +090044 * <p>It can be created only by {@link HdmiCecController#create}
45 *
46 * <p>Declared as package-private, accessed by {@link HdmiControlService} only.
47 */
Jungshik Janga9095ba2014-05-02 13:06:22 +090048final class HdmiCecController {
Jungshik Jang0792d372014-04-23 17:57:26 +090049 private static final String TAG = "HdmiCecController";
50
Jungshik Jang3ee65722014-06-03 16:22:30 +090051 /**
52 * Interface to report allocated logical address.
53 */
54 interface AllocateAddressCallback {
55 /**
56 * Called when a new logical address is allocated.
57 *
58 * @param deviceType requested device type to allocate logical address
59 * @param logicalAddress allocated logical address. If it is
60 * {@link HdmiCec#ADDR_UNREGISTERED}, it means that
61 * it failed to allocate logical address for the given device type
62 */
63 void onAllocated(int deviceType, int logicalAddress);
64 }
65
Jungshik Jang3f74ab02014-04-30 14:31:02 +090066 private static final byte[] EMPTY_BODY = EmptyArray.BYTE;
67
Jungshik Jange9c77c82014-04-24 20:30:09 +090068 // A message to pass cec send command to IO looper.
69 private static final int MSG_SEND_CEC_COMMAND = 1;
Jungshik Jang3f74ab02014-04-30 14:31:02 +090070 // A message to delegate logical allocation to IO looper.
71 private static final int MSG_ALLOCATE_LOGICAL_ADDRESS = 2;
Jungshik Jange9c77c82014-04-24 20:30:09 +090072
73 // Message types to handle incoming message in main service looper.
74 private final static int MSG_RECEIVE_CEC_COMMAND = 1;
Jungshik Jang3f74ab02014-04-30 14:31:02 +090075 // A message to report allocated logical address to main control looper.
76 private final static int MSG_REPORT_LOGICAL_ADDRESS = 2;
Jungshik Jange9c77c82014-04-24 20:30:09 +090077
Jungshik Jang3f74ab02014-04-30 14:31:02 +090078 private static final int NUM_LOGICAL_ADDRESS = 16;
79
Jungshik Jang02bb4262014-05-23 16:48:31 +090080 private static final int RETRY_COUNT_FOR_LOGICAL_ADDRESS_ALLOCATION = 3;
81
Jungshik Jang0f8b4b72014-05-28 17:58:58 +090082 // Predicate for whether the given logical address is remote device's one or not.
83 private final Predicate<Integer> mRemoteDeviceAddressPredicate = new Predicate<Integer>() {
84 @Override
85 public boolean apply(Integer address) {
86 return !isAllocatedLocalDeviceAddress(address);
87 }
88 };
89
90 // Predicate whether the given logical address is system audio's one or not
91 private final Predicate<Integer> mSystemAudioAddressPredicate = new Predicate<Integer>() {
92 @Override
93 public boolean apply(Integer address) {
94 return HdmiCec.getTypeFromAddress(address) == HdmiCec.ADDR_AUDIO_SYSTEM;
95 }
96 };
97
Jungshik Jang0792d372014-04-23 17:57:26 +090098 // Handler instance to process synchronous I/O (mainly send) message.
99 private Handler mIoHandler;
100
101 // Handler instance to process various messages coming from other CEC
102 // device or issued by internal state change.
Jungshik Jange9c77c82014-04-24 20:30:09 +0900103 private Handler mControlHandler;
Jungshik Jang0792d372014-04-23 17:57:26 +0900104
105 // Stores the pointer to the native implementation of the service that
106 // interacts with HAL.
Jungshik Jang02bb4262014-05-23 16:48:31 +0900107 private volatile long mNativePtr;
Jungshik Jang0792d372014-04-23 17:57:26 +0900108
Jungshik Janga1fa91f2014-05-08 20:56:41 +0900109 private HdmiControlService mService;
110
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +0900111 // Stores the local CEC devices in the system. Device type is used for key.
112 private final SparseArray<HdmiCecLocalDevice> mLocalDevices = new SparseArray<>();
Jungshik Jang7d9a8432014-04-29 15:12:43 +0900113
Jungshik Jang0792d372014-04-23 17:57:26 +0900114 // Private constructor. Use HdmiCecController.create().
115 private HdmiCecController() {
116 }
117
118 /**
119 * A factory method to get {@link HdmiCecController}. If it fails to initialize
120 * inner device or has no device it will return {@code null}.
121 *
122 * <p>Declared as package-private, accessed by {@link HdmiControlService} only.
Jungshik Jange9c77c82014-04-24 20:30:09 +0900123 * @param service {@link HdmiControlService} instance used to create internal handler
124 * and to pass callback for incoming message or event.
Jungshik Jang0792d372014-04-23 17:57:26 +0900125 * @return {@link HdmiCecController} if device is initialized successfully. Otherwise,
126 * returns {@code null}.
127 */
Jungshik Jange9c77c82014-04-24 20:30:09 +0900128 static HdmiCecController create(HdmiControlService service) {
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900129 HdmiCecController controller = new HdmiCecController();
Jungshik Jang4085d0e2014-05-27 19:52:39 +0900130 long nativePtr = nativeInit(controller, service.getServiceLooper().getQueue());
Jungshik Jang0792d372014-04-23 17:57:26 +0900131 if (nativePtr == 0L) {
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900132 controller = null;
Jungshik Jang0792d372014-04-23 17:57:26 +0900133 return null;
134 }
135
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900136 controller.init(service, nativePtr);
137 return controller;
138 }
139
140 private void init(HdmiControlService service, long nativePtr) {
141 mService = service;
142 mIoHandler = new Handler(service.getServiceLooper());
143 mControlHandler = new Handler(service.getServiceLooper());
144 mNativePtr = nativePtr;
Jungshik Jang0792d372014-04-23 17:57:26 +0900145 }
146
Jungshik Jang3ee65722014-06-03 16:22:30 +0900147 void addLocalDevice(int deviceType, HdmiCecLocalDevice device) {
148 mLocalDevices.put(deviceType, device);
Jungshik Jang3f74ab02014-04-30 14:31:02 +0900149 }
150
151 /**
152 * Allocate a new logical address of the given device type. Allocated
Jungshik Jang3ee65722014-06-03 16:22:30 +0900153 * address will be reported through {@link AllocateAddressCallback}.
Jungshik Jang3f74ab02014-04-30 14:31:02 +0900154 *
155 * <p> Declared as package-private, accessed by {@link HdmiControlService} only.
156 *
157 * @param deviceType type of device to used to determine logical address
158 * @param preferredAddress a logical address preferred to be allocated.
159 * If sets {@link HdmiCec#ADDR_UNREGISTERED}, scans
160 * the smallest logical address matched with the given device type.
161 * Otherwise, scan address will start from {@code preferredAddress}
162 * @param callback callback interface to report allocated logical address to caller
163 */
Jungshik Jangd643f762014-05-22 19:28:09 +0900164 void allocateLogicalAddress(final int deviceType, final int preferredAddress,
Jungshik Jang3ee65722014-06-03 16:22:30 +0900165 final AllocateAddressCallback callback) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900166 assertRunOnServiceThread();
167
Jungshik Jangd643f762014-05-22 19:28:09 +0900168 runOnIoThread(new Runnable() {
169 @Override
170 public void run() {
171 handleAllocateLogicalAddress(deviceType, preferredAddress, callback);
172 }
173 });
174 }
175
176 private void handleAllocateLogicalAddress(final int deviceType, int preferredAddress,
Jungshik Jang3ee65722014-06-03 16:22:30 +0900177 final AllocateAddressCallback callback) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900178 assertRunOnIoThread();
Jungshik Jangd643f762014-05-22 19:28:09 +0900179 int startAddress = preferredAddress;
180 // If preferred address is "unregistered", start address will be the smallest
181 // address matched with the given device type.
182 if (preferredAddress == HdmiCec.ADDR_UNREGISTERED) {
183 for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) {
184 if (deviceType == HdmiCec.getTypeFromAddress(i)) {
185 startAddress = i;
186 break;
187 }
188 }
189 }
190
191 int logicalAddress = HdmiCec.ADDR_UNREGISTERED;
192 // Iterates all possible addresses which has the same device type.
193 for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) {
194 int curAddress = (startAddress + i) % NUM_LOGICAL_ADDRESS;
195 if (curAddress != HdmiCec.ADDR_UNREGISTERED
Jinsuk Kim0bc8b072014-05-27 17:23:27 +0900196 && deviceType == HdmiCec.getTypeFromAddress(curAddress)) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900197 if (!sendPollMessage(curAddress, RETRY_COUNT_FOR_LOGICAL_ADDRESS_ALLOCATION)) {
Jungshik Jangd643f762014-05-22 19:28:09 +0900198 logicalAddress = curAddress;
199 break;
200 }
201 }
202 }
203
204 final int assignedAddress = logicalAddress;
205 if (callback != null) {
206 runOnServiceThread(new Runnable() {
207 @Override
208 public void run() {
209 callback.onAllocated(deviceType, assignedAddress);
210 }
211 });
212 }
Jungshik Jang3f74ab02014-04-30 14:31:02 +0900213 }
214
Jungshik Jange9c77c82014-04-24 20:30:09 +0900215 private static byte[] buildBody(int opcode, byte[] params) {
216 byte[] body = new byte[params.length + 1];
217 body[0] = (byte) opcode;
218 System.arraycopy(params, 0, body, 1, params.length);
219 return body;
220 }
Jungshik Jang0792d372014-04-23 17:57:26 +0900221
Jungshik Jang7d9a8432014-04-29 15:12:43 +0900222
Jinsuk Kim0340bbc2014-06-05 11:07:47 +0900223 HdmiPortInfo[] getPortInfos() {
224 return nativeGetPortInfos(mNativePtr);
225 }
226
Jungshik Janga9095ba2014-05-02 13:06:22 +0900227 /**
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +0900228 * Return the locally hosted logical device of a given type.
229 *
230 * @param deviceType logical device type
231 * @return {@link HdmiCecLocalDevice} instance if the instance of the type is available;
232 * otherwise null.
233 */
234 HdmiCecLocalDevice getLocalDevice(int deviceType) {
235 return mLocalDevices.get(deviceType);
236 }
237
238 /**
Jungshik Janga9095ba2014-05-02 13:06:22 +0900239 * Add a new logical address to the device. Device's HW should be notified
240 * when a new logical address is assigned to a device, so that it can accept
241 * a command having available destinations.
242 *
243 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
244 *
245 * @param newLogicalAddress a logical address to be added
246 * @return 0 on success. Otherwise, returns negative value
247 */
248 int addLogicalAddress(int newLogicalAddress) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900249 assertRunOnServiceThread();
Jungshik Janga9095ba2014-05-02 13:06:22 +0900250 if (HdmiCec.isValidAddress(newLogicalAddress)) {
251 return nativeAddLogicalAddress(mNativePtr, newLogicalAddress);
252 } else {
253 return -1;
254 }
255 }
256
257 /**
258 * Clear all logical addresses registered in the device.
259 *
260 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
261 */
262 void clearLogicalAddress() {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900263 assertRunOnServiceThread();
Jungshik Janga1fa91f2014-05-08 20:56:41 +0900264 // TODO: consider to backup logical address so that new logical address
265 // allocation can use it as preferred address.
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +0900266 for (int i = 0; i < mLocalDevices.size(); ++i) {
267 mLocalDevices.valueAt(i).clearAddress();
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900268 }
Jungshik Janga9095ba2014-05-02 13:06:22 +0900269 nativeClearLogicalAddress(mNativePtr);
270 }
271
272 /**
273 * Return the physical address of the device.
274 *
275 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
276 *
277 * @return CEC physical address of the device. The range of success address
278 * is between 0x0000 and 0xFFFF. If failed it returns -1
279 */
280 int getPhysicalAddress() {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900281 assertRunOnServiceThread();
Jungshik Janga9095ba2014-05-02 13:06:22 +0900282 return nativeGetPhysicalAddress(mNativePtr);
283 }
284
285 /**
286 * Return CEC version of the device.
287 *
288 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
289 */
290 int getVersion() {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900291 assertRunOnServiceThread();
Jungshik Janga9095ba2014-05-02 13:06:22 +0900292 return nativeGetVersion(mNativePtr);
293 }
294
295 /**
296 * Return vendor id of the device.
297 *
298 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
299 */
300 int getVendorId() {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900301 assertRunOnServiceThread();
Jungshik Janga9095ba2014-05-02 13:06:22 +0900302 return nativeGetVendorId(mNativePtr);
303 }
304
Jungshik Jang02bb4262014-05-23 16:48:31 +0900305 /**
Jungshik Jang092b4452014-06-11 15:19:17 +0900306 * Pass a option to CEC HAL.
307 *
308 * @param flag a key of option. For more details, look at
309 * {@link HdmiConstants#FLAG_HDMI_OPTION_WAKEUP} to
310 * {@link HdmiConstants#FLAG_HDMI_OPTION_SYSTEM_CEC_CONTROL}
311 * @param value a value of option. Actual value varies flag. For more
312 * details, look at description of flags
313 */
314 void setOption(int flag, int value) {
315 assertRunOnServiceThread();
316 nativeSetOption(mNativePtr, flag, value);
317 }
318
319 /**
320 * Configure ARC circuit in the hardware logic to start or stop the feature.
321 *
322 * @param enabled whether to enable/disable ARC
323 */
324 void setAudioReturnChannel(boolean enabled) {
325 assertRunOnServiceThread();
326 nativeSetAudioReturnChannel(mNativePtr, enabled);
327 }
328
329 /**
330 * Return the connection status of the specified port
331 *
332 * @param port port number to check connection status
333 * @return true if connected; otherwise, return false
334 */
335 boolean isConnected(int port) {
336 assertRunOnServiceThread();
337 return nativeIsConnected(mNativePtr, port);
338 }
339
340 /**
Jungshik Jang02bb4262014-05-23 16:48:31 +0900341 * Poll all remote devices. It sends &lt;Polling Message&gt; to all remote
342 * devices.
343 *
344 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
345 *
346 * @param callback an interface used to get a list of all remote devices' address
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900347 * @param pickStrategy strategy how to pick polling candidates
Jungshik Jang02bb4262014-05-23 16:48:31 +0900348 * @param retryCount the number of retry used to send polling message to remote devices
349 */
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900350 void pollDevices(DevicePollingCallback callback, int pickStrategy, int retryCount) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900351 assertRunOnServiceThread();
Jungshik Jang02bb4262014-05-23 16:48:31 +0900352
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900353 // Extract polling candidates. No need to poll against local devices.
354 List<Integer> pollingCandidates = pickPollCandidates(pickStrategy);
Jungshik Jang02bb4262014-05-23 16:48:31 +0900355 runDevicePolling(pollingCandidates, retryCount, callback);
356 }
357
Jungshik Jangcc5ef8c2014-05-27 13:27:36 +0900358 /**
359 * Return a list of all {@link HdmiCecLocalDevice}s.
360 *
361 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
362 */
363 List<HdmiCecLocalDevice> getLocalDeviceList() {
364 assertRunOnServiceThread();
Jungshik Jang79c58a42014-06-16 16:45:36 +0900365 return HdmiUtils.sparseArrayToList(mLocalDevices);
Jungshik Jangcc5ef8c2014-05-27 13:27:36 +0900366 }
367
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900368 private List<Integer> pickPollCandidates(int pickStrategy) {
369 int strategy = pickStrategy & HdmiControlService.POLL_STRATEGY_MASK;
370 Predicate<Integer> pickPredicate = null;
371 switch (strategy) {
372 case HdmiControlService.POLL_STRATEGY_SYSTEM_AUDIO:
373 pickPredicate = mSystemAudioAddressPredicate;
374 break;
375 case HdmiControlService.POLL_STRATEGY_REMOTES_DEVICES:
376 default: // The default is POLL_STRATEGY_REMOTES_DEVICES.
377 pickPredicate = mRemoteDeviceAddressPredicate;
378 break;
379 }
380
381 int iterationStrategy = pickStrategy & HdmiControlService.POLL_ITERATION_STRATEGY_MASK;
382 ArrayList<Integer> pollingCandidates = new ArrayList<>();
383 switch (iterationStrategy) {
384 case HdmiControlService.POLL_ITERATION_IN_ORDER:
385 for (int i = HdmiCec.ADDR_TV; i <= HdmiCec.ADDR_SPECIFIC_USE; ++i) {
386 if (pickPredicate.apply(i)) {
387 pollingCandidates.add(i);
388 }
389 }
390 break;
391 case HdmiControlService.POLL_ITERATION_REVERSE_ORDER:
392 default: // The default is reverse order.
393 for (int i = HdmiCec.ADDR_SPECIFIC_USE; i >= HdmiCec.ADDR_TV; --i) {
394 if (pickPredicate.apply(i)) {
395 pollingCandidates.add(i);
396 }
397 }
398 break;
399 }
400 return pollingCandidates;
401 }
402
Jungshik Jang02bb4262014-05-23 16:48:31 +0900403 private boolean isAllocatedLocalDeviceAddress(int address) {
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +0900404 for (int i = 0; i < mLocalDevices.size(); ++i) {
405 if (mLocalDevices.valueAt(i).isAddressOf(address)) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900406 return true;
407 }
408 }
409 return false;
410 }
411
412 private void runDevicePolling(final List<Integer> candidates, final int retryCount,
413 final DevicePollingCallback callback) {
414 assertRunOnServiceThread();
415 runOnIoThread(new Runnable() {
416 @Override
417 public void run() {
418 final ArrayList<Integer> allocated = new ArrayList<>();
419 for (Integer address : candidates) {
420 if (sendPollMessage(address, retryCount)) {
421 allocated.add(address);
422 }
423 }
424 if (callback != null) {
425 runOnServiceThread(new Runnable() {
426 @Override
427 public void run() {
428 callback.onPollingFinished(allocated);
429 }
430 });
431 }
432 }
433 });
434 }
435
436 private boolean sendPollMessage(int address, int retryCount) {
437 assertRunOnIoThread();
438 for (int i = 0; i < retryCount; ++i) {
439 // <Polling Message> is a message which has empty body and
440 // uses same address for both source and destination address.
441 // If sending <Polling Message> failed (NAK), it becomes
442 // new logical address for the device because no device uses
443 // it as logical address of the device.
444 if (nativeSendCecCommand(mNativePtr, address, address, EMPTY_BODY)
445 == HdmiControlService.SEND_RESULT_SUCCESS) {
446 return true;
447 }
448 }
449 return false;
450 }
451
452 private void assertRunOnIoThread() {
453 if (Looper.myLooper() != mIoHandler.getLooper()) {
454 throw new IllegalStateException("Should run on io thread.");
455 }
456 }
457
458 private void assertRunOnServiceThread() {
459 if (Looper.myLooper() != mControlHandler.getLooper()) {
460 throw new IllegalStateException("Should run on service thread.");
461 }
462 }
463
464 // Run a Runnable on IO thread.
465 // It should be careful to access member variables on IO thread because
466 // it can be accessed from system thread as well.
Jungshik Jangd643f762014-05-22 19:28:09 +0900467 private void runOnIoThread(Runnable runnable) {
468 mIoHandler.post(runnable);
469 }
470
471 private void runOnServiceThread(Runnable runnable) {
472 mControlHandler.post(runnable);
473 }
474
Jungshik Janga1fa91f2014-05-08 20:56:41 +0900475 private boolean isAcceptableAddress(int address) {
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900476 // Can access command targeting devices available in local device or broadcast command.
477 if (address == HdmiCec.ADDR_BROADCAST) {
478 return true;
479 }
Jungshik Jang02bb4262014-05-23 16:48:31 +0900480 return isAllocatedLocalDeviceAddress(address);
Jungshik Janga1fa91f2014-05-08 20:56:41 +0900481 }
482
Jungshik Jange9c77c82014-04-24 20:30:09 +0900483 private void onReceiveCommand(HdmiCecMessage message) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900484 assertRunOnServiceThread();
Jungshik Jang4085d0e2014-05-27 19:52:39 +0900485 if (isAcceptableAddress(message.getDestination())
486 && mService.handleCecCommand(message)) {
Jungshik Janga1fa91f2014-05-08 20:56:41 +0900487 return;
488 }
Jungshik Jange9c77c82014-04-24 20:30:09 +0900489
Jinsuk Kim3a2f7432014-05-29 06:52:45 +0900490 if (message.getDestination() != HdmiCec.ADDR_BROADCAST) {
491 int sourceAddress = message.getDestination();
492 // Reply <Feature Abort> to initiator (source) for all requests.
493 HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildFeatureAbortCommand(
494 sourceAddress, message.getSource(), message.getOpcode(),
Yuncheol Heo63a2e062014-05-27 23:06:01 +0900495 HdmiConstants.ABORT_REFUSED);
Jinsuk Kim3a2f7432014-05-29 06:52:45 +0900496 sendCommand(cecMessage, null);
497 }
Jungshik Jange9c77c82014-04-24 20:30:09 +0900498 }
499
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900500 void sendCommand(HdmiCecMessage cecMessage) {
501 sendCommand(cecMessage, null);
502 }
503
Jungshik Jangd643f762014-05-22 19:28:09 +0900504 void sendCommand(final HdmiCecMessage cecMessage,
505 final HdmiControlService.SendMessageCallback callback) {
506 runOnIoThread(new Runnable() {
507 @Override
508 public void run() {
509 byte[] body = buildBody(cecMessage.getOpcode(), cecMessage.getParams());
510 final int error = nativeSendCecCommand(mNativePtr, cecMessage.getSource(),
511 cecMessage.getDestination(), body);
Yuncheol Heoece603b2014-05-23 20:10:19 +0900512 if (error != HdmiControlService.SEND_RESULT_SUCCESS) {
513 Slog.w(TAG, "Failed to send " + cecMessage);
514 }
Jungshik Jangd643f762014-05-22 19:28:09 +0900515 if (callback != null) {
516 runOnServiceThread(new Runnable() {
517 @Override
518 public void run() {
519 callback.onSendCompleted(error);
520 }
521 });
522 }
523 }
524 });
Jungshik Jange9c77c82014-04-24 20:30:09 +0900525 }
526
Jungshik Jang0792d372014-04-23 17:57:26 +0900527 /**
Jungshik Jange9c77c82014-04-24 20:30:09 +0900528 * Called by native when incoming CEC message arrived.
Jungshik Jang0792d372014-04-23 17:57:26 +0900529 */
Jungshik Jange9c77c82014-04-24 20:30:09 +0900530 private void handleIncomingCecCommand(int srcAddress, int dstAddress, byte[] body) {
Jungshik Jang4085d0e2014-05-27 19:52:39 +0900531 assertRunOnServiceThread();
532 onReceiveCommand(HdmiCecMessageBuilder.of(srcAddress, dstAddress, body));
Jungshik Jange9c77c82014-04-24 20:30:09 +0900533 }
534
535 /**
536 * Called by native when a hotplug event issues.
537 */
538 private void handleHotplug(boolean connected) {
Jungshik Jang67ea5212014-05-15 14:05:24 +0900539 // TODO: once add port number to cec HAL interface, pass port number
540 // to the service.
541 mService.onHotplug(0, connected);
Jungshik Jang0792d372014-04-23 17:57:26 +0900542 }
543
Jungshik Jang4085d0e2014-05-27 19:52:39 +0900544 private static native long nativeInit(HdmiCecController handler, MessageQueue messageQueue);
Jungshik Janga9095ba2014-05-02 13:06:22 +0900545 private static native int nativeSendCecCommand(long controllerPtr, int srcAddress,
Jungshik Jange9c77c82014-04-24 20:30:09 +0900546 int dstAddress, byte[] body);
Jungshik Janga9095ba2014-05-02 13:06:22 +0900547 private static native int nativeAddLogicalAddress(long controllerPtr, int logicalAddress);
548 private static native void nativeClearLogicalAddress(long controllerPtr);
549 private static native int nativeGetPhysicalAddress(long controllerPtr);
550 private static native int nativeGetVersion(long controllerPtr);
551 private static native int nativeGetVendorId(long controllerPtr);
Jinsuk Kim0340bbc2014-06-05 11:07:47 +0900552 private static native HdmiPortInfo[] nativeGetPortInfos(long controllerPtr);
Jungshik Jang092b4452014-06-11 15:19:17 +0900553 private static native void nativeSetOption(long controllerPtr, int flag, int value);
554 private static native void nativeSetAudioReturnChannel(long controllerPtr, boolean flag);
555 private static native boolean nativeIsConnected(long controllerPtr, int port);
Jungshik Jang0792d372014-04-23 17:57:26 +0900556}