blob: 7dfea6aa19505190e82f4904c91ee73bd221d6fb [file] [log] [blame]
Jungshik Jang0792d372014-04-23 17:57:26 +09001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.hdmi;
18
Jungshik Jange9c77c82014-04-24 20:30:09 +090019import android.hardware.hdmi.HdmiCec;
20import android.hardware.hdmi.HdmiCecMessage;
Jinsuk Kim0340bbc2014-06-05 11:07:47 +090021import android.hardware.hdmi.HdmiPortInfo;
Jungshik Jang0792d372014-04-23 17:57:26 +090022import android.os.Handler;
Jungshik Jang02bb4262014-05-23 16:48:31 +090023import android.os.Looper;
Jungshik Jang4085d0e2014-05-27 19:52:39 +090024import android.os.MessageQueue;
Yuncheol Heoece603b2014-05-23 20:10:19 +090025import android.util.Slog;
Jungshik Jang7d9a8432014-04-29 15:12:43 +090026import android.util.SparseArray;
Jungshik Jange9c77c82014-04-24 20:30:09 +090027
Jungshik Jang0f8b4b72014-05-28 17:58:58 +090028import com.android.internal.util.Predicate;
Jungshik 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;
32
Jungshik Jang3f74ab02014-04-30 14:31:02 +090033import libcore.util.EmptyArray;
34
Jungshik Jang7d9a8432014-04-29 15:12:43 +090035import java.util.ArrayList;
Jungshik Jang7d9a8432014-04-29 15:12:43 +090036import java.util.List;
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
62 * {@link HdmiCec#ADDR_UNREGISTERED}, it means that
63 * 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 Jange9c77c82014-04-24 20:30:09 +090070 // A message to pass cec send command to IO looper.
71 private static final int MSG_SEND_CEC_COMMAND = 1;
Jungshik Jang3f74ab02014-04-30 14:31:02 +090072 // A message to delegate logical allocation to IO looper.
73 private static final int MSG_ALLOCATE_LOGICAL_ADDRESS = 2;
Jungshik Jange9c77c82014-04-24 20:30:09 +090074
75 // Message types to handle incoming message in main service looper.
76 private final static int MSG_RECEIVE_CEC_COMMAND = 1;
Jungshik Jang3f74ab02014-04-30 14:31:02 +090077 // A message to report allocated logical address to main control looper.
78 private final static int MSG_REPORT_LOGICAL_ADDRESS = 2;
Jungshik Jange9c77c82014-04-24 20:30:09 +090079
Jungshik Jang3f74ab02014-04-30 14:31:02 +090080 private static final int NUM_LOGICAL_ADDRESS = 16;
81
Jungshik Jang02bb4262014-05-23 16:48:31 +090082 private static final int RETRY_COUNT_FOR_LOGICAL_ADDRESS_ALLOCATION = 3;
83
Jungshik Jang0f8b4b72014-05-28 17:58:58 +090084 // Predicate for whether the given logical address is remote device's one or not.
85 private final Predicate<Integer> mRemoteDeviceAddressPredicate = new Predicate<Integer>() {
86 @Override
87 public boolean apply(Integer address) {
88 return !isAllocatedLocalDeviceAddress(address);
89 }
90 };
91
92 // Predicate whether the given logical address is system audio's one or not
93 private final Predicate<Integer> mSystemAudioAddressPredicate = new Predicate<Integer>() {
94 @Override
95 public boolean apply(Integer address) {
96 return HdmiCec.getTypeFromAddress(address) == HdmiCec.ADDR_AUDIO_SYSTEM;
97 }
98 };
99
Jungshik Jang0792d372014-04-23 17:57:26 +0900100 // Handler instance to process synchronous I/O (mainly send) message.
101 private Handler mIoHandler;
102
103 // Handler instance to process various messages coming from other CEC
104 // device or issued by internal state change.
Jungshik Jange9c77c82014-04-24 20:30:09 +0900105 private Handler mControlHandler;
Jungshik Jang0792d372014-04-23 17:57:26 +0900106
107 // Stores the pointer to the native implementation of the service that
108 // interacts with HAL.
Jungshik Jang02bb4262014-05-23 16:48:31 +0900109 private volatile long mNativePtr;
Jungshik Jang0792d372014-04-23 17:57:26 +0900110
Jungshik Janga1fa91f2014-05-08 20:56:41 +0900111 private HdmiControlService mService;
112
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +0900113 // Stores the local CEC devices in the system. Device type is used for key.
114 private final SparseArray<HdmiCecLocalDevice> mLocalDevices = new SparseArray<>();
Jungshik Jang7d9a8432014-04-29 15:12:43 +0900115
Jungshik Jang0792d372014-04-23 17:57:26 +0900116 // Private constructor. Use HdmiCecController.create().
117 private HdmiCecController() {
118 }
119
120 /**
121 * A factory method to get {@link HdmiCecController}. If it fails to initialize
122 * inner device or has no device it will return {@code null}.
123 *
124 * <p>Declared as package-private, accessed by {@link HdmiControlService} only.
Jungshik Jange9c77c82014-04-24 20:30:09 +0900125 * @param service {@link HdmiControlService} instance used to create internal handler
126 * and to pass callback for incoming message or event.
Jungshik Jang0792d372014-04-23 17:57:26 +0900127 * @return {@link HdmiCecController} if device is initialized successfully. Otherwise,
128 * returns {@code null}.
129 */
Jungshik Jange9c77c82014-04-24 20:30:09 +0900130 static HdmiCecController create(HdmiControlService service) {
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900131 HdmiCecController controller = new HdmiCecController();
Jungshik Jang4085d0e2014-05-27 19:52:39 +0900132 long nativePtr = nativeInit(controller, service.getServiceLooper().getQueue());
Jungshik Jang0792d372014-04-23 17:57:26 +0900133 if (nativePtr == 0L) {
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900134 controller = null;
Jungshik Jang0792d372014-04-23 17:57:26 +0900135 return null;
136 }
137
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900138 controller.init(service, nativePtr);
139 return controller;
140 }
141
142 private void init(HdmiControlService service, long nativePtr) {
143 mService = service;
144 mIoHandler = new Handler(service.getServiceLooper());
145 mControlHandler = new Handler(service.getServiceLooper());
146 mNativePtr = nativePtr;
Jungshik Jang0792d372014-04-23 17:57:26 +0900147 }
148
Jungshik Janga5b74142014-06-23 18:03:10 +0900149 @ServiceThreadOnly
Jungshik Jang3ee65722014-06-03 16:22:30 +0900150 void addLocalDevice(int deviceType, HdmiCecLocalDevice device) {
Jungshik Janga5b74142014-06-23 18:03:10 +0900151 assertRunOnServiceThread();
Jungshik Jang3ee65722014-06-03 16:22:30 +0900152 mLocalDevices.put(deviceType, device);
Jungshik Jang3f74ab02014-04-30 14:31:02 +0900153 }
154
155 /**
156 * Allocate a new logical address of the given device type. Allocated
Jungshik Jang3ee65722014-06-03 16:22:30 +0900157 * address will be reported through {@link AllocateAddressCallback}.
Jungshik Jang3f74ab02014-04-30 14:31:02 +0900158 *
159 * <p> Declared as package-private, accessed by {@link HdmiControlService} only.
160 *
161 * @param deviceType type of device to used to determine logical address
162 * @param preferredAddress a logical address preferred to be allocated.
163 * If sets {@link HdmiCec#ADDR_UNREGISTERED}, scans
164 * the smallest logical address matched with the given device type.
165 * Otherwise, scan address will start from {@code preferredAddress}
166 * @param callback callback interface to report allocated logical address to caller
167 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900168 @ServiceThreadOnly
Jungshik Jangd643f762014-05-22 19:28:09 +0900169 void allocateLogicalAddress(final int deviceType, final int preferredAddress,
Jungshik Jang3ee65722014-06-03 16:22:30 +0900170 final AllocateAddressCallback callback) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900171 assertRunOnServiceThread();
172
Jungshik Jangd643f762014-05-22 19:28:09 +0900173 runOnIoThread(new Runnable() {
174 @Override
175 public void run() {
176 handleAllocateLogicalAddress(deviceType, preferredAddress, callback);
177 }
178 });
179 }
180
Jungshik Janga5b74142014-06-23 18:03:10 +0900181 @IoThreadOnly
Jungshik Jangd643f762014-05-22 19:28:09 +0900182 private void handleAllocateLogicalAddress(final int deviceType, int preferredAddress,
Jungshik Jang3ee65722014-06-03 16:22:30 +0900183 final AllocateAddressCallback callback) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900184 assertRunOnIoThread();
Jungshik Jangd643f762014-05-22 19:28:09 +0900185 int startAddress = preferredAddress;
186 // If preferred address is "unregistered", start address will be the smallest
187 // address matched with the given device type.
188 if (preferredAddress == HdmiCec.ADDR_UNREGISTERED) {
189 for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) {
190 if (deviceType == HdmiCec.getTypeFromAddress(i)) {
191 startAddress = i;
192 break;
193 }
194 }
195 }
196
197 int logicalAddress = HdmiCec.ADDR_UNREGISTERED;
198 // Iterates all possible addresses which has the same device type.
199 for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) {
200 int curAddress = (startAddress + i) % NUM_LOGICAL_ADDRESS;
201 if (curAddress != HdmiCec.ADDR_UNREGISTERED
Jinsuk Kim0bc8b072014-05-27 17:23:27 +0900202 && deviceType == HdmiCec.getTypeFromAddress(curAddress)) {
Jungshik Jang1de51422014-07-03 11:14:26 +0900203 if (!sendPollMessage(curAddress, curAddress,
204 RETRY_COUNT_FOR_LOGICAL_ADDRESS_ALLOCATION)) {
Jungshik Jangd643f762014-05-22 19:28:09 +0900205 logicalAddress = curAddress;
206 break;
207 }
208 }
209 }
210
211 final int assignedAddress = logicalAddress;
212 if (callback != null) {
213 runOnServiceThread(new Runnable() {
214 @Override
215 public void run() {
216 callback.onAllocated(deviceType, assignedAddress);
217 }
218 });
219 }
Jungshik Jang3f74ab02014-04-30 14:31:02 +0900220 }
221
Jungshik Jange9c77c82014-04-24 20:30:09 +0900222 private static byte[] buildBody(int opcode, byte[] params) {
223 byte[] body = new byte[params.length + 1];
224 body[0] = (byte) opcode;
225 System.arraycopy(params, 0, body, 1, params.length);
226 return body;
227 }
Jungshik Jang0792d372014-04-23 17:57:26 +0900228
Jungshik Jang7d9a8432014-04-29 15:12:43 +0900229
Jinsuk Kim0340bbc2014-06-05 11:07:47 +0900230 HdmiPortInfo[] getPortInfos() {
231 return nativeGetPortInfos(mNativePtr);
232 }
233
Jungshik Janga9095ba2014-05-02 13:06:22 +0900234 /**
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +0900235 * Return the locally hosted logical device of a given type.
236 *
237 * @param deviceType logical device type
238 * @return {@link HdmiCecLocalDevice} instance if the instance of the type is available;
239 * otherwise null.
240 */
241 HdmiCecLocalDevice getLocalDevice(int deviceType) {
242 return mLocalDevices.get(deviceType);
243 }
244
245 /**
Jungshik Janga9095ba2014-05-02 13:06:22 +0900246 * Add a new logical address to the device. Device's HW should be notified
247 * when a new logical address is assigned to a device, so that it can accept
248 * a command having available destinations.
249 *
250 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
251 *
252 * @param newLogicalAddress a logical address to be added
253 * @return 0 on success. Otherwise, returns negative value
254 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900255 @ServiceThreadOnly
Jungshik Janga9095ba2014-05-02 13:06:22 +0900256 int addLogicalAddress(int newLogicalAddress) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900257 assertRunOnServiceThread();
Jungshik Janga9095ba2014-05-02 13:06:22 +0900258 if (HdmiCec.isValidAddress(newLogicalAddress)) {
259 return nativeAddLogicalAddress(mNativePtr, newLogicalAddress);
260 } else {
261 return -1;
262 }
263 }
264
265 /**
266 * Clear all logical addresses registered in the device.
267 *
268 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
269 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900270 @ServiceThreadOnly
Jungshik Janga9095ba2014-05-02 13:06:22 +0900271 void clearLogicalAddress() {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900272 assertRunOnServiceThread();
Jungshik Janga1fa91f2014-05-08 20:56:41 +0900273 // TODO: consider to backup logical address so that new logical address
274 // allocation can use it as preferred address.
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +0900275 for (int i = 0; i < mLocalDevices.size(); ++i) {
276 mLocalDevices.valueAt(i).clearAddress();
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900277 }
Jungshik Janga9095ba2014-05-02 13:06:22 +0900278 nativeClearLogicalAddress(mNativePtr);
279 }
280
281 /**
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 /**
Jungshik Jang092b4452014-06-11 15:19:17 +0900318 * Pass a option to CEC HAL.
319 *
320 * @param flag a key of option. For more details, look at
321 * {@link HdmiConstants#FLAG_HDMI_OPTION_WAKEUP} to
Jinsuk Kim160a6e52014-07-02 06:16:36 +0900322 * {@link HdmiConstants#FLAG_HDMI_OPTION_SYSTEM_CEC_CONTROL}.
323 * @param value a value of option. Actual value varies from flag to flag. For more
324 * details, look at description of flags.
Jungshik Jang092b4452014-06-11 15:19:17 +0900325 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900326 @ServiceThreadOnly
Jungshik Jang092b4452014-06-11 15:19:17 +0900327 void setOption(int flag, int value) {
328 assertRunOnServiceThread();
329 nativeSetOption(mNativePtr, flag, value);
330 }
331
332 /**
333 * Configure ARC circuit in the hardware logic to start or stop the feature.
334 *
335 * @param enabled whether to enable/disable ARC
336 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900337 @ServiceThreadOnly
Jungshik Jang092b4452014-06-11 15:19:17 +0900338 void setAudioReturnChannel(boolean enabled) {
339 assertRunOnServiceThread();
340 nativeSetAudioReturnChannel(mNativePtr, enabled);
341 }
342
343 /**
344 * Return the connection status of the specified port
345 *
346 * @param port port number to check connection status
347 * @return true if connected; otherwise, return false
348 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900349 @ServiceThreadOnly
Jungshik Jang092b4452014-06-11 15:19:17 +0900350 boolean isConnected(int port) {
351 assertRunOnServiceThread();
352 return nativeIsConnected(mNativePtr, port);
353 }
354
355 /**
Jungshik Jang02bb4262014-05-23 16:48:31 +0900356 * Poll all remote devices. It sends &lt;Polling Message&gt; to all remote
357 * devices.
358 *
359 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
360 *
361 * @param callback an interface used to get a list of all remote devices' address
Jungshik Jang1de51422014-07-03 11:14:26 +0900362 * @param sourceAddress a logical address of source device where sends polling message
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900363 * @param pickStrategy strategy how to pick polling candidates
Jungshik Jang02bb4262014-05-23 16:48:31 +0900364 * @param retryCount the number of retry used to send polling message to remote devices
365 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900366 @ServiceThreadOnly
Jungshik Jang1de51422014-07-03 11:14:26 +0900367 void pollDevices(DevicePollingCallback callback, int sourceAddress, int pickStrategy,
368 int retryCount) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900369 assertRunOnServiceThread();
Jungshik Jang02bb4262014-05-23 16:48:31 +0900370
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900371 // Extract polling candidates. No need to poll against local devices.
372 List<Integer> pollingCandidates = pickPollCandidates(pickStrategy);
Jungshik Jang1de51422014-07-03 11:14:26 +0900373 runDevicePolling(sourceAddress, pollingCandidates, retryCount, callback);
Jungshik Jang02bb4262014-05-23 16:48:31 +0900374 }
375
Jungshik Jangcc5ef8c2014-05-27 13:27:36 +0900376 /**
377 * Return a list of all {@link HdmiCecLocalDevice}s.
378 *
379 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
380 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900381 @ServiceThreadOnly
Jungshik Jangcc5ef8c2014-05-27 13:27:36 +0900382 List<HdmiCecLocalDevice> getLocalDeviceList() {
383 assertRunOnServiceThread();
Jungshik Jang79c58a42014-06-16 16:45:36 +0900384 return HdmiUtils.sparseArrayToList(mLocalDevices);
Jungshik Jangcc5ef8c2014-05-27 13:27:36 +0900385 }
386
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900387 private List<Integer> pickPollCandidates(int pickStrategy) {
Jungshik Jang3ecdd8322014-06-17 14:04:38 +0900388 int strategy = pickStrategy & HdmiConstants.POLL_STRATEGY_MASK;
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900389 Predicate<Integer> pickPredicate = null;
390 switch (strategy) {
Jungshik Jang3ecdd8322014-06-17 14:04:38 +0900391 case HdmiConstants.POLL_STRATEGY_SYSTEM_AUDIO:
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900392 pickPredicate = mSystemAudioAddressPredicate;
393 break;
Jungshik Jang3ecdd8322014-06-17 14:04:38 +0900394 case HdmiConstants.POLL_STRATEGY_REMOTES_DEVICES:
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900395 default: // The default is POLL_STRATEGY_REMOTES_DEVICES.
396 pickPredicate = mRemoteDeviceAddressPredicate;
397 break;
398 }
399
Jungshik Jang3ecdd8322014-06-17 14:04:38 +0900400 int iterationStrategy = pickStrategy & HdmiConstants.POLL_ITERATION_STRATEGY_MASK;
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900401 ArrayList<Integer> pollingCandidates = new ArrayList<>();
402 switch (iterationStrategy) {
Jungshik Jang3ecdd8322014-06-17 14:04:38 +0900403 case HdmiConstants.POLL_ITERATION_IN_ORDER:
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900404 for (int i = HdmiCec.ADDR_TV; i <= HdmiCec.ADDR_SPECIFIC_USE; ++i) {
405 if (pickPredicate.apply(i)) {
406 pollingCandidates.add(i);
407 }
408 }
409 break;
Jungshik Jang3ecdd8322014-06-17 14:04:38 +0900410 case HdmiConstants.POLL_ITERATION_REVERSE_ORDER:
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900411 default: // The default is reverse order.
412 for (int i = HdmiCec.ADDR_SPECIFIC_USE; i >= HdmiCec.ADDR_TV; --i) {
413 if (pickPredicate.apply(i)) {
414 pollingCandidates.add(i);
415 }
416 }
417 break;
418 }
419 return pollingCandidates;
420 }
421
Jungshik Janga5b74142014-06-23 18:03:10 +0900422 @ServiceThreadOnly
Jungshik Jang02bb4262014-05-23 16:48:31 +0900423 private boolean isAllocatedLocalDeviceAddress(int address) {
Jungshik Janga5b74142014-06-23 18:03:10 +0900424 assertRunOnServiceThread();
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +0900425 for (int i = 0; i < mLocalDevices.size(); ++i) {
426 if (mLocalDevices.valueAt(i).isAddressOf(address)) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900427 return true;
428 }
429 }
430 return false;
431 }
432
Jungshik Janga5b74142014-06-23 18:03:10 +0900433 @ServiceThreadOnly
Jungshik Jang1de51422014-07-03 11:14:26 +0900434 private void runDevicePolling(final int sourceAddress,
435 final List<Integer> candidates, final int retryCount,
Jungshik Jang02bb4262014-05-23 16:48:31 +0900436 final DevicePollingCallback callback) {
437 assertRunOnServiceThread();
438 runOnIoThread(new Runnable() {
439 @Override
440 public void run() {
441 final ArrayList<Integer> allocated = new ArrayList<>();
442 for (Integer address : candidates) {
Jungshik Jang1de51422014-07-03 11:14:26 +0900443 if (sendPollMessage(sourceAddress, address, retryCount)) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900444 allocated.add(address);
445 }
446 }
447 if (callback != null) {
448 runOnServiceThread(new Runnable() {
449 @Override
450 public void run() {
451 callback.onPollingFinished(allocated);
452 }
453 });
454 }
455 }
456 });
457 }
458
Jungshik Janga5b74142014-06-23 18:03:10 +0900459 @IoThreadOnly
Jungshik Jang1de51422014-07-03 11:14:26 +0900460 private boolean sendPollMessage(int sourceAddress, int destinationAddress, int retryCount) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900461 assertRunOnIoThread();
462 for (int i = 0; i < retryCount; ++i) {
Jungshik Jang1de51422014-07-03 11:14:26 +0900463 // <Polling Message> is a message which has empty body.
Jungshik Jang02bb4262014-05-23 16:48:31 +0900464 // If sending <Polling Message> failed (NAK), it becomes
465 // new logical address for the device because no device uses
466 // it as logical address of the device.
Jungshik Jang1de51422014-07-03 11:14:26 +0900467 if (nativeSendCecCommand(mNativePtr, sourceAddress, destinationAddress, EMPTY_BODY)
Jungshik Jang3ecdd8322014-06-17 14:04:38 +0900468 == HdmiConstants.SEND_RESULT_SUCCESS) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900469 return true;
470 }
471 }
472 return false;
473 }
474
475 private void assertRunOnIoThread() {
476 if (Looper.myLooper() != mIoHandler.getLooper()) {
477 throw new IllegalStateException("Should run on io thread.");
478 }
479 }
480
481 private void assertRunOnServiceThread() {
482 if (Looper.myLooper() != mControlHandler.getLooper()) {
483 throw new IllegalStateException("Should run on service thread.");
484 }
485 }
486
487 // Run a Runnable on IO thread.
488 // It should be careful to access member variables on IO thread because
489 // it can be accessed from system thread as well.
Jungshik Jangd643f762014-05-22 19:28:09 +0900490 private void runOnIoThread(Runnable runnable) {
491 mIoHandler.post(runnable);
492 }
493
494 private void runOnServiceThread(Runnable runnable) {
495 mControlHandler.post(runnable);
496 }
497
Jungshik Janga1fa91f2014-05-08 20:56:41 +0900498 private boolean isAcceptableAddress(int address) {
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900499 // Can access command targeting devices available in local device or broadcast command.
500 if (address == HdmiCec.ADDR_BROADCAST) {
501 return true;
502 }
Jungshik Jang02bb4262014-05-23 16:48:31 +0900503 return isAllocatedLocalDeviceAddress(address);
Jungshik Janga1fa91f2014-05-08 20:56:41 +0900504 }
505
Jungshik Janga5b74142014-06-23 18:03:10 +0900506 @ServiceThreadOnly
Jungshik Jange9c77c82014-04-24 20:30:09 +0900507 private void onReceiveCommand(HdmiCecMessage message) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900508 assertRunOnServiceThread();
Jungshik Jang4085d0e2014-05-27 19:52:39 +0900509 if (isAcceptableAddress(message.getDestination())
510 && mService.handleCecCommand(message)) {
Jungshik Janga1fa91f2014-05-08 20:56:41 +0900511 return;
512 }
Jungshik Jange9c77c82014-04-24 20:30:09 +0900513
Jinsuk Kim3a2f7432014-05-29 06:52:45 +0900514 if (message.getDestination() != HdmiCec.ADDR_BROADCAST) {
515 int sourceAddress = message.getDestination();
516 // Reply <Feature Abort> to initiator (source) for all requests.
517 HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildFeatureAbortCommand(
518 sourceAddress, message.getSource(), message.getOpcode(),
Yuncheol Heo63a2e062014-05-27 23:06:01 +0900519 HdmiConstants.ABORT_REFUSED);
Jungshik Janga5b74142014-06-23 18:03:10 +0900520 sendCommand(cecMessage);
Jinsuk Kim3a2f7432014-05-29 06:52:45 +0900521 }
Jungshik Jange9c77c82014-04-24 20:30:09 +0900522 }
523
Jungshik Janga5b74142014-06-23 18:03:10 +0900524 @ServiceThreadOnly
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900525 void sendCommand(HdmiCecMessage cecMessage) {
Jungshik Janga5b74142014-06-23 18:03:10 +0900526 assertRunOnServiceThread();
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900527 sendCommand(cecMessage, null);
528 }
529
Jungshik Janga5b74142014-06-23 18:03:10 +0900530 @ServiceThreadOnly
Jungshik Jangd643f762014-05-22 19:28:09 +0900531 void sendCommand(final HdmiCecMessage cecMessage,
532 final HdmiControlService.SendMessageCallback callback) {
Jungshik Janga5b74142014-06-23 18:03:10 +0900533 assertRunOnServiceThread();
Jungshik Jangd643f762014-05-22 19:28:09 +0900534 runOnIoThread(new Runnable() {
535 @Override
536 public void run() {
537 byte[] body = buildBody(cecMessage.getOpcode(), cecMessage.getParams());
538 final int error = nativeSendCecCommand(mNativePtr, cecMessage.getSource(),
539 cecMessage.getDestination(), body);
Jungshik Jang3ecdd8322014-06-17 14:04:38 +0900540 if (error != HdmiConstants.SEND_RESULT_SUCCESS) {
Yuncheol Heoece603b2014-05-23 20:10:19 +0900541 Slog.w(TAG, "Failed to send " + cecMessage);
542 }
Jungshik Jangd643f762014-05-22 19:28:09 +0900543 if (callback != null) {
544 runOnServiceThread(new Runnable() {
545 @Override
546 public void run() {
547 callback.onSendCompleted(error);
548 }
549 });
550 }
551 }
552 });
Jungshik Jange9c77c82014-04-24 20:30:09 +0900553 }
554
Jungshik Jang0792d372014-04-23 17:57:26 +0900555 /**
Jungshik Jange9c77c82014-04-24 20:30:09 +0900556 * Called by native when incoming CEC message arrived.
Jungshik Jang0792d372014-04-23 17:57:26 +0900557 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900558 @ServiceThreadOnly
Jungshik Jange9c77c82014-04-24 20:30:09 +0900559 private void handleIncomingCecCommand(int srcAddress, int dstAddress, byte[] body) {
Jungshik Jang4085d0e2014-05-27 19:52:39 +0900560 assertRunOnServiceThread();
561 onReceiveCommand(HdmiCecMessageBuilder.of(srcAddress, dstAddress, body));
Jungshik Jange9c77c82014-04-24 20:30:09 +0900562 }
563
564 /**
565 * Called by native when a hotplug event issues.
566 */
567 private void handleHotplug(boolean connected) {
Jungshik Jang67ea5212014-05-15 14:05:24 +0900568 // TODO: once add port number to cec HAL interface, pass port number
569 // to the service.
570 mService.onHotplug(0, connected);
Jungshik Jang0792d372014-04-23 17:57:26 +0900571 }
572
Jungshik Jang4085d0e2014-05-27 19:52:39 +0900573 private static native long nativeInit(HdmiCecController handler, MessageQueue messageQueue);
Jungshik Janga9095ba2014-05-02 13:06:22 +0900574 private static native int nativeSendCecCommand(long controllerPtr, int srcAddress,
Jungshik Jange9c77c82014-04-24 20:30:09 +0900575 int dstAddress, byte[] body);
Jungshik Janga9095ba2014-05-02 13:06:22 +0900576 private static native int nativeAddLogicalAddress(long controllerPtr, int logicalAddress);
577 private static native void nativeClearLogicalAddress(long controllerPtr);
578 private static native int nativeGetPhysicalAddress(long controllerPtr);
579 private static native int nativeGetVersion(long controllerPtr);
580 private static native int nativeGetVendorId(long controllerPtr);
Jinsuk Kim0340bbc2014-06-05 11:07:47 +0900581 private static native HdmiPortInfo[] nativeGetPortInfos(long controllerPtr);
Jungshik Jang092b4452014-06-11 15:19:17 +0900582 private static native void nativeSetOption(long controllerPtr, int flag, int value);
583 private static native void nativeSetAudioReturnChannel(long controllerPtr, boolean flag);
584 private static native boolean nativeIsConnected(long controllerPtr, int port);
Jungshik Jang0792d372014-04-23 17:57:26 +0900585}