blob: c5a6dbd788b71381f362e21bf7e246969ebe323a [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
Jinsuk Kim0340bbc2014-06-05 11:07:47 +090019import android.hardware.hdmi.HdmiPortInfo;
Jungshik Jang0792d372014-04-23 17:57:26 +090020import android.os.Handler;
Jungshik Jang02bb4262014-05-23 16:48:31 +090021import android.os.Looper;
Jungshik Jang4085d0e2014-05-27 19:52:39 +090022import android.os.MessageQueue;
Yuncheol Heoece603b2014-05-23 20:10:19 +090023import android.util.Slog;
Jungshik Jang7d9a8432014-04-29 15:12:43 +090024import android.util.SparseArray;
Jungshik Jange9c77c82014-04-24 20:30:09 +090025
Terry Heo959d2db2014-08-28 16:45:41 +090026import com.android.internal.util.IndentingPrintWriter;
Jungshik Jang0f8b4b72014-05-28 17:58:58 +090027import com.android.internal.util.Predicate;
Jungshik Janga5b74142014-06-23 18:03:10 +090028import com.android.server.hdmi.HdmiAnnotations.IoThreadOnly;
29import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
Jungshik Jang02bb4262014-05-23 16:48:31 +090030import com.android.server.hdmi.HdmiControlService.DevicePollingCallback;
31
Jungshik Jang3f74ab02014-04-30 14:31:02 +090032import libcore.util.EmptyArray;
33
Jungshik Jang7d9a8432014-04-29 15:12:43 +090034import java.util.ArrayList;
Jungshik Jang7d9a8432014-04-29 15:12:43 +090035import java.util.List;
Jungshik Jang0792d372014-04-23 17:57:26 +090036
37/**
38 * Manages HDMI-CEC command and behaviors. It converts user's command into CEC command
39 * and pass it to CEC HAL so that it sends message to other device. For incoming
40 * message it translates the message and delegates it to proper module.
41 *
Jungshik Jang02bb4262014-05-23 16:48:31 +090042 * <p>It should be careful to access member variables on IO thread because
43 * it can be accessed from system thread as well.
44 *
Jungshik Jang0792d372014-04-23 17:57:26 +090045 * <p>It can be created only by {@link HdmiCecController#create}
46 *
47 * <p>Declared as package-private, accessed by {@link HdmiControlService} only.
48 */
Jungshik Janga9095ba2014-05-02 13:06:22 +090049final class HdmiCecController {
Jungshik Jang0792d372014-04-23 17:57:26 +090050 private static final String TAG = "HdmiCecController";
51
Jungshik Jang3ee65722014-06-03 16:22:30 +090052 /**
53 * Interface to report allocated logical address.
54 */
55 interface AllocateAddressCallback {
56 /**
57 * Called when a new logical address is allocated.
58 *
59 * @param deviceType requested device type to allocate logical address
60 * @param logicalAddress allocated logical address. If it is
Jungshik Jang42230722014-07-07 17:40:25 +090061 * {@link Constants#ADDR_UNREGISTERED}, it means that
Jungshik Jang3ee65722014-06-03 16:22:30 +090062 * it failed to allocate logical address for the given device type
63 */
64 void onAllocated(int deviceType, int logicalAddress);
65 }
66
Jungshik Jang3f74ab02014-04-30 14:31:02 +090067 private static final byte[] EMPTY_BODY = EmptyArray.BYTE;
68
Jungshik Jang3f74ab02014-04-30 14:31:02 +090069 private static final int NUM_LOGICAL_ADDRESS = 16;
70
Jungshik Jang0f8b4b72014-05-28 17:58:58 +090071 // Predicate for whether the given logical address is remote device's one or not.
72 private final Predicate<Integer> mRemoteDeviceAddressPredicate = new Predicate<Integer>() {
73 @Override
74 public boolean apply(Integer address) {
75 return !isAllocatedLocalDeviceAddress(address);
76 }
77 };
78
79 // Predicate whether the given logical address is system audio's one or not
80 private final Predicate<Integer> mSystemAudioAddressPredicate = new Predicate<Integer>() {
81 @Override
82 public boolean apply(Integer address) {
Jinsuk Kimc0c20d02014-07-04 14:34:31 +090083 return HdmiUtils.getTypeFromAddress(address) == Constants.ADDR_AUDIO_SYSTEM;
Jungshik Jang0f8b4b72014-05-28 17:58:58 +090084 }
85 };
86
Jungshik Jang0792d372014-04-23 17:57:26 +090087 // Handler instance to process synchronous I/O (mainly send) message.
88 private Handler mIoHandler;
89
90 // Handler instance to process various messages coming from other CEC
91 // device or issued by internal state change.
Jungshik Jange9c77c82014-04-24 20:30:09 +090092 private Handler mControlHandler;
Jungshik Jang0792d372014-04-23 17:57:26 +090093
94 // Stores the pointer to the native implementation of the service that
95 // interacts with HAL.
Jungshik Jang02bb4262014-05-23 16:48:31 +090096 private volatile long mNativePtr;
Jungshik Jang0792d372014-04-23 17:57:26 +090097
Jungshik Jang7df52862014-08-11 14:35:27 +090098 private final HdmiControlService mService;
Jungshik Janga1fa91f2014-05-08 20:56:41 +090099
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +0900100 // Stores the local CEC devices in the system. Device type is used for key.
101 private final SparseArray<HdmiCecLocalDevice> mLocalDevices = new SparseArray<>();
Jungshik Jang7d9a8432014-04-29 15:12:43 +0900102
Jungshik Jang0792d372014-04-23 17:57:26 +0900103 // Private constructor. Use HdmiCecController.create().
Jungshik Jang7df52862014-08-11 14:35:27 +0900104 private HdmiCecController(HdmiControlService service) {
105 mService = service;
Jungshik Jang0792d372014-04-23 17:57:26 +0900106 }
107
108 /**
109 * A factory method to get {@link HdmiCecController}. If it fails to initialize
110 * inner device or has no device it will return {@code null}.
111 *
112 * <p>Declared as package-private, accessed by {@link HdmiControlService} only.
Jungshik Jange9c77c82014-04-24 20:30:09 +0900113 * @param service {@link HdmiControlService} instance used to create internal handler
114 * and to pass callback for incoming message or event.
Jungshik Jang0792d372014-04-23 17:57:26 +0900115 * @return {@link HdmiCecController} if device is initialized successfully. Otherwise,
116 * returns {@code null}.
117 */
Jungshik Jange9c77c82014-04-24 20:30:09 +0900118 static HdmiCecController create(HdmiControlService service) {
Jungshik Jang7df52862014-08-11 14:35:27 +0900119 HdmiCecController controller = new HdmiCecController(service);
Jungshik Jang4085d0e2014-05-27 19:52:39 +0900120 long nativePtr = nativeInit(controller, service.getServiceLooper().getQueue());
Jungshik Jang0792d372014-04-23 17:57:26 +0900121 if (nativePtr == 0L) {
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900122 controller = null;
Jungshik Jang0792d372014-04-23 17:57:26 +0900123 return null;
124 }
125
Jungshik Jang7df52862014-08-11 14:35:27 +0900126 controller.init(nativePtr);
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900127 return controller;
128 }
129
Jungshik Jang7df52862014-08-11 14:35:27 +0900130 private void init(long nativePtr) {
131 mIoHandler = new Handler(mService.getServiceLooper());
132 mControlHandler = new Handler(mService.getServiceLooper());
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900133 mNativePtr = nativePtr;
Jungshik Jang0792d372014-04-23 17:57:26 +0900134 }
135
Jungshik Janga5b74142014-06-23 18:03:10 +0900136 @ServiceThreadOnly
Jungshik Jang3ee65722014-06-03 16:22:30 +0900137 void addLocalDevice(int deviceType, HdmiCecLocalDevice device) {
Jungshik Janga5b74142014-06-23 18:03:10 +0900138 assertRunOnServiceThread();
Jungshik Jang3ee65722014-06-03 16:22:30 +0900139 mLocalDevices.put(deviceType, device);
Jungshik Jang3f74ab02014-04-30 14:31:02 +0900140 }
141
142 /**
143 * Allocate a new logical address of the given device type. Allocated
Jungshik Jang3ee65722014-06-03 16:22:30 +0900144 * address will be reported through {@link AllocateAddressCallback}.
Jungshik Jang3f74ab02014-04-30 14:31:02 +0900145 *
146 * <p> Declared as package-private, accessed by {@link HdmiControlService} only.
147 *
148 * @param deviceType type of device to used to determine logical address
149 * @param preferredAddress a logical address preferred to be allocated.
Jungshik Jang42230722014-07-07 17:40:25 +0900150 * If sets {@link Constants#ADDR_UNREGISTERED}, scans
Jungshik Jang3f74ab02014-04-30 14:31:02 +0900151 * the smallest logical address matched with the given device type.
152 * Otherwise, scan address will start from {@code preferredAddress}
153 * @param callback callback interface to report allocated logical address to caller
154 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900155 @ServiceThreadOnly
Jungshik Jangd643f762014-05-22 19:28:09 +0900156 void allocateLogicalAddress(final int deviceType, final int preferredAddress,
Jungshik Jang3ee65722014-06-03 16:22:30 +0900157 final AllocateAddressCallback callback) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900158 assertRunOnServiceThread();
159
Jungshik Jangd643f762014-05-22 19:28:09 +0900160 runOnIoThread(new Runnable() {
161 @Override
162 public void run() {
163 handleAllocateLogicalAddress(deviceType, preferredAddress, callback);
164 }
165 });
166 }
167
Jungshik Janga5b74142014-06-23 18:03:10 +0900168 @IoThreadOnly
Jungshik Jangd643f762014-05-22 19:28:09 +0900169 private void handleAllocateLogicalAddress(final int deviceType, int preferredAddress,
Jungshik Jang3ee65722014-06-03 16:22:30 +0900170 final AllocateAddressCallback callback) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900171 assertRunOnIoThread();
Jungshik Jangd643f762014-05-22 19:28:09 +0900172 int startAddress = preferredAddress;
173 // If preferred address is "unregistered", start address will be the smallest
174 // address matched with the given device type.
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900175 if (preferredAddress == Constants.ADDR_UNREGISTERED) {
Jungshik Jangd643f762014-05-22 19:28:09 +0900176 for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) {
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900177 if (deviceType == HdmiUtils.getTypeFromAddress(i)) {
Jungshik Jangd643f762014-05-22 19:28:09 +0900178 startAddress = i;
179 break;
180 }
181 }
182 }
183
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900184 int logicalAddress = Constants.ADDR_UNREGISTERED;
Jungshik Jangd643f762014-05-22 19:28:09 +0900185 // Iterates all possible addresses which has the same device type.
186 for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) {
187 int curAddress = (startAddress + i) % NUM_LOGICAL_ADDRESS;
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900188 if (curAddress != Constants.ADDR_UNREGISTERED
189 && deviceType == HdmiUtils.getTypeFromAddress(curAddress)) {
Jungshik Jang8e93c842014-08-06 15:48:33 +0900190 int failedPollingCount = 0;
191 for (int j = 0; j < HdmiConfig.ADDRESS_ALLOCATION_RETRY; ++j) {
192 if (!sendPollMessage(curAddress, curAddress, 1)) {
193 failedPollingCount++;
194 }
195 }
196
197 // Pick logical address if failed ratio is more than a half of all retries.
198 if (failedPollingCount * 2 > HdmiConfig.ADDRESS_ALLOCATION_RETRY) {
Jungshik Jangd643f762014-05-22 19:28:09 +0900199 logicalAddress = curAddress;
200 break;
201 }
202 }
203 }
204
205 final int assignedAddress = logicalAddress;
Jungshik Jang2e8f1b62014-09-03 08:28:02 +0900206 HdmiLogger.debug("New logical address for device [%d]: [preferred:%d, assigned:%d]",
207 deviceType, preferredAddress, assignedAddress);
Jungshik Jangd643f762014-05-22 19:28:09 +0900208 if (callback != null) {
209 runOnServiceThread(new Runnable() {
Jungshik Jang8e93c842014-08-06 15:48:33 +0900210 @Override
Jungshik Jangd643f762014-05-22 19:28:09 +0900211 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
Jinsuk Kim0340bbc2014-06-05 11:07:47 +0900226 HdmiPortInfo[] getPortInfos() {
227 return nativeGetPortInfos(mNativePtr);
228 }
229
Jungshik Janga9095ba2014-05-02 13:06:22 +0900230 /**
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +0900231 * Return the locally hosted logical device of a given type.
232 *
233 * @param deviceType logical device type
234 * @return {@link HdmiCecLocalDevice} instance if the instance of the type is available;
235 * otherwise null.
236 */
237 HdmiCecLocalDevice getLocalDevice(int deviceType) {
238 return mLocalDevices.get(deviceType);
239 }
240
241 /**
Jungshik Janga9095ba2014-05-02 13:06:22 +0900242 * Add a new logical address to the device. Device's HW should be notified
243 * when a new logical address is assigned to a device, so that it can accept
244 * a command having available destinations.
245 *
246 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
247 *
248 * @param newLogicalAddress a logical address to be added
249 * @return 0 on success. Otherwise, returns negative value
250 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900251 @ServiceThreadOnly
Jungshik Janga9095ba2014-05-02 13:06:22 +0900252 int addLogicalAddress(int newLogicalAddress) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900253 assertRunOnServiceThread();
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900254 if (HdmiUtils.isValidAddress(newLogicalAddress)) {
Jungshik Janga9095ba2014-05-02 13:06:22 +0900255 return nativeAddLogicalAddress(mNativePtr, newLogicalAddress);
256 } else {
257 return -1;
258 }
259 }
260
261 /**
262 * Clear all logical addresses registered in the device.
263 *
264 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
265 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900266 @ServiceThreadOnly
Jungshik Janga9095ba2014-05-02 13:06:22 +0900267 void clearLogicalAddress() {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900268 assertRunOnServiceThread();
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +0900269 for (int i = 0; i < mLocalDevices.size(); ++i) {
270 mLocalDevices.valueAt(i).clearAddress();
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900271 }
Jungshik Janga9095ba2014-05-02 13:06:22 +0900272 nativeClearLogicalAddress(mNativePtr);
273 }
274
Jungshik Jang4fc1d102014-07-09 19:24:50 +0900275 @ServiceThreadOnly
276 void clearLocalDevices() {
277 assertRunOnServiceThread();
278 mLocalDevices.clear();
279 }
280
Jungshik Janga9095ba2014-05-02 13:06:22 +0900281 /**
282 * Return the physical address of the device.
283 *
284 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
285 *
286 * @return CEC physical address of the device. The range of success address
287 * is between 0x0000 and 0xFFFF. If failed it returns -1
288 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900289 @ServiceThreadOnly
Jungshik Janga9095ba2014-05-02 13:06:22 +0900290 int getPhysicalAddress() {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900291 assertRunOnServiceThread();
Jungshik Janga9095ba2014-05-02 13:06:22 +0900292 return nativeGetPhysicalAddress(mNativePtr);
293 }
294
295 /**
296 * Return CEC version of the device.
297 *
298 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
299 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900300 @ServiceThreadOnly
Jungshik Janga9095ba2014-05-02 13:06:22 +0900301 int getVersion() {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900302 assertRunOnServiceThread();
Jungshik Janga9095ba2014-05-02 13:06:22 +0900303 return nativeGetVersion(mNativePtr);
304 }
305
306 /**
307 * Return vendor id of the device.
308 *
309 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
310 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900311 @ServiceThreadOnly
Jungshik Janga9095ba2014-05-02 13:06:22 +0900312 int getVendorId() {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900313 assertRunOnServiceThread();
Jungshik Janga9095ba2014-05-02 13:06:22 +0900314 return nativeGetVendorId(mNativePtr);
315 }
316
Jungshik Jang02bb4262014-05-23 16:48:31 +0900317 /**
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900318 * Set an option to CEC HAL.
Jungshik Jang092b4452014-06-11 15:19:17 +0900319 *
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900320 * @param flag key of option
321 * @param value value of option
Jungshik Jang092b4452014-06-11 15:19:17 +0900322 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900323 @ServiceThreadOnly
Jungshik Jang092b4452014-06-11 15:19:17 +0900324 void setOption(int flag, int value) {
325 assertRunOnServiceThread();
326 nativeSetOption(mNativePtr, flag, value);
327 }
328
329 /**
330 * Configure ARC circuit in the hardware logic to start or stop the feature.
331 *
332 * @param enabled whether to enable/disable ARC
333 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900334 @ServiceThreadOnly
Jungshik Jang092b4452014-06-11 15:19:17 +0900335 void setAudioReturnChannel(boolean enabled) {
336 assertRunOnServiceThread();
337 nativeSetAudioReturnChannel(mNativePtr, enabled);
338 }
339
340 /**
341 * Return the connection status of the specified port
342 *
343 * @param port port number to check connection status
344 * @return true if connected; otherwise, return false
345 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900346 @ServiceThreadOnly
Jungshik Jang092b4452014-06-11 15:19:17 +0900347 boolean isConnected(int port) {
348 assertRunOnServiceThread();
349 return nativeIsConnected(mNativePtr, port);
350 }
351
352 /**
Jungshik Jang02bb4262014-05-23 16:48:31 +0900353 * Poll all remote devices. It sends &lt;Polling Message&gt; to all remote
354 * devices.
355 *
356 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
357 *
358 * @param callback an interface used to get a list of all remote devices' address
Jungshik Jang1de51422014-07-03 11:14:26 +0900359 * @param sourceAddress a logical address of source device where sends polling message
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900360 * @param pickStrategy strategy how to pick polling candidates
Jungshik Jang02bb4262014-05-23 16:48:31 +0900361 * @param retryCount the number of retry used to send polling message to remote devices
362 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900363 @ServiceThreadOnly
Jungshik Jang1de51422014-07-03 11:14:26 +0900364 void pollDevices(DevicePollingCallback callback, int sourceAddress, int pickStrategy,
365 int retryCount) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900366 assertRunOnServiceThread();
Jungshik Jang02bb4262014-05-23 16:48:31 +0900367
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900368 // Extract polling candidates. No need to poll against local devices.
369 List<Integer> pollingCandidates = pickPollCandidates(pickStrategy);
Jungshik Jang1de51422014-07-03 11:14:26 +0900370 runDevicePolling(sourceAddress, pollingCandidates, retryCount, callback);
Jungshik Jang02bb4262014-05-23 16:48:31 +0900371 }
372
Jungshik Jangcc5ef8c2014-05-27 13:27:36 +0900373 /**
374 * Return a list of all {@link HdmiCecLocalDevice}s.
375 *
376 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
377 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900378 @ServiceThreadOnly
Jungshik Jangcc5ef8c2014-05-27 13:27:36 +0900379 List<HdmiCecLocalDevice> getLocalDeviceList() {
380 assertRunOnServiceThread();
Jungshik Jang79c58a42014-06-16 16:45:36 +0900381 return HdmiUtils.sparseArrayToList(mLocalDevices);
Jungshik Jangcc5ef8c2014-05-27 13:27:36 +0900382 }
383
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900384 private List<Integer> pickPollCandidates(int pickStrategy) {
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900385 int strategy = pickStrategy & Constants.POLL_STRATEGY_MASK;
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900386 Predicate<Integer> pickPredicate = null;
387 switch (strategy) {
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900388 case Constants.POLL_STRATEGY_SYSTEM_AUDIO:
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900389 pickPredicate = mSystemAudioAddressPredicate;
390 break;
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900391 case Constants.POLL_STRATEGY_REMOTES_DEVICES:
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900392 default: // The default is POLL_STRATEGY_REMOTES_DEVICES.
393 pickPredicate = mRemoteDeviceAddressPredicate;
394 break;
395 }
396
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900397 int iterationStrategy = pickStrategy & Constants.POLL_ITERATION_STRATEGY_MASK;
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900398 ArrayList<Integer> pollingCandidates = new ArrayList<>();
399 switch (iterationStrategy) {
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900400 case Constants.POLL_ITERATION_IN_ORDER:
401 for (int i = Constants.ADDR_TV; i <= Constants.ADDR_SPECIFIC_USE; ++i) {
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900402 if (pickPredicate.apply(i)) {
403 pollingCandidates.add(i);
404 }
405 }
406 break;
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900407 case Constants.POLL_ITERATION_REVERSE_ORDER:
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900408 default: // The default is reverse order.
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900409 for (int i = Constants.ADDR_SPECIFIC_USE; i >= Constants.ADDR_TV; --i) {
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900410 if (pickPredicate.apply(i)) {
411 pollingCandidates.add(i);
412 }
413 }
414 break;
415 }
416 return pollingCandidates;
417 }
418
Jungshik Janga5b74142014-06-23 18:03:10 +0900419 @ServiceThreadOnly
Jungshik Jang02bb4262014-05-23 16:48:31 +0900420 private boolean isAllocatedLocalDeviceAddress(int address) {
Jungshik Janga5b74142014-06-23 18:03:10 +0900421 assertRunOnServiceThread();
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +0900422 for (int i = 0; i < mLocalDevices.size(); ++i) {
423 if (mLocalDevices.valueAt(i).isAddressOf(address)) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900424 return true;
425 }
426 }
427 return false;
428 }
429
Jungshik Janga5b74142014-06-23 18:03:10 +0900430 @ServiceThreadOnly
Jungshik Jang1de51422014-07-03 11:14:26 +0900431 private void runDevicePolling(final int sourceAddress,
432 final List<Integer> candidates, final int retryCount,
Jungshik Jang02bb4262014-05-23 16:48:31 +0900433 final DevicePollingCallback callback) {
434 assertRunOnServiceThread();
435 runOnIoThread(new Runnable() {
436 @Override
437 public void run() {
438 final ArrayList<Integer> allocated = new ArrayList<>();
439 for (Integer address : candidates) {
Jungshik Jang1de51422014-07-03 11:14:26 +0900440 if (sendPollMessage(sourceAddress, address, retryCount)) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900441 allocated.add(address);
442 }
443 }
Jungshik Jang2e8f1b62014-09-03 08:28:02 +0900444 HdmiLogger.debug("[P]:Allocated Address=" + allocated);
Jungshik Jang02bb4262014-05-23 16:48:31 +0900445 if (callback != null) {
446 runOnServiceThread(new Runnable() {
447 @Override
448 public void run() {
449 callback.onPollingFinished(allocated);
450 }
451 });
452 }
453 }
454 });
455 }
456
Jungshik Janga5b74142014-06-23 18:03:10 +0900457 @IoThreadOnly
Jungshik Jang1de51422014-07-03 11:14:26 +0900458 private boolean sendPollMessage(int sourceAddress, int destinationAddress, int retryCount) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900459 assertRunOnIoThread();
460 for (int i = 0; i < retryCount; ++i) {
Jungshik Jang1de51422014-07-03 11:14:26 +0900461 // <Polling Message> is a message which has empty body.
Jungshik Jang02bb4262014-05-23 16:48:31 +0900462 // If sending <Polling Message> failed (NAK), it becomes
463 // new logical address for the device because no device uses
464 // it as logical address of the device.
Jungshik Jang1de51422014-07-03 11:14:26 +0900465 if (nativeSendCecCommand(mNativePtr, sourceAddress, destinationAddress, EMPTY_BODY)
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900466 == Constants.SEND_RESULT_SUCCESS) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900467 return true;
468 }
469 }
470 return false;
471 }
472
473 private void assertRunOnIoThread() {
474 if (Looper.myLooper() != mIoHandler.getLooper()) {
475 throw new IllegalStateException("Should run on io thread.");
476 }
477 }
478
479 private void assertRunOnServiceThread() {
480 if (Looper.myLooper() != mControlHandler.getLooper()) {
481 throw new IllegalStateException("Should run on service thread.");
482 }
483 }
484
485 // Run a Runnable on IO thread.
486 // It should be careful to access member variables on IO thread because
487 // it can be accessed from system thread as well.
Jungshik Jangd643f762014-05-22 19:28:09 +0900488 private void runOnIoThread(Runnable runnable) {
489 mIoHandler.post(runnable);
490 }
491
492 private void runOnServiceThread(Runnable runnable) {
493 mControlHandler.post(runnable);
494 }
495
Jungshik Janga1fa91f2014-05-08 20:56:41 +0900496 private boolean isAcceptableAddress(int address) {
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900497 // Can access command targeting devices available in local device or broadcast command.
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900498 if (address == Constants.ADDR_BROADCAST) {
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900499 return true;
500 }
Jungshik Jang02bb4262014-05-23 16:48:31 +0900501 return isAllocatedLocalDeviceAddress(address);
Jungshik Janga1fa91f2014-05-08 20:56:41 +0900502 }
503
Jungshik Janga5b74142014-06-23 18:03:10 +0900504 @ServiceThreadOnly
Jungshik Jange9c77c82014-04-24 20:30:09 +0900505 private void onReceiveCommand(HdmiCecMessage message) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900506 assertRunOnServiceThread();
Yuncheol Heo6aae6522014-08-05 14:48:37 +0900507 if (isAcceptableAddress(message.getDestination()) && mService.handleCecCommand(message)) {
Jungshik Janga1fa91f2014-05-08 20:56:41 +0900508 return;
509 }
Yuncheol Heo6aae6522014-08-05 14:48:37 +0900510 // Not handled message, so we will reply it with <Feature Abort>.
511 maySendFeatureAbortCommand(message, Constants.ABORT_UNRECOGNIZED_OPCODE);
512 }
Jungshik Jang1827fdc2014-07-17 13:58:14 +0900513
Yuncheol Heo6aae6522014-08-05 14:48:37 +0900514 @ServiceThreadOnly
515 void maySendFeatureAbortCommand(HdmiCecMessage message, int reason) {
516 assertRunOnServiceThread();
517 // Swap the source and the destination.
518 int src = message.getDestination();
519 int dest = message.getSource();
520 if (src == Constants.ADDR_BROADCAST || dest == Constants.ADDR_UNREGISTERED) {
521 // Don't reply <Feature Abort> from the unregistered devices or for the broadcasted
522 // messages. See CEC 12.2 Protocol General Rules for detail.
523 return;
524 }
525 int originalOpcode = message.getOpcode();
526 if (originalOpcode == Constants.MESSAGE_FEATURE_ABORT) {
527 return;
528 }
529 sendCommand(
530 HdmiCecMessageBuilder.buildFeatureAbortCommand(src, dest, originalOpcode, reason));
Jungshik Jange9c77c82014-04-24 20:30:09 +0900531 }
532
Jungshik Janga5b74142014-06-23 18:03:10 +0900533 @ServiceThreadOnly
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900534 void sendCommand(HdmiCecMessage cecMessage) {
Jungshik Janga5b74142014-06-23 18:03:10 +0900535 assertRunOnServiceThread();
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900536 sendCommand(cecMessage, null);
537 }
538
Jungshik Janga5b74142014-06-23 18:03:10 +0900539 @ServiceThreadOnly
Jungshik Jangd643f762014-05-22 19:28:09 +0900540 void sendCommand(final HdmiCecMessage cecMessage,
541 final HdmiControlService.SendMessageCallback callback) {
Jungshik Janga5b74142014-06-23 18:03:10 +0900542 assertRunOnServiceThread();
Jungshik Jangd643f762014-05-22 19:28:09 +0900543 runOnIoThread(new Runnable() {
544 @Override
545 public void run() {
Jungshik Jang2e8f1b62014-09-03 08:28:02 +0900546 HdmiLogger.debug("[S]:" + cecMessage);
Jungshik Jangd643f762014-05-22 19:28:09 +0900547 byte[] body = buildBody(cecMessage.getOpcode(), cecMessage.getParams());
Jungshik Jang8ed86c42014-07-11 11:56:46 +0900548 int i = 0;
549 int errorCode = Constants.SEND_RESULT_SUCCESS;
550 do {
551 errorCode = nativeSendCecCommand(mNativePtr, cecMessage.getSource(),
552 cecMessage.getDestination(), body);
553 if (errorCode == Constants.SEND_RESULT_SUCCESS) {
554 break;
555 }
556 } while (i++ < HdmiConfig.RETRANSMISSION_COUNT);
557
558 final int finalError = errorCode;
559 if (finalError != Constants.SEND_RESULT_SUCCESS) {
Yuncheol Heoece603b2014-05-23 20:10:19 +0900560 Slog.w(TAG, "Failed to send " + cecMessage);
561 }
Jungshik Jangd643f762014-05-22 19:28:09 +0900562 if (callback != null) {
563 runOnServiceThread(new Runnable() {
564 @Override
565 public void run() {
Jungshik Jang8ed86c42014-07-11 11:56:46 +0900566 callback.onSendCompleted(finalError);
Jungshik Jangd643f762014-05-22 19:28:09 +0900567 }
568 });
569 }
570 }
571 });
Jungshik Jange9c77c82014-04-24 20:30:09 +0900572 }
573
Jungshik Jang0792d372014-04-23 17:57:26 +0900574 /**
Jungshik Jange9c77c82014-04-24 20:30:09 +0900575 * Called by native when incoming CEC message arrived.
Jungshik Jang0792d372014-04-23 17:57:26 +0900576 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900577 @ServiceThreadOnly
Jungshik Jange9c77c82014-04-24 20:30:09 +0900578 private void handleIncomingCecCommand(int srcAddress, int dstAddress, byte[] body) {
Jungshik Jang4085d0e2014-05-27 19:52:39 +0900579 assertRunOnServiceThread();
Jungshik Jangc94ac5c2014-08-27 13:48:37 +0900580 HdmiCecMessage command = HdmiCecMessageBuilder.of(srcAddress, dstAddress, body);
Jungshik Jang2e8f1b62014-09-03 08:28:02 +0900581 HdmiLogger.debug("[R]:" + command);
Jungshik Jangc94ac5c2014-08-27 13:48:37 +0900582 onReceiveCommand(command);
Jungshik Jange9c77c82014-04-24 20:30:09 +0900583 }
584
585 /**
586 * Called by native when a hotplug event issues.
587 */
Jungshik Jang42230722014-07-07 17:40:25 +0900588 @ServiceThreadOnly
589 private void handleHotplug(int port, boolean connected) {
590 assertRunOnServiceThread();
Jungshik Jang2e8f1b62014-09-03 08:28:02 +0900591 HdmiLogger.debug("Hotplug event:[port:%d, connected:%b]", port, connected);
Jungshik Jang42230722014-07-07 17:40:25 +0900592 mService.onHotplug(port, connected);
Jungshik Jang0792d372014-04-23 17:57:26 +0900593 }
594
Terry Heo959d2db2014-08-28 16:45:41 +0900595 void dump(final IndentingPrintWriter pw) {
596 for (int i = 0; i < mLocalDevices.size(); ++i) {
597 pw.println("HdmiCecLocalDevice #" + i + ":");
598 pw.increaseIndent();
599 mLocalDevices.valueAt(i).dump(pw);
600 pw.decreaseIndent();
601 }
602 }
603
Jungshik Jang4085d0e2014-05-27 19:52:39 +0900604 private static native long nativeInit(HdmiCecController handler, MessageQueue messageQueue);
Jungshik Janga9095ba2014-05-02 13:06:22 +0900605 private static native int nativeSendCecCommand(long controllerPtr, int srcAddress,
Jungshik Jange9c77c82014-04-24 20:30:09 +0900606 int dstAddress, byte[] body);
Jungshik Janga9095ba2014-05-02 13:06:22 +0900607 private static native int nativeAddLogicalAddress(long controllerPtr, int logicalAddress);
608 private static native void nativeClearLogicalAddress(long controllerPtr);
609 private static native int nativeGetPhysicalAddress(long controllerPtr);
610 private static native int nativeGetVersion(long controllerPtr);
611 private static native int nativeGetVendorId(long controllerPtr);
Jinsuk Kim0340bbc2014-06-05 11:07:47 +0900612 private static native HdmiPortInfo[] nativeGetPortInfos(long controllerPtr);
Jungshik Jang092b4452014-06-11 15:19:17 +0900613 private static native void nativeSetOption(long controllerPtr, int flag, int value);
614 private static native void nativeSetAudioReturnChannel(long controllerPtr, boolean flag);
615 private static native boolean nativeIsConnected(long controllerPtr, int port);
Jungshik Jang0792d372014-04-23 17:57:26 +0900616}