blob: cbccc1dd31b17f484525dcfcfea376e405ca57f1 [file] [log] [blame]
Jungshik Jang0792d372014-04-23 17:57:26 +09001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.hdmi;
18
Jinsuk Kim0340bbc2014-06-05 11:07:47 +090019import android.hardware.hdmi.HdmiPortInfo;
Jungshik Jang0792d372014-04-23 17:57:26 +090020import android.os.Handler;
Jungshik Jang02bb4262014-05-23 16:48:31 +090021import android.os.Looper;
Jungshik Jang4085d0e2014-05-27 19:52:39 +090022import android.os.MessageQueue;
Yuncheol Heoece603b2014-05-23 20:10:19 +090023import android.util.Slog;
Jungshik Jang7d9a8432014-04-29 15:12:43 +090024import android.util.SparseArray;
Jungshik Jange9c77c82014-04-24 20:30:09 +090025
Jungshik Jang0f8b4b72014-05-28 17:58:58 +090026import com.android.internal.util.Predicate;
Jungshik Janga5b74142014-06-23 18:03:10 +090027import com.android.server.hdmi.HdmiAnnotations.IoThreadOnly;
28import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
Jungshik Jang02bb4262014-05-23 16:48:31 +090029import com.android.server.hdmi.HdmiControlService.DevicePollingCallback;
30
Jungshik Jang3f74ab02014-04-30 14:31:02 +090031import libcore.util.EmptyArray;
32
Jungshik Jang7d9a8432014-04-29 15:12:43 +090033import java.util.ArrayList;
Jungshik Jang7d9a8432014-04-29 15:12:43 +090034import java.util.List;
Jungshik Jang0792d372014-04-23 17:57:26 +090035
36/**
37 * Manages HDMI-CEC command and behaviors. It converts user's command into CEC command
38 * and pass it to CEC HAL so that it sends message to other device. For incoming
39 * message it translates the message and delegates it to proper module.
40 *
Jungshik Jang02bb4262014-05-23 16:48:31 +090041 * <p>It should be careful to access member variables on IO thread because
42 * it can be accessed from system thread as well.
43 *
Jungshik Jang0792d372014-04-23 17:57:26 +090044 * <p>It can be created only by {@link HdmiCecController#create}
45 *
46 * <p>Declared as package-private, accessed by {@link HdmiControlService} only.
47 */
Jungshik Janga9095ba2014-05-02 13:06:22 +090048final class HdmiCecController {
Jungshik Jang0792d372014-04-23 17:57:26 +090049 private static final String TAG = "HdmiCecController";
50
Jungshik Jang3ee65722014-06-03 16:22:30 +090051 /**
52 * Interface to report allocated logical address.
53 */
54 interface AllocateAddressCallback {
55 /**
56 * Called when a new logical address is allocated.
57 *
58 * @param deviceType requested device type to allocate logical address
59 * @param logicalAddress allocated logical address. If it is
Jungshik Jang42230722014-07-07 17:40:25 +090060 * {@link Constants#ADDR_UNREGISTERED}, it means that
Jungshik Jang3ee65722014-06-03 16:22:30 +090061 * it failed to allocate logical address for the given device type
62 */
63 void onAllocated(int deviceType, int logicalAddress);
64 }
65
Jungshik Jang3f74ab02014-04-30 14:31:02 +090066 private static final byte[] EMPTY_BODY = EmptyArray.BYTE;
67
Jungshik Jange9c77c82014-04-24 20:30:09 +090068 // A message to pass cec send command to IO looper.
69 private static final int MSG_SEND_CEC_COMMAND = 1;
Jungshik Jang3f74ab02014-04-30 14:31:02 +090070 // A message to delegate logical allocation to IO looper.
71 private static final int MSG_ALLOCATE_LOGICAL_ADDRESS = 2;
Jungshik Jange9c77c82014-04-24 20:30:09 +090072
73 // Message types to handle incoming message in main service looper.
74 private final static int MSG_RECEIVE_CEC_COMMAND = 1;
Jungshik Jang3f74ab02014-04-30 14:31:02 +090075 // A message to report allocated logical address to main control looper.
76 private final static int MSG_REPORT_LOGICAL_ADDRESS = 2;
Jungshik Jange9c77c82014-04-24 20:30:09 +090077
Jungshik Jang3f74ab02014-04-30 14:31:02 +090078 private static final int NUM_LOGICAL_ADDRESS = 16;
79
Jungshik Jang0f8b4b72014-05-28 17:58:58 +090080 // Predicate for whether the given logical address is remote device's one or not.
81 private final Predicate<Integer> mRemoteDeviceAddressPredicate = new Predicate<Integer>() {
82 @Override
83 public boolean apply(Integer address) {
84 return !isAllocatedLocalDeviceAddress(address);
85 }
86 };
87
88 // Predicate whether the given logical address is system audio's one or not
89 private final Predicate<Integer> mSystemAudioAddressPredicate = new Predicate<Integer>() {
90 @Override
91 public boolean apply(Integer address) {
Jinsuk Kimc0c20d02014-07-04 14:34:31 +090092 return HdmiUtils.getTypeFromAddress(address) == Constants.ADDR_AUDIO_SYSTEM;
Jungshik Jang0f8b4b72014-05-28 17:58:58 +090093 }
94 };
95
Jungshik Jang0792d372014-04-23 17:57:26 +090096 // Handler instance to process synchronous I/O (mainly send) message.
97 private Handler mIoHandler;
98
99 // Handler instance to process various messages coming from other CEC
100 // device or issued by internal state change.
Jungshik Jange9c77c82014-04-24 20:30:09 +0900101 private Handler mControlHandler;
Jungshik Jang0792d372014-04-23 17:57:26 +0900102
103 // Stores the pointer to the native implementation of the service that
104 // interacts with HAL.
Jungshik Jang02bb4262014-05-23 16:48:31 +0900105 private volatile long mNativePtr;
Jungshik Jang0792d372014-04-23 17:57:26 +0900106
Jungshik Janga1fa91f2014-05-08 20:56:41 +0900107 private HdmiControlService mService;
108
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +0900109 // Stores the local CEC devices in the system. Device type is used for key.
110 private final SparseArray<HdmiCecLocalDevice> mLocalDevices = new SparseArray<>();
Jungshik Jang7d9a8432014-04-29 15:12:43 +0900111
Jungshik Jang0792d372014-04-23 17:57:26 +0900112 // Private constructor. Use HdmiCecController.create().
113 private HdmiCecController() {
114 }
115
116 /**
117 * A factory method to get {@link HdmiCecController}. If it fails to initialize
118 * inner device or has no device it will return {@code null}.
119 *
120 * <p>Declared as package-private, accessed by {@link HdmiControlService} only.
Jungshik Jange9c77c82014-04-24 20:30:09 +0900121 * @param service {@link HdmiControlService} instance used to create internal handler
122 * and to pass callback for incoming message or event.
Jungshik Jang0792d372014-04-23 17:57:26 +0900123 * @return {@link HdmiCecController} if device is initialized successfully. Otherwise,
124 * returns {@code null}.
125 */
Jungshik Jange9c77c82014-04-24 20:30:09 +0900126 static HdmiCecController create(HdmiControlService service) {
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900127 HdmiCecController controller = new HdmiCecController();
Jungshik Jang4085d0e2014-05-27 19:52:39 +0900128 long nativePtr = nativeInit(controller, service.getServiceLooper().getQueue());
Jungshik Jang0792d372014-04-23 17:57:26 +0900129 if (nativePtr == 0L) {
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900130 controller = null;
Jungshik Jang0792d372014-04-23 17:57:26 +0900131 return null;
132 }
133
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900134 controller.init(service, nativePtr);
135 return controller;
136 }
137
138 private void init(HdmiControlService service, long nativePtr) {
139 mService = service;
140 mIoHandler = new Handler(service.getServiceLooper());
141 mControlHandler = new Handler(service.getServiceLooper());
142 mNativePtr = nativePtr;
Jungshik Jang0792d372014-04-23 17:57:26 +0900143 }
144
Jungshik Janga5b74142014-06-23 18:03:10 +0900145 @ServiceThreadOnly
Jungshik Jang3ee65722014-06-03 16:22:30 +0900146 void addLocalDevice(int deviceType, HdmiCecLocalDevice device) {
Jungshik Janga5b74142014-06-23 18:03:10 +0900147 assertRunOnServiceThread();
Jungshik Jang3ee65722014-06-03 16:22:30 +0900148 mLocalDevices.put(deviceType, device);
Jungshik Jang3f74ab02014-04-30 14:31:02 +0900149 }
150
151 /**
152 * Allocate a new logical address of the given device type. Allocated
Jungshik Jang3ee65722014-06-03 16:22:30 +0900153 * address will be reported through {@link AllocateAddressCallback}.
Jungshik Jang3f74ab02014-04-30 14:31:02 +0900154 *
155 * <p> Declared as package-private, accessed by {@link HdmiControlService} only.
156 *
157 * @param deviceType type of device to used to determine logical address
158 * @param preferredAddress a logical address preferred to be allocated.
Jungshik Jang42230722014-07-07 17:40:25 +0900159 * If sets {@link Constants#ADDR_UNREGISTERED}, scans
Jungshik Jang3f74ab02014-04-30 14:31:02 +0900160 * the smallest logical address matched with the given device type.
161 * Otherwise, scan address will start from {@code preferredAddress}
162 * @param callback callback interface to report allocated logical address to caller
163 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900164 @ServiceThreadOnly
Jungshik Jangd643f762014-05-22 19:28:09 +0900165 void allocateLogicalAddress(final int deviceType, final int preferredAddress,
Jungshik Jang3ee65722014-06-03 16:22:30 +0900166 final AllocateAddressCallback callback) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900167 assertRunOnServiceThread();
168
Jungshik Jangd643f762014-05-22 19:28:09 +0900169 runOnIoThread(new Runnable() {
170 @Override
171 public void run() {
172 handleAllocateLogicalAddress(deviceType, preferredAddress, callback);
173 }
174 });
175 }
176
Jungshik Janga5b74142014-06-23 18:03:10 +0900177 @IoThreadOnly
Jungshik Jangd643f762014-05-22 19:28:09 +0900178 private void handleAllocateLogicalAddress(final int deviceType, int preferredAddress,
Jungshik Jang3ee65722014-06-03 16:22:30 +0900179 final AllocateAddressCallback callback) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900180 assertRunOnIoThread();
Jungshik Jangd643f762014-05-22 19:28:09 +0900181 int startAddress = preferredAddress;
182 // If preferred address is "unregistered", start address will be the smallest
183 // address matched with the given device type.
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900184 if (preferredAddress == Constants.ADDR_UNREGISTERED) {
Jungshik Jangd643f762014-05-22 19:28:09 +0900185 for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) {
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900186 if (deviceType == HdmiUtils.getTypeFromAddress(i)) {
Jungshik Jangd643f762014-05-22 19:28:09 +0900187 startAddress = i;
188 break;
189 }
190 }
191 }
192
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900193 int logicalAddress = Constants.ADDR_UNREGISTERED;
Jungshik Jangd643f762014-05-22 19:28:09 +0900194 // Iterates all possible addresses which has the same device type.
195 for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) {
196 int curAddress = (startAddress + i) % NUM_LOGICAL_ADDRESS;
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900197 if (curAddress != Constants.ADDR_UNREGISTERED
198 && deviceType == HdmiUtils.getTypeFromAddress(curAddress)) {
Jinsuk Kim5fba96d2014-07-11 11:51:34 +0900199 if (!sendPollMessage(curAddress, curAddress, HdmiConfig.ADDRESS_ALLOCATION_RETRY)) {
Jungshik Jangd643f762014-05-22 19:28:09 +0900200 logicalAddress = curAddress;
201 break;
202 }
203 }
204 }
205
206 final int assignedAddress = logicalAddress;
207 if (callback != null) {
208 runOnServiceThread(new Runnable() {
209 @Override
210 public void run() {
211 callback.onAllocated(deviceType, assignedAddress);
212 }
213 });
214 }
Jungshik Jang3f74ab02014-04-30 14:31:02 +0900215 }
216
Jungshik Jange9c77c82014-04-24 20:30:09 +0900217 private static byte[] buildBody(int opcode, byte[] params) {
218 byte[] body = new byte[params.length + 1];
219 body[0] = (byte) opcode;
220 System.arraycopy(params, 0, body, 1, params.length);
221 return body;
222 }
Jungshik Jang0792d372014-04-23 17:57:26 +0900223
Jungshik Jang7d9a8432014-04-29 15:12:43 +0900224
Jinsuk Kim0340bbc2014-06-05 11:07:47 +0900225 HdmiPortInfo[] getPortInfos() {
226 return nativeGetPortInfos(mNativePtr);
227 }
228
Jungshik Janga9095ba2014-05-02 13:06:22 +0900229 /**
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +0900230 * Return the locally hosted logical device of a given type.
231 *
232 * @param deviceType logical device type
233 * @return {@link HdmiCecLocalDevice} instance if the instance of the type is available;
234 * otherwise null.
235 */
236 HdmiCecLocalDevice getLocalDevice(int deviceType) {
237 return mLocalDevices.get(deviceType);
238 }
239
240 /**
Jungshik Janga9095ba2014-05-02 13:06:22 +0900241 * Add a new logical address to the device. Device's HW should be notified
242 * when a new logical address is assigned to a device, so that it can accept
243 * a command having available destinations.
244 *
245 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
246 *
247 * @param newLogicalAddress a logical address to be added
248 * @return 0 on success. Otherwise, returns negative value
249 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900250 @ServiceThreadOnly
Jungshik Janga9095ba2014-05-02 13:06:22 +0900251 int addLogicalAddress(int newLogicalAddress) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900252 assertRunOnServiceThread();
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900253 if (HdmiUtils.isValidAddress(newLogicalAddress)) {
Jungshik Janga9095ba2014-05-02 13:06:22 +0900254 return nativeAddLogicalAddress(mNativePtr, newLogicalAddress);
255 } else {
256 return -1;
257 }
258 }
259
260 /**
261 * Clear all logical addresses registered in the device.
262 *
263 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
264 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900265 @ServiceThreadOnly
Jungshik Janga9095ba2014-05-02 13:06:22 +0900266 void clearLogicalAddress() {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900267 assertRunOnServiceThread();
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +0900268 for (int i = 0; i < mLocalDevices.size(); ++i) {
269 mLocalDevices.valueAt(i).clearAddress();
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900270 }
Jungshik Janga9095ba2014-05-02 13:06:22 +0900271 nativeClearLogicalAddress(mNativePtr);
272 }
273
Jungshik Jang4fc1d102014-07-09 19:24:50 +0900274 @ServiceThreadOnly
275 void clearLocalDevices() {
276 assertRunOnServiceThread();
277 mLocalDevices.clear();
278 }
279
Jungshik Janga9095ba2014-05-02 13:06:22 +0900280 /**
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 /**
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900317 * Set an option to CEC HAL.
Jungshik Jang092b4452014-06-11 15:19:17 +0900318 *
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900319 * @param flag key of option
320 * @param value value of option
Jungshik Jang092b4452014-06-11 15:19:17 +0900321 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900322 @ServiceThreadOnly
Jungshik Jang092b4452014-06-11 15:19:17 +0900323 void setOption(int flag, int value) {
324 assertRunOnServiceThread();
325 nativeSetOption(mNativePtr, flag, value);
326 }
327
328 /**
329 * Configure ARC circuit in the hardware logic to start or stop the feature.
330 *
331 * @param enabled whether to enable/disable ARC
332 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900333 @ServiceThreadOnly
Jungshik Jang092b4452014-06-11 15:19:17 +0900334 void setAudioReturnChannel(boolean enabled) {
335 assertRunOnServiceThread();
336 nativeSetAudioReturnChannel(mNativePtr, enabled);
337 }
338
339 /**
340 * Return the connection status of the specified port
341 *
342 * @param port port number to check connection status
343 * @return true if connected; otherwise, return false
344 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900345 @ServiceThreadOnly
Jungshik Jang092b4452014-06-11 15:19:17 +0900346 boolean isConnected(int port) {
347 assertRunOnServiceThread();
348 return nativeIsConnected(mNativePtr, port);
349 }
350
351 /**
Jungshik Jang02bb4262014-05-23 16:48:31 +0900352 * Poll all remote devices. It sends &lt;Polling Message&gt; to all remote
353 * devices.
354 *
355 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
356 *
357 * @param callback an interface used to get a list of all remote devices' address
Jungshik Jang1de51422014-07-03 11:14:26 +0900358 * @param sourceAddress a logical address of source device where sends polling message
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900359 * @param pickStrategy strategy how to pick polling candidates
Jungshik Jang02bb4262014-05-23 16:48:31 +0900360 * @param retryCount the number of retry used to send polling message to remote devices
361 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900362 @ServiceThreadOnly
Jungshik Jang1de51422014-07-03 11:14:26 +0900363 void pollDevices(DevicePollingCallback callback, int sourceAddress, int pickStrategy,
364 int retryCount) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900365 assertRunOnServiceThread();
Jungshik Jang02bb4262014-05-23 16:48:31 +0900366
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900367 // Extract polling candidates. No need to poll against local devices.
368 List<Integer> pollingCandidates = pickPollCandidates(pickStrategy);
Jungshik Jang1de51422014-07-03 11:14:26 +0900369 runDevicePolling(sourceAddress, pollingCandidates, retryCount, callback);
Jungshik Jang02bb4262014-05-23 16:48:31 +0900370 }
371
Jungshik Jangcc5ef8c2014-05-27 13:27:36 +0900372 /**
373 * Return a list of all {@link HdmiCecLocalDevice}s.
374 *
375 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
376 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900377 @ServiceThreadOnly
Jungshik Jangcc5ef8c2014-05-27 13:27:36 +0900378 List<HdmiCecLocalDevice> getLocalDeviceList() {
379 assertRunOnServiceThread();
Jungshik Jang79c58a42014-06-16 16:45:36 +0900380 return HdmiUtils.sparseArrayToList(mLocalDevices);
Jungshik Jangcc5ef8c2014-05-27 13:27:36 +0900381 }
382
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900383 private List<Integer> pickPollCandidates(int pickStrategy) {
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900384 int strategy = pickStrategy & Constants.POLL_STRATEGY_MASK;
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900385 Predicate<Integer> pickPredicate = null;
386 switch (strategy) {
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900387 case Constants.POLL_STRATEGY_SYSTEM_AUDIO:
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900388 pickPredicate = mSystemAudioAddressPredicate;
389 break;
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900390 case Constants.POLL_STRATEGY_REMOTES_DEVICES:
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900391 default: // The default is POLL_STRATEGY_REMOTES_DEVICES.
392 pickPredicate = mRemoteDeviceAddressPredicate;
393 break;
394 }
395
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900396 int iterationStrategy = pickStrategy & Constants.POLL_ITERATION_STRATEGY_MASK;
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900397 ArrayList<Integer> pollingCandidates = new ArrayList<>();
398 switch (iterationStrategy) {
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900399 case Constants.POLL_ITERATION_IN_ORDER:
400 for (int i = Constants.ADDR_TV; i <= Constants.ADDR_SPECIFIC_USE; ++i) {
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900401 if (pickPredicate.apply(i)) {
402 pollingCandidates.add(i);
403 }
404 }
405 break;
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900406 case Constants.POLL_ITERATION_REVERSE_ORDER:
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900407 default: // The default is reverse order.
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900408 for (int i = Constants.ADDR_SPECIFIC_USE; i >= Constants.ADDR_TV; --i) {
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900409 if (pickPredicate.apply(i)) {
410 pollingCandidates.add(i);
411 }
412 }
413 break;
414 }
415 return pollingCandidates;
416 }
417
Jungshik Janga5b74142014-06-23 18:03:10 +0900418 @ServiceThreadOnly
Jungshik Jang02bb4262014-05-23 16:48:31 +0900419 private boolean isAllocatedLocalDeviceAddress(int address) {
Jungshik Janga5b74142014-06-23 18:03:10 +0900420 assertRunOnServiceThread();
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +0900421 for (int i = 0; i < mLocalDevices.size(); ++i) {
422 if (mLocalDevices.valueAt(i).isAddressOf(address)) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900423 return true;
424 }
425 }
426 return false;
427 }
428
Jungshik Janga5b74142014-06-23 18:03:10 +0900429 @ServiceThreadOnly
Jungshik Jang1de51422014-07-03 11:14:26 +0900430 private void runDevicePolling(final int sourceAddress,
431 final List<Integer> candidates, final int retryCount,
Jungshik Jang02bb4262014-05-23 16:48:31 +0900432 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) {
Jungshik Jang1de51422014-07-03 11:14:26 +0900439 if (sendPollMessage(sourceAddress, address, retryCount)) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900440 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 Jang1de51422014-07-03 11:14:26 +0900456 private boolean sendPollMessage(int sourceAddress, int destinationAddress, int retryCount) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900457 assertRunOnIoThread();
458 for (int i = 0; i < retryCount; ++i) {
Jungshik Jang1de51422014-07-03 11:14:26 +0900459 // <Polling Message> is a message which has empty body.
Jungshik Jang02bb4262014-05-23 16:48:31 +0900460 // If sending <Polling Message> failed (NAK), it becomes
461 // new logical address for the device because no device uses
462 // it as logical address of the device.
Jungshik Jang1de51422014-07-03 11:14:26 +0900463 if (nativeSendCecCommand(mNativePtr, sourceAddress, destinationAddress, EMPTY_BODY)
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900464 == Constants.SEND_RESULT_SUCCESS) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900465 return true;
466 }
467 }
468 return false;
469 }
470
471 private void assertRunOnIoThread() {
472 if (Looper.myLooper() != mIoHandler.getLooper()) {
473 throw new IllegalStateException("Should run on io thread.");
474 }
475 }
476
477 private void assertRunOnServiceThread() {
478 if (Looper.myLooper() != mControlHandler.getLooper()) {
479 throw new IllegalStateException("Should run on service thread.");
480 }
481 }
482
483 // Run a Runnable on IO thread.
484 // It should be careful to access member variables on IO thread because
485 // it can be accessed from system thread as well.
Jungshik Jangd643f762014-05-22 19:28:09 +0900486 private void runOnIoThread(Runnable runnable) {
487 mIoHandler.post(runnable);
488 }
489
490 private void runOnServiceThread(Runnable runnable) {
491 mControlHandler.post(runnable);
492 }
493
Jungshik Janga1fa91f2014-05-08 20:56:41 +0900494 private boolean isAcceptableAddress(int address) {
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900495 // Can access command targeting devices available in local device or broadcast command.
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900496 if (address == Constants.ADDR_BROADCAST) {
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900497 return true;
498 }
Jungshik Jang02bb4262014-05-23 16:48:31 +0900499 return isAllocatedLocalDeviceAddress(address);
Jungshik Janga1fa91f2014-05-08 20:56:41 +0900500 }
501
Jungshik Janga5b74142014-06-23 18:03:10 +0900502 @ServiceThreadOnly
Jungshik Jange9c77c82014-04-24 20:30:09 +0900503 private void onReceiveCommand(HdmiCecMessage message) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900504 assertRunOnServiceThread();
Jungshik Jang4085d0e2014-05-27 19:52:39 +0900505 if (isAcceptableAddress(message.getDestination())
506 && mService.handleCecCommand(message)) {
Jungshik Janga1fa91f2014-05-08 20:56:41 +0900507 return;
508 }
Jungshik Jang1827fdc2014-07-17 13:58:14 +0900509 if (message.getDestination() == Constants.ADDR_BROADCAST) {
510 return;
Jinsuk Kim3a2f7432014-05-29 06:52:45 +0900511 }
Jungshik Jang1827fdc2014-07-17 13:58:14 +0900512 if (message.getOpcode() == Constants.MESSAGE_FEATURE_ABORT) {
513 Slog.v(TAG, "Unhandled <Feature Abort> message:" + message);
514 return;
515 }
516
517 int sourceAddress = message.getDestination();
518 // Reply <Feature Abort> to initiator (source) for all requests.
519 HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildFeatureAbortCommand(
520 sourceAddress, message.getSource(), message.getOpcode(),
521 Constants.ABORT_REFUSED);
522 sendCommand(cecMessage);
Jungshik Jange9c77c82014-04-24 20:30:09 +0900523 }
524
Jungshik Janga5b74142014-06-23 18:03:10 +0900525 @ServiceThreadOnly
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900526 void sendCommand(HdmiCecMessage cecMessage) {
Jungshik Janga5b74142014-06-23 18:03:10 +0900527 assertRunOnServiceThread();
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900528 sendCommand(cecMessage, null);
529 }
530
Jungshik Janga5b74142014-06-23 18:03:10 +0900531 @ServiceThreadOnly
Jungshik Jangd643f762014-05-22 19:28:09 +0900532 void sendCommand(final HdmiCecMessage cecMessage,
533 final HdmiControlService.SendMessageCallback callback) {
Jungshik Janga5b74142014-06-23 18:03:10 +0900534 assertRunOnServiceThread();
Jungshik Jangd643f762014-05-22 19:28:09 +0900535 runOnIoThread(new Runnable() {
536 @Override
537 public void run() {
538 byte[] body = buildBody(cecMessage.getOpcode(), cecMessage.getParams());
Jungshik Jang8ed86c42014-07-11 11:56:46 +0900539 int i = 0;
540 int errorCode = Constants.SEND_RESULT_SUCCESS;
541 do {
542 errorCode = nativeSendCecCommand(mNativePtr, cecMessage.getSource(),
543 cecMessage.getDestination(), body);
544 if (errorCode == Constants.SEND_RESULT_SUCCESS) {
545 break;
546 }
547 } while (i++ < HdmiConfig.RETRANSMISSION_COUNT);
548
549 final int finalError = errorCode;
550 if (finalError != Constants.SEND_RESULT_SUCCESS) {
Yuncheol Heoece603b2014-05-23 20:10:19 +0900551 Slog.w(TAG, "Failed to send " + cecMessage);
552 }
Jungshik Jangd643f762014-05-22 19:28:09 +0900553 if (callback != null) {
554 runOnServiceThread(new Runnable() {
555 @Override
556 public void run() {
Jungshik Jang8ed86c42014-07-11 11:56:46 +0900557 callback.onSendCompleted(finalError);
Jungshik Jangd643f762014-05-22 19:28:09 +0900558 }
559 });
560 }
561 }
562 });
Jungshik Jange9c77c82014-04-24 20:30:09 +0900563 }
564
Jungshik Jang0792d372014-04-23 17:57:26 +0900565 /**
Jungshik Jange9c77c82014-04-24 20:30:09 +0900566 * Called by native when incoming CEC message arrived.
Jungshik Jang0792d372014-04-23 17:57:26 +0900567 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900568 @ServiceThreadOnly
Jungshik Jange9c77c82014-04-24 20:30:09 +0900569 private void handleIncomingCecCommand(int srcAddress, int dstAddress, byte[] body) {
Jungshik Jang4085d0e2014-05-27 19:52:39 +0900570 assertRunOnServiceThread();
571 onReceiveCommand(HdmiCecMessageBuilder.of(srcAddress, dstAddress, body));
Jungshik Jange9c77c82014-04-24 20:30:09 +0900572 }
573
574 /**
575 * Called by native when a hotplug event issues.
576 */
Jungshik Jang42230722014-07-07 17:40:25 +0900577 @ServiceThreadOnly
578 private void handleHotplug(int port, boolean connected) {
579 assertRunOnServiceThread();
580 mService.onHotplug(port, connected);
Jungshik Jang0792d372014-04-23 17:57:26 +0900581 }
582
Jungshik Jang4085d0e2014-05-27 19:52:39 +0900583 private static native long nativeInit(HdmiCecController handler, MessageQueue messageQueue);
Jungshik Janga9095ba2014-05-02 13:06:22 +0900584 private static native int nativeSendCecCommand(long controllerPtr, int srcAddress,
Jungshik Jange9c77c82014-04-24 20:30:09 +0900585 int dstAddress, byte[] body);
Jungshik Janga9095ba2014-05-02 13:06:22 +0900586 private static native int nativeAddLogicalAddress(long controllerPtr, int logicalAddress);
587 private static native void nativeClearLogicalAddress(long controllerPtr);
588 private static native int nativeGetPhysicalAddress(long controllerPtr);
589 private static native int nativeGetVersion(long controllerPtr);
590 private static native int nativeGetVendorId(long controllerPtr);
Jinsuk Kim0340bbc2014-06-05 11:07:47 +0900591 private static native HdmiPortInfo[] nativeGetPortInfos(long controllerPtr);
Jungshik Jang092b4452014-06-11 15:19:17 +0900592 private static native void nativeSetOption(long controllerPtr, int flag, int value);
593 private static native void nativeSetAudioReturnChannel(long controllerPtr, boolean flag);
594 private static native boolean nativeIsConnected(long controllerPtr, int port);
Jungshik Jang0792d372014-04-23 17:57:26 +0900595}