blob: bb22b4dde1051188559ee193377894a224e22c0c [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
Terry Heo959d2db2014-08-28 16:45:41 +090026import com.android.internal.util.IndentingPrintWriter;
Jungshik Jang0f8b4b72014-05-28 17:58:58 +090027import com.android.internal.util.Predicate;
Jungshik Janga5b74142014-06-23 18:03:10 +090028import com.android.server.hdmi.HdmiAnnotations.IoThreadOnly;
29import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
Jungshik Jang02bb4262014-05-23 16:48:31 +090030import com.android.server.hdmi.HdmiControlService.DevicePollingCallback;
31
Jungshik Jang3f74ab02014-04-30 14:31:02 +090032import libcore.util.EmptyArray;
33
Terry Heo959d2db2014-08-28 16:45:41 +090034import java.io.FileDescriptor;
35import java.io.PrintWriter;
Jungshik Jang7d9a8432014-04-29 15:12:43 +090036import java.util.ArrayList;
Jungshik Jang7d9a8432014-04-29 15:12:43 +090037import java.util.List;
Jungshik Jang0792d372014-04-23 17:57:26 +090038
39/**
40 * Manages HDMI-CEC command and behaviors. It converts user's command into CEC command
41 * and pass it to CEC HAL so that it sends message to other device. For incoming
42 * message it translates the message and delegates it to proper module.
43 *
Jungshik Jang02bb4262014-05-23 16:48:31 +090044 * <p>It should be careful to access member variables on IO thread because
45 * it can be accessed from system thread as well.
46 *
Jungshik Jang0792d372014-04-23 17:57:26 +090047 * <p>It can be created only by {@link HdmiCecController#create}
48 *
49 * <p>Declared as package-private, accessed by {@link HdmiControlService} only.
50 */
Jungshik Janga9095ba2014-05-02 13:06:22 +090051final class HdmiCecController {
Jungshik Jang0792d372014-04-23 17:57:26 +090052 private static final String TAG = "HdmiCecController";
53
Jungshik Jang3ee65722014-06-03 16:22:30 +090054 /**
55 * Interface to report allocated logical address.
56 */
57 interface AllocateAddressCallback {
58 /**
59 * Called when a new logical address is allocated.
60 *
61 * @param deviceType requested device type to allocate logical address
62 * @param logicalAddress allocated logical address. If it is
Jungshik Jang42230722014-07-07 17:40:25 +090063 * {@link Constants#ADDR_UNREGISTERED}, it means that
Jungshik Jang3ee65722014-06-03 16:22:30 +090064 * it failed to allocate logical address for the given device type
65 */
66 void onAllocated(int deviceType, int logicalAddress);
67 }
68
Jungshik Jang3f74ab02014-04-30 14:31:02 +090069 private static final byte[] EMPTY_BODY = EmptyArray.BYTE;
70
Jungshik Jang3f74ab02014-04-30 14:31:02 +090071 private static final int NUM_LOGICAL_ADDRESS = 16;
72
Jungshik Jang0f8b4b72014-05-28 17:58:58 +090073 // Predicate for whether the given logical address is remote device's one or not.
74 private final Predicate<Integer> mRemoteDeviceAddressPredicate = new Predicate<Integer>() {
75 @Override
76 public boolean apply(Integer address) {
77 return !isAllocatedLocalDeviceAddress(address);
78 }
79 };
80
81 // Predicate whether the given logical address is system audio's one or not
82 private final Predicate<Integer> mSystemAudioAddressPredicate = new Predicate<Integer>() {
83 @Override
84 public boolean apply(Integer address) {
Jinsuk Kimc0c20d02014-07-04 14:34:31 +090085 return HdmiUtils.getTypeFromAddress(address) == Constants.ADDR_AUDIO_SYSTEM;
Jungshik Jang0f8b4b72014-05-28 17:58:58 +090086 }
87 };
88
Jungshik Jang0792d372014-04-23 17:57:26 +090089 // Handler instance to process synchronous I/O (mainly send) message.
90 private Handler mIoHandler;
91
92 // Handler instance to process various messages coming from other CEC
93 // device or issued by internal state change.
Jungshik Jange9c77c82014-04-24 20:30:09 +090094 private Handler mControlHandler;
Jungshik Jang0792d372014-04-23 17:57:26 +090095
96 // Stores the pointer to the native implementation of the service that
97 // interacts with HAL.
Jungshik Jang02bb4262014-05-23 16:48:31 +090098 private volatile long mNativePtr;
Jungshik Jang0792d372014-04-23 17:57:26 +090099
Jungshik Jang7df52862014-08-11 14:35:27 +0900100 private final HdmiControlService mService;
Jungshik Janga1fa91f2014-05-08 20:56:41 +0900101
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +0900102 // Stores the local CEC devices in the system. Device type is used for key.
103 private final SparseArray<HdmiCecLocalDevice> mLocalDevices = new SparseArray<>();
Jungshik Jang7d9a8432014-04-29 15:12:43 +0900104
Jungshik Jangc94ac5c2014-08-27 13:48:37 +0900105 @IoThreadOnly
106 private final HdmiLogger mIoThreadLogger = new HdmiLogger(TAG);
107 @ServiceThreadOnly
108 private final HdmiLogger mServiceThreadLogger = new HdmiLogger(TAG);
109
Jungshik Jang0792d372014-04-23 17:57:26 +0900110 // Private constructor. Use HdmiCecController.create().
Jungshik Jang7df52862014-08-11 14:35:27 +0900111 private HdmiCecController(HdmiControlService service) {
112 mService = service;
Jungshik Jang0792d372014-04-23 17:57:26 +0900113 }
114
115 /**
116 * A factory method to get {@link HdmiCecController}. If it fails to initialize
117 * inner device or has no device it will return {@code null}.
118 *
119 * <p>Declared as package-private, accessed by {@link HdmiControlService} only.
Jungshik Jange9c77c82014-04-24 20:30:09 +0900120 * @param service {@link HdmiControlService} instance used to create internal handler
121 * and to pass callback for incoming message or event.
Jungshik Jang0792d372014-04-23 17:57:26 +0900122 * @return {@link HdmiCecController} if device is initialized successfully. Otherwise,
123 * returns {@code null}.
124 */
Jungshik Jange9c77c82014-04-24 20:30:09 +0900125 static HdmiCecController create(HdmiControlService service) {
Jungshik Jang7df52862014-08-11 14:35:27 +0900126 HdmiCecController controller = new HdmiCecController(service);
Jungshik Jang4085d0e2014-05-27 19:52:39 +0900127 long nativePtr = nativeInit(controller, service.getServiceLooper().getQueue());
Jungshik Jang0792d372014-04-23 17:57:26 +0900128 if (nativePtr == 0L) {
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900129 controller = null;
Jungshik Jang0792d372014-04-23 17:57:26 +0900130 return null;
131 }
132
Jungshik Jang7df52862014-08-11 14:35:27 +0900133 controller.init(nativePtr);
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900134 return controller;
135 }
136
Jungshik Jang7df52862014-08-11 14:35:27 +0900137 private void init(long nativePtr) {
138 mIoHandler = new Handler(mService.getServiceLooper());
139 mControlHandler = new Handler(mService.getServiceLooper());
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900140 mNativePtr = nativePtr;
Jungshik Jang0792d372014-04-23 17:57:26 +0900141 }
142
Jungshik Janga5b74142014-06-23 18:03:10 +0900143 @ServiceThreadOnly
Jungshik Jang3ee65722014-06-03 16:22:30 +0900144 void addLocalDevice(int deviceType, HdmiCecLocalDevice device) {
Jungshik Janga5b74142014-06-23 18:03:10 +0900145 assertRunOnServiceThread();
Jungshik Jang3ee65722014-06-03 16:22:30 +0900146 mLocalDevices.put(deviceType, device);
Jungshik Jang3f74ab02014-04-30 14:31:02 +0900147 }
148
149 /**
150 * Allocate a new logical address of the given device type. Allocated
Jungshik Jang3ee65722014-06-03 16:22:30 +0900151 * address will be reported through {@link AllocateAddressCallback}.
Jungshik Jang3f74ab02014-04-30 14:31:02 +0900152 *
153 * <p> Declared as package-private, accessed by {@link HdmiControlService} only.
154 *
155 * @param deviceType type of device to used to determine logical address
156 * @param preferredAddress a logical address preferred to be allocated.
Jungshik Jang42230722014-07-07 17:40:25 +0900157 * If sets {@link Constants#ADDR_UNREGISTERED}, scans
Jungshik Jang3f74ab02014-04-30 14:31:02 +0900158 * the smallest logical address matched with the given device type.
159 * Otherwise, scan address will start from {@code preferredAddress}
160 * @param callback callback interface to report allocated logical address to caller
161 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900162 @ServiceThreadOnly
Jungshik Jangd643f762014-05-22 19:28:09 +0900163 void allocateLogicalAddress(final int deviceType, final int preferredAddress,
Jungshik Jang3ee65722014-06-03 16:22:30 +0900164 final AllocateAddressCallback callback) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900165 assertRunOnServiceThread();
166
Jungshik Jangd643f762014-05-22 19:28:09 +0900167 runOnIoThread(new Runnable() {
168 @Override
169 public void run() {
170 handleAllocateLogicalAddress(deviceType, preferredAddress, callback);
171 }
172 });
173 }
174
Jungshik Janga5b74142014-06-23 18:03:10 +0900175 @IoThreadOnly
Jungshik Jangd643f762014-05-22 19:28:09 +0900176 private void handleAllocateLogicalAddress(final int deviceType, int preferredAddress,
Jungshik Jang3ee65722014-06-03 16:22:30 +0900177 final AllocateAddressCallback callback) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900178 assertRunOnIoThread();
Jungshik Jangd643f762014-05-22 19:28:09 +0900179 int startAddress = preferredAddress;
180 // If preferred address is "unregistered", start address will be the smallest
181 // address matched with the given device type.
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900182 if (preferredAddress == Constants.ADDR_UNREGISTERED) {
Jungshik Jangd643f762014-05-22 19:28:09 +0900183 for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) {
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900184 if (deviceType == HdmiUtils.getTypeFromAddress(i)) {
Jungshik Jangd643f762014-05-22 19:28:09 +0900185 startAddress = i;
186 break;
187 }
188 }
189 }
190
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900191 int logicalAddress = Constants.ADDR_UNREGISTERED;
Jungshik Jangd643f762014-05-22 19:28:09 +0900192 // Iterates all possible addresses which has the same device type.
193 for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) {
194 int curAddress = (startAddress + i) % NUM_LOGICAL_ADDRESS;
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900195 if (curAddress != Constants.ADDR_UNREGISTERED
196 && deviceType == HdmiUtils.getTypeFromAddress(curAddress)) {
Jungshik Jang8e93c842014-08-06 15:48:33 +0900197 int failedPollingCount = 0;
198 for (int j = 0; j < HdmiConfig.ADDRESS_ALLOCATION_RETRY; ++j) {
199 if (!sendPollMessage(curAddress, curAddress, 1)) {
200 failedPollingCount++;
201 }
202 }
203
204 // Pick logical address if failed ratio is more than a half of all retries.
205 if (failedPollingCount * 2 > HdmiConfig.ADDRESS_ALLOCATION_RETRY) {
Jungshik Jangd643f762014-05-22 19:28:09 +0900206 logicalAddress = curAddress;
207 break;
208 }
209 }
210 }
211
212 final int assignedAddress = logicalAddress;
Jungshik Jangc94ac5c2014-08-27 13:48:37 +0900213 mIoThreadLogger.debug(
214 String.format("New logical address for device [%d]: [preferred:%d, assigned:%d]",
215 deviceType, preferredAddress, assignedAddress));
Jungshik Jangd643f762014-05-22 19:28:09 +0900216 if (callback != null) {
217 runOnServiceThread(new Runnable() {
Jungshik Jang8e93c842014-08-06 15:48:33 +0900218 @Override
Jungshik Jangd643f762014-05-22 19:28:09 +0900219 public void run() {
220 callback.onAllocated(deviceType, assignedAddress);
221 }
222 });
223 }
Jungshik Jang3f74ab02014-04-30 14:31:02 +0900224 }
225
Jungshik Jange9c77c82014-04-24 20:30:09 +0900226 private static byte[] buildBody(int opcode, byte[] params) {
227 byte[] body = new byte[params.length + 1];
228 body[0] = (byte) opcode;
229 System.arraycopy(params, 0, body, 1, params.length);
230 return body;
231 }
Jungshik Jang0792d372014-04-23 17:57:26 +0900232
Jungshik Jang7d9a8432014-04-29 15:12:43 +0900233
Jinsuk Kim0340bbc2014-06-05 11:07:47 +0900234 HdmiPortInfo[] getPortInfos() {
235 return nativeGetPortInfos(mNativePtr);
236 }
237
Jungshik Janga9095ba2014-05-02 13:06:22 +0900238 /**
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +0900239 * Return the locally hosted logical device of a given type.
240 *
241 * @param deviceType logical device type
242 * @return {@link HdmiCecLocalDevice} instance if the instance of the type is available;
243 * otherwise null.
244 */
245 HdmiCecLocalDevice getLocalDevice(int deviceType) {
246 return mLocalDevices.get(deviceType);
247 }
248
249 /**
Jungshik Janga9095ba2014-05-02 13:06:22 +0900250 * Add a new logical address to the device. Device's HW should be notified
251 * when a new logical address is assigned to a device, so that it can accept
252 * a command having available destinations.
253 *
254 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
255 *
256 * @param newLogicalAddress a logical address to be added
257 * @return 0 on success. Otherwise, returns negative value
258 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900259 @ServiceThreadOnly
Jungshik Janga9095ba2014-05-02 13:06:22 +0900260 int addLogicalAddress(int newLogicalAddress) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900261 assertRunOnServiceThread();
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900262 if (HdmiUtils.isValidAddress(newLogicalAddress)) {
Jungshik Janga9095ba2014-05-02 13:06:22 +0900263 return nativeAddLogicalAddress(mNativePtr, newLogicalAddress);
264 } else {
265 return -1;
266 }
267 }
268
269 /**
270 * Clear all logical addresses registered in the device.
271 *
272 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
273 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900274 @ServiceThreadOnly
Jungshik Janga9095ba2014-05-02 13:06:22 +0900275 void clearLogicalAddress() {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900276 assertRunOnServiceThread();
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +0900277 for (int i = 0; i < mLocalDevices.size(); ++i) {
278 mLocalDevices.valueAt(i).clearAddress();
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900279 }
Jungshik Janga9095ba2014-05-02 13:06:22 +0900280 nativeClearLogicalAddress(mNativePtr);
281 }
282
Jungshik Jang4fc1d102014-07-09 19:24:50 +0900283 @ServiceThreadOnly
284 void clearLocalDevices() {
285 assertRunOnServiceThread();
286 mLocalDevices.clear();
287 }
288
Jungshik Janga9095ba2014-05-02 13:06:22 +0900289 /**
290 * Return the physical address of the device.
291 *
292 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
293 *
294 * @return CEC physical address of the device. The range of success address
295 * is between 0x0000 and 0xFFFF. If failed it returns -1
296 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900297 @ServiceThreadOnly
Jungshik Janga9095ba2014-05-02 13:06:22 +0900298 int getPhysicalAddress() {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900299 assertRunOnServiceThread();
Jungshik Janga9095ba2014-05-02 13:06:22 +0900300 return nativeGetPhysicalAddress(mNativePtr);
301 }
302
303 /**
304 * Return CEC version of the device.
305 *
306 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
307 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900308 @ServiceThreadOnly
Jungshik Janga9095ba2014-05-02 13:06:22 +0900309 int getVersion() {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900310 assertRunOnServiceThread();
Jungshik Janga9095ba2014-05-02 13:06:22 +0900311 return nativeGetVersion(mNativePtr);
312 }
313
314 /**
315 * Return vendor id of the device.
316 *
317 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
318 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900319 @ServiceThreadOnly
Jungshik Janga9095ba2014-05-02 13:06:22 +0900320 int getVendorId() {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900321 assertRunOnServiceThread();
Jungshik Janga9095ba2014-05-02 13:06:22 +0900322 return nativeGetVendorId(mNativePtr);
323 }
324
Jungshik Jang02bb4262014-05-23 16:48:31 +0900325 /**
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900326 * Set an option to CEC HAL.
Jungshik Jang092b4452014-06-11 15:19:17 +0900327 *
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900328 * @param flag key of option
329 * @param value value of option
Jungshik Jang092b4452014-06-11 15:19:17 +0900330 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900331 @ServiceThreadOnly
Jungshik Jang092b4452014-06-11 15:19:17 +0900332 void setOption(int flag, int value) {
333 assertRunOnServiceThread();
334 nativeSetOption(mNativePtr, flag, value);
335 }
336
337 /**
338 * Configure ARC circuit in the hardware logic to start or stop the feature.
339 *
340 * @param enabled whether to enable/disable ARC
341 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900342 @ServiceThreadOnly
Jungshik Jang092b4452014-06-11 15:19:17 +0900343 void setAudioReturnChannel(boolean enabled) {
344 assertRunOnServiceThread();
345 nativeSetAudioReturnChannel(mNativePtr, enabled);
346 }
347
348 /**
349 * Return the connection status of the specified port
350 *
351 * @param port port number to check connection status
352 * @return true if connected; otherwise, return false
353 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900354 @ServiceThreadOnly
Jungshik Jang092b4452014-06-11 15:19:17 +0900355 boolean isConnected(int port) {
356 assertRunOnServiceThread();
357 return nativeIsConnected(mNativePtr, port);
358 }
359
360 /**
Jungshik Jang02bb4262014-05-23 16:48:31 +0900361 * Poll all remote devices. It sends &lt;Polling Message&gt; to all remote
362 * devices.
363 *
364 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
365 *
366 * @param callback an interface used to get a list of all remote devices' address
Jungshik Jang1de51422014-07-03 11:14:26 +0900367 * @param sourceAddress a logical address of source device where sends polling message
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900368 * @param pickStrategy strategy how to pick polling candidates
Jungshik Jang02bb4262014-05-23 16:48:31 +0900369 * @param retryCount the number of retry used to send polling message to remote devices
370 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900371 @ServiceThreadOnly
Jungshik Jang1de51422014-07-03 11:14:26 +0900372 void pollDevices(DevicePollingCallback callback, int sourceAddress, int pickStrategy,
373 int retryCount) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900374 assertRunOnServiceThread();
Jungshik Jang02bb4262014-05-23 16:48:31 +0900375
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900376 // Extract polling candidates. No need to poll against local devices.
377 List<Integer> pollingCandidates = pickPollCandidates(pickStrategy);
Jungshik Jang1de51422014-07-03 11:14:26 +0900378 runDevicePolling(sourceAddress, pollingCandidates, retryCount, callback);
Jungshik Jang02bb4262014-05-23 16:48:31 +0900379 }
380
Jungshik Jangcc5ef8c2014-05-27 13:27:36 +0900381 /**
382 * Return a list of all {@link HdmiCecLocalDevice}s.
383 *
384 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
385 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900386 @ServiceThreadOnly
Jungshik Jangcc5ef8c2014-05-27 13:27:36 +0900387 List<HdmiCecLocalDevice> getLocalDeviceList() {
388 assertRunOnServiceThread();
Jungshik Jang79c58a42014-06-16 16:45:36 +0900389 return HdmiUtils.sparseArrayToList(mLocalDevices);
Jungshik Jangcc5ef8c2014-05-27 13:27:36 +0900390 }
391
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900392 private List<Integer> pickPollCandidates(int pickStrategy) {
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900393 int strategy = pickStrategy & Constants.POLL_STRATEGY_MASK;
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900394 Predicate<Integer> pickPredicate = null;
395 switch (strategy) {
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900396 case Constants.POLL_STRATEGY_SYSTEM_AUDIO:
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900397 pickPredicate = mSystemAudioAddressPredicate;
398 break;
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900399 case Constants.POLL_STRATEGY_REMOTES_DEVICES:
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900400 default: // The default is POLL_STRATEGY_REMOTES_DEVICES.
401 pickPredicate = mRemoteDeviceAddressPredicate;
402 break;
403 }
404
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900405 int iterationStrategy = pickStrategy & Constants.POLL_ITERATION_STRATEGY_MASK;
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900406 ArrayList<Integer> pollingCandidates = new ArrayList<>();
407 switch (iterationStrategy) {
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900408 case Constants.POLL_ITERATION_IN_ORDER:
409 for (int i = Constants.ADDR_TV; i <= Constants.ADDR_SPECIFIC_USE; ++i) {
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900410 if (pickPredicate.apply(i)) {
411 pollingCandidates.add(i);
412 }
413 }
414 break;
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900415 case Constants.POLL_ITERATION_REVERSE_ORDER:
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900416 default: // The default is reverse order.
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900417 for (int i = Constants.ADDR_SPECIFIC_USE; i >= Constants.ADDR_TV; --i) {
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900418 if (pickPredicate.apply(i)) {
419 pollingCandidates.add(i);
420 }
421 }
422 break;
423 }
424 return pollingCandidates;
425 }
426
Jungshik Janga5b74142014-06-23 18:03:10 +0900427 @ServiceThreadOnly
Jungshik Jang02bb4262014-05-23 16:48:31 +0900428 private boolean isAllocatedLocalDeviceAddress(int address) {
Jungshik Janga5b74142014-06-23 18:03:10 +0900429 assertRunOnServiceThread();
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +0900430 for (int i = 0; i < mLocalDevices.size(); ++i) {
431 if (mLocalDevices.valueAt(i).isAddressOf(address)) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900432 return true;
433 }
434 }
435 return false;
436 }
437
Jungshik Janga5b74142014-06-23 18:03:10 +0900438 @ServiceThreadOnly
Jungshik Jang1de51422014-07-03 11:14:26 +0900439 private void runDevicePolling(final int sourceAddress,
440 final List<Integer> candidates, final int retryCount,
Jungshik Jang02bb4262014-05-23 16:48:31 +0900441 final DevicePollingCallback callback) {
442 assertRunOnServiceThread();
443 runOnIoThread(new Runnable() {
444 @Override
445 public void run() {
446 final ArrayList<Integer> allocated = new ArrayList<>();
447 for (Integer address : candidates) {
Jungshik Jang1de51422014-07-03 11:14:26 +0900448 if (sendPollMessage(sourceAddress, address, retryCount)) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900449 allocated.add(address);
450 }
451 }
Jungshik Janga7221ce2014-08-28 16:35:30 +0900452 mIoThreadLogger.debug("[P]:Allocated Address=" + allocated);
Jungshik Jang02bb4262014-05-23 16:48:31 +0900453 if (callback != null) {
454 runOnServiceThread(new Runnable() {
455 @Override
456 public void run() {
457 callback.onPollingFinished(allocated);
458 }
459 });
460 }
461 }
462 });
463 }
464
Jungshik Janga5b74142014-06-23 18:03:10 +0900465 @IoThreadOnly
Jungshik Jang1de51422014-07-03 11:14:26 +0900466 private boolean sendPollMessage(int sourceAddress, int destinationAddress, int retryCount) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900467 assertRunOnIoThread();
468 for (int i = 0; i < retryCount; ++i) {
Jungshik Jang1de51422014-07-03 11:14:26 +0900469 // <Polling Message> is a message which has empty body.
Jungshik Jang02bb4262014-05-23 16:48:31 +0900470 // If sending <Polling Message> failed (NAK), it becomes
471 // new logical address for the device because no device uses
472 // it as logical address of the device.
Jungshik Jang1de51422014-07-03 11:14:26 +0900473 if (nativeSendCecCommand(mNativePtr, sourceAddress, destinationAddress, EMPTY_BODY)
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900474 == Constants.SEND_RESULT_SUCCESS) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900475 return true;
476 }
477 }
478 return false;
479 }
480
481 private void assertRunOnIoThread() {
482 if (Looper.myLooper() != mIoHandler.getLooper()) {
483 throw new IllegalStateException("Should run on io thread.");
484 }
485 }
486
487 private void assertRunOnServiceThread() {
488 if (Looper.myLooper() != mControlHandler.getLooper()) {
489 throw new IllegalStateException("Should run on service thread.");
490 }
491 }
492
493 // Run a Runnable on IO thread.
494 // It should be careful to access member variables on IO thread because
495 // it can be accessed from system thread as well.
Jungshik Jangd643f762014-05-22 19:28:09 +0900496 private void runOnIoThread(Runnable runnable) {
497 mIoHandler.post(runnable);
498 }
499
500 private void runOnServiceThread(Runnable runnable) {
501 mControlHandler.post(runnable);
502 }
503
Jungshik Janga1fa91f2014-05-08 20:56:41 +0900504 private boolean isAcceptableAddress(int address) {
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900505 // Can access command targeting devices available in local device or broadcast command.
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900506 if (address == Constants.ADDR_BROADCAST) {
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900507 return true;
508 }
Jungshik Jang02bb4262014-05-23 16:48:31 +0900509 return isAllocatedLocalDeviceAddress(address);
Jungshik Janga1fa91f2014-05-08 20:56:41 +0900510 }
511
Jungshik Janga5b74142014-06-23 18:03:10 +0900512 @ServiceThreadOnly
Jungshik Jange9c77c82014-04-24 20:30:09 +0900513 private void onReceiveCommand(HdmiCecMessage message) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900514 assertRunOnServiceThread();
Yuncheol Heo6aae6522014-08-05 14:48:37 +0900515 if (isAcceptableAddress(message.getDestination()) && mService.handleCecCommand(message)) {
Jungshik Janga1fa91f2014-05-08 20:56:41 +0900516 return;
517 }
Yuncheol Heo6aae6522014-08-05 14:48:37 +0900518 // Not handled message, so we will reply it with <Feature Abort>.
519 maySendFeatureAbortCommand(message, Constants.ABORT_UNRECOGNIZED_OPCODE);
520 }
Jungshik Jang1827fdc2014-07-17 13:58:14 +0900521
Yuncheol Heo6aae6522014-08-05 14:48:37 +0900522 @ServiceThreadOnly
523 void maySendFeatureAbortCommand(HdmiCecMessage message, int reason) {
524 assertRunOnServiceThread();
525 // Swap the source and the destination.
526 int src = message.getDestination();
527 int dest = message.getSource();
528 if (src == Constants.ADDR_BROADCAST || dest == Constants.ADDR_UNREGISTERED) {
529 // Don't reply <Feature Abort> from the unregistered devices or for the broadcasted
530 // messages. See CEC 12.2 Protocol General Rules for detail.
531 return;
532 }
533 int originalOpcode = message.getOpcode();
534 if (originalOpcode == Constants.MESSAGE_FEATURE_ABORT) {
535 return;
536 }
537 sendCommand(
538 HdmiCecMessageBuilder.buildFeatureAbortCommand(src, dest, originalOpcode, reason));
Jungshik Jange9c77c82014-04-24 20:30:09 +0900539 }
540
Jungshik Janga5b74142014-06-23 18:03:10 +0900541 @ServiceThreadOnly
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900542 void sendCommand(HdmiCecMessage cecMessage) {
Jungshik Janga5b74142014-06-23 18:03:10 +0900543 assertRunOnServiceThread();
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900544 sendCommand(cecMessage, null);
545 }
546
Jungshik Janga5b74142014-06-23 18:03:10 +0900547 @ServiceThreadOnly
Jungshik Jangd643f762014-05-22 19:28:09 +0900548 void sendCommand(final HdmiCecMessage cecMessage,
549 final HdmiControlService.SendMessageCallback callback) {
Jungshik Janga5b74142014-06-23 18:03:10 +0900550 assertRunOnServiceThread();
Jungshik Jangd643f762014-05-22 19:28:09 +0900551 runOnIoThread(new Runnable() {
552 @Override
553 public void run() {
Jungshik Janga7221ce2014-08-28 16:35:30 +0900554 mIoThreadLogger.debug("[S]:" + cecMessage);
Jungshik Jangd643f762014-05-22 19:28:09 +0900555 byte[] body = buildBody(cecMessage.getOpcode(), cecMessage.getParams());
Jungshik Jang8ed86c42014-07-11 11:56:46 +0900556 int i = 0;
557 int errorCode = Constants.SEND_RESULT_SUCCESS;
558 do {
559 errorCode = nativeSendCecCommand(mNativePtr, cecMessage.getSource(),
560 cecMessage.getDestination(), body);
561 if (errorCode == Constants.SEND_RESULT_SUCCESS) {
562 break;
563 }
564 } while (i++ < HdmiConfig.RETRANSMISSION_COUNT);
565
566 final int finalError = errorCode;
567 if (finalError != Constants.SEND_RESULT_SUCCESS) {
Yuncheol Heoece603b2014-05-23 20:10:19 +0900568 Slog.w(TAG, "Failed to send " + cecMessage);
569 }
Jungshik Jangd643f762014-05-22 19:28:09 +0900570 if (callback != null) {
571 runOnServiceThread(new Runnable() {
572 @Override
573 public void run() {
Jungshik Jang8ed86c42014-07-11 11:56:46 +0900574 callback.onSendCompleted(finalError);
Jungshik Jangd643f762014-05-22 19:28:09 +0900575 }
576 });
577 }
578 }
579 });
Jungshik Jange9c77c82014-04-24 20:30:09 +0900580 }
581
Jungshik Jang0792d372014-04-23 17:57:26 +0900582 /**
Jungshik Jange9c77c82014-04-24 20:30:09 +0900583 * Called by native when incoming CEC message arrived.
Jungshik Jang0792d372014-04-23 17:57:26 +0900584 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900585 @ServiceThreadOnly
Jungshik Jange9c77c82014-04-24 20:30:09 +0900586 private void handleIncomingCecCommand(int srcAddress, int dstAddress, byte[] body) {
Jungshik Jang4085d0e2014-05-27 19:52:39 +0900587 assertRunOnServiceThread();
Jungshik Jangc94ac5c2014-08-27 13:48:37 +0900588 HdmiCecMessage command = HdmiCecMessageBuilder.of(srcAddress, dstAddress, body);
Jungshik Janga7221ce2014-08-28 16:35:30 +0900589 mServiceThreadLogger.debug("[R]:" + command);
Jungshik Jangc94ac5c2014-08-27 13:48:37 +0900590 onReceiveCommand(command);
Jungshik Jange9c77c82014-04-24 20:30:09 +0900591 }
592
593 /**
594 * Called by native when a hotplug event issues.
595 */
Jungshik Jang42230722014-07-07 17:40:25 +0900596 @ServiceThreadOnly
597 private void handleHotplug(int port, boolean connected) {
598 assertRunOnServiceThread();
Jungshik Jangc94ac5c2014-08-27 13:48:37 +0900599 mServiceThreadLogger.debug(
600 "Hotplug event:[port:" + port + " , connected:" + connected + "]");
Jungshik Jang42230722014-07-07 17:40:25 +0900601 mService.onHotplug(port, connected);
Jungshik Jang0792d372014-04-23 17:57:26 +0900602 }
603
Terry Heo959d2db2014-08-28 16:45:41 +0900604 void dump(final IndentingPrintWriter pw) {
605 for (int i = 0; i < mLocalDevices.size(); ++i) {
606 pw.println("HdmiCecLocalDevice #" + i + ":");
607 pw.increaseIndent();
608 mLocalDevices.valueAt(i).dump(pw);
609 pw.decreaseIndent();
610 }
611 }
612
Jungshik Jang4085d0e2014-05-27 19:52:39 +0900613 private static native long nativeInit(HdmiCecController handler, MessageQueue messageQueue);
Jungshik Janga9095ba2014-05-02 13:06:22 +0900614 private static native int nativeSendCecCommand(long controllerPtr, int srcAddress,
Jungshik Jange9c77c82014-04-24 20:30:09 +0900615 int dstAddress, byte[] body);
Jungshik Janga9095ba2014-05-02 13:06:22 +0900616 private static native int nativeAddLogicalAddress(long controllerPtr, int logicalAddress);
617 private static native void nativeClearLogicalAddress(long controllerPtr);
618 private static native int nativeGetPhysicalAddress(long controllerPtr);
619 private static native int nativeGetVersion(long controllerPtr);
620 private static native int nativeGetVendorId(long controllerPtr);
Jinsuk Kim0340bbc2014-06-05 11:07:47 +0900621 private static native HdmiPortInfo[] nativeGetPortInfos(long controllerPtr);
Jungshik Jang092b4452014-06-11 15:19:17 +0900622 private static native void nativeSetOption(long controllerPtr, int flag, int value);
623 private static native void nativeSetAudioReturnChannel(long controllerPtr, boolean flag);
624 private static native boolean nativeIsConnected(long controllerPtr, int port);
Jungshik Jang0792d372014-04-23 17:57:26 +0900625}