blob: 827b3ed524c5ccf6f540ba0c6792b78983105c77 [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
Jungshik Jang0f8b4b72014-05-28 17:58:58 +090026import com.android.internal.util.Predicate;
Jungshik Janga5b74142014-06-23 18:03:10 +090027import com.android.server.hdmi.HdmiAnnotations.IoThreadOnly;
28import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
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
Jungshik Jang42230722014-07-07 17:40:25 +090060 * {@link Constants#ADDR_UNREGISTERED}, it means that
Jungshik Jang3ee65722014-06-03 16:22:30 +090061 * 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 Jang3f74ab02014-04-30 14:31:02 +090068 private static final int NUM_LOGICAL_ADDRESS = 16;
69
Jungshik Jang0f8b4b72014-05-28 17:58:58 +090070 // Predicate for whether the given logical address is remote device's one or not.
71 private final Predicate<Integer> mRemoteDeviceAddressPredicate = new Predicate<Integer>() {
72 @Override
73 public boolean apply(Integer address) {
74 return !isAllocatedLocalDeviceAddress(address);
75 }
76 };
77
78 // Predicate whether the given logical address is system audio's one or not
79 private final Predicate<Integer> mSystemAudioAddressPredicate = new Predicate<Integer>() {
80 @Override
81 public boolean apply(Integer address) {
Jinsuk Kimc0c20d02014-07-04 14:34:31 +090082 return HdmiUtils.getTypeFromAddress(address) == Constants.ADDR_AUDIO_SYSTEM;
Jungshik Jang0f8b4b72014-05-28 17:58:58 +090083 }
84 };
85
Jungshik Jang0792d372014-04-23 17:57:26 +090086 // Handler instance to process synchronous I/O (mainly send) message.
87 private Handler mIoHandler;
88
89 // Handler instance to process various messages coming from other CEC
90 // device or issued by internal state change.
Jungshik Jange9c77c82014-04-24 20:30:09 +090091 private Handler mControlHandler;
Jungshik Jang0792d372014-04-23 17:57:26 +090092
93 // Stores the pointer to the native implementation of the service that
94 // interacts with HAL.
Jungshik Jang02bb4262014-05-23 16:48:31 +090095 private volatile long mNativePtr;
Jungshik Jang0792d372014-04-23 17:57:26 +090096
Jungshik Jang7df52862014-08-11 14:35:27 +090097 private final HdmiControlService mService;
Jungshik Janga1fa91f2014-05-08 20:56:41 +090098
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +090099 // Stores the local CEC devices in the system. Device type is used for key.
100 private final SparseArray<HdmiCecLocalDevice> mLocalDevices = new SparseArray<>();
Jungshik Jang7d9a8432014-04-29 15:12:43 +0900101
Jungshik Jangc94ac5c2014-08-27 13:48:37 +0900102 @IoThreadOnly
103 private final HdmiLogger mIoThreadLogger = new HdmiLogger(TAG);
104 @ServiceThreadOnly
105 private final HdmiLogger mServiceThreadLogger = new HdmiLogger(TAG);
106
Jungshik Jang0792d372014-04-23 17:57:26 +0900107 // Private constructor. Use HdmiCecController.create().
Jungshik Jang7df52862014-08-11 14:35:27 +0900108 private HdmiCecController(HdmiControlService service) {
109 mService = service;
Jungshik Jang0792d372014-04-23 17:57:26 +0900110 }
111
112 /**
113 * A factory method to get {@link HdmiCecController}. If it fails to initialize
114 * inner device or has no device it will return {@code null}.
115 *
116 * <p>Declared as package-private, accessed by {@link HdmiControlService} only.
Jungshik Jange9c77c82014-04-24 20:30:09 +0900117 * @param service {@link HdmiControlService} instance used to create internal handler
118 * and to pass callback for incoming message or event.
Jungshik Jang0792d372014-04-23 17:57:26 +0900119 * @return {@link HdmiCecController} if device is initialized successfully. Otherwise,
120 * returns {@code null}.
121 */
Jungshik Jange9c77c82014-04-24 20:30:09 +0900122 static HdmiCecController create(HdmiControlService service) {
Jungshik Jang7df52862014-08-11 14:35:27 +0900123 HdmiCecController controller = new HdmiCecController(service);
Jungshik Jang4085d0e2014-05-27 19:52:39 +0900124 long nativePtr = nativeInit(controller, service.getServiceLooper().getQueue());
Jungshik Jang0792d372014-04-23 17:57:26 +0900125 if (nativePtr == 0L) {
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900126 controller = null;
Jungshik Jang0792d372014-04-23 17:57:26 +0900127 return null;
128 }
129
Jungshik Jang7df52862014-08-11 14:35:27 +0900130 controller.init(nativePtr);
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900131 return controller;
132 }
133
Jungshik Jang7df52862014-08-11 14:35:27 +0900134 private void init(long nativePtr) {
135 mIoHandler = new Handler(mService.getServiceLooper());
136 mControlHandler = new Handler(mService.getServiceLooper());
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900137 mNativePtr = nativePtr;
Jungshik Jang0792d372014-04-23 17:57:26 +0900138 }
139
Jungshik Janga5b74142014-06-23 18:03:10 +0900140 @ServiceThreadOnly
Jungshik Jang3ee65722014-06-03 16:22:30 +0900141 void addLocalDevice(int deviceType, HdmiCecLocalDevice device) {
Jungshik Janga5b74142014-06-23 18:03:10 +0900142 assertRunOnServiceThread();
Jungshik Jang3ee65722014-06-03 16:22:30 +0900143 mLocalDevices.put(deviceType, device);
Jungshik Jang3f74ab02014-04-30 14:31:02 +0900144 }
145
146 /**
147 * Allocate a new logical address of the given device type. Allocated
Jungshik Jang3ee65722014-06-03 16:22:30 +0900148 * address will be reported through {@link AllocateAddressCallback}.
Jungshik Jang3f74ab02014-04-30 14:31:02 +0900149 *
150 * <p> Declared as package-private, accessed by {@link HdmiControlService} only.
151 *
152 * @param deviceType type of device to used to determine logical address
153 * @param preferredAddress a logical address preferred to be allocated.
Jungshik Jang42230722014-07-07 17:40:25 +0900154 * If sets {@link Constants#ADDR_UNREGISTERED}, scans
Jungshik Jang3f74ab02014-04-30 14:31:02 +0900155 * the smallest logical address matched with the given device type.
156 * Otherwise, scan address will start from {@code preferredAddress}
157 * @param callback callback interface to report allocated logical address to caller
158 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900159 @ServiceThreadOnly
Jungshik Jangd643f762014-05-22 19:28:09 +0900160 void allocateLogicalAddress(final int deviceType, final int preferredAddress,
Jungshik Jang3ee65722014-06-03 16:22:30 +0900161 final AllocateAddressCallback callback) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900162 assertRunOnServiceThread();
163
Jungshik Jangd643f762014-05-22 19:28:09 +0900164 runOnIoThread(new Runnable() {
165 @Override
166 public void run() {
167 handleAllocateLogicalAddress(deviceType, preferredAddress, callback);
168 }
169 });
170 }
171
Jungshik Janga5b74142014-06-23 18:03:10 +0900172 @IoThreadOnly
Jungshik Jangd643f762014-05-22 19:28:09 +0900173 private void handleAllocateLogicalAddress(final int deviceType, int preferredAddress,
Jungshik Jang3ee65722014-06-03 16:22:30 +0900174 final AllocateAddressCallback callback) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900175 assertRunOnIoThread();
Jungshik Jangd643f762014-05-22 19:28:09 +0900176 int startAddress = preferredAddress;
177 // If preferred address is "unregistered", start address will be the smallest
178 // address matched with the given device type.
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900179 if (preferredAddress == Constants.ADDR_UNREGISTERED) {
Jungshik Jangd643f762014-05-22 19:28:09 +0900180 for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) {
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900181 if (deviceType == HdmiUtils.getTypeFromAddress(i)) {
Jungshik Jangd643f762014-05-22 19:28:09 +0900182 startAddress = i;
183 break;
184 }
185 }
186 }
187
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900188 int logicalAddress = Constants.ADDR_UNREGISTERED;
Jungshik Jangd643f762014-05-22 19:28:09 +0900189 // Iterates all possible addresses which has the same device type.
190 for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) {
191 int curAddress = (startAddress + i) % NUM_LOGICAL_ADDRESS;
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900192 if (curAddress != Constants.ADDR_UNREGISTERED
193 && deviceType == HdmiUtils.getTypeFromAddress(curAddress)) {
Jungshik Jang8e93c842014-08-06 15:48:33 +0900194 int failedPollingCount = 0;
195 for (int j = 0; j < HdmiConfig.ADDRESS_ALLOCATION_RETRY; ++j) {
196 if (!sendPollMessage(curAddress, curAddress, 1)) {
197 failedPollingCount++;
198 }
199 }
200
201 // Pick logical address if failed ratio is more than a half of all retries.
202 if (failedPollingCount * 2 > HdmiConfig.ADDRESS_ALLOCATION_RETRY) {
Jungshik Jangd643f762014-05-22 19:28:09 +0900203 logicalAddress = curAddress;
204 break;
205 }
206 }
207 }
208
209 final int assignedAddress = logicalAddress;
Jungshik Jangc94ac5c2014-08-27 13:48:37 +0900210 mIoThreadLogger.debug(
211 String.format("New logical address for device [%d]: [preferred:%d, assigned:%d]",
212 deviceType, preferredAddress, assignedAddress));
Jungshik Jangd643f762014-05-22 19:28:09 +0900213 if (callback != null) {
214 runOnServiceThread(new Runnable() {
Jungshik Jang8e93c842014-08-06 15:48:33 +0900215 @Override
Jungshik Jangd643f762014-05-22 19:28:09 +0900216 public void run() {
217 callback.onAllocated(deviceType, assignedAddress);
218 }
219 });
220 }
Jungshik Jang3f74ab02014-04-30 14:31:02 +0900221 }
222
Jungshik Jange9c77c82014-04-24 20:30:09 +0900223 private static byte[] buildBody(int opcode, byte[] params) {
224 byte[] body = new byte[params.length + 1];
225 body[0] = (byte) opcode;
226 System.arraycopy(params, 0, body, 1, params.length);
227 return body;
228 }
Jungshik Jang0792d372014-04-23 17:57:26 +0900229
Jungshik Jang7d9a8432014-04-29 15:12:43 +0900230
Jinsuk Kim0340bbc2014-06-05 11:07:47 +0900231 HdmiPortInfo[] getPortInfos() {
232 return nativeGetPortInfos(mNativePtr);
233 }
234
Jungshik Janga9095ba2014-05-02 13:06:22 +0900235 /**
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +0900236 * Return the locally hosted logical device of a given type.
237 *
238 * @param deviceType logical device type
239 * @return {@link HdmiCecLocalDevice} instance if the instance of the type is available;
240 * otherwise null.
241 */
242 HdmiCecLocalDevice getLocalDevice(int deviceType) {
243 return mLocalDevices.get(deviceType);
244 }
245
246 /**
Jungshik Janga9095ba2014-05-02 13:06:22 +0900247 * Add a new logical address to the device. Device's HW should be notified
248 * when a new logical address is assigned to a device, so that it can accept
249 * a command having available destinations.
250 *
251 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
252 *
253 * @param newLogicalAddress a logical address to be added
254 * @return 0 on success. Otherwise, returns negative value
255 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900256 @ServiceThreadOnly
Jungshik Janga9095ba2014-05-02 13:06:22 +0900257 int addLogicalAddress(int newLogicalAddress) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900258 assertRunOnServiceThread();
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900259 if (HdmiUtils.isValidAddress(newLogicalAddress)) {
Jungshik Janga9095ba2014-05-02 13:06:22 +0900260 return nativeAddLogicalAddress(mNativePtr, newLogicalAddress);
261 } else {
262 return -1;
263 }
264 }
265
266 /**
267 * Clear all logical addresses registered in the device.
268 *
269 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
270 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900271 @ServiceThreadOnly
Jungshik Janga9095ba2014-05-02 13:06:22 +0900272 void clearLogicalAddress() {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900273 assertRunOnServiceThread();
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +0900274 for (int i = 0; i < mLocalDevices.size(); ++i) {
275 mLocalDevices.valueAt(i).clearAddress();
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900276 }
Jungshik Janga9095ba2014-05-02 13:06:22 +0900277 nativeClearLogicalAddress(mNativePtr);
278 }
279
Jungshik Jang4fc1d102014-07-09 19:24:50 +0900280 @ServiceThreadOnly
281 void clearLocalDevices() {
282 assertRunOnServiceThread();
283 mLocalDevices.clear();
284 }
285
Jungshik Janga9095ba2014-05-02 13:06:22 +0900286 /**
287 * Return the physical address of the device.
288 *
289 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
290 *
291 * @return CEC physical address of the device. The range of success address
292 * is between 0x0000 and 0xFFFF. If failed it returns -1
293 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900294 @ServiceThreadOnly
Jungshik Janga9095ba2014-05-02 13:06:22 +0900295 int getPhysicalAddress() {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900296 assertRunOnServiceThread();
Jungshik Janga9095ba2014-05-02 13:06:22 +0900297 return nativeGetPhysicalAddress(mNativePtr);
298 }
299
300 /**
301 * Return CEC version of the device.
302 *
303 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
304 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900305 @ServiceThreadOnly
Jungshik Janga9095ba2014-05-02 13:06:22 +0900306 int getVersion() {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900307 assertRunOnServiceThread();
Jungshik Janga9095ba2014-05-02 13:06:22 +0900308 return nativeGetVersion(mNativePtr);
309 }
310
311 /**
312 * Return vendor id of the device.
313 *
314 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
315 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900316 @ServiceThreadOnly
Jungshik Janga9095ba2014-05-02 13:06:22 +0900317 int getVendorId() {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900318 assertRunOnServiceThread();
Jungshik Janga9095ba2014-05-02 13:06:22 +0900319 return nativeGetVendorId(mNativePtr);
320 }
321
Jungshik Jang02bb4262014-05-23 16:48:31 +0900322 /**
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900323 * Set an option to CEC HAL.
Jungshik Jang092b4452014-06-11 15:19:17 +0900324 *
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900325 * @param flag key of option
326 * @param value value of option
Jungshik Jang092b4452014-06-11 15:19:17 +0900327 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900328 @ServiceThreadOnly
Jungshik Jang092b4452014-06-11 15:19:17 +0900329 void setOption(int flag, int value) {
330 assertRunOnServiceThread();
331 nativeSetOption(mNativePtr, flag, value);
332 }
333
334 /**
335 * Configure ARC circuit in the hardware logic to start or stop the feature.
336 *
337 * @param enabled whether to enable/disable ARC
338 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900339 @ServiceThreadOnly
Jungshik Jang092b4452014-06-11 15:19:17 +0900340 void setAudioReturnChannel(boolean enabled) {
341 assertRunOnServiceThread();
342 nativeSetAudioReturnChannel(mNativePtr, enabled);
343 }
344
345 /**
346 * Return the connection status of the specified port
347 *
348 * @param port port number to check connection status
349 * @return true if connected; otherwise, return false
350 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900351 @ServiceThreadOnly
Jungshik Jang092b4452014-06-11 15:19:17 +0900352 boolean isConnected(int port) {
353 assertRunOnServiceThread();
354 return nativeIsConnected(mNativePtr, port);
355 }
356
357 /**
Jungshik Jang02bb4262014-05-23 16:48:31 +0900358 * Poll all remote devices. It sends &lt;Polling Message&gt; to all remote
359 * devices.
360 *
361 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
362 *
363 * @param callback an interface used to get a list of all remote devices' address
Jungshik Jang1de51422014-07-03 11:14:26 +0900364 * @param sourceAddress a logical address of source device where sends polling message
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900365 * @param pickStrategy strategy how to pick polling candidates
Jungshik Jang02bb4262014-05-23 16:48:31 +0900366 * @param retryCount the number of retry used to send polling message to remote devices
367 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900368 @ServiceThreadOnly
Jungshik Jang1de51422014-07-03 11:14:26 +0900369 void pollDevices(DevicePollingCallback callback, int sourceAddress, int pickStrategy,
370 int retryCount) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900371 assertRunOnServiceThread();
Jungshik Jang02bb4262014-05-23 16:48:31 +0900372
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900373 // Extract polling candidates. No need to poll against local devices.
374 List<Integer> pollingCandidates = pickPollCandidates(pickStrategy);
Jungshik Jang1de51422014-07-03 11:14:26 +0900375 runDevicePolling(sourceAddress, pollingCandidates, retryCount, callback);
Jungshik Jang02bb4262014-05-23 16:48:31 +0900376 }
377
Jungshik Jangcc5ef8c2014-05-27 13:27:36 +0900378 /**
379 * Return a list of all {@link HdmiCecLocalDevice}s.
380 *
381 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
382 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900383 @ServiceThreadOnly
Jungshik Jangcc5ef8c2014-05-27 13:27:36 +0900384 List<HdmiCecLocalDevice> getLocalDeviceList() {
385 assertRunOnServiceThread();
Jungshik Jang79c58a42014-06-16 16:45:36 +0900386 return HdmiUtils.sparseArrayToList(mLocalDevices);
Jungshik Jangcc5ef8c2014-05-27 13:27:36 +0900387 }
388
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900389 private List<Integer> pickPollCandidates(int pickStrategy) {
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900390 int strategy = pickStrategy & Constants.POLL_STRATEGY_MASK;
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900391 Predicate<Integer> pickPredicate = null;
392 switch (strategy) {
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900393 case Constants.POLL_STRATEGY_SYSTEM_AUDIO:
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900394 pickPredicate = mSystemAudioAddressPredicate;
395 break;
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900396 case Constants.POLL_STRATEGY_REMOTES_DEVICES:
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900397 default: // The default is POLL_STRATEGY_REMOTES_DEVICES.
398 pickPredicate = mRemoteDeviceAddressPredicate;
399 break;
400 }
401
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900402 int iterationStrategy = pickStrategy & Constants.POLL_ITERATION_STRATEGY_MASK;
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900403 ArrayList<Integer> pollingCandidates = new ArrayList<>();
404 switch (iterationStrategy) {
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900405 case Constants.POLL_ITERATION_IN_ORDER:
406 for (int i = Constants.ADDR_TV; i <= Constants.ADDR_SPECIFIC_USE; ++i) {
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900407 if (pickPredicate.apply(i)) {
408 pollingCandidates.add(i);
409 }
410 }
411 break;
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900412 case Constants.POLL_ITERATION_REVERSE_ORDER:
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900413 default: // The default is reverse order.
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900414 for (int i = Constants.ADDR_SPECIFIC_USE; i >= Constants.ADDR_TV; --i) {
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900415 if (pickPredicate.apply(i)) {
416 pollingCandidates.add(i);
417 }
418 }
419 break;
420 }
421 return pollingCandidates;
422 }
423
Jungshik Janga5b74142014-06-23 18:03:10 +0900424 @ServiceThreadOnly
Jungshik Jang02bb4262014-05-23 16:48:31 +0900425 private boolean isAllocatedLocalDeviceAddress(int address) {
Jungshik Janga5b74142014-06-23 18:03:10 +0900426 assertRunOnServiceThread();
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +0900427 for (int i = 0; i < mLocalDevices.size(); ++i) {
428 if (mLocalDevices.valueAt(i).isAddressOf(address)) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900429 return true;
430 }
431 }
432 return false;
433 }
434
Jungshik Janga5b74142014-06-23 18:03:10 +0900435 @ServiceThreadOnly
Jungshik Jang1de51422014-07-03 11:14:26 +0900436 private void runDevicePolling(final int sourceAddress,
437 final List<Integer> candidates, final int retryCount,
Jungshik Jang02bb4262014-05-23 16:48:31 +0900438 final DevicePollingCallback callback) {
439 assertRunOnServiceThread();
440 runOnIoThread(new Runnable() {
441 @Override
442 public void run() {
443 final ArrayList<Integer> allocated = new ArrayList<>();
444 for (Integer address : candidates) {
Jungshik Jang1de51422014-07-03 11:14:26 +0900445 if (sendPollMessage(sourceAddress, address, retryCount)) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900446 allocated.add(address);
447 }
448 }
Jungshik Jangc94ac5c2014-08-27 13:48:37 +0900449 mIoThreadLogger.debug("DevicePollingResult:" + allocated);
Jungshik Jang02bb4262014-05-23 16:48:31 +0900450 if (callback != null) {
451 runOnServiceThread(new Runnable() {
452 @Override
453 public void run() {
454 callback.onPollingFinished(allocated);
455 }
456 });
457 }
458 }
459 });
460 }
461
Jungshik Janga5b74142014-06-23 18:03:10 +0900462 @IoThreadOnly
Jungshik Jang1de51422014-07-03 11:14:26 +0900463 private boolean sendPollMessage(int sourceAddress, int destinationAddress, int retryCount) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900464 assertRunOnIoThread();
465 for (int i = 0; i < retryCount; ++i) {
Jungshik Jang1de51422014-07-03 11:14:26 +0900466 // <Polling Message> is a message which has empty body.
Jungshik Jang02bb4262014-05-23 16:48:31 +0900467 // If sending <Polling Message> failed (NAK), it becomes
468 // new logical address for the device because no device uses
469 // it as logical address of the device.
Jungshik Jang1de51422014-07-03 11:14:26 +0900470 if (nativeSendCecCommand(mNativePtr, sourceAddress, destinationAddress, EMPTY_BODY)
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900471 == Constants.SEND_RESULT_SUCCESS) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900472 return true;
473 }
474 }
475 return false;
476 }
477
478 private void assertRunOnIoThread() {
479 if (Looper.myLooper() != mIoHandler.getLooper()) {
480 throw new IllegalStateException("Should run on io thread.");
481 }
482 }
483
484 private void assertRunOnServiceThread() {
485 if (Looper.myLooper() != mControlHandler.getLooper()) {
486 throw new IllegalStateException("Should run on service thread.");
487 }
488 }
489
490 // Run a Runnable on IO thread.
491 // It should be careful to access member variables on IO thread because
492 // it can be accessed from system thread as well.
Jungshik Jangd643f762014-05-22 19:28:09 +0900493 private void runOnIoThread(Runnable runnable) {
494 mIoHandler.post(runnable);
495 }
496
497 private void runOnServiceThread(Runnable runnable) {
498 mControlHandler.post(runnable);
499 }
500
Jungshik Janga1fa91f2014-05-08 20:56:41 +0900501 private boolean isAcceptableAddress(int address) {
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900502 // Can access command targeting devices available in local device or broadcast command.
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900503 if (address == Constants.ADDR_BROADCAST) {
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900504 return true;
505 }
Jungshik Jang02bb4262014-05-23 16:48:31 +0900506 return isAllocatedLocalDeviceAddress(address);
Jungshik Janga1fa91f2014-05-08 20:56:41 +0900507 }
508
Jungshik Janga5b74142014-06-23 18:03:10 +0900509 @ServiceThreadOnly
Jungshik Jange9c77c82014-04-24 20:30:09 +0900510 private void onReceiveCommand(HdmiCecMessage message) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900511 assertRunOnServiceThread();
Yuncheol Heo6aae6522014-08-05 14:48:37 +0900512 if (isAcceptableAddress(message.getDestination()) && mService.handleCecCommand(message)) {
Jungshik Janga1fa91f2014-05-08 20:56:41 +0900513 return;
514 }
Yuncheol Heo6aae6522014-08-05 14:48:37 +0900515 // Not handled message, so we will reply it with <Feature Abort>.
516 maySendFeatureAbortCommand(message, Constants.ABORT_UNRECOGNIZED_OPCODE);
517 }
Jungshik Jang1827fdc2014-07-17 13:58:14 +0900518
Yuncheol Heo6aae6522014-08-05 14:48:37 +0900519 @ServiceThreadOnly
520 void maySendFeatureAbortCommand(HdmiCecMessage message, int reason) {
521 assertRunOnServiceThread();
522 // Swap the source and the destination.
523 int src = message.getDestination();
524 int dest = message.getSource();
525 if (src == Constants.ADDR_BROADCAST || dest == Constants.ADDR_UNREGISTERED) {
526 // Don't reply <Feature Abort> from the unregistered devices or for the broadcasted
527 // messages. See CEC 12.2 Protocol General Rules for detail.
528 return;
529 }
530 int originalOpcode = message.getOpcode();
531 if (originalOpcode == Constants.MESSAGE_FEATURE_ABORT) {
532 return;
533 }
534 sendCommand(
535 HdmiCecMessageBuilder.buildFeatureAbortCommand(src, dest, originalOpcode, reason));
Jungshik Jange9c77c82014-04-24 20:30:09 +0900536 }
537
Jungshik Janga5b74142014-06-23 18:03:10 +0900538 @ServiceThreadOnly
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900539 void sendCommand(HdmiCecMessage cecMessage) {
Jungshik Janga5b74142014-06-23 18:03:10 +0900540 assertRunOnServiceThread();
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900541 sendCommand(cecMessage, null);
542 }
543
Jungshik Janga5b74142014-06-23 18:03:10 +0900544 @ServiceThreadOnly
Jungshik Jangd643f762014-05-22 19:28:09 +0900545 void sendCommand(final HdmiCecMessage cecMessage,
546 final HdmiControlService.SendMessageCallback callback) {
Jungshik Janga5b74142014-06-23 18:03:10 +0900547 assertRunOnServiceThread();
Jungshik Jangd643f762014-05-22 19:28:09 +0900548 runOnIoThread(new Runnable() {
549 @Override
550 public void run() {
Jungshik Jangc94ac5c2014-08-27 13:48:37 +0900551 mIoThreadLogger.debug("SendCommand:" + cecMessage);
Jungshik Jangd643f762014-05-22 19:28:09 +0900552 byte[] body = buildBody(cecMessage.getOpcode(), cecMessage.getParams());
Jungshik Jang8ed86c42014-07-11 11:56:46 +0900553 int i = 0;
554 int errorCode = Constants.SEND_RESULT_SUCCESS;
555 do {
556 errorCode = nativeSendCecCommand(mNativePtr, cecMessage.getSource(),
557 cecMessage.getDestination(), body);
558 if (errorCode == Constants.SEND_RESULT_SUCCESS) {
559 break;
560 }
561 } while (i++ < HdmiConfig.RETRANSMISSION_COUNT);
562
563 final int finalError = errorCode;
564 if (finalError != Constants.SEND_RESULT_SUCCESS) {
Yuncheol Heoece603b2014-05-23 20:10:19 +0900565 Slog.w(TAG, "Failed to send " + cecMessage);
566 }
Jungshik Jangd643f762014-05-22 19:28:09 +0900567 if (callback != null) {
568 runOnServiceThread(new Runnable() {
569 @Override
570 public void run() {
Jungshik Jang8ed86c42014-07-11 11:56:46 +0900571 callback.onSendCompleted(finalError);
Jungshik Jangd643f762014-05-22 19:28:09 +0900572 }
573 });
574 }
575 }
576 });
Jungshik Jange9c77c82014-04-24 20:30:09 +0900577 }
578
Jungshik Jang0792d372014-04-23 17:57:26 +0900579 /**
Jungshik Jange9c77c82014-04-24 20:30:09 +0900580 * Called by native when incoming CEC message arrived.
Jungshik Jang0792d372014-04-23 17:57:26 +0900581 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900582 @ServiceThreadOnly
Jungshik Jange9c77c82014-04-24 20:30:09 +0900583 private void handleIncomingCecCommand(int srcAddress, int dstAddress, byte[] body) {
Jungshik Jang4085d0e2014-05-27 19:52:39 +0900584 assertRunOnServiceThread();
Jungshik Jangc94ac5c2014-08-27 13:48:37 +0900585 HdmiCecMessage command = HdmiCecMessageBuilder.of(srcAddress, dstAddress, body);
586 mServiceThreadLogger.debug("ReceiveCommand:" + command);
587 onReceiveCommand(command);
Jungshik Jange9c77c82014-04-24 20:30:09 +0900588 }
589
590 /**
591 * Called by native when a hotplug event issues.
592 */
Jungshik Jang42230722014-07-07 17:40:25 +0900593 @ServiceThreadOnly
594 private void handleHotplug(int port, boolean connected) {
595 assertRunOnServiceThread();
Jungshik Jangc94ac5c2014-08-27 13:48:37 +0900596 mServiceThreadLogger.debug(
597 "Hotplug event:[port:" + port + " , connected:" + connected + "]");
Jungshik Jang42230722014-07-07 17:40:25 +0900598 mService.onHotplug(port, 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);
Jinsuk Kim0340bbc2014-06-05 11:07:47 +0900609 private static native HdmiPortInfo[] nativeGetPortInfos(long controllerPtr);
Jungshik Jang092b4452014-06-11 15:19:17 +0900610 private static native void nativeSetOption(long controllerPtr, int flag, int value);
611 private static native void nativeSetAudioReturnChannel(long controllerPtr, boolean flag);
612 private static native boolean nativeIsConnected(long controllerPtr, int port);
Jungshik Jang0792d372014-04-23 17:57:26 +0900613}