blob: 5c420d76e3f5356decc22fb3c1d411f9de333ff4 [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 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
Jungshik Jang1a4485d2014-05-26 11:02:36 +0900111 // Map-like container of all cec devices including local ones.
112 // A logical address of device is used as key of container.
Jungshik Jang02bb4262014-05-23 16:48:31 +0900113 private final SparseArray<HdmiCecDeviceInfo> mDeviceInfos = new SparseArray<>();
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900114
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +0900115 // Stores the local CEC devices in the system. Device type is used for key.
116 private final SparseArray<HdmiCecLocalDevice> mLocalDevices = new SparseArray<>();
Jungshik Jang7d9a8432014-04-29 15:12:43 +0900117
Jungshik Jang0792d372014-04-23 17:57:26 +0900118 // Private constructor. Use HdmiCecController.create().
119 private HdmiCecController() {
120 }
121
122 /**
123 * A factory method to get {@link HdmiCecController}. If it fails to initialize
124 * inner device or has no device it will return {@code null}.
125 *
126 * <p>Declared as package-private, accessed by {@link HdmiControlService} only.
Jungshik Jange9c77c82014-04-24 20:30:09 +0900127 * @param service {@link HdmiControlService} instance used to create internal handler
128 * and to pass callback for incoming message or event.
Jungshik Jang0792d372014-04-23 17:57:26 +0900129 * @return {@link HdmiCecController} if device is initialized successfully. Otherwise,
130 * returns {@code null}.
131 */
Jungshik Jange9c77c82014-04-24 20:30:09 +0900132 static HdmiCecController create(HdmiControlService service) {
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900133 HdmiCecController controller = new HdmiCecController();
Jungshik Jang4085d0e2014-05-27 19:52:39 +0900134 long nativePtr = nativeInit(controller, service.getServiceLooper().getQueue());
Jungshik Jang0792d372014-04-23 17:57:26 +0900135 if (nativePtr == 0L) {
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900136 controller = null;
Jungshik Jang0792d372014-04-23 17:57:26 +0900137 return null;
138 }
139
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900140 controller.init(service, nativePtr);
141 return controller;
142 }
143
144 private void init(HdmiControlService service, long nativePtr) {
145 mService = service;
146 mIoHandler = new Handler(service.getServiceLooper());
147 mControlHandler = new Handler(service.getServiceLooper());
148 mNativePtr = nativePtr;
Jungshik Jang0792d372014-04-23 17:57:26 +0900149 }
150
Jungshik Jang3ee65722014-06-03 16:22:30 +0900151 void addLocalDevice(int deviceType, HdmiCecLocalDevice device) {
152 mLocalDevices.put(deviceType, device);
Jungshik Jang3f74ab02014-04-30 14:31:02 +0900153 }
154
155 /**
156 * Allocate a new logical address of the given device type. Allocated
Jungshik Jang3ee65722014-06-03 16:22:30 +0900157 * address will be reported through {@link AllocateAddressCallback}.
Jungshik Jang3f74ab02014-04-30 14:31:02 +0900158 *
159 * <p> Declared as package-private, accessed by {@link HdmiControlService} only.
160 *
161 * @param deviceType type of device to used to determine logical address
162 * @param preferredAddress a logical address preferred to be allocated.
163 * If sets {@link HdmiCec#ADDR_UNREGISTERED}, scans
164 * the smallest logical address matched with the given device type.
165 * Otherwise, scan address will start from {@code preferredAddress}
166 * @param callback callback interface to report allocated logical address to caller
167 */
Jungshik Jangd643f762014-05-22 19:28:09 +0900168 void allocateLogicalAddress(final int deviceType, final int preferredAddress,
Jungshik Jang3ee65722014-06-03 16:22:30 +0900169 final AllocateAddressCallback callback) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900170 assertRunOnServiceThread();
171
Jungshik Jangd643f762014-05-22 19:28:09 +0900172 runOnIoThread(new Runnable() {
173 @Override
174 public void run() {
175 handleAllocateLogicalAddress(deviceType, preferredAddress, callback);
176 }
177 });
178 }
179
180 private void handleAllocateLogicalAddress(final int deviceType, int preferredAddress,
Jungshik Jang3ee65722014-06-03 16:22:30 +0900181 final AllocateAddressCallback callback) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900182 assertRunOnIoThread();
Jungshik Jangd643f762014-05-22 19:28:09 +0900183 int startAddress = preferredAddress;
184 // If preferred address is "unregistered", start address will be the smallest
185 // address matched with the given device type.
186 if (preferredAddress == HdmiCec.ADDR_UNREGISTERED) {
187 for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) {
188 if (deviceType == HdmiCec.getTypeFromAddress(i)) {
189 startAddress = i;
190 break;
191 }
192 }
193 }
194
195 int logicalAddress = HdmiCec.ADDR_UNREGISTERED;
196 // Iterates all possible addresses which has the same device type.
197 for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) {
198 int curAddress = (startAddress + i) % NUM_LOGICAL_ADDRESS;
199 if (curAddress != HdmiCec.ADDR_UNREGISTERED
Jinsuk Kim0bc8b072014-05-27 17:23:27 +0900200 && deviceType == HdmiCec.getTypeFromAddress(curAddress)) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900201 if (!sendPollMessage(curAddress, RETRY_COUNT_FOR_LOGICAL_ADDRESS_ALLOCATION)) {
Jungshik Jangd643f762014-05-22 19:28:09 +0900202 logicalAddress = curAddress;
203 break;
204 }
205 }
206 }
207
208 final int assignedAddress = logicalAddress;
209 if (callback != null) {
210 runOnServiceThread(new Runnable() {
211 @Override
212 public void run() {
213 callback.onAllocated(deviceType, assignedAddress);
214 }
215 });
216 }
Jungshik Jang3f74ab02014-04-30 14:31:02 +0900217 }
218
Jungshik Jange9c77c82014-04-24 20:30:09 +0900219 private static byte[] buildBody(int opcode, byte[] params) {
220 byte[] body = new byte[params.length + 1];
221 body[0] = (byte) opcode;
222 System.arraycopy(params, 0, body, 1, params.length);
223 return body;
224 }
Jungshik Jang0792d372014-04-23 17:57:26 +0900225
Jungshik Jang7d9a8432014-04-29 15:12:43 +0900226 /**
227 * Add a new {@link HdmiCecDeviceInfo}. It returns old device info which has the same
228 * logical address as new device info's.
229 *
230 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
231 *
232 * @param deviceInfo a new {@link HdmiCecDeviceInfo} to be added.
233 * @return {@code null} if it is new device. Otherwise, returns old {@HdmiCecDeviceInfo}
234 * that has the same logical address as new one has.
235 */
236 HdmiCecDeviceInfo addDeviceInfo(HdmiCecDeviceInfo deviceInfo) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900237 assertRunOnServiceThread();
Jungshik Jang7d9a8432014-04-29 15:12:43 +0900238 HdmiCecDeviceInfo oldDeviceInfo = getDeviceInfo(deviceInfo.getLogicalAddress());
239 if (oldDeviceInfo != null) {
240 removeDeviceInfo(deviceInfo.getLogicalAddress());
241 }
242 mDeviceInfos.append(deviceInfo.getLogicalAddress(), deviceInfo);
243 return oldDeviceInfo;
244 }
245
246 /**
247 * Remove a device info corresponding to the given {@code logicalAddress}.
248 * It returns removed {@link HdmiCecDeviceInfo} if exists.
249 *
250 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
251 *
252 * @param logicalAddress logical address of device to be removed
253 * @return removed {@link HdmiCecDeviceInfo} it exists. Otherwise, returns {@code null}
254 */
255 HdmiCecDeviceInfo removeDeviceInfo(int logicalAddress) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900256 assertRunOnServiceThread();
Jungshik Jang7d9a8432014-04-29 15:12:43 +0900257 HdmiCecDeviceInfo deviceInfo = mDeviceInfos.get(logicalAddress);
258 if (deviceInfo != null) {
259 mDeviceInfos.remove(logicalAddress);
260 }
261 return deviceInfo;
262 }
263
264 /**
Jungshik Jangcc5ef8c2014-05-27 13:27:36 +0900265 * Clear all device info.
266 *
267 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
268 */
269 void clearDeviceInfoList() {
270 assertRunOnServiceThread();
271 mDeviceInfos.clear();
272 }
273
274 /**
Jungshik Jang02bb4262014-05-23 16:48:31 +0900275 * Return a list of all {@link HdmiCecDeviceInfo}.
Jungshik Jang7d9a8432014-04-29 15:12:43 +0900276 *
277 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900278 *
279 * @param includeLocalDevice whether to add local device or not
Jungshik Jang7d9a8432014-04-29 15:12:43 +0900280 */
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900281 List<HdmiCecDeviceInfo> getDeviceInfoList(boolean includeLocalDevice) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900282 assertRunOnServiceThread();
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900283 if (includeLocalDevice) {
284 return sparseArrayToList(mDeviceInfos);
285 } else {
286 ArrayList<HdmiCecDeviceInfo> infoList = new ArrayList<>();
287 for (int i = 0; i < mDeviceInfos.size(); ++i) {
288 HdmiCecDeviceInfo info = mDeviceInfos.valueAt(i);
289 if (mRemoteDeviceAddressPredicate.apply(info.getLogicalAddress())) {
290 infoList.add(info);
291 }
292 }
293 return infoList;
294 }
Jungshik Jang7d9a8432014-04-29 15:12:43 +0900295 }
296
297 /**
298 * Return a {@link HdmiCecDeviceInfo} corresponding to the given {@code logicalAddress}.
299 *
300 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
301 *
302 * @param logicalAddress logical address to be retrieved
303 * @return {@link HdmiCecDeviceInfo} matched with the given {@code logicalAddress}.
304 * Returns null if no logical address matched
305 */
306 HdmiCecDeviceInfo getDeviceInfo(int logicalAddress) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900307 assertRunOnServiceThread();
Jungshik Jang7d9a8432014-04-29 15:12:43 +0900308 return mDeviceInfos.get(logicalAddress);
309 }
310
Jungshik Janga9095ba2014-05-02 13:06:22 +0900311 /**
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +0900312 * Return the locally hosted logical device of a given type.
313 *
314 * @param deviceType logical device type
315 * @return {@link HdmiCecLocalDevice} instance if the instance of the type is available;
316 * otherwise null.
317 */
318 HdmiCecLocalDevice getLocalDevice(int deviceType) {
319 return mLocalDevices.get(deviceType);
320 }
321
322 /**
Jungshik Janga9095ba2014-05-02 13:06:22 +0900323 * Add a new logical address to the device. Device's HW should be notified
324 * when a new logical address is assigned to a device, so that it can accept
325 * a command having available destinations.
326 *
327 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
328 *
329 * @param newLogicalAddress a logical address to be added
330 * @return 0 on success. Otherwise, returns negative value
331 */
332 int addLogicalAddress(int newLogicalAddress) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900333 assertRunOnServiceThread();
Jungshik Janga9095ba2014-05-02 13:06:22 +0900334 if (HdmiCec.isValidAddress(newLogicalAddress)) {
335 return nativeAddLogicalAddress(mNativePtr, newLogicalAddress);
336 } else {
337 return -1;
338 }
339 }
340
341 /**
342 * Clear all logical addresses registered in the device.
343 *
344 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
345 */
346 void clearLogicalAddress() {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900347 assertRunOnServiceThread();
Jungshik Janga1fa91f2014-05-08 20:56:41 +0900348 // TODO: consider to backup logical address so that new logical address
349 // allocation can use it as preferred address.
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +0900350 for (int i = 0; i < mLocalDevices.size(); ++i) {
351 mLocalDevices.valueAt(i).clearAddress();
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900352 }
Jungshik Janga9095ba2014-05-02 13:06:22 +0900353 nativeClearLogicalAddress(mNativePtr);
354 }
355
356 /**
357 * Return the physical address of the device.
358 *
359 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
360 *
361 * @return CEC physical address of the device. The range of success address
362 * is between 0x0000 and 0xFFFF. If failed it returns -1
363 */
364 int getPhysicalAddress() {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900365 assertRunOnServiceThread();
Jungshik Janga9095ba2014-05-02 13:06:22 +0900366 return nativeGetPhysicalAddress(mNativePtr);
367 }
368
369 /**
370 * Return CEC version of the device.
371 *
372 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
373 */
374 int getVersion() {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900375 assertRunOnServiceThread();
Jungshik Janga9095ba2014-05-02 13:06:22 +0900376 return nativeGetVersion(mNativePtr);
377 }
378
379 /**
380 * Return vendor id of the device.
381 *
382 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
383 */
384 int getVendorId() {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900385 assertRunOnServiceThread();
Jungshik Janga9095ba2014-05-02 13:06:22 +0900386 return nativeGetVendorId(mNativePtr);
387 }
388
Jungshik Jang02bb4262014-05-23 16:48:31 +0900389 /**
390 * Poll all remote devices. It sends &lt;Polling Message&gt; to all remote
391 * devices.
392 *
393 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
394 *
395 * @param callback an interface used to get a list of all remote devices' address
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900396 * @param pickStrategy strategy how to pick polling candidates
Jungshik Jang02bb4262014-05-23 16:48:31 +0900397 * @param retryCount the number of retry used to send polling message to remote devices
398 */
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900399 void pollDevices(DevicePollingCallback callback, int pickStrategy, int retryCount) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900400 assertRunOnServiceThread();
Jungshik Jang02bb4262014-05-23 16:48:31 +0900401
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900402 // Extract polling candidates. No need to poll against local devices.
403 List<Integer> pollingCandidates = pickPollCandidates(pickStrategy);
Jungshik Jang02bb4262014-05-23 16:48:31 +0900404 runDevicePolling(pollingCandidates, retryCount, callback);
405 }
406
Jungshik Jangcc5ef8c2014-05-27 13:27:36 +0900407 /**
408 * Return a list of all {@link HdmiCecLocalDevice}s.
409 *
410 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
411 */
412 List<HdmiCecLocalDevice> getLocalDeviceList() {
413 assertRunOnServiceThread();
414 return sparseArrayToList(mLocalDevices);
415 }
416
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900417 private List<Integer> pickPollCandidates(int pickStrategy) {
418 int strategy = pickStrategy & HdmiControlService.POLL_STRATEGY_MASK;
419 Predicate<Integer> pickPredicate = null;
420 switch (strategy) {
421 case HdmiControlService.POLL_STRATEGY_SYSTEM_AUDIO:
422 pickPredicate = mSystemAudioAddressPredicate;
423 break;
424 case HdmiControlService.POLL_STRATEGY_REMOTES_DEVICES:
425 default: // The default is POLL_STRATEGY_REMOTES_DEVICES.
426 pickPredicate = mRemoteDeviceAddressPredicate;
427 break;
428 }
429
430 int iterationStrategy = pickStrategy & HdmiControlService.POLL_ITERATION_STRATEGY_MASK;
431 ArrayList<Integer> pollingCandidates = new ArrayList<>();
432 switch (iterationStrategy) {
433 case HdmiControlService.POLL_ITERATION_IN_ORDER:
434 for (int i = HdmiCec.ADDR_TV; i <= HdmiCec.ADDR_SPECIFIC_USE; ++i) {
435 if (pickPredicate.apply(i)) {
436 pollingCandidates.add(i);
437 }
438 }
439 break;
440 case HdmiControlService.POLL_ITERATION_REVERSE_ORDER:
441 default: // The default is reverse order.
442 for (int i = HdmiCec.ADDR_SPECIFIC_USE; i >= HdmiCec.ADDR_TV; --i) {
443 if (pickPredicate.apply(i)) {
444 pollingCandidates.add(i);
445 }
446 }
447 break;
448 }
449 return pollingCandidates;
450 }
451
Jungshik Jangcc5ef8c2014-05-27 13:27:36 +0900452 private static <T> List<T> sparseArrayToList(SparseArray<T> array) {
453 ArrayList<T> list = new ArrayList<>();
454 for (int i = 0; i < array.size(); ++i) {
455 list.add(array.valueAt(i));
456 }
457 return list;
458 }
459
Jungshik Jang02bb4262014-05-23 16:48:31 +0900460 private boolean isAllocatedLocalDeviceAddress(int address) {
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +0900461 for (int i = 0; i < mLocalDevices.size(); ++i) {
462 if (mLocalDevices.valueAt(i).isAddressOf(address)) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900463 return true;
464 }
465 }
466 return false;
467 }
468
469 private void runDevicePolling(final List<Integer> candidates, final int retryCount,
470 final DevicePollingCallback callback) {
471 assertRunOnServiceThread();
472 runOnIoThread(new Runnable() {
473 @Override
474 public void run() {
475 final ArrayList<Integer> allocated = new ArrayList<>();
476 for (Integer address : candidates) {
477 if (sendPollMessage(address, retryCount)) {
478 allocated.add(address);
479 }
480 }
481 if (callback != null) {
482 runOnServiceThread(new Runnable() {
483 @Override
484 public void run() {
485 callback.onPollingFinished(allocated);
486 }
487 });
488 }
489 }
490 });
491 }
492
493 private boolean sendPollMessage(int address, int retryCount) {
494 assertRunOnIoThread();
495 for (int i = 0; i < retryCount; ++i) {
496 // <Polling Message> is a message which has empty body and
497 // uses same address for both source and destination address.
498 // If sending <Polling Message> failed (NAK), it becomes
499 // new logical address for the device because no device uses
500 // it as logical address of the device.
501 if (nativeSendCecCommand(mNativePtr, address, address, EMPTY_BODY)
502 == HdmiControlService.SEND_RESULT_SUCCESS) {
503 return true;
504 }
505 }
506 return false;
507 }
508
509 private void assertRunOnIoThread() {
510 if (Looper.myLooper() != mIoHandler.getLooper()) {
511 throw new IllegalStateException("Should run on io thread.");
512 }
513 }
514
515 private void assertRunOnServiceThread() {
516 if (Looper.myLooper() != mControlHandler.getLooper()) {
517 throw new IllegalStateException("Should run on service thread.");
518 }
519 }
520
521 // Run a Runnable on IO thread.
522 // It should be careful to access member variables on IO thread because
523 // it can be accessed from system thread as well.
Jungshik Jangd643f762014-05-22 19:28:09 +0900524 private void runOnIoThread(Runnable runnable) {
525 mIoHandler.post(runnable);
526 }
527
528 private void runOnServiceThread(Runnable runnable) {
529 mControlHandler.post(runnable);
530 }
531
Jungshik Janga1fa91f2014-05-08 20:56:41 +0900532 private boolean isAcceptableAddress(int address) {
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900533 // Can access command targeting devices available in local device or broadcast command.
534 if (address == HdmiCec.ADDR_BROADCAST) {
535 return true;
536 }
Jungshik Jang02bb4262014-05-23 16:48:31 +0900537 return isAllocatedLocalDeviceAddress(address);
Jungshik Janga1fa91f2014-05-08 20:56:41 +0900538 }
539
Jungshik Jange9c77c82014-04-24 20:30:09 +0900540 private void onReceiveCommand(HdmiCecMessage message) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900541 assertRunOnServiceThread();
Jungshik Jang4085d0e2014-05-27 19:52:39 +0900542 if (isAcceptableAddress(message.getDestination())
543 && mService.handleCecCommand(message)) {
Jungshik Janga1fa91f2014-05-08 20:56:41 +0900544 return;
545 }
Jungshik Jange9c77c82014-04-24 20:30:09 +0900546
Jinsuk Kim3a2f7432014-05-29 06:52:45 +0900547 if (message.getDestination() != HdmiCec.ADDR_BROADCAST) {
548 int sourceAddress = message.getDestination();
549 // Reply <Feature Abort> to initiator (source) for all requests.
550 HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildFeatureAbortCommand(
551 sourceAddress, message.getSource(), message.getOpcode(),
Yuncheol Heo63a2e062014-05-27 23:06:01 +0900552 HdmiConstants.ABORT_REFUSED);
Jinsuk Kim3a2f7432014-05-29 06:52:45 +0900553 sendCommand(cecMessage, null);
554 }
Jungshik Jange9c77c82014-04-24 20:30:09 +0900555 }
556
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900557 void sendCommand(HdmiCecMessage cecMessage) {
558 sendCommand(cecMessage, null);
559 }
560
Jungshik Jangd643f762014-05-22 19:28:09 +0900561 void sendCommand(final HdmiCecMessage cecMessage,
562 final HdmiControlService.SendMessageCallback callback) {
563 runOnIoThread(new Runnable() {
564 @Override
565 public void run() {
566 byte[] body = buildBody(cecMessage.getOpcode(), cecMessage.getParams());
567 final int error = nativeSendCecCommand(mNativePtr, cecMessage.getSource(),
568 cecMessage.getDestination(), body);
Yuncheol Heoece603b2014-05-23 20:10:19 +0900569 if (error != HdmiControlService.SEND_RESULT_SUCCESS) {
570 Slog.w(TAG, "Failed to send " + cecMessage);
571 }
Jungshik Jangd643f762014-05-22 19:28:09 +0900572 if (callback != null) {
573 runOnServiceThread(new Runnable() {
574 @Override
575 public void run() {
576 callback.onSendCompleted(error);
577 }
578 });
579 }
580 }
581 });
Jungshik Jange9c77c82014-04-24 20:30:09 +0900582 }
583
Jungshik Jang0792d372014-04-23 17:57:26 +0900584 /**
Jungshik Jange9c77c82014-04-24 20:30:09 +0900585 * Called by native when incoming CEC message arrived.
Jungshik Jang0792d372014-04-23 17:57:26 +0900586 */
Jungshik Jange9c77c82014-04-24 20:30:09 +0900587 private void handleIncomingCecCommand(int srcAddress, int dstAddress, byte[] body) {
Jungshik Jang4085d0e2014-05-27 19:52:39 +0900588 assertRunOnServiceThread();
589 onReceiveCommand(HdmiCecMessageBuilder.of(srcAddress, dstAddress, body));
Jungshik Jange9c77c82014-04-24 20:30:09 +0900590 }
591
592 /**
593 * Called by native when a hotplug event issues.
594 */
595 private void handleHotplug(boolean connected) {
Jungshik Jang67ea5212014-05-15 14:05:24 +0900596 // TODO: once add port number to cec HAL interface, pass port number
597 // to the service.
598 mService.onHotplug(0, connected);
Jungshik Jang0792d372014-04-23 17:57:26 +0900599 }
600
Jungshik Jang4085d0e2014-05-27 19:52:39 +0900601 private static native long nativeInit(HdmiCecController handler, MessageQueue messageQueue);
Jungshik Janga9095ba2014-05-02 13:06:22 +0900602 private static native int nativeSendCecCommand(long controllerPtr, int srcAddress,
Jungshik Jange9c77c82014-04-24 20:30:09 +0900603 int dstAddress, byte[] body);
Jungshik Janga9095ba2014-05-02 13:06:22 +0900604 private static native int nativeAddLogicalAddress(long controllerPtr, int logicalAddress);
605 private static native void nativeClearLogicalAddress(long controllerPtr);
606 private static native int nativeGetPhysicalAddress(long controllerPtr);
607 private static native int nativeGetVersion(long controllerPtr);
608 private static native int nativeGetVendorId(long controllerPtr);
Jungshik Jang0792d372014-04-23 17:57:26 +0900609}