blob: 4fbe84515ecdf77cd5c56ba8bf329c85e0466952 [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 Jang02bb4262014-05-23 16:48:31 +0900203 if (!sendPollMessage(curAddress, RETRY_COUNT_FOR_LOGICAL_ADDRESS_ALLOCATION)) {
Jungshik Jangd643f762014-05-22 19:28:09 +0900204 logicalAddress = curAddress;
205 break;
206 }
207 }
208 }
209
210 final int assignedAddress = logicalAddress;
211 if (callback != null) {
212 runOnServiceThread(new Runnable() {
213 @Override
214 public void run() {
215 callback.onAllocated(deviceType, assignedAddress);
216 }
217 });
218 }
Jungshik Jang3f74ab02014-04-30 14:31:02 +0900219 }
220
Jungshik Jange9c77c82014-04-24 20:30:09 +0900221 private static byte[] buildBody(int opcode, byte[] params) {
222 byte[] body = new byte[params.length + 1];
223 body[0] = (byte) opcode;
224 System.arraycopy(params, 0, body, 1, params.length);
225 return body;
226 }
Jungshik Jang0792d372014-04-23 17:57:26 +0900227
Jungshik Jang7d9a8432014-04-29 15:12:43 +0900228
Jinsuk Kim0340bbc2014-06-05 11:07:47 +0900229 HdmiPortInfo[] getPortInfos() {
230 return nativeGetPortInfos(mNativePtr);
231 }
232
Jungshik Janga9095ba2014-05-02 13:06:22 +0900233 /**
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +0900234 * Return the locally hosted logical device of a given type.
235 *
236 * @param deviceType logical device type
237 * @return {@link HdmiCecLocalDevice} instance if the instance of the type is available;
238 * otherwise null.
239 */
240 HdmiCecLocalDevice getLocalDevice(int deviceType) {
241 return mLocalDevices.get(deviceType);
242 }
243
244 /**
Jungshik Janga9095ba2014-05-02 13:06:22 +0900245 * Add a new logical address to the device. Device's HW should be notified
246 * when a new logical address is assigned to a device, so that it can accept
247 * a command having available destinations.
248 *
249 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
250 *
251 * @param newLogicalAddress a logical address to be added
252 * @return 0 on success. Otherwise, returns negative value
253 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900254 @ServiceThreadOnly
Jungshik Janga9095ba2014-05-02 13:06:22 +0900255 int addLogicalAddress(int newLogicalAddress) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900256 assertRunOnServiceThread();
Jungshik Janga9095ba2014-05-02 13:06:22 +0900257 if (HdmiCec.isValidAddress(newLogicalAddress)) {
258 return nativeAddLogicalAddress(mNativePtr, newLogicalAddress);
259 } else {
260 return -1;
261 }
262 }
263
264 /**
265 * Clear all logical addresses registered in the device.
266 *
267 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
268 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900269 @ServiceThreadOnly
Jungshik Janga9095ba2014-05-02 13:06:22 +0900270 void clearLogicalAddress() {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900271 assertRunOnServiceThread();
Jungshik Janga1fa91f2014-05-08 20:56:41 +0900272 // TODO: consider to backup logical address so that new logical address
273 // allocation can use it as preferred address.
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
280 /**
281 * Return the physical address of the device.
282 *
283 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
284 *
285 * @return CEC physical address of the device. The range of success address
286 * is between 0x0000 and 0xFFFF. If failed it returns -1
287 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900288 @ServiceThreadOnly
Jungshik Janga9095ba2014-05-02 13:06:22 +0900289 int getPhysicalAddress() {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900290 assertRunOnServiceThread();
Jungshik Janga9095ba2014-05-02 13:06:22 +0900291 return nativeGetPhysicalAddress(mNativePtr);
292 }
293
294 /**
295 * Return CEC version of the device.
296 *
297 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
298 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900299 @ServiceThreadOnly
Jungshik Janga9095ba2014-05-02 13:06:22 +0900300 int getVersion() {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900301 assertRunOnServiceThread();
Jungshik Janga9095ba2014-05-02 13:06:22 +0900302 return nativeGetVersion(mNativePtr);
303 }
304
305 /**
306 * Return vendor id of the device.
307 *
308 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
309 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900310 @ServiceThreadOnly
Jungshik Janga9095ba2014-05-02 13:06:22 +0900311 int getVendorId() {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900312 assertRunOnServiceThread();
Jungshik Janga9095ba2014-05-02 13:06:22 +0900313 return nativeGetVendorId(mNativePtr);
314 }
315
Jungshik Jang02bb4262014-05-23 16:48:31 +0900316 /**
Jungshik Jang092b4452014-06-11 15:19:17 +0900317 * Pass a option to CEC HAL.
318 *
319 * @param flag a key of option. For more details, look at
320 * {@link HdmiConstants#FLAG_HDMI_OPTION_WAKEUP} to
321 * {@link HdmiConstants#FLAG_HDMI_OPTION_SYSTEM_CEC_CONTROL}
322 * @param value a value of option. Actual value varies flag. For more
323 * details, look at description of flags
324 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900325 @ServiceThreadOnly
Jungshik Jang092b4452014-06-11 15:19:17 +0900326 void setOption(int flag, int value) {
327 assertRunOnServiceThread();
328 nativeSetOption(mNativePtr, flag, value);
329 }
330
331 /**
332 * Configure ARC circuit in the hardware logic to start or stop the feature.
333 *
334 * @param enabled whether to enable/disable ARC
335 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900336 @ServiceThreadOnly
Jungshik Jang092b4452014-06-11 15:19:17 +0900337 void setAudioReturnChannel(boolean enabled) {
338 assertRunOnServiceThread();
339 nativeSetAudioReturnChannel(mNativePtr, enabled);
340 }
341
342 /**
343 * Return the connection status of the specified port
344 *
345 * @param port port number to check connection status
346 * @return true if connected; otherwise, return false
347 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900348 @ServiceThreadOnly
Jungshik Jang092b4452014-06-11 15:19:17 +0900349 boolean isConnected(int port) {
350 assertRunOnServiceThread();
351 return nativeIsConnected(mNativePtr, port);
352 }
353
354 /**
Jungshik Jang02bb4262014-05-23 16:48:31 +0900355 * Poll all remote devices. It sends &lt;Polling Message&gt; to all remote
356 * devices.
357 *
358 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
359 *
360 * @param callback an interface used to get a list of all remote devices' address
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900361 * @param pickStrategy strategy how to pick polling candidates
Jungshik Jang02bb4262014-05-23 16:48:31 +0900362 * @param retryCount the number of retry used to send polling message to remote devices
363 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900364 @ServiceThreadOnly
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900365 void pollDevices(DevicePollingCallback callback, int pickStrategy, int retryCount) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900366 assertRunOnServiceThread();
Jungshik Jang02bb4262014-05-23 16:48:31 +0900367
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900368 // Extract polling candidates. No need to poll against local devices.
369 List<Integer> pollingCandidates = pickPollCandidates(pickStrategy);
Jungshik Jang02bb4262014-05-23 16:48:31 +0900370 runDevicePolling(pollingCandidates, retryCount, callback);
371 }
372
Jungshik Jangcc5ef8c2014-05-27 13:27:36 +0900373 /**
374 * Return a list of all {@link HdmiCecLocalDevice}s.
375 *
376 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
377 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900378 @ServiceThreadOnly
Jungshik Jangcc5ef8c2014-05-27 13:27:36 +0900379 List<HdmiCecLocalDevice> getLocalDeviceList() {
380 assertRunOnServiceThread();
Jungshik Jang79c58a42014-06-16 16:45:36 +0900381 return HdmiUtils.sparseArrayToList(mLocalDevices);
Jungshik Jangcc5ef8c2014-05-27 13:27:36 +0900382 }
383
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900384 private List<Integer> pickPollCandidates(int pickStrategy) {
Jungshik Jang3ecdd8322014-06-17 14:04:38 +0900385 int strategy = pickStrategy & HdmiConstants.POLL_STRATEGY_MASK;
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900386 Predicate<Integer> pickPredicate = null;
387 switch (strategy) {
Jungshik Jang3ecdd8322014-06-17 14:04:38 +0900388 case HdmiConstants.POLL_STRATEGY_SYSTEM_AUDIO:
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900389 pickPredicate = mSystemAudioAddressPredicate;
390 break;
Jungshik Jang3ecdd8322014-06-17 14:04:38 +0900391 case HdmiConstants.POLL_STRATEGY_REMOTES_DEVICES:
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900392 default: // The default is POLL_STRATEGY_REMOTES_DEVICES.
393 pickPredicate = mRemoteDeviceAddressPredicate;
394 break;
395 }
396
Jungshik Jang3ecdd8322014-06-17 14:04:38 +0900397 int iterationStrategy = pickStrategy & HdmiConstants.POLL_ITERATION_STRATEGY_MASK;
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900398 ArrayList<Integer> pollingCandidates = new ArrayList<>();
399 switch (iterationStrategy) {
Jungshik Jang3ecdd8322014-06-17 14:04:38 +0900400 case HdmiConstants.POLL_ITERATION_IN_ORDER:
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900401 for (int i = HdmiCec.ADDR_TV; i <= HdmiCec.ADDR_SPECIFIC_USE; ++i) {
402 if (pickPredicate.apply(i)) {
403 pollingCandidates.add(i);
404 }
405 }
406 break;
Jungshik Jang3ecdd8322014-06-17 14:04:38 +0900407 case HdmiConstants.POLL_ITERATION_REVERSE_ORDER:
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900408 default: // The default is reverse order.
409 for (int i = HdmiCec.ADDR_SPECIFIC_USE; i >= HdmiCec.ADDR_TV; --i) {
410 if (pickPredicate.apply(i)) {
411 pollingCandidates.add(i);
412 }
413 }
414 break;
415 }
416 return pollingCandidates;
417 }
418
Jungshik Janga5b74142014-06-23 18:03:10 +0900419 @ServiceThreadOnly
Jungshik Jang02bb4262014-05-23 16:48:31 +0900420 private boolean isAllocatedLocalDeviceAddress(int address) {
Jungshik Janga5b74142014-06-23 18:03:10 +0900421 assertRunOnServiceThread();
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +0900422 for (int i = 0; i < mLocalDevices.size(); ++i) {
423 if (mLocalDevices.valueAt(i).isAddressOf(address)) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900424 return true;
425 }
426 }
427 return false;
428 }
429
Jungshik Janga5b74142014-06-23 18:03:10 +0900430 @ServiceThreadOnly
Jungshik Jang02bb4262014-05-23 16:48:31 +0900431 private void runDevicePolling(final List<Integer> candidates, final int retryCount,
432 final DevicePollingCallback callback) {
433 assertRunOnServiceThread();
434 runOnIoThread(new Runnable() {
435 @Override
436 public void run() {
437 final ArrayList<Integer> allocated = new ArrayList<>();
438 for (Integer address : candidates) {
439 if (sendPollMessage(address, retryCount)) {
440 allocated.add(address);
441 }
442 }
443 if (callback != null) {
444 runOnServiceThread(new Runnable() {
445 @Override
446 public void run() {
447 callback.onPollingFinished(allocated);
448 }
449 });
450 }
451 }
452 });
453 }
454
Jungshik Janga5b74142014-06-23 18:03:10 +0900455 @IoThreadOnly
Jungshik Jang02bb4262014-05-23 16:48:31 +0900456 private boolean sendPollMessage(int address, int retryCount) {
457 assertRunOnIoThread();
458 for (int i = 0; i < retryCount; ++i) {
459 // <Polling Message> is a message which has empty body and
460 // uses same address for both source and destination address.
461 // If sending <Polling Message> failed (NAK), it becomes
462 // new logical address for the device because no device uses
463 // it as logical address of the device.
464 if (nativeSendCecCommand(mNativePtr, address, address, EMPTY_BODY)
Jungshik Jang3ecdd8322014-06-17 14:04:38 +0900465 == HdmiConstants.SEND_RESULT_SUCCESS) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900466 return true;
467 }
468 }
469 return false;
470 }
471
472 private void assertRunOnIoThread() {
473 if (Looper.myLooper() != mIoHandler.getLooper()) {
474 throw new IllegalStateException("Should run on io thread.");
475 }
476 }
477
478 private void assertRunOnServiceThread() {
479 if (Looper.myLooper() != mControlHandler.getLooper()) {
480 throw new IllegalStateException("Should run on service thread.");
481 }
482 }
483
484 // Run a Runnable on IO thread.
485 // It should be careful to access member variables on IO thread because
486 // it can be accessed from system thread as well.
Jungshik Jangd643f762014-05-22 19:28:09 +0900487 private void runOnIoThread(Runnable runnable) {
488 mIoHandler.post(runnable);
489 }
490
491 private void runOnServiceThread(Runnable runnable) {
492 mControlHandler.post(runnable);
493 }
494
Jungshik Janga1fa91f2014-05-08 20:56:41 +0900495 private boolean isAcceptableAddress(int address) {
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900496 // Can access command targeting devices available in local device or broadcast command.
497 if (address == HdmiCec.ADDR_BROADCAST) {
498 return true;
499 }
Jungshik Jang02bb4262014-05-23 16:48:31 +0900500 return isAllocatedLocalDeviceAddress(address);
Jungshik Janga1fa91f2014-05-08 20:56:41 +0900501 }
502
Jungshik Janga5b74142014-06-23 18:03:10 +0900503 @ServiceThreadOnly
Jungshik Jange9c77c82014-04-24 20:30:09 +0900504 private void onReceiveCommand(HdmiCecMessage message) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900505 assertRunOnServiceThread();
Jungshik Jang4085d0e2014-05-27 19:52:39 +0900506 if (isAcceptableAddress(message.getDestination())
507 && mService.handleCecCommand(message)) {
Jungshik Janga1fa91f2014-05-08 20:56:41 +0900508 return;
509 }
Jungshik Jange9c77c82014-04-24 20:30:09 +0900510
Jinsuk Kim3a2f7432014-05-29 06:52:45 +0900511 if (message.getDestination() != HdmiCec.ADDR_BROADCAST) {
512 int sourceAddress = message.getDestination();
513 // Reply <Feature Abort> to initiator (source) for all requests.
514 HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildFeatureAbortCommand(
515 sourceAddress, message.getSource(), message.getOpcode(),
Yuncheol Heo63a2e062014-05-27 23:06:01 +0900516 HdmiConstants.ABORT_REFUSED);
Jungshik Janga5b74142014-06-23 18:03:10 +0900517 sendCommand(cecMessage);
Jinsuk Kim3a2f7432014-05-29 06:52:45 +0900518 }
Jungshik Jange9c77c82014-04-24 20:30:09 +0900519 }
520
Jungshik Janga5b74142014-06-23 18:03:10 +0900521 @ServiceThreadOnly
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900522 void sendCommand(HdmiCecMessage cecMessage) {
Jungshik Janga5b74142014-06-23 18:03:10 +0900523 assertRunOnServiceThread();
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900524 sendCommand(cecMessage, null);
525 }
526
Jungshik Janga5b74142014-06-23 18:03:10 +0900527 @ServiceThreadOnly
Jungshik Jangd643f762014-05-22 19:28:09 +0900528 void sendCommand(final HdmiCecMessage cecMessage,
529 final HdmiControlService.SendMessageCallback callback) {
Jungshik Janga5b74142014-06-23 18:03:10 +0900530 assertRunOnServiceThread();
Jungshik Jangd643f762014-05-22 19:28:09 +0900531 runOnIoThread(new Runnable() {
532 @Override
533 public void run() {
534 byte[] body = buildBody(cecMessage.getOpcode(), cecMessage.getParams());
535 final int error = nativeSendCecCommand(mNativePtr, cecMessage.getSource(),
536 cecMessage.getDestination(), body);
Jungshik Jang3ecdd8322014-06-17 14:04:38 +0900537 if (error != HdmiConstants.SEND_RESULT_SUCCESS) {
Yuncheol Heoece603b2014-05-23 20:10:19 +0900538 Slog.w(TAG, "Failed to send " + cecMessage);
539 }
Jungshik Jangd643f762014-05-22 19:28:09 +0900540 if (callback != null) {
541 runOnServiceThread(new Runnable() {
542 @Override
543 public void run() {
544 callback.onSendCompleted(error);
545 }
546 });
547 }
548 }
549 });
Jungshik Jange9c77c82014-04-24 20:30:09 +0900550 }
551
Jungshik Jang0792d372014-04-23 17:57:26 +0900552 /**
Jungshik Jange9c77c82014-04-24 20:30:09 +0900553 * Called by native when incoming CEC message arrived.
Jungshik Jang0792d372014-04-23 17:57:26 +0900554 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900555 @ServiceThreadOnly
Jungshik Jange9c77c82014-04-24 20:30:09 +0900556 private void handleIncomingCecCommand(int srcAddress, int dstAddress, byte[] body) {
Jungshik Jang4085d0e2014-05-27 19:52:39 +0900557 assertRunOnServiceThread();
558 onReceiveCommand(HdmiCecMessageBuilder.of(srcAddress, dstAddress, body));
Jungshik Jange9c77c82014-04-24 20:30:09 +0900559 }
560
561 /**
562 * Called by native when a hotplug event issues.
563 */
564 private void handleHotplug(boolean connected) {
Jungshik Jang67ea5212014-05-15 14:05:24 +0900565 // TODO: once add port number to cec HAL interface, pass port number
566 // to the service.
567 mService.onHotplug(0, connected);
Jungshik Jang0792d372014-04-23 17:57:26 +0900568 }
569
Jungshik Jang4085d0e2014-05-27 19:52:39 +0900570 private static native long nativeInit(HdmiCecController handler, MessageQueue messageQueue);
Jungshik Janga9095ba2014-05-02 13:06:22 +0900571 private static native int nativeSendCecCommand(long controllerPtr, int srcAddress,
Jungshik Jange9c77c82014-04-24 20:30:09 +0900572 int dstAddress, byte[] body);
Jungshik Janga9095ba2014-05-02 13:06:22 +0900573 private static native int nativeAddLogicalAddress(long controllerPtr, int logicalAddress);
574 private static native void nativeClearLogicalAddress(long controllerPtr);
575 private static native int nativeGetPhysicalAddress(long controllerPtr);
576 private static native int nativeGetVersion(long controllerPtr);
577 private static native int nativeGetVendorId(long controllerPtr);
Jinsuk Kim0340bbc2014-06-05 11:07:47 +0900578 private static native HdmiPortInfo[] nativeGetPortInfos(long controllerPtr);
Jungshik Jang092b4452014-06-11 15:19:17 +0900579 private static native void nativeSetOption(long controllerPtr, int flag, int value);
580 private static native void nativeSetAudioReturnChannel(long controllerPtr, boolean flag);
581 private static native boolean nativeIsConnected(long controllerPtr, int port);
Jungshik Jang0792d372014-04-23 17:57:26 +0900582}