blob: 461a9b0636e468879eb7fa945150c54a9fdabd8f [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;
Donghyun Chobc6e3722016-11-04 05:25:52 +090020import android.hardware.tv.cec.V1_0.Result;
21import android.hardware.tv.cec.V1_0.SendMessageResult;
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;
Terry Heo959d2db2014-08-28 16:45:41 +090027import com.android.internal.util.IndentingPrintWriter;
Jungshik Jang0f8b4b72014-05-28 17:58:58 +090028import com.android.internal.util.Predicate;
Jungshik Janga5b74142014-06-23 18:03:10 +090029import com.android.server.hdmi.HdmiAnnotations.IoThreadOnly;
30import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
Jungshik Jang02bb4262014-05-23 16:48:31 +090031import com.android.server.hdmi.HdmiControlService.DevicePollingCallback;
Jungshik Jang7d9a8432014-04-29 15:12:43 +090032import java.util.ArrayList;
Jungshik Jangb3ecb722014-09-11 16:09:45 +090033import java.util.LinkedList;
Jungshik Jang7d9a8432014-04-29 15:12:43 +090034import java.util.List;
Donghyun Chobc6e3722016-11-04 05:25:52 +090035import libcore.util.EmptyArray;
36import sun.util.locale.LanguageTag;
Jungshik Jang0792d372014-04-23 17:57:26 +090037
38/**
39 * Manages HDMI-CEC command and behaviors. It converts user's command into CEC command
40 * and pass it to CEC HAL so that it sends message to other device. For incoming
41 * message it translates the message and delegates it to proper module.
42 *
Jungshik Jang02bb4262014-05-23 16:48:31 +090043 * <p>It should be careful to access member variables on IO thread because
44 * it can be accessed from system thread as well.
45 *
Jungshik Jang0792d372014-04-23 17:57:26 +090046 * <p>It can be created only by {@link HdmiCecController#create}
47 *
48 * <p>Declared as package-private, accessed by {@link HdmiControlService} only.
49 */
Jungshik Janga9095ba2014-05-02 13:06:22 +090050final class HdmiCecController {
Jungshik Jang0792d372014-04-23 17:57:26 +090051 private static final String TAG = "HdmiCecController";
52
Jungshik Jang3ee65722014-06-03 16:22:30 +090053 /**
54 * Interface to report allocated logical address.
55 */
56 interface AllocateAddressCallback {
57 /**
58 * Called when a new logical address is allocated.
59 *
60 * @param deviceType requested device type to allocate logical address
61 * @param logicalAddress allocated logical address. If it is
Jungshik Jang42230722014-07-07 17:40:25 +090062 * {@link Constants#ADDR_UNREGISTERED}, it means that
Jungshik Jang3ee65722014-06-03 16:22:30 +090063 * it failed to allocate logical address for the given device type
64 */
65 void onAllocated(int deviceType, int logicalAddress);
66 }
67
Jungshik Jang3f74ab02014-04-30 14:31:02 +090068 private static final byte[] EMPTY_BODY = EmptyArray.BYTE;
69
Jungshik Jang3f74ab02014-04-30 14:31:02 +090070 private static final int NUM_LOGICAL_ADDRESS = 16;
71
Jungshik Jang0f8b4b72014-05-28 17:58:58 +090072 // Predicate for whether the given logical address is remote device's one or not.
73 private final Predicate<Integer> mRemoteDeviceAddressPredicate = new Predicate<Integer>() {
74 @Override
75 public boolean apply(Integer address) {
76 return !isAllocatedLocalDeviceAddress(address);
77 }
78 };
79
80 // Predicate whether the given logical address is system audio's one or not
81 private final Predicate<Integer> mSystemAudioAddressPredicate = new Predicate<Integer>() {
82 @Override
83 public boolean apply(Integer address) {
Jinsuk Kimc0c20d02014-07-04 14:34:31 +090084 return HdmiUtils.getTypeFromAddress(address) == Constants.ADDR_AUDIO_SYSTEM;
Jungshik Jang0f8b4b72014-05-28 17:58:58 +090085 }
86 };
87
Jungshik Jang0792d372014-04-23 17:57:26 +090088 // Handler instance to process synchronous I/O (mainly send) message.
89 private Handler mIoHandler;
90
91 // Handler instance to process various messages coming from other CEC
92 // device or issued by internal state change.
Jungshik Jange9c77c82014-04-24 20:30:09 +090093 private Handler mControlHandler;
Jungshik Jang0792d372014-04-23 17:57:26 +090094
95 // Stores the pointer to the native implementation of the service that
96 // interacts with HAL.
Jungshik Jang02bb4262014-05-23 16:48:31 +090097 private volatile long mNativePtr;
Jungshik Jang0792d372014-04-23 17:57:26 +090098
Jungshik Jang7df52862014-08-11 14:35:27 +090099 private final HdmiControlService mService;
Jungshik Janga1fa91f2014-05-08 20:56:41 +0900100
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +0900101 // Stores the local CEC devices in the system. Device type is used for key.
102 private final SparseArray<HdmiCecLocalDevice> mLocalDevices = new SparseArray<>();
Jungshik Jang7d9a8432014-04-29 15:12:43 +0900103
Jungshik Jang0792d372014-04-23 17:57:26 +0900104 // Private constructor. Use HdmiCecController.create().
Jungshik Jang7df52862014-08-11 14:35:27 +0900105 private HdmiCecController(HdmiControlService service) {
106 mService = service;
Jungshik Jang0792d372014-04-23 17:57:26 +0900107 }
108
109 /**
110 * A factory method to get {@link HdmiCecController}. If it fails to initialize
111 * inner device or has no device it will return {@code null}.
112 *
113 * <p>Declared as package-private, accessed by {@link HdmiControlService} only.
Jungshik Jange9c77c82014-04-24 20:30:09 +0900114 * @param service {@link HdmiControlService} instance used to create internal handler
115 * and to pass callback for incoming message or event.
Jungshik Jang0792d372014-04-23 17:57:26 +0900116 * @return {@link HdmiCecController} if device is initialized successfully. Otherwise,
117 * returns {@code null}.
118 */
Jungshik Jange9c77c82014-04-24 20:30:09 +0900119 static HdmiCecController create(HdmiControlService service) {
Jungshik Jang7df52862014-08-11 14:35:27 +0900120 HdmiCecController controller = new HdmiCecController(service);
Jungshik Jang4085d0e2014-05-27 19:52:39 +0900121 long nativePtr = nativeInit(controller, service.getServiceLooper().getQueue());
Jungshik Jang0792d372014-04-23 17:57:26 +0900122 if (nativePtr == 0L) {
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900123 controller = null;
Jungshik Jang0792d372014-04-23 17:57:26 +0900124 return null;
125 }
126
Jungshik Jang7df52862014-08-11 14:35:27 +0900127 controller.init(nativePtr);
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900128 return controller;
129 }
130
Jungshik Jang7df52862014-08-11 14:35:27 +0900131 private void init(long nativePtr) {
Yuncheol Heof1702482014-11-27 19:52:01 +0900132 mIoHandler = new Handler(mService.getIoLooper());
Jungshik Jang7df52862014-08-11 14:35:27 +0900133 mControlHandler = new Handler(mService.getServiceLooper());
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900134 mNativePtr = nativePtr;
Jungshik Jang0792d372014-04-23 17:57:26 +0900135 }
136
Jungshik Janga5b74142014-06-23 18:03:10 +0900137 @ServiceThreadOnly
Jungshik Jang3ee65722014-06-03 16:22:30 +0900138 void addLocalDevice(int deviceType, HdmiCecLocalDevice device) {
Jungshik Janga5b74142014-06-23 18:03:10 +0900139 assertRunOnServiceThread();
Jungshik Jang3ee65722014-06-03 16:22:30 +0900140 mLocalDevices.put(deviceType, device);
Jungshik Jang3f74ab02014-04-30 14:31:02 +0900141 }
142
143 /**
144 * Allocate a new logical address of the given device type. Allocated
Jungshik Jang3ee65722014-06-03 16:22:30 +0900145 * address will be reported through {@link AllocateAddressCallback}.
Jungshik Jang3f74ab02014-04-30 14:31:02 +0900146 *
147 * <p> Declared as package-private, accessed by {@link HdmiControlService} only.
148 *
149 * @param deviceType type of device to used to determine logical address
150 * @param preferredAddress a logical address preferred to be allocated.
Jungshik Jang42230722014-07-07 17:40:25 +0900151 * If sets {@link Constants#ADDR_UNREGISTERED}, scans
Jungshik Jang3f74ab02014-04-30 14:31:02 +0900152 * the smallest logical address matched with the given device type.
153 * Otherwise, scan address will start from {@code preferredAddress}
154 * @param callback callback interface to report allocated logical address to caller
155 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900156 @ServiceThreadOnly
Jungshik Jangd643f762014-05-22 19:28:09 +0900157 void allocateLogicalAddress(final int deviceType, final int preferredAddress,
Jungshik Jang3ee65722014-06-03 16:22:30 +0900158 final AllocateAddressCallback callback) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900159 assertRunOnServiceThread();
160
Jungshik Jangd643f762014-05-22 19:28:09 +0900161 runOnIoThread(new Runnable() {
162 @Override
163 public void run() {
164 handleAllocateLogicalAddress(deviceType, preferredAddress, callback);
165 }
166 });
167 }
168
Jungshik Janga5b74142014-06-23 18:03:10 +0900169 @IoThreadOnly
Jungshik Jangd643f762014-05-22 19:28:09 +0900170 private void handleAllocateLogicalAddress(final int deviceType, int preferredAddress,
Jungshik Jang3ee65722014-06-03 16:22:30 +0900171 final AllocateAddressCallback callback) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900172 assertRunOnIoThread();
Jungshik Jangd643f762014-05-22 19:28:09 +0900173 int startAddress = preferredAddress;
174 // If preferred address is "unregistered", start address will be the smallest
175 // address matched with the given device type.
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900176 if (preferredAddress == Constants.ADDR_UNREGISTERED) {
Jungshik Jangd643f762014-05-22 19:28:09 +0900177 for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) {
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900178 if (deviceType == HdmiUtils.getTypeFromAddress(i)) {
Jungshik Jangd643f762014-05-22 19:28:09 +0900179 startAddress = i;
180 break;
181 }
182 }
183 }
184
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900185 int logicalAddress = Constants.ADDR_UNREGISTERED;
Jungshik Jangd643f762014-05-22 19:28:09 +0900186 // Iterates all possible addresses which has the same device type.
187 for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) {
188 int curAddress = (startAddress + i) % NUM_LOGICAL_ADDRESS;
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900189 if (curAddress != Constants.ADDR_UNREGISTERED
190 && deviceType == HdmiUtils.getTypeFromAddress(curAddress)) {
Donghyun Chodbf9f9c2016-07-04 20:39:26 +0900191 boolean acked = false;
Jungshik Jang8e93c842014-08-06 15:48:33 +0900192 for (int j = 0; j < HdmiConfig.ADDRESS_ALLOCATION_RETRY; ++j) {
Donghyun Chodbf9f9c2016-07-04 20:39:26 +0900193 if (sendPollMessage(curAddress, curAddress, 1)) {
194 acked = true;
195 break;
Jungshik Jang8e93c842014-08-06 15:48:33 +0900196 }
197 }
Donghyun Chodbf9f9c2016-07-04 20:39:26 +0900198 // If sending <Polling Message> failed, it becomes new logical address for the
199 // device because no device uses it as logical address of the device.
200 if (!acked) {
Jungshik Jangd643f762014-05-22 19:28:09 +0900201 logicalAddress = curAddress;
202 break;
203 }
204 }
205 }
206
207 final int assignedAddress = logicalAddress;
Jungshik Jang2e8f1b62014-09-03 08:28:02 +0900208 HdmiLogger.debug("New logical address for device [%d]: [preferred:%d, assigned:%d]",
209 deviceType, preferredAddress, assignedAddress);
Jungshik Jangd643f762014-05-22 19:28:09 +0900210 if (callback != null) {
211 runOnServiceThread(new Runnable() {
Jungshik Jang8e93c842014-08-06 15:48:33 +0900212 @Override
Jungshik Jangd643f762014-05-22 19:28:09 +0900213 public void run() {
214 callback.onAllocated(deviceType, assignedAddress);
215 }
216 });
217 }
Jungshik Jang3f74ab02014-04-30 14:31:02 +0900218 }
219
Jungshik Jange9c77c82014-04-24 20:30:09 +0900220 private static byte[] buildBody(int opcode, byte[] params) {
221 byte[] body = new byte[params.length + 1];
222 body[0] = (byte) opcode;
223 System.arraycopy(params, 0, body, 1, params.length);
224 return body;
225 }
Jungshik Jang0792d372014-04-23 17:57:26 +0900226
Jungshik Jang7d9a8432014-04-29 15:12:43 +0900227
Jinsuk Kim0340bbc2014-06-05 11:07:47 +0900228 HdmiPortInfo[] getPortInfos() {
229 return nativeGetPortInfos(mNativePtr);
230 }
231
Jungshik Janga9095ba2014-05-02 13:06:22 +0900232 /**
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +0900233 * Return the locally hosted logical device of a given type.
234 *
235 * @param deviceType logical device type
236 * @return {@link HdmiCecLocalDevice} instance if the instance of the type is available;
237 * otherwise null.
238 */
239 HdmiCecLocalDevice getLocalDevice(int deviceType) {
240 return mLocalDevices.get(deviceType);
241 }
242
243 /**
Jungshik Janga9095ba2014-05-02 13:06:22 +0900244 * Add a new logical address to the device. Device's HW should be notified
245 * when a new logical address is assigned to a device, so that it can accept
246 * a command having available destinations.
247 *
248 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
249 *
250 * @param newLogicalAddress a logical address to be added
251 * @return 0 on success. Otherwise, returns negative value
252 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900253 @ServiceThreadOnly
Jungshik Janga9095ba2014-05-02 13:06:22 +0900254 int addLogicalAddress(int newLogicalAddress) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900255 assertRunOnServiceThread();
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900256 if (HdmiUtils.isValidAddress(newLogicalAddress)) {
Jungshik Janga9095ba2014-05-02 13:06:22 +0900257 return nativeAddLogicalAddress(mNativePtr, newLogicalAddress);
258 } else {
Donghyun Chobc6e3722016-11-04 05:25:52 +0900259 return Result.FAILURE_INVALID_ARGS;
Jungshik Janga9095ba2014-05-02 13:06:22 +0900260 }
261 }
262
263 /**
264 * Clear all logical addresses registered in the device.
265 *
266 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
267 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900268 @ServiceThreadOnly
Jungshik Janga9095ba2014-05-02 13:06:22 +0900269 void clearLogicalAddress() {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900270 assertRunOnServiceThread();
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +0900271 for (int i = 0; i < mLocalDevices.size(); ++i) {
272 mLocalDevices.valueAt(i).clearAddress();
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900273 }
Jungshik Janga9095ba2014-05-02 13:06:22 +0900274 nativeClearLogicalAddress(mNativePtr);
275 }
276
Jungshik Jang4fc1d102014-07-09 19:24:50 +0900277 @ServiceThreadOnly
278 void clearLocalDevices() {
279 assertRunOnServiceThread();
280 mLocalDevices.clear();
281 }
282
Jungshik Janga9095ba2014-05-02 13:06:22 +0900283 /**
284 * Return the physical address of the device.
285 *
286 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
287 *
288 * @return CEC physical address of the device. The range of success address
289 * is between 0x0000 and 0xFFFF. If failed it returns -1
290 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900291 @ServiceThreadOnly
Jungshik Janga9095ba2014-05-02 13:06:22 +0900292 int getPhysicalAddress() {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900293 assertRunOnServiceThread();
Jungshik Janga9095ba2014-05-02 13:06:22 +0900294 return nativeGetPhysicalAddress(mNativePtr);
295 }
296
297 /**
298 * Return CEC version of the device.
299 *
300 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
301 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900302 @ServiceThreadOnly
Jungshik Janga9095ba2014-05-02 13:06:22 +0900303 int getVersion() {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900304 assertRunOnServiceThread();
Jungshik Janga9095ba2014-05-02 13:06:22 +0900305 return nativeGetVersion(mNativePtr);
306 }
307
308 /**
309 * Return vendor id of the device.
310 *
311 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
312 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900313 @ServiceThreadOnly
Jungshik Janga9095ba2014-05-02 13:06:22 +0900314 int getVendorId() {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900315 assertRunOnServiceThread();
Jungshik Janga9095ba2014-05-02 13:06:22 +0900316 return nativeGetVendorId(mNativePtr);
317 }
318
Jungshik Jang02bb4262014-05-23 16:48:31 +0900319 /**
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900320 * Set an option to CEC HAL.
Jungshik Jang092b4452014-06-11 15:19:17 +0900321 *
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900322 * @param flag key of option
Donghyun Chobc6e3722016-11-04 05:25:52 +0900323 * @param enabled whether to enable/disable the given option.
Jungshik Jang092b4452014-06-11 15:19:17 +0900324 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900325 @ServiceThreadOnly
Donghyun Chobc6e3722016-11-04 05:25:52 +0900326 void setOption(int flag, boolean enabled) {
Jungshik Jang092b4452014-06-11 15:19:17 +0900327 assertRunOnServiceThread();
Donghyun Chobc6e3722016-11-04 05:25:52 +0900328 HdmiLogger.debug("setOption: [flag:%d, enabled:%b]", flag, enabled);
329 nativeSetOption(mNativePtr, flag, enabled);
330 }
331
332 /**
333 * Informs CEC HAL about the current system language.
334 *
335 * @param language Three-letter code defined in ISO/FDIS 639-2. Must be lowercase letters.
336 */
337 @ServiceThreadOnly
338 void setLanguage(String language) {
339 assertRunOnServiceThread();
340 if (!LanguageTag.isLanguage(language)) {
341 return;
342 }
343 nativeSetLanguage(mNativePtr, language);
Jungshik Jang092b4452014-06-11 15:19:17 +0900344 }
345
346 /**
347 * Configure ARC circuit in the hardware logic to start or stop the feature.
348 *
Jinsuk Kim1481a422014-12-17 16:15:05 +0900349 * @param port ID of HDMI port to which AVR is connected
Jungshik Jang092b4452014-06-11 15:19:17 +0900350 * @param enabled whether to enable/disable ARC
351 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900352 @ServiceThreadOnly
Donghyun Chobc6e3722016-11-04 05:25:52 +0900353 void enableAudioReturnChannel(int port, boolean enabled) {
Jungshik Jang092b4452014-06-11 15:19:17 +0900354 assertRunOnServiceThread();
Donghyun Chobc6e3722016-11-04 05:25:52 +0900355 nativeEnableAudioReturnChannel(mNativePtr, port, enabled);
Jungshik Jang092b4452014-06-11 15:19:17 +0900356 }
357
358 /**
359 * Return the connection status of the specified port
360 *
361 * @param port port number to check connection status
362 * @return true if connected; otherwise, return false
363 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900364 @ServiceThreadOnly
Jungshik Jang092b4452014-06-11 15:19:17 +0900365 boolean isConnected(int port) {
366 assertRunOnServiceThread();
367 return nativeIsConnected(mNativePtr, port);
368 }
369
370 /**
Jungshik Jang02bb4262014-05-23 16:48:31 +0900371 * Poll all remote devices. It sends &lt;Polling Message&gt; to all remote
372 * devices.
373 *
374 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
375 *
376 * @param callback an interface used to get a list of all remote devices' address
Jungshik Jang1de51422014-07-03 11:14:26 +0900377 * @param sourceAddress a logical address of source device where sends polling message
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900378 * @param pickStrategy strategy how to pick polling candidates
Jungshik Jang02bb4262014-05-23 16:48:31 +0900379 * @param retryCount the number of retry used to send polling message to remote devices
380 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900381 @ServiceThreadOnly
Jungshik Jang1de51422014-07-03 11:14:26 +0900382 void pollDevices(DevicePollingCallback callback, int sourceAddress, int pickStrategy,
383 int retryCount) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900384 assertRunOnServiceThread();
Jungshik Jang02bb4262014-05-23 16:48:31 +0900385
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900386 // Extract polling candidates. No need to poll against local devices.
387 List<Integer> pollingCandidates = pickPollCandidates(pickStrategy);
Jungshik Jangb3ecb722014-09-11 16:09:45 +0900388 ArrayList<Integer> allocated = new ArrayList<>();
389 runDevicePolling(sourceAddress, pollingCandidates, retryCount, callback, allocated);
Jungshik Jang02bb4262014-05-23 16:48:31 +0900390 }
391
Jungshik Jangcc5ef8c2014-05-27 13:27:36 +0900392 /**
393 * Return a list of all {@link HdmiCecLocalDevice}s.
394 *
395 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
396 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900397 @ServiceThreadOnly
Jungshik Jangcc5ef8c2014-05-27 13:27:36 +0900398 List<HdmiCecLocalDevice> getLocalDeviceList() {
399 assertRunOnServiceThread();
Jungshik Jang79c58a42014-06-16 16:45:36 +0900400 return HdmiUtils.sparseArrayToList(mLocalDevices);
Jungshik Jangcc5ef8c2014-05-27 13:27:36 +0900401 }
402
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900403 private List<Integer> pickPollCandidates(int pickStrategy) {
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900404 int strategy = pickStrategy & Constants.POLL_STRATEGY_MASK;
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900405 Predicate<Integer> pickPredicate = null;
406 switch (strategy) {
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900407 case Constants.POLL_STRATEGY_SYSTEM_AUDIO:
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900408 pickPredicate = mSystemAudioAddressPredicate;
409 break;
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900410 case Constants.POLL_STRATEGY_REMOTES_DEVICES:
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900411 default: // The default is POLL_STRATEGY_REMOTES_DEVICES.
412 pickPredicate = mRemoteDeviceAddressPredicate;
413 break;
414 }
415
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900416 int iterationStrategy = pickStrategy & Constants.POLL_ITERATION_STRATEGY_MASK;
Jungshik Jangb3ecb722014-09-11 16:09:45 +0900417 LinkedList<Integer> pollingCandidates = new LinkedList<>();
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900418 switch (iterationStrategy) {
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900419 case Constants.POLL_ITERATION_IN_ORDER:
420 for (int i = Constants.ADDR_TV; i <= Constants.ADDR_SPECIFIC_USE; ++i) {
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900421 if (pickPredicate.apply(i)) {
422 pollingCandidates.add(i);
423 }
424 }
425 break;
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900426 case Constants.POLL_ITERATION_REVERSE_ORDER:
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900427 default: // The default is reverse order.
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900428 for (int i = Constants.ADDR_SPECIFIC_USE; i >= Constants.ADDR_TV; --i) {
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900429 if (pickPredicate.apply(i)) {
430 pollingCandidates.add(i);
431 }
432 }
433 break;
434 }
435 return pollingCandidates;
436 }
437
Jungshik Janga5b74142014-06-23 18:03:10 +0900438 @ServiceThreadOnly
Jungshik Jang02bb4262014-05-23 16:48:31 +0900439 private boolean isAllocatedLocalDeviceAddress(int address) {
Jungshik Janga5b74142014-06-23 18:03:10 +0900440 assertRunOnServiceThread();
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +0900441 for (int i = 0; i < mLocalDevices.size(); ++i) {
442 if (mLocalDevices.valueAt(i).isAddressOf(address)) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900443 return true;
444 }
445 }
446 return false;
447 }
448
Jungshik Janga5b74142014-06-23 18:03:10 +0900449 @ServiceThreadOnly
Jungshik Jang1de51422014-07-03 11:14:26 +0900450 private void runDevicePolling(final int sourceAddress,
451 final List<Integer> candidates, final int retryCount,
Jungshik Jangb3ecb722014-09-11 16:09:45 +0900452 final DevicePollingCallback callback, final List<Integer> allocated) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900453 assertRunOnServiceThread();
Jungshik Jangb3ecb722014-09-11 16:09:45 +0900454 if (candidates.isEmpty()) {
455 if (callback != null) {
456 HdmiLogger.debug("[P]:AllocatedAddress=%s", allocated.toString());
457 callback.onPollingFinished(allocated);
458 }
459 return;
460 }
461
462 final Integer candidate = candidates.remove(0);
463 // Proceed polling action for the next address once polling action for the
464 // previous address is done.
Jungshik Jang02bb4262014-05-23 16:48:31 +0900465 runOnIoThread(new Runnable() {
466 @Override
467 public void run() {
Jungshik Jangb3ecb722014-09-11 16:09:45 +0900468 if (sendPollMessage(sourceAddress, candidate, retryCount)) {
469 allocated.add(candidate);
470 }
471 runOnServiceThread(new Runnable() {
472 @Override
473 public void run() {
474 runDevicePolling(sourceAddress, candidates, retryCount, callback,
475 allocated);
Jungshik Jang02bb4262014-05-23 16:48:31 +0900476 }
Jungshik Jangb3ecb722014-09-11 16:09:45 +0900477 });
Jungshik Jang02bb4262014-05-23 16:48:31 +0900478 }
479 });
480 }
481
Jungshik Janga5b74142014-06-23 18:03:10 +0900482 @IoThreadOnly
Jungshik Jang1de51422014-07-03 11:14:26 +0900483 private boolean sendPollMessage(int sourceAddress, int destinationAddress, int retryCount) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900484 assertRunOnIoThread();
485 for (int i = 0; i < retryCount; ++i) {
Jungshik Jang1de51422014-07-03 11:14:26 +0900486 // <Polling Message> is a message which has empty body.
Donghyun Chodbf9f9c2016-07-04 20:39:26 +0900487 int ret =
488 nativeSendCecCommand(mNativePtr, sourceAddress, destinationAddress, EMPTY_BODY);
Donghyun Chobc6e3722016-11-04 05:25:52 +0900489 if (ret == SendMessageResult.SUCCESS) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900490 return true;
Donghyun Chobc6e3722016-11-04 05:25:52 +0900491 } else if (ret != SendMessageResult.NACK) {
Donghyun Chodbf9f9c2016-07-04 20:39:26 +0900492 // Unusual failure
493 HdmiLogger.warning("Failed to send a polling message(%d->%d) with return code %d",
494 sourceAddress, destinationAddress, ret);
Jungshik Jang02bb4262014-05-23 16:48:31 +0900495 }
496 }
497 return false;
498 }
499
500 private void assertRunOnIoThread() {
501 if (Looper.myLooper() != mIoHandler.getLooper()) {
502 throw new IllegalStateException("Should run on io thread.");
503 }
504 }
505
506 private void assertRunOnServiceThread() {
507 if (Looper.myLooper() != mControlHandler.getLooper()) {
508 throw new IllegalStateException("Should run on service thread.");
509 }
510 }
511
512 // Run a Runnable on IO thread.
513 // It should be careful to access member variables on IO thread because
514 // it can be accessed from system thread as well.
Jungshik Jangd643f762014-05-22 19:28:09 +0900515 private void runOnIoThread(Runnable runnable) {
516 mIoHandler.post(runnable);
517 }
518
519 private void runOnServiceThread(Runnable runnable) {
520 mControlHandler.post(runnable);
521 }
522
Yuncheol Heof1702482014-11-27 19:52:01 +0900523 @ServiceThreadOnly
524 void flush(final Runnable runnable) {
525 assertRunOnServiceThread();
526 runOnIoThread(new Runnable() {
527 @Override
528 public void run() {
529 // This ensures the runnable for cleanup is performed after all the pending
530 // commands are processed by IO thread.
531 runOnServiceThread(runnable);
532 }
533 });
534 }
535
Jungshik Janga1fa91f2014-05-08 20:56:41 +0900536 private boolean isAcceptableAddress(int address) {
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900537 // Can access command targeting devices available in local device or broadcast command.
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900538 if (address == Constants.ADDR_BROADCAST) {
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900539 return true;
540 }
Jungshik Jang02bb4262014-05-23 16:48:31 +0900541 return isAllocatedLocalDeviceAddress(address);
Jungshik Janga1fa91f2014-05-08 20:56:41 +0900542 }
543
Jungshik Janga5b74142014-06-23 18:03:10 +0900544 @ServiceThreadOnly
Jungshik Jange9c77c82014-04-24 20:30:09 +0900545 private void onReceiveCommand(HdmiCecMessage message) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900546 assertRunOnServiceThread();
Yuncheol Heo6aae6522014-08-05 14:48:37 +0900547 if (isAcceptableAddress(message.getDestination()) && mService.handleCecCommand(message)) {
Jungshik Janga1fa91f2014-05-08 20:56:41 +0900548 return;
549 }
Yuncheol Heo6aae6522014-08-05 14:48:37 +0900550 // Not handled message, so we will reply it with <Feature Abort>.
551 maySendFeatureAbortCommand(message, Constants.ABORT_UNRECOGNIZED_OPCODE);
552 }
Jungshik Jang1827fdc2014-07-17 13:58:14 +0900553
Yuncheol Heo6aae6522014-08-05 14:48:37 +0900554 @ServiceThreadOnly
555 void maySendFeatureAbortCommand(HdmiCecMessage message, int reason) {
556 assertRunOnServiceThread();
557 // Swap the source and the destination.
558 int src = message.getDestination();
559 int dest = message.getSource();
560 if (src == Constants.ADDR_BROADCAST || dest == Constants.ADDR_UNREGISTERED) {
561 // Don't reply <Feature Abort> from the unregistered devices or for the broadcasted
562 // messages. See CEC 12.2 Protocol General Rules for detail.
563 return;
564 }
565 int originalOpcode = message.getOpcode();
566 if (originalOpcode == Constants.MESSAGE_FEATURE_ABORT) {
567 return;
568 }
569 sendCommand(
570 HdmiCecMessageBuilder.buildFeatureAbortCommand(src, dest, originalOpcode, reason));
Jungshik Jange9c77c82014-04-24 20:30:09 +0900571 }
572
Jungshik Janga5b74142014-06-23 18:03:10 +0900573 @ServiceThreadOnly
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900574 void sendCommand(HdmiCecMessage cecMessage) {
Jungshik Janga5b74142014-06-23 18:03:10 +0900575 assertRunOnServiceThread();
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900576 sendCommand(cecMessage, null);
577 }
578
Jungshik Janga5b74142014-06-23 18:03:10 +0900579 @ServiceThreadOnly
Jungshik Jangd643f762014-05-22 19:28:09 +0900580 void sendCommand(final HdmiCecMessage cecMessage,
581 final HdmiControlService.SendMessageCallback callback) {
Jungshik Janga5b74142014-06-23 18:03:10 +0900582 assertRunOnServiceThread();
Jungshik Jangd643f762014-05-22 19:28:09 +0900583 runOnIoThread(new Runnable() {
584 @Override
585 public void run() {
Jungshik Jang2e8f1b62014-09-03 08:28:02 +0900586 HdmiLogger.debug("[S]:" + cecMessage);
Jungshik Jangd643f762014-05-22 19:28:09 +0900587 byte[] body = buildBody(cecMessage.getOpcode(), cecMessage.getParams());
Jungshik Jang8ed86c42014-07-11 11:56:46 +0900588 int i = 0;
Donghyun Chobc6e3722016-11-04 05:25:52 +0900589 int errorCode = SendMessageResult.SUCCESS;
Jungshik Jang8ed86c42014-07-11 11:56:46 +0900590 do {
591 errorCode = nativeSendCecCommand(mNativePtr, cecMessage.getSource(),
592 cecMessage.getDestination(), body);
Donghyun Chobc6e3722016-11-04 05:25:52 +0900593 if (errorCode == SendMessageResult.SUCCESS) {
Jungshik Jang8ed86c42014-07-11 11:56:46 +0900594 break;
595 }
596 } while (i++ < HdmiConfig.RETRANSMISSION_COUNT);
597
598 final int finalError = errorCode;
Donghyun Chobc6e3722016-11-04 05:25:52 +0900599 if (finalError != SendMessageResult.SUCCESS) {
Yuncheol Heoece603b2014-05-23 20:10:19 +0900600 Slog.w(TAG, "Failed to send " + cecMessage);
601 }
Jungshik Jangd643f762014-05-22 19:28:09 +0900602 if (callback != null) {
603 runOnServiceThread(new Runnable() {
604 @Override
605 public void run() {
Jungshik Jang8ed86c42014-07-11 11:56:46 +0900606 callback.onSendCompleted(finalError);
Jungshik Jangd643f762014-05-22 19:28:09 +0900607 }
608 });
609 }
610 }
611 });
Jungshik Jange9c77c82014-04-24 20:30:09 +0900612 }
613
Jungshik Jang0792d372014-04-23 17:57:26 +0900614 /**
Jungshik Jange9c77c82014-04-24 20:30:09 +0900615 * Called by native when incoming CEC message arrived.
Jungshik Jang0792d372014-04-23 17:57:26 +0900616 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900617 @ServiceThreadOnly
Jungshik Jange9c77c82014-04-24 20:30:09 +0900618 private void handleIncomingCecCommand(int srcAddress, int dstAddress, byte[] body) {
Jungshik Jang4085d0e2014-05-27 19:52:39 +0900619 assertRunOnServiceThread();
Jungshik Jangc94ac5c2014-08-27 13:48:37 +0900620 HdmiCecMessage command = HdmiCecMessageBuilder.of(srcAddress, dstAddress, body);
Jungshik Jang2e8f1b62014-09-03 08:28:02 +0900621 HdmiLogger.debug("[R]:" + command);
Jungshik Jangc94ac5c2014-08-27 13:48:37 +0900622 onReceiveCommand(command);
Jungshik Jange9c77c82014-04-24 20:30:09 +0900623 }
624
625 /**
626 * Called by native when a hotplug event issues.
627 */
Jungshik Jang42230722014-07-07 17:40:25 +0900628 @ServiceThreadOnly
629 private void handleHotplug(int port, boolean connected) {
630 assertRunOnServiceThread();
Jungshik Jang2e8f1b62014-09-03 08:28:02 +0900631 HdmiLogger.debug("Hotplug event:[port:%d, connected:%b]", port, connected);
Jungshik Jang42230722014-07-07 17:40:25 +0900632 mService.onHotplug(port, connected);
Jungshik Jang0792d372014-04-23 17:57:26 +0900633 }
634
Terry Heo959d2db2014-08-28 16:45:41 +0900635 void dump(final IndentingPrintWriter pw) {
636 for (int i = 0; i < mLocalDevices.size(); ++i) {
637 pw.println("HdmiCecLocalDevice #" + i + ":");
638 pw.increaseIndent();
639 mLocalDevices.valueAt(i).dump(pw);
640 pw.decreaseIndent();
641 }
642 }
643
Jungshik Jang4085d0e2014-05-27 19:52:39 +0900644 private static native long nativeInit(HdmiCecController handler, MessageQueue messageQueue);
Jungshik Janga9095ba2014-05-02 13:06:22 +0900645 private static native int nativeSendCecCommand(long controllerPtr, int srcAddress,
Jungshik Jange9c77c82014-04-24 20:30:09 +0900646 int dstAddress, byte[] body);
Jungshik Janga9095ba2014-05-02 13:06:22 +0900647 private static native int nativeAddLogicalAddress(long controllerPtr, int logicalAddress);
648 private static native void nativeClearLogicalAddress(long controllerPtr);
649 private static native int nativeGetPhysicalAddress(long controllerPtr);
650 private static native int nativeGetVersion(long controllerPtr);
651 private static native int nativeGetVendorId(long controllerPtr);
Jinsuk Kim0340bbc2014-06-05 11:07:47 +0900652 private static native HdmiPortInfo[] nativeGetPortInfos(long controllerPtr);
Donghyun Chobc6e3722016-11-04 05:25:52 +0900653 private static native void nativeSetOption(long controllerPtr, int flag, boolean enabled);
654 private static native void nativeSetLanguage(long controllerPtr, String language);
655 private static native void nativeEnableAudioReturnChannel(long controllerPtr, int port, boolean flag);
Jungshik Jang092b4452014-06-11 15:19:17 +0900656 private static native boolean nativeIsConnected(long controllerPtr, int port);
Jungshik Jang0792d372014-04-23 17:57:26 +0900657}