blob: 6174e5418cafb67d20a31242eca045c066060e5d [file] [log] [blame]
Jungshik Jang0792d372014-04-23 17:57:26 +09001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.hdmi;
18
Jinsuk Kim0340bbc2014-06-05 11:07:47 +090019import android.hardware.hdmi.HdmiPortInfo;
Donghyun Chobc6e3722016-11-04 05:25:52 +090020import android.hardware.tv.cec.V1_0.Result;
21import android.hardware.tv.cec.V1_0.SendMessageResult;
Jungshik Jang0792d372014-04-23 17:57:26 +090022import android.os.Handler;
Jungshik Jang02bb4262014-05-23 16:48:31 +090023import android.os.Looper;
Jungshik Jang4085d0e2014-05-27 19:52:39 +090024import android.os.MessageQueue;
Amy8027c942018-09-18 10:23:20 -070025import android.os.SystemProperties;
Yuncheol Heoece603b2014-05-23 20:10:19 +090026import android.util.Slog;
Jungshik Jang7d9a8432014-04-29 15:12:43 +090027import android.util.SparseArray;
Amy8027c942018-09-18 10:23:20 -070028
Terry Heo959d2db2014-08-28 16:45:41 +090029import com.android.internal.util.IndentingPrintWriter;
Jungshik Janga5b74142014-06-23 18:03:10 +090030import com.android.server.hdmi.HdmiAnnotations.IoThreadOnly;
31import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
Jungshik Jang02bb4262014-05-23 16:48:31 +090032import com.android.server.hdmi.HdmiControlService.DevicePollingCallback;
Amy8027c942018-09-18 10:23:20 -070033
34import libcore.util.EmptyArray;
35
Donghyun Cho0b485b22016-12-13 17:21:01 +090036import java.text.SimpleDateFormat;
Jungshik Jang7d9a8432014-04-29 15:12:43 +090037import java.util.ArrayList;
Donghyun Cho0b485b22016-12-13 17:21:01 +090038import java.util.Date;
Jungshik Jangb3ecb722014-09-11 16:09:45 +090039import java.util.LinkedList;
Jungshik Jang7d9a8432014-04-29 15:12:43 +090040import java.util.List;
Donghyun Cho0b485b22016-12-13 17:21:01 +090041import java.util.concurrent.ArrayBlockingQueue;
Amy8027c942018-09-18 10:23:20 -070042import java.util.function.Predicate;
43
Donghyun Chobc6e3722016-11-04 05:25:52 +090044import sun.util.locale.LanguageTag;
Jungshik Jang0792d372014-04-23 17:57:26 +090045
46/**
47 * Manages HDMI-CEC command and behaviors. It converts user's command into CEC command
48 * and pass it to CEC HAL so that it sends message to other device. For incoming
49 * message it translates the message and delegates it to proper module.
50 *
Jungshik Jang02bb4262014-05-23 16:48:31 +090051 * <p>It should be careful to access member variables on IO thread because
52 * it can be accessed from system thread as well.
53 *
Jungshik Jang0792d372014-04-23 17:57:26 +090054 * <p>It can be created only by {@link HdmiCecController#create}
55 *
56 * <p>Declared as package-private, accessed by {@link HdmiControlService} only.
57 */
Jungshik Janga9095ba2014-05-02 13:06:22 +090058final class HdmiCecController {
Jungshik Jang0792d372014-04-23 17:57:26 +090059 private static final String TAG = "HdmiCecController";
60
Jungshik Jang3ee65722014-06-03 16:22:30 +090061 /**
62 * Interface to report allocated logical address.
63 */
64 interface AllocateAddressCallback {
65 /**
66 * Called when a new logical address is allocated.
67 *
68 * @param deviceType requested device type to allocate logical address
69 * @param logicalAddress allocated logical address. If it is
Jungshik Jang42230722014-07-07 17:40:25 +090070 * {@link Constants#ADDR_UNREGISTERED}, it means that
Jungshik Jang3ee65722014-06-03 16:22:30 +090071 * it failed to allocate logical address for the given device type
72 */
73 void onAllocated(int deviceType, int logicalAddress);
74 }
75
Jungshik Jang3f74ab02014-04-30 14:31:02 +090076 private static final byte[] EMPTY_BODY = EmptyArray.BYTE;
77
Jungshik Jang3f74ab02014-04-30 14:31:02 +090078 private static final int NUM_LOGICAL_ADDRESS = 16;
79
Nick Chalkob9e48e22018-10-23 06:59:39 -070080 private static final int MAX_CEC_MESSAGE_HISTORY = 200;
Donghyun Cho0b485b22016-12-13 17:21:01 +090081
Jungshik Jang0f8b4b72014-05-28 17:58:58 +090082 // Predicate for whether the given logical address is remote device's one or not.
83 private final Predicate<Integer> mRemoteDeviceAddressPredicate = new Predicate<Integer>() {
84 @Override
Paul Duffinca4964c2017-02-07 15:04:10 +000085 public boolean test(Integer address) {
Jungshik Jang0f8b4b72014-05-28 17:58:58 +090086 return !isAllocatedLocalDeviceAddress(address);
87 }
88 };
89
90 // Predicate whether the given logical address is system audio's one or not
91 private final Predicate<Integer> mSystemAudioAddressPredicate = new Predicate<Integer>() {
92 @Override
Paul Duffinca4964c2017-02-07 15:04:10 +000093 public boolean test(Integer address) {
Jinsuk Kimc0c20d02014-07-04 14:34:31 +090094 return HdmiUtils.getTypeFromAddress(address) == Constants.ADDR_AUDIO_SYSTEM;
Jungshik Jang0f8b4b72014-05-28 17:58:58 +090095 }
96 };
97
Jungshik Jang0792d372014-04-23 17:57:26 +090098 // Handler instance to process synchronous I/O (mainly send) message.
99 private Handler mIoHandler;
100
101 // Handler instance to process various messages coming from other CEC
102 // device or issued by internal state change.
Jungshik Jange9c77c82014-04-24 20:30:09 +0900103 private Handler mControlHandler;
Jungshik Jang0792d372014-04-23 17:57:26 +0900104
105 // Stores the pointer to the native implementation of the service that
106 // interacts with HAL.
Jungshik Jang02bb4262014-05-23 16:48:31 +0900107 private volatile long mNativePtr;
Jungshik Jang0792d372014-04-23 17:57:26 +0900108
Jungshik Jang7df52862014-08-11 14:35:27 +0900109 private final HdmiControlService mService;
Jungshik Janga1fa91f2014-05-08 20:56:41 +0900110
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +0900111 // Stores the local CEC devices in the system. Device type is used for key.
112 private final SparseArray<HdmiCecLocalDevice> mLocalDevices = new SparseArray<>();
Jungshik Jang7d9a8432014-04-29 15:12:43 +0900113
Donghyun Cho0b485b22016-12-13 17:21:01 +0900114 // Stores recent CEC messages history for debugging purpose.
115 private final ArrayBlockingQueue<MessageHistoryRecord> mMessageHistory =
116 new ArrayBlockingQueue<>(MAX_CEC_MESSAGE_HISTORY);
117
Amy28a9a9e2018-05-17 17:57:33 -0700118 private final NativeWrapper mNativeWrapperImpl;
119
Amy8027c942018-09-18 10:23:20 -0700120 /** List of logical addresses that should not be assigned to the current device.
121 *
122 * <p>Parsed from {@link Constants#PROPERTY_HDMI_CEC_NEVER_ASSIGN_LOGICAL_ADDRESSES}
123 */
124 private final List<Integer> mNeverAssignLogicalAddresses;
125
Jungshik Jang0792d372014-04-23 17:57:26 +0900126 // Private constructor. Use HdmiCecController.create().
Amy28a9a9e2018-05-17 17:57:33 -0700127 private HdmiCecController(HdmiControlService service, NativeWrapper nativeWrapper) {
Jungshik Jang7df52862014-08-11 14:35:27 +0900128 mService = service;
Amy28a9a9e2018-05-17 17:57:33 -0700129 mNativeWrapperImpl = nativeWrapper;
Amy8027c942018-09-18 10:23:20 -0700130 mNeverAssignLogicalAddresses = mService.getIntList(SystemProperties.get(
131 Constants.PROPERTY_HDMI_CEC_NEVER_ASSIGN_LOGICAL_ADDRESSES));
Jungshik Jang0792d372014-04-23 17:57:26 +0900132 }
133
134 /**
135 * A factory method to get {@link HdmiCecController}. If it fails to initialize
136 * inner device or has no device it will return {@code null}.
137 *
138 * <p>Declared as package-private, accessed by {@link HdmiControlService} only.
Jungshik Jange9c77c82014-04-24 20:30:09 +0900139 * @param service {@link HdmiControlService} instance used to create internal handler
140 * and to pass callback for incoming message or event.
Jungshik Jang0792d372014-04-23 17:57:26 +0900141 * @return {@link HdmiCecController} if device is initialized successfully. Otherwise,
142 * returns {@code null}.
143 */
Jungshik Jange9c77c82014-04-24 20:30:09 +0900144 static HdmiCecController create(HdmiControlService service) {
Amy28a9a9e2018-05-17 17:57:33 -0700145 return createWithNativeWrapper(service, new NativeWrapperImpl());
146 }
Jungshik Jang0792d372014-04-23 17:57:26 +0900147
Amy28a9a9e2018-05-17 17:57:33 -0700148 /**
149 * A factory method with injection of native methods for testing.
150 */
151 static HdmiCecController createWithNativeWrapper(
152 HdmiControlService service, NativeWrapper nativeWrapper) {
153 HdmiCecController controller = new HdmiCecController(service, nativeWrapper);
154 long nativePtr = nativeWrapper
155 .nativeInit(controller, service.getServiceLooper().getQueue());
156 if (nativePtr == 0L) {
157 controller = null;
158 return null;
159 }
160
161 controller.init(nativePtr);
162 return controller;
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900163 }
164
Jungshik Jang7df52862014-08-11 14:35:27 +0900165 private void init(long nativePtr) {
Yuncheol Heof1702482014-11-27 19:52:01 +0900166 mIoHandler = new Handler(mService.getIoLooper());
Jungshik Jang7df52862014-08-11 14:35:27 +0900167 mControlHandler = new Handler(mService.getServiceLooper());
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900168 mNativePtr = nativePtr;
Jungshik Jang0792d372014-04-23 17:57:26 +0900169 }
170
Jungshik Janga5b74142014-06-23 18:03:10 +0900171 @ServiceThreadOnly
Jungshik Jang3ee65722014-06-03 16:22:30 +0900172 void addLocalDevice(int deviceType, HdmiCecLocalDevice device) {
Jungshik Janga5b74142014-06-23 18:03:10 +0900173 assertRunOnServiceThread();
Jungshik Jang3ee65722014-06-03 16:22:30 +0900174 mLocalDevices.put(deviceType, device);
Jungshik Jang3f74ab02014-04-30 14:31:02 +0900175 }
176
177 /**
178 * Allocate a new logical address of the given device type. Allocated
Jungshik Jang3ee65722014-06-03 16:22:30 +0900179 * address will be reported through {@link AllocateAddressCallback}.
Jungshik Jang3f74ab02014-04-30 14:31:02 +0900180 *
181 * <p> Declared as package-private, accessed by {@link HdmiControlService} only.
182 *
183 * @param deviceType type of device to used to determine logical address
184 * @param preferredAddress a logical address preferred to be allocated.
Jungshik Jang42230722014-07-07 17:40:25 +0900185 * If sets {@link Constants#ADDR_UNREGISTERED}, scans
Jungshik Jang3f74ab02014-04-30 14:31:02 +0900186 * the smallest logical address matched with the given device type.
187 * Otherwise, scan address will start from {@code preferredAddress}
188 * @param callback callback interface to report allocated logical address to caller
189 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900190 @ServiceThreadOnly
Jungshik Jangd643f762014-05-22 19:28:09 +0900191 void allocateLogicalAddress(final int deviceType, final int preferredAddress,
Jungshik Jang3ee65722014-06-03 16:22:30 +0900192 final AllocateAddressCallback callback) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900193 assertRunOnServiceThread();
194
Jungshik Jangd643f762014-05-22 19:28:09 +0900195 runOnIoThread(new Runnable() {
196 @Override
197 public void run() {
198 handleAllocateLogicalAddress(deviceType, preferredAddress, callback);
199 }
200 });
201 }
202
Jungshik Janga5b74142014-06-23 18:03:10 +0900203 @IoThreadOnly
Jungshik Jangd643f762014-05-22 19:28:09 +0900204 private void handleAllocateLogicalAddress(final int deviceType, int preferredAddress,
Jungshik Jang3ee65722014-06-03 16:22:30 +0900205 final AllocateAddressCallback callback) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900206 assertRunOnIoThread();
Jungshik Jangd643f762014-05-22 19:28:09 +0900207 int startAddress = preferredAddress;
208 // If preferred address is "unregistered", start address will be the smallest
209 // address matched with the given device type.
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900210 if (preferredAddress == Constants.ADDR_UNREGISTERED) {
Jungshik Jangd643f762014-05-22 19:28:09 +0900211 for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) {
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900212 if (deviceType == HdmiUtils.getTypeFromAddress(i)) {
Jungshik Jangd643f762014-05-22 19:28:09 +0900213 startAddress = i;
214 break;
215 }
216 }
217 }
218
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900219 int logicalAddress = Constants.ADDR_UNREGISTERED;
Jungshik Jangd643f762014-05-22 19:28:09 +0900220 // Iterates all possible addresses which has the same device type.
221 for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) {
222 int curAddress = (startAddress + i) % NUM_LOGICAL_ADDRESS;
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900223 if (curAddress != Constants.ADDR_UNREGISTERED
Amy8027c942018-09-18 10:23:20 -0700224 && deviceType == HdmiUtils.getTypeFromAddress(curAddress)
225 && !mNeverAssignLogicalAddresses.contains(curAddress)) {
Donghyun Chodbf9f9c2016-07-04 20:39:26 +0900226 boolean acked = false;
Jungshik Jang8e93c842014-08-06 15:48:33 +0900227 for (int j = 0; j < HdmiConfig.ADDRESS_ALLOCATION_RETRY; ++j) {
Donghyun Chodbf9f9c2016-07-04 20:39:26 +0900228 if (sendPollMessage(curAddress, curAddress, 1)) {
229 acked = true;
230 break;
Jungshik Jang8e93c842014-08-06 15:48:33 +0900231 }
232 }
Donghyun Chodbf9f9c2016-07-04 20:39:26 +0900233 // If sending <Polling Message> failed, it becomes new logical address for the
234 // device because no device uses it as logical address of the device.
235 if (!acked) {
Jungshik Jangd643f762014-05-22 19:28:09 +0900236 logicalAddress = curAddress;
237 break;
238 }
239 }
240 }
241
242 final int assignedAddress = logicalAddress;
Jungshik Jang2e8f1b62014-09-03 08:28:02 +0900243 HdmiLogger.debug("New logical address for device [%d]: [preferred:%d, assigned:%d]",
244 deviceType, preferredAddress, assignedAddress);
Jungshik Jangd643f762014-05-22 19:28:09 +0900245 if (callback != null) {
246 runOnServiceThread(new Runnable() {
Jungshik Jang8e93c842014-08-06 15:48:33 +0900247 @Override
Jungshik Jangd643f762014-05-22 19:28:09 +0900248 public void run() {
249 callback.onAllocated(deviceType, assignedAddress);
250 }
251 });
252 }
Jungshik Jang3f74ab02014-04-30 14:31:02 +0900253 }
254
Jungshik Jange9c77c82014-04-24 20:30:09 +0900255 private static byte[] buildBody(int opcode, byte[] params) {
256 byte[] body = new byte[params.length + 1];
257 body[0] = (byte) opcode;
258 System.arraycopy(params, 0, body, 1, params.length);
259 return body;
260 }
Jungshik Jang0792d372014-04-23 17:57:26 +0900261
Jungshik Jang7d9a8432014-04-29 15:12:43 +0900262
Jinsuk Kim0340bbc2014-06-05 11:07:47 +0900263 HdmiPortInfo[] getPortInfos() {
Amy28a9a9e2018-05-17 17:57:33 -0700264 return mNativeWrapperImpl.nativeGetPortInfos(mNativePtr);
Jinsuk Kim0340bbc2014-06-05 11:07:47 +0900265 }
266
Jungshik Janga9095ba2014-05-02 13:06:22 +0900267 /**
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +0900268 * Return the locally hosted logical device of a given type.
269 *
270 * @param deviceType logical device type
271 * @return {@link HdmiCecLocalDevice} instance if the instance of the type is available;
272 * otherwise null.
273 */
274 HdmiCecLocalDevice getLocalDevice(int deviceType) {
275 return mLocalDevices.get(deviceType);
276 }
277
278 /**
Jungshik Janga9095ba2014-05-02 13:06:22 +0900279 * Add a new logical address to the device. Device's HW should be notified
280 * when a new logical address is assigned to a device, so that it can accept
281 * a command having available destinations.
282 *
283 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
284 *
285 * @param newLogicalAddress a logical address to be added
286 * @return 0 on success. Otherwise, returns negative value
287 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900288 @ServiceThreadOnly
Jungshik Janga9095ba2014-05-02 13:06:22 +0900289 int addLogicalAddress(int newLogicalAddress) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900290 assertRunOnServiceThread();
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900291 if (HdmiUtils.isValidAddress(newLogicalAddress)) {
Amy28a9a9e2018-05-17 17:57:33 -0700292 return mNativeWrapperImpl.nativeAddLogicalAddress(mNativePtr, newLogicalAddress);
Jungshik Janga9095ba2014-05-02 13:06:22 +0900293 } else {
Donghyun Chobc6e3722016-11-04 05:25:52 +0900294 return Result.FAILURE_INVALID_ARGS;
Jungshik Janga9095ba2014-05-02 13:06:22 +0900295 }
296 }
297
298 /**
299 * Clear all logical addresses registered in the device.
300 *
301 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
302 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900303 @ServiceThreadOnly
Jungshik Janga9095ba2014-05-02 13:06:22 +0900304 void clearLogicalAddress() {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900305 assertRunOnServiceThread();
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +0900306 for (int i = 0; i < mLocalDevices.size(); ++i) {
307 mLocalDevices.valueAt(i).clearAddress();
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900308 }
Amy28a9a9e2018-05-17 17:57:33 -0700309 mNativeWrapperImpl.nativeClearLogicalAddress(mNativePtr);
Jungshik Janga9095ba2014-05-02 13:06:22 +0900310 }
311
Jungshik Jang4fc1d102014-07-09 19:24:50 +0900312 @ServiceThreadOnly
313 void clearLocalDevices() {
314 assertRunOnServiceThread();
315 mLocalDevices.clear();
316 }
317
Jungshik Janga9095ba2014-05-02 13:06:22 +0900318 /**
319 * Return the physical address of the device.
320 *
321 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
322 *
323 * @return CEC physical address of the device. The range of success address
324 * is between 0x0000 and 0xFFFF. If failed it returns -1
325 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900326 @ServiceThreadOnly
Jungshik Janga9095ba2014-05-02 13:06:22 +0900327 int getPhysicalAddress() {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900328 assertRunOnServiceThread();
Amy28a9a9e2018-05-17 17:57:33 -0700329 return mNativeWrapperImpl.nativeGetPhysicalAddress(mNativePtr);
Jungshik Janga9095ba2014-05-02 13:06:22 +0900330 }
331
332 /**
333 * Return CEC version of the device.
334 *
335 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
336 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900337 @ServiceThreadOnly
Jungshik Janga9095ba2014-05-02 13:06:22 +0900338 int getVersion() {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900339 assertRunOnServiceThread();
Amy28a9a9e2018-05-17 17:57:33 -0700340 return mNativeWrapperImpl.nativeGetVersion(mNativePtr);
Jungshik Janga9095ba2014-05-02 13:06:22 +0900341 }
342
343 /**
344 * Return vendor id of the device.
345 *
346 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
347 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900348 @ServiceThreadOnly
Jungshik Janga9095ba2014-05-02 13:06:22 +0900349 int getVendorId() {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900350 assertRunOnServiceThread();
Amy28a9a9e2018-05-17 17:57:33 -0700351 return mNativeWrapperImpl.nativeGetVendorId(mNativePtr);
Jungshik Janga9095ba2014-05-02 13:06:22 +0900352 }
353
Jungshik Jang02bb4262014-05-23 16:48:31 +0900354 /**
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900355 * Set an option to CEC HAL.
Jungshik Jang092b4452014-06-11 15:19:17 +0900356 *
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900357 * @param flag key of option
Donghyun Chobc6e3722016-11-04 05:25:52 +0900358 * @param enabled whether to enable/disable the given option.
Jungshik Jang092b4452014-06-11 15:19:17 +0900359 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900360 @ServiceThreadOnly
Donghyun Chobc6e3722016-11-04 05:25:52 +0900361 void setOption(int flag, boolean enabled) {
Jungshik Jang092b4452014-06-11 15:19:17 +0900362 assertRunOnServiceThread();
Donghyun Chobc6e3722016-11-04 05:25:52 +0900363 HdmiLogger.debug("setOption: [flag:%d, enabled:%b]", flag, enabled);
Amy28a9a9e2018-05-17 17:57:33 -0700364 mNativeWrapperImpl.nativeSetOption(mNativePtr, flag, enabled);
Donghyun Chobc6e3722016-11-04 05:25:52 +0900365 }
366
367 /**
368 * Informs CEC HAL about the current system language.
369 *
370 * @param language Three-letter code defined in ISO/FDIS 639-2. Must be lowercase letters.
371 */
372 @ServiceThreadOnly
373 void setLanguage(String language) {
374 assertRunOnServiceThread();
375 if (!LanguageTag.isLanguage(language)) {
376 return;
377 }
Amy28a9a9e2018-05-17 17:57:33 -0700378 mNativeWrapperImpl.nativeSetLanguage(mNativePtr, language);
Jungshik Jang092b4452014-06-11 15:19:17 +0900379 }
380
381 /**
382 * Configure ARC circuit in the hardware logic to start or stop the feature.
383 *
Jinsuk Kim1481a422014-12-17 16:15:05 +0900384 * @param port ID of HDMI port to which AVR is connected
Jungshik Jang092b4452014-06-11 15:19:17 +0900385 * @param enabled whether to enable/disable ARC
386 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900387 @ServiceThreadOnly
Donghyun Chobc6e3722016-11-04 05:25:52 +0900388 void enableAudioReturnChannel(int port, boolean enabled) {
Jungshik Jang092b4452014-06-11 15:19:17 +0900389 assertRunOnServiceThread();
Amy28a9a9e2018-05-17 17:57:33 -0700390 mNativeWrapperImpl.nativeEnableAudioReturnChannel(mNativePtr, port, enabled);
Jungshik Jang092b4452014-06-11 15:19:17 +0900391 }
392
393 /**
394 * Return the connection status of the specified port
395 *
396 * @param port port number to check connection status
397 * @return true if connected; otherwise, return false
398 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900399 @ServiceThreadOnly
Jungshik Jang092b4452014-06-11 15:19:17 +0900400 boolean isConnected(int port) {
401 assertRunOnServiceThread();
Amy28a9a9e2018-05-17 17:57:33 -0700402 return mNativeWrapperImpl.nativeIsConnected(mNativePtr, port);
Jungshik Jang092b4452014-06-11 15:19:17 +0900403 }
404
405 /**
Jungshik Jang02bb4262014-05-23 16:48:31 +0900406 * Poll all remote devices. It sends &lt;Polling Message&gt; to all remote
407 * devices.
408 *
409 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
410 *
411 * @param callback an interface used to get a list of all remote devices' address
Jungshik Jang1de51422014-07-03 11:14:26 +0900412 * @param sourceAddress a logical address of source device where sends polling message
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900413 * @param pickStrategy strategy how to pick polling candidates
Jungshik Jang02bb4262014-05-23 16:48:31 +0900414 * @param retryCount the number of retry used to send polling message to remote devices
415 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900416 @ServiceThreadOnly
Jungshik Jang1de51422014-07-03 11:14:26 +0900417 void pollDevices(DevicePollingCallback callback, int sourceAddress, int pickStrategy,
418 int retryCount) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900419 assertRunOnServiceThread();
Jungshik Jang02bb4262014-05-23 16:48:31 +0900420
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900421 // Extract polling candidates. No need to poll against local devices.
422 List<Integer> pollingCandidates = pickPollCandidates(pickStrategy);
Jungshik Jangb3ecb722014-09-11 16:09:45 +0900423 ArrayList<Integer> allocated = new ArrayList<>();
424 runDevicePolling(sourceAddress, pollingCandidates, retryCount, callback, allocated);
Jungshik Jang02bb4262014-05-23 16:48:31 +0900425 }
426
Jungshik Jangcc5ef8c2014-05-27 13:27:36 +0900427 /**
428 * Return a list of all {@link HdmiCecLocalDevice}s.
429 *
430 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
431 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900432 @ServiceThreadOnly
Jungshik Jangcc5ef8c2014-05-27 13:27:36 +0900433 List<HdmiCecLocalDevice> getLocalDeviceList() {
434 assertRunOnServiceThread();
Jungshik Jang79c58a42014-06-16 16:45:36 +0900435 return HdmiUtils.sparseArrayToList(mLocalDevices);
Jungshik Jangcc5ef8c2014-05-27 13:27:36 +0900436 }
437
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900438 private List<Integer> pickPollCandidates(int pickStrategy) {
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900439 int strategy = pickStrategy & Constants.POLL_STRATEGY_MASK;
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900440 Predicate<Integer> pickPredicate = null;
441 switch (strategy) {
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900442 case Constants.POLL_STRATEGY_SYSTEM_AUDIO:
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900443 pickPredicate = mSystemAudioAddressPredicate;
444 break;
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900445 case Constants.POLL_STRATEGY_REMOTES_DEVICES:
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900446 default: // The default is POLL_STRATEGY_REMOTES_DEVICES.
447 pickPredicate = mRemoteDeviceAddressPredicate;
448 break;
449 }
450
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900451 int iterationStrategy = pickStrategy & Constants.POLL_ITERATION_STRATEGY_MASK;
Jungshik Jangb3ecb722014-09-11 16:09:45 +0900452 LinkedList<Integer> pollingCandidates = new LinkedList<>();
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900453 switch (iterationStrategy) {
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900454 case Constants.POLL_ITERATION_IN_ORDER:
455 for (int i = Constants.ADDR_TV; i <= Constants.ADDR_SPECIFIC_USE; ++i) {
Paul Duffinca4964c2017-02-07 15:04:10 +0000456 if (pickPredicate.test(i)) {
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900457 pollingCandidates.add(i);
458 }
459 }
460 break;
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900461 case Constants.POLL_ITERATION_REVERSE_ORDER:
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900462 default: // The default is reverse order.
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900463 for (int i = Constants.ADDR_SPECIFIC_USE; i >= Constants.ADDR_TV; --i) {
Paul Duffinca4964c2017-02-07 15:04:10 +0000464 if (pickPredicate.test(i)) {
Jungshik Jang0f8b4b72014-05-28 17:58:58 +0900465 pollingCandidates.add(i);
466 }
467 }
468 break;
469 }
470 return pollingCandidates;
471 }
472
Jungshik Janga5b74142014-06-23 18:03:10 +0900473 @ServiceThreadOnly
Jungshik Jang02bb4262014-05-23 16:48:31 +0900474 private boolean isAllocatedLocalDeviceAddress(int address) {
Jungshik Janga5b74142014-06-23 18:03:10 +0900475 assertRunOnServiceThread();
Jinsuk Kim7fe2ae02014-05-26 17:33:05 +0900476 for (int i = 0; i < mLocalDevices.size(); ++i) {
477 if (mLocalDevices.valueAt(i).isAddressOf(address)) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900478 return true;
479 }
480 }
481 return false;
482 }
483
Jungshik Janga5b74142014-06-23 18:03:10 +0900484 @ServiceThreadOnly
Jungshik Jang1de51422014-07-03 11:14:26 +0900485 private void runDevicePolling(final int sourceAddress,
486 final List<Integer> candidates, final int retryCount,
Jungshik Jangb3ecb722014-09-11 16:09:45 +0900487 final DevicePollingCallback callback, final List<Integer> allocated) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900488 assertRunOnServiceThread();
Jungshik Jangb3ecb722014-09-11 16:09:45 +0900489 if (candidates.isEmpty()) {
490 if (callback != null) {
491 HdmiLogger.debug("[P]:AllocatedAddress=%s", allocated.toString());
492 callback.onPollingFinished(allocated);
493 }
494 return;
495 }
496
497 final Integer candidate = candidates.remove(0);
498 // Proceed polling action for the next address once polling action for the
499 // previous address is done.
Jungshik Jang02bb4262014-05-23 16:48:31 +0900500 runOnIoThread(new Runnable() {
501 @Override
502 public void run() {
Jungshik Jangb3ecb722014-09-11 16:09:45 +0900503 if (sendPollMessage(sourceAddress, candidate, retryCount)) {
504 allocated.add(candidate);
505 }
506 runOnServiceThread(new Runnable() {
507 @Override
508 public void run() {
509 runDevicePolling(sourceAddress, candidates, retryCount, callback,
510 allocated);
Jungshik Jang02bb4262014-05-23 16:48:31 +0900511 }
Jungshik Jangb3ecb722014-09-11 16:09:45 +0900512 });
Jungshik Jang02bb4262014-05-23 16:48:31 +0900513 }
514 });
515 }
516
Jungshik Janga5b74142014-06-23 18:03:10 +0900517 @IoThreadOnly
Jungshik Jang1de51422014-07-03 11:14:26 +0900518 private boolean sendPollMessage(int sourceAddress, int destinationAddress, int retryCount) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900519 assertRunOnIoThread();
520 for (int i = 0; i < retryCount; ++i) {
Jungshik Jang1de51422014-07-03 11:14:26 +0900521 // <Polling Message> is a message which has empty body.
Donghyun Chodbf9f9c2016-07-04 20:39:26 +0900522 int ret =
Amy28a9a9e2018-05-17 17:57:33 -0700523 mNativeWrapperImpl.nativeSendCecCommand(
524 mNativePtr, sourceAddress, destinationAddress, EMPTY_BODY);
Donghyun Chobc6e3722016-11-04 05:25:52 +0900525 if (ret == SendMessageResult.SUCCESS) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900526 return true;
Donghyun Chobc6e3722016-11-04 05:25:52 +0900527 } else if (ret != SendMessageResult.NACK) {
Donghyun Chodbf9f9c2016-07-04 20:39:26 +0900528 // Unusual failure
529 HdmiLogger.warning("Failed to send a polling message(%d->%d) with return code %d",
530 sourceAddress, destinationAddress, ret);
Jungshik Jang02bb4262014-05-23 16:48:31 +0900531 }
532 }
533 return false;
534 }
535
536 private void assertRunOnIoThread() {
537 if (Looper.myLooper() != mIoHandler.getLooper()) {
538 throw new IllegalStateException("Should run on io thread.");
539 }
540 }
541
542 private void assertRunOnServiceThread() {
543 if (Looper.myLooper() != mControlHandler.getLooper()) {
544 throw new IllegalStateException("Should run on service thread.");
545 }
546 }
547
548 // Run a Runnable on IO thread.
549 // It should be careful to access member variables on IO thread because
550 // it can be accessed from system thread as well.
Jungshik Jangd643f762014-05-22 19:28:09 +0900551 private void runOnIoThread(Runnable runnable) {
552 mIoHandler.post(runnable);
553 }
554
555 private void runOnServiceThread(Runnable runnable) {
556 mControlHandler.post(runnable);
557 }
558
Yuncheol Heof1702482014-11-27 19:52:01 +0900559 @ServiceThreadOnly
560 void flush(final Runnable runnable) {
561 assertRunOnServiceThread();
562 runOnIoThread(new Runnable() {
563 @Override
564 public void run() {
565 // This ensures the runnable for cleanup is performed after all the pending
566 // commands are processed by IO thread.
567 runOnServiceThread(runnable);
568 }
569 });
570 }
571
Jungshik Janga1fa91f2014-05-08 20:56:41 +0900572 private boolean isAcceptableAddress(int address) {
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900573 // Can access command targeting devices available in local device or broadcast command.
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900574 if (address == Constants.ADDR_BROADCAST) {
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900575 return true;
576 }
Jungshik Jang02bb4262014-05-23 16:48:31 +0900577 return isAllocatedLocalDeviceAddress(address);
Jungshik Janga1fa91f2014-05-08 20:56:41 +0900578 }
579
Jungshik Janga5b74142014-06-23 18:03:10 +0900580 @ServiceThreadOnly
Jungshik Jange9c77c82014-04-24 20:30:09 +0900581 private void onReceiveCommand(HdmiCecMessage message) {
Jungshik Jang02bb4262014-05-23 16:48:31 +0900582 assertRunOnServiceThread();
Jinping Wange55d3d42019-04-16 17:20:51 +0800583 if ((isAcceptableAddress(message.getDestination())
584 || !mService.isAddressAllocated())
585 && mService.handleCecCommand(message)) {
Jungshik Janga1fa91f2014-05-08 20:56:41 +0900586 return;
587 }
Yuncheol Heo6aae6522014-08-05 14:48:37 +0900588 // Not handled message, so we will reply it with <Feature Abort>.
589 maySendFeatureAbortCommand(message, Constants.ABORT_UNRECOGNIZED_OPCODE);
590 }
Jungshik Jang1827fdc2014-07-17 13:58:14 +0900591
Yuncheol Heo6aae6522014-08-05 14:48:37 +0900592 @ServiceThreadOnly
593 void maySendFeatureAbortCommand(HdmiCecMessage message, int reason) {
594 assertRunOnServiceThread();
595 // Swap the source and the destination.
596 int src = message.getDestination();
597 int dest = message.getSource();
598 if (src == Constants.ADDR_BROADCAST || dest == Constants.ADDR_UNREGISTERED) {
599 // Don't reply <Feature Abort> from the unregistered devices or for the broadcasted
600 // messages. See CEC 12.2 Protocol General Rules for detail.
601 return;
602 }
603 int originalOpcode = message.getOpcode();
604 if (originalOpcode == Constants.MESSAGE_FEATURE_ABORT) {
605 return;
606 }
607 sendCommand(
608 HdmiCecMessageBuilder.buildFeatureAbortCommand(src, dest, originalOpcode, reason));
Jungshik Jange9c77c82014-04-24 20:30:09 +0900609 }
610
Jungshik Janga5b74142014-06-23 18:03:10 +0900611 @ServiceThreadOnly
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900612 void sendCommand(HdmiCecMessage cecMessage) {
Jungshik Janga5b74142014-06-23 18:03:10 +0900613 assertRunOnServiceThread();
Jinsuk Kim2918e9e2014-05-20 16:45:45 +0900614 sendCommand(cecMessage, null);
615 }
616
Jungshik Janga5b74142014-06-23 18:03:10 +0900617 @ServiceThreadOnly
Jungshik Jangd643f762014-05-22 19:28:09 +0900618 void sendCommand(final HdmiCecMessage cecMessage,
619 final HdmiControlService.SendMessageCallback callback) {
Jungshik Janga5b74142014-06-23 18:03:10 +0900620 assertRunOnServiceThread();
Donghyun Cho0b485b22016-12-13 17:21:01 +0900621 addMessageToHistory(false /* isReceived */, cecMessage);
Jungshik Jangd643f762014-05-22 19:28:09 +0900622 runOnIoThread(new Runnable() {
623 @Override
624 public void run() {
Jungshik Jang2e8f1b62014-09-03 08:28:02 +0900625 HdmiLogger.debug("[S]:" + cecMessage);
Jungshik Jangd643f762014-05-22 19:28:09 +0900626 byte[] body = buildBody(cecMessage.getOpcode(), cecMessage.getParams());
Jungshik Jang8ed86c42014-07-11 11:56:46 +0900627 int i = 0;
Donghyun Chobc6e3722016-11-04 05:25:52 +0900628 int errorCode = SendMessageResult.SUCCESS;
Jungshik Jang8ed86c42014-07-11 11:56:46 +0900629 do {
Amy28a9a9e2018-05-17 17:57:33 -0700630 errorCode = mNativeWrapperImpl.nativeSendCecCommand(mNativePtr,
631 cecMessage.getSource(), cecMessage.getDestination(), body);
Donghyun Chobc6e3722016-11-04 05:25:52 +0900632 if (errorCode == SendMessageResult.SUCCESS) {
Jungshik Jang8ed86c42014-07-11 11:56:46 +0900633 break;
634 }
635 } while (i++ < HdmiConfig.RETRANSMISSION_COUNT);
636
637 final int finalError = errorCode;
Donghyun Chobc6e3722016-11-04 05:25:52 +0900638 if (finalError != SendMessageResult.SUCCESS) {
Donghyun Cho0b485b22016-12-13 17:21:01 +0900639 Slog.w(TAG, "Failed to send " + cecMessage + " with errorCode=" + finalError);
Yuncheol Heoece603b2014-05-23 20:10:19 +0900640 }
Jungshik Jangd643f762014-05-22 19:28:09 +0900641 if (callback != null) {
642 runOnServiceThread(new Runnable() {
643 @Override
644 public void run() {
Jungshik Jang8ed86c42014-07-11 11:56:46 +0900645 callback.onSendCompleted(finalError);
Jungshik Jangd643f762014-05-22 19:28:09 +0900646 }
647 });
648 }
649 }
650 });
Jungshik Jange9c77c82014-04-24 20:30:09 +0900651 }
652
Jungshik Jang0792d372014-04-23 17:57:26 +0900653 /**
Jungshik Jange9c77c82014-04-24 20:30:09 +0900654 * Called by native when incoming CEC message arrived.
Jungshik Jang0792d372014-04-23 17:57:26 +0900655 */
Jungshik Janga5b74142014-06-23 18:03:10 +0900656 @ServiceThreadOnly
Jungshik Jange9c77c82014-04-24 20:30:09 +0900657 private void handleIncomingCecCommand(int srcAddress, int dstAddress, byte[] body) {
Jungshik Jang4085d0e2014-05-27 19:52:39 +0900658 assertRunOnServiceThread();
Jungshik Jangc94ac5c2014-08-27 13:48:37 +0900659 HdmiCecMessage command = HdmiCecMessageBuilder.of(srcAddress, dstAddress, body);
Jungshik Jang2e8f1b62014-09-03 08:28:02 +0900660 HdmiLogger.debug("[R]:" + command);
Donghyun Cho0b485b22016-12-13 17:21:01 +0900661 addMessageToHistory(true /* isReceived */, command);
Jungshik Jangc94ac5c2014-08-27 13:48:37 +0900662 onReceiveCommand(command);
Jungshik Jange9c77c82014-04-24 20:30:09 +0900663 }
664
665 /**
666 * Called by native when a hotplug event issues.
667 */
Jungshik Jang42230722014-07-07 17:40:25 +0900668 @ServiceThreadOnly
669 private void handleHotplug(int port, boolean connected) {
670 assertRunOnServiceThread();
Jungshik Jang2e8f1b62014-09-03 08:28:02 +0900671 HdmiLogger.debug("Hotplug event:[port:%d, connected:%b]", port, connected);
Jungshik Jang42230722014-07-07 17:40:25 +0900672 mService.onHotplug(port, connected);
Jungshik Jang0792d372014-04-23 17:57:26 +0900673 }
674
Donghyun Cho0b485b22016-12-13 17:21:01 +0900675 @ServiceThreadOnly
676 private void addMessageToHistory(boolean isReceived, HdmiCecMessage message) {
677 assertRunOnServiceThread();
678 MessageHistoryRecord record = new MessageHistoryRecord(isReceived, message);
679 if (!mMessageHistory.offer(record)) {
680 mMessageHistory.poll();
681 mMessageHistory.offer(record);
682 }
683 }
684
Terry Heo959d2db2014-08-28 16:45:41 +0900685 void dump(final IndentingPrintWriter pw) {
686 for (int i = 0; i < mLocalDevices.size(); ++i) {
Nick Chalkob9e48e22018-10-23 06:59:39 -0700687 pw.println("HdmiCecLocalDevice #" + mLocalDevices.keyAt(i) + ":");
Terry Heo959d2db2014-08-28 16:45:41 +0900688 pw.increaseIndent();
689 mLocalDevices.valueAt(i).dump(pw);
690 pw.decreaseIndent();
691 }
Donghyun Cho0b485b22016-12-13 17:21:01 +0900692 pw.println("CEC message history:");
693 pw.increaseIndent();
694 final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
695 for (MessageHistoryRecord record : mMessageHistory) {
696 record.dump(pw, sdf);
697 }
698 pw.decreaseIndent();
Terry Heo959d2db2014-08-28 16:45:41 +0900699 }
700
Amy28a9a9e2018-05-17 17:57:33 -0700701 protected interface NativeWrapper {
702 long nativeInit(HdmiCecController handler, MessageQueue messageQueue);
703 int nativeSendCecCommand(long controllerPtr, int srcAddress, int dstAddress, byte[] body);
704 int nativeAddLogicalAddress(long controllerPtr, int logicalAddress);
705 void nativeClearLogicalAddress(long controllerPtr);
706 int nativeGetPhysicalAddress(long controllerPtr);
707 int nativeGetVersion(long controllerPtr);
708 int nativeGetVendorId(long controllerPtr);
709 HdmiPortInfo[] nativeGetPortInfos(long controllerPtr);
710 void nativeSetOption(long controllerPtr, int flag, boolean enabled);
711 void nativeSetLanguage(long controllerPtr, String language);
712 void nativeEnableAudioReturnChannel(long controllerPtr, int port, boolean flag);
713 boolean nativeIsConnected(long controllerPtr, int port);
714 }
715
Jungshik Jang4085d0e2014-05-27 19:52:39 +0900716 private static native long nativeInit(HdmiCecController handler, MessageQueue messageQueue);
Jungshik Janga9095ba2014-05-02 13:06:22 +0900717 private static native int nativeSendCecCommand(long controllerPtr, int srcAddress,
Amy28a9a9e2018-05-17 17:57:33 -0700718 int dstAddress, byte[] body);
Jungshik Janga9095ba2014-05-02 13:06:22 +0900719 private static native int nativeAddLogicalAddress(long controllerPtr, int logicalAddress);
720 private static native void nativeClearLogicalAddress(long controllerPtr);
721 private static native int nativeGetPhysicalAddress(long controllerPtr);
722 private static native int nativeGetVersion(long controllerPtr);
723 private static native int nativeGetVendorId(long controllerPtr);
Jinsuk Kim0340bbc2014-06-05 11:07:47 +0900724 private static native HdmiPortInfo[] nativeGetPortInfos(long controllerPtr);
Donghyun Chobc6e3722016-11-04 05:25:52 +0900725 private static native void nativeSetOption(long controllerPtr, int flag, boolean enabled);
726 private static native void nativeSetLanguage(long controllerPtr, String language);
Amy28a9a9e2018-05-17 17:57:33 -0700727 private static native void nativeEnableAudioReturnChannel(long controllerPtr,
728 int port, boolean flag);
Jungshik Jang092b4452014-06-11 15:19:17 +0900729 private static native boolean nativeIsConnected(long controllerPtr, int port);
Donghyun Cho0b485b22016-12-13 17:21:01 +0900730
Amy28a9a9e2018-05-17 17:57:33 -0700731 private static final class NativeWrapperImpl implements NativeWrapper {
732
733 @Override
734 public long nativeInit(HdmiCecController handler, MessageQueue messageQueue) {
735 return HdmiCecController.nativeInit(handler, messageQueue);
736 }
737
738 @Override
739 public int nativeSendCecCommand(long controllerPtr, int srcAddress, int dstAddress,
740 byte[] body) {
741 return HdmiCecController.nativeSendCecCommand(controllerPtr, srcAddress, dstAddress, body);
742 }
743
744 @Override
745 public int nativeAddLogicalAddress(long controllerPtr, int logicalAddress) {
746 return HdmiCecController.nativeAddLogicalAddress(controllerPtr, logicalAddress);
747 }
748
749 @Override
750 public void nativeClearLogicalAddress(long controllerPtr) {
751 HdmiCecController.nativeClearLogicalAddress(controllerPtr);
752 }
753
754 @Override
755 public int nativeGetPhysicalAddress(long controllerPtr) {
756 return HdmiCecController.nativeGetPhysicalAddress(controllerPtr);
757 }
758
759 @Override
760 public int nativeGetVersion(long controllerPtr) {
761 return HdmiCecController.nativeGetVersion(controllerPtr);
762 }
763
764 @Override
765 public int nativeGetVendorId(long controllerPtr) {
766 return HdmiCecController.nativeGetVendorId(controllerPtr);
767 }
768
769 @Override
770 public HdmiPortInfo[] nativeGetPortInfos(long controllerPtr) {
771 return HdmiCecController.nativeGetPortInfos(controllerPtr);
772 }
773
774 @Override
775 public void nativeSetOption(long controllerPtr, int flag, boolean enabled) {
776 HdmiCecController.nativeSetOption(controllerPtr, flag, enabled);
777 }
778
779 @Override
780 public void nativeSetLanguage(long controllerPtr, String language) {
781 HdmiCecController.nativeSetLanguage(controllerPtr, language);
782 }
783
784 @Override
785 public void nativeEnableAudioReturnChannel(long controllerPtr, int port, boolean flag) {
786 HdmiCecController.nativeEnableAudioReturnChannel(controllerPtr, port, flag);
787 }
788
789 @Override
790 public boolean nativeIsConnected(long controllerPtr, int port) {
791 return HdmiCecController.nativeIsConnected(controllerPtr, port);
792 }
793 }
794
Donghyun Cho0b485b22016-12-13 17:21:01 +0900795 private final class MessageHistoryRecord {
796 private final long mTime;
797 private final boolean mIsReceived; // true if received message and false if sent message
798 private final HdmiCecMessage mMessage;
799
800 public MessageHistoryRecord(boolean isReceived, HdmiCecMessage message) {
801 mTime = System.currentTimeMillis();
802 mIsReceived = isReceived;
803 mMessage = message;
804 }
805
806 void dump(final IndentingPrintWriter pw, SimpleDateFormat sdf) {
807 pw.print(mIsReceived ? "[R]" : "[S]");
808 pw.print(" time=");
809 pw.print(sdf.format(new Date(mTime)));
810 pw.print(" message=");
811 pw.println(mMessage);
812 }
813 }
Jungshik Jang0792d372014-04-23 17:57:26 +0900814}