HdmiCecController.java revision 2e8f1b6399089626b4f0249427626ba6e63a62ef
10792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang/* 20792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * Copyright (C) 2014 The Android Open Source Project 30792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * 40792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * Licensed under the Apache License, Version 2.0 (the "License"); 50792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * you may not use this file except in compliance with the License. 60792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * You may obtain a copy of the License at 70792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * 80792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * http://www.apache.org/licenses/LICENSE-2.0 90792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * 100792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * Unless required by applicable law or agreed to in writing, software 110792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * distributed under the License is distributed on an "AS IS" BASIS, 120792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 130792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * See the License for the specific language governing permissions and 140792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * limitations under the License. 150792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang */ 160792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang 170792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangpackage com.android.server.hdmi; 180792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang 190340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kimimport android.hardware.hdmi.HdmiPortInfo; 200792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport android.os.Handler; 2102bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jangimport android.os.Looper; 224085d0ef62554c7e139b82bca0f97161bb159f8cJungshik Jangimport android.os.MessageQueue; 23ece603b7955938d6001c376f351ca0a2219330acYuncheol Heoimport android.util.Slog; 247d9a843af83dbc75b1d170c743603198ede5a10fJungshik Jangimport android.util.SparseArray; 25e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang 26959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heoimport com.android.internal.util.IndentingPrintWriter; 270f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jangimport com.android.internal.util.Predicate; 28a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jangimport com.android.server.hdmi.HdmiAnnotations.IoThreadOnly; 29a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jangimport com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly; 3002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jangimport com.android.server.hdmi.HdmiControlService.DevicePollingCallback; 3102bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang 323f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jangimport libcore.util.EmptyArray; 333f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang 347d9a843af83dbc75b1d170c743603198ede5a10fJungshik Jangimport java.util.ArrayList; 357d9a843af83dbc75b1d170c743603198ede5a10fJungshik Jangimport java.util.List; 360792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang 370792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang/** 380792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * Manages HDMI-CEC command and behaviors. It converts user's command into CEC command 390792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * and pass it to CEC HAL so that it sends message to other device. For incoming 400792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * message it translates the message and delegates it to proper module. 410792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * 4202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang * <p>It should be careful to access member variables on IO thread because 4302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang * it can be accessed from system thread as well. 4402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang * 450792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * <p>It can be created only by {@link HdmiCecController#create} 460792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * 470792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * <p>Declared as package-private, accessed by {@link HdmiControlService} only. 480792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang */ 49a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jangfinal class HdmiCecController { 500792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang private static final String TAG = "HdmiCecController"; 510792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang 523ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang /** 533ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang * Interface to report allocated logical address. 543ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang */ 553ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang interface AllocateAddressCallback { 563ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang /** 573ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang * Called when a new logical address is allocated. 583ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang * 593ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang * @param deviceType requested device type to allocate logical address 603ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang * @param logicalAddress allocated logical address. If it is 6142230728b8212738c2351939c5577730f05a58deJungshik Jang * {@link Constants#ADDR_UNREGISTERED}, it means that 623ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang * it failed to allocate logical address for the given device type 633ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang */ 643ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang void onAllocated(int deviceType, int logicalAddress); 653ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang } 663ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang 673f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang private static final byte[] EMPTY_BODY = EmptyArray.BYTE; 683f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang 693f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang private static final int NUM_LOGICAL_ADDRESS = 16; 703f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang 710f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang // Predicate for whether the given logical address is remote device's one or not. 720f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang private final Predicate<Integer> mRemoteDeviceAddressPredicate = new Predicate<Integer>() { 730f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang @Override 740f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang public boolean apply(Integer address) { 750f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang return !isAllocatedLocalDeviceAddress(address); 760f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang } 770f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang }; 780f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang 790f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang // Predicate whether the given logical address is system audio's one or not 800f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang private final Predicate<Integer> mSystemAudioAddressPredicate = new Predicate<Integer>() { 810f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang @Override 820f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang public boolean apply(Integer address) { 83c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim return HdmiUtils.getTypeFromAddress(address) == Constants.ADDR_AUDIO_SYSTEM; 840f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang } 850f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang }; 860f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang 870792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang // Handler instance to process synchronous I/O (mainly send) message. 880792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang private Handler mIoHandler; 890792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang 900792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang // Handler instance to process various messages coming from other CEC 910792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang // device or issued by internal state change. 92e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang private Handler mControlHandler; 930792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang 940792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang // Stores the pointer to the native implementation of the service that 950792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang // interacts with HAL. 9602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang private volatile long mNativePtr; 970792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang 987df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang private final HdmiControlService mService; 99a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang 1007fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim // Stores the local CEC devices in the system. Device type is used for key. 1017fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim private final SparseArray<HdmiCecLocalDevice> mLocalDevices = new SparseArray<>(); 1027d9a843af83dbc75b1d170c743603198ede5a10fJungshik Jang 1030792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang // Private constructor. Use HdmiCecController.create(). 1047df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang private HdmiCecController(HdmiControlService service) { 1057df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang mService = service; 1060792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang } 1070792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang 1080792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang /** 1090792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * A factory method to get {@link HdmiCecController}. If it fails to initialize 1100792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * inner device or has no device it will return {@code null}. 1110792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * 1120792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * <p>Declared as package-private, accessed by {@link HdmiControlService} only. 113e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang * @param service {@link HdmiControlService} instance used to create internal handler 114e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang * and to pass callback for incoming message or event. 1150792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * @return {@link HdmiCecController} if device is initialized successfully. Otherwise, 1160792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * returns {@code null}. 1170792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang */ 118e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang static HdmiCecController create(HdmiControlService service) { 1197df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang HdmiCecController controller = new HdmiCecController(service); 1204085d0ef62554c7e139b82bca0f97161bb159f8cJungshik Jang long nativePtr = nativeInit(controller, service.getServiceLooper().getQueue()); 1210792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang if (nativePtr == 0L) { 1222918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim controller = null; 1230792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang return null; 1240792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang } 1250792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang 1267df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang controller.init(nativePtr); 1272918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim return controller; 1280792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang } 1290792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang 1307df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang private void init(long nativePtr) { 1317df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang mIoHandler = new Handler(mService.getServiceLooper()); 1327df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang mControlHandler = new Handler(mService.getServiceLooper()); 1332918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim mNativePtr = nativePtr; 134a8a5e50c6f9ba3ae0ff59eda76354e93515d6f8fJinsuk Kim } 135a8a5e50c6f9ba3ae0ff59eda76354e93515d6f8fJinsuk Kim 136a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang @ServiceThreadOnly 1373ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang void addLocalDevice(int deviceType, HdmiCecLocalDevice device) { 138a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang assertRunOnServiceThread(); 1393ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang mLocalDevices.put(deviceType, device); 1403f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang } 1413f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang 1423f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang /** 1433f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang * Allocate a new logical address of the given device type. Allocated 1443ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang * address will be reported through {@link AllocateAddressCallback}. 1453f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang * 1463f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang * <p> Declared as package-private, accessed by {@link HdmiControlService} only. 1473f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang * 1483f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang * @param deviceType type of device to used to determine logical address 1493f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang * @param preferredAddress a logical address preferred to be allocated. 15042230728b8212738c2351939c5577730f05a58deJungshik Jang * If sets {@link Constants#ADDR_UNREGISTERED}, scans 1513f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang * the smallest logical address matched with the given device type. 1523f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang * Otherwise, scan address will start from {@code preferredAddress} 1533f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang * @param callback callback interface to report allocated logical address to caller 1543f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang */ 155a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang @ServiceThreadOnly 156d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang void allocateLogicalAddress(final int deviceType, final int preferredAddress, 1573ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang final AllocateAddressCallback callback) { 15802bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang assertRunOnServiceThread(); 15902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang 160d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang runOnIoThread(new Runnable() { 161d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang @Override 162d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang public void run() { 163d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang handleAllocateLogicalAddress(deviceType, preferredAddress, callback); 164d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang } 165d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang }); 166e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang } 167e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang 168a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang @IoThreadOnly 169d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang private void handleAllocateLogicalAddress(final int deviceType, int preferredAddress, 1703ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang final AllocateAddressCallback callback) { 17102bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang assertRunOnIoThread(); 172d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang int startAddress = preferredAddress; 173d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang // If preferred address is "unregistered", start address will be the smallest 174d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang // address matched with the given device type. 175c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim if (preferredAddress == Constants.ADDR_UNREGISTERED) { 176d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) { 177c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim if (deviceType == HdmiUtils.getTypeFromAddress(i)) { 178d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang startAddress = i; 179e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang break; 180d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang } 181e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang } 182e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang } 1833f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang 184c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim int logicalAddress = Constants.ADDR_UNREGISTERED; 185d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang // Iterates all possible addresses which has the same device type. 186d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) { 187d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang int curAddress = (startAddress + i) % NUM_LOGICAL_ADDRESS; 188c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim if (curAddress != Constants.ADDR_UNREGISTERED 189c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim && deviceType == HdmiUtils.getTypeFromAddress(curAddress)) { 1908e93c84739902f5adaa499b474f39e3c4807bc1cJungshik Jang int failedPollingCount = 0; 1918e93c84739902f5adaa499b474f39e3c4807bc1cJungshik Jang for (int j = 0; j < HdmiConfig.ADDRESS_ALLOCATION_RETRY; ++j) { 1928e93c84739902f5adaa499b474f39e3c4807bc1cJungshik Jang if (!sendPollMessage(curAddress, curAddress, 1)) { 1938e93c84739902f5adaa499b474f39e3c4807bc1cJungshik Jang failedPollingCount++; 1948e93c84739902f5adaa499b474f39e3c4807bc1cJungshik Jang } 1958e93c84739902f5adaa499b474f39e3c4807bc1cJungshik Jang } 1968e93c84739902f5adaa499b474f39e3c4807bc1cJungshik Jang 1978e93c84739902f5adaa499b474f39e3c4807bc1cJungshik Jang // Pick logical address if failed ratio is more than a half of all retries. 1988e93c84739902f5adaa499b474f39e3c4807bc1cJungshik Jang if (failedPollingCount * 2 > HdmiConfig.ADDRESS_ALLOCATION_RETRY) { 199d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang logicalAddress = curAddress; 200d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang break; 2013f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang } 2023f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang } 203d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang } 2043f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang 205d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang final int assignedAddress = logicalAddress; 2062e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang HdmiLogger.debug("New logical address for device [%d]: [preferred:%d, assigned:%d]", 2072e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang deviceType, preferredAddress, assignedAddress); 208d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang if (callback != null) { 209d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang runOnServiceThread(new Runnable() { 2108e93c84739902f5adaa499b474f39e3c4807bc1cJungshik Jang @Override 211d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang public void run() { 212d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang callback.onAllocated(deviceType, assignedAddress); 2133f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang } 214d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang }); 2153f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang } 216e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang } 217e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang 218d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang private static byte[] buildBody(int opcode, byte[] params) { 219d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang byte[] body = new byte[params.length + 1]; 220d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang body[0] = (byte) opcode; 221d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang System.arraycopy(params, 0, body, 1, params.length); 222d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang return body; 223e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang } 2240792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang 2257d9a843af83dbc75b1d170c743603198ede5a10fJungshik Jang 2260340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim HdmiPortInfo[] getPortInfos() { 2270340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim return nativeGetPortInfos(mNativePtr); 2280340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim } 2290340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim 230a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang /** 2317fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim * Return the locally hosted logical device of a given type. 2327fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim * 2337fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim * @param deviceType logical device type 2347fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim * @return {@link HdmiCecLocalDevice} instance if the instance of the type is available; 2357fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim * otherwise null. 2367fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim */ 2377fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim HdmiCecLocalDevice getLocalDevice(int deviceType) { 2387fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim return mLocalDevices.get(deviceType); 2397fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim } 2407fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim 2417fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim /** 242a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang * Add a new logical address to the device. Device's HW should be notified 243a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang * when a new logical address is assigned to a device, so that it can accept 244a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang * a command having available destinations. 245a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang * 246a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang * <p>Declared as package-private. accessed by {@link HdmiControlService} only. 247a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang * 248a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang * @param newLogicalAddress a logical address to be added 249a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang * @return 0 on success. Otherwise, returns negative value 250a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang */ 251a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang @ServiceThreadOnly 252a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang int addLogicalAddress(int newLogicalAddress) { 25302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang assertRunOnServiceThread(); 254c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim if (HdmiUtils.isValidAddress(newLogicalAddress)) { 255a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang return nativeAddLogicalAddress(mNativePtr, newLogicalAddress); 256a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang } else { 257a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang return -1; 258a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang } 259a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang } 260a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang 261a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang /** 262a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang * Clear all logical addresses registered in the device. 263a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang * 264a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang * <p>Declared as package-private. accessed by {@link HdmiControlService} only. 265a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang */ 266a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang @ServiceThreadOnly 267a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang void clearLogicalAddress() { 26802bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang assertRunOnServiceThread(); 2697fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim for (int i = 0; i < mLocalDevices.size(); ++i) { 2707fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim mLocalDevices.valueAt(i).clearAddress(); 2712918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim } 272a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang nativeClearLogicalAddress(mNativePtr); 273a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang } 274a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang 2754fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang @ServiceThreadOnly 2764fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang void clearLocalDevices() { 2774fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang assertRunOnServiceThread(); 2784fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang mLocalDevices.clear(); 2794fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang } 2804fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang 281a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang /** 282a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang * Return the physical address of the device. 283a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang * 284a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang * <p>Declared as package-private. accessed by {@link HdmiControlService} only. 285a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang * 286a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang * @return CEC physical address of the device. The range of success address 287a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang * is between 0x0000 and 0xFFFF. If failed it returns -1 288a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang */ 289a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang @ServiceThreadOnly 290a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang int getPhysicalAddress() { 29102bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang assertRunOnServiceThread(); 292a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang return nativeGetPhysicalAddress(mNativePtr); 293a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang } 294a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang 295a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang /** 296a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang * Return CEC version of the device. 297a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang * 298a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang * <p>Declared as package-private. accessed by {@link HdmiControlService} only. 299a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang */ 300a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang @ServiceThreadOnly 301a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang int getVersion() { 30202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang assertRunOnServiceThread(); 303a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang return nativeGetVersion(mNativePtr); 304a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang } 305a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang 306a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang /** 307a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang * Return vendor id of the device. 308a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang * 309a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang * <p>Declared as package-private. accessed by {@link HdmiControlService} only. 310a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang */ 311a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang @ServiceThreadOnly 312a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang int getVendorId() { 31302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang assertRunOnServiceThread(); 314a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang return nativeGetVendorId(mNativePtr); 315a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang } 316a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang 31702bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang /** 318c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim * Set an option to CEC HAL. 319092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang * 320c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim * @param flag key of option 321c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim * @param value value of option 322092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang */ 323a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang @ServiceThreadOnly 324092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang void setOption(int flag, int value) { 325092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang assertRunOnServiceThread(); 326092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang nativeSetOption(mNativePtr, flag, value); 327092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang } 328092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang 329092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang /** 330092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang * Configure ARC circuit in the hardware logic to start or stop the feature. 331092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang * 332092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang * @param enabled whether to enable/disable ARC 333092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang */ 334a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang @ServiceThreadOnly 335092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang void setAudioReturnChannel(boolean enabled) { 336092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang assertRunOnServiceThread(); 337092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang nativeSetAudioReturnChannel(mNativePtr, enabled); 338092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang } 339092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang 340092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang /** 341092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang * Return the connection status of the specified port 342092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang * 343092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang * @param port port number to check connection status 344092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang * @return true if connected; otherwise, return false 345092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang */ 346a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang @ServiceThreadOnly 347092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang boolean isConnected(int port) { 348092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang assertRunOnServiceThread(); 349092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang return nativeIsConnected(mNativePtr, port); 350092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang } 351092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang 352092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang /** 35302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang * Poll all remote devices. It sends <Polling Message> to all remote 35402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang * devices. 35502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang * 35602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang * <p>Declared as package-private. accessed by {@link HdmiControlService} only. 35702bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang * 35802bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang * @param callback an interface used to get a list of all remote devices' address 3591de514256fd3015cf45256f3198ab5472024af9bJungshik Jang * @param sourceAddress a logical address of source device where sends polling message 3600f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang * @param pickStrategy strategy how to pick polling candidates 36102bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang * @param retryCount the number of retry used to send polling message to remote devices 36202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang */ 363a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang @ServiceThreadOnly 3641de514256fd3015cf45256f3198ab5472024af9bJungshik Jang void pollDevices(DevicePollingCallback callback, int sourceAddress, int pickStrategy, 3651de514256fd3015cf45256f3198ab5472024af9bJungshik Jang int retryCount) { 36602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang assertRunOnServiceThread(); 36702bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang 3680f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang // Extract polling candidates. No need to poll against local devices. 3690f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang List<Integer> pollingCandidates = pickPollCandidates(pickStrategy); 3701de514256fd3015cf45256f3198ab5472024af9bJungshik Jang runDevicePolling(sourceAddress, pollingCandidates, retryCount, callback); 37102bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang } 37202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang 373cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang /** 374cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang * Return a list of all {@link HdmiCecLocalDevice}s. 375cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang * 376cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang * <p>Declared as package-private. accessed by {@link HdmiControlService} only. 377cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang */ 378a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang @ServiceThreadOnly 379cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang List<HdmiCecLocalDevice> getLocalDeviceList() { 380cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang assertRunOnServiceThread(); 38179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang return HdmiUtils.sparseArrayToList(mLocalDevices); 382cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang } 383cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang 3840f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang private List<Integer> pickPollCandidates(int pickStrategy) { 385c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim int strategy = pickStrategy & Constants.POLL_STRATEGY_MASK; 3860f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang Predicate<Integer> pickPredicate = null; 3870f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang switch (strategy) { 388c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim case Constants.POLL_STRATEGY_SYSTEM_AUDIO: 3890f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang pickPredicate = mSystemAudioAddressPredicate; 3900f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang break; 391c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim case Constants.POLL_STRATEGY_REMOTES_DEVICES: 3920f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang default: // The default is POLL_STRATEGY_REMOTES_DEVICES. 3930f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang pickPredicate = mRemoteDeviceAddressPredicate; 3940f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang break; 3950f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang } 3960f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang 397c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim int iterationStrategy = pickStrategy & Constants.POLL_ITERATION_STRATEGY_MASK; 3980f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang ArrayList<Integer> pollingCandidates = new ArrayList<>(); 3990f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang switch (iterationStrategy) { 400c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim case Constants.POLL_ITERATION_IN_ORDER: 401c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim for (int i = Constants.ADDR_TV; i <= Constants.ADDR_SPECIFIC_USE; ++i) { 4020f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang if (pickPredicate.apply(i)) { 4030f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang pollingCandidates.add(i); 4040f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang } 4050f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang } 4060f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang break; 407c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim case Constants.POLL_ITERATION_REVERSE_ORDER: 4080f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang default: // The default is reverse order. 409c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim for (int i = Constants.ADDR_SPECIFIC_USE; i >= Constants.ADDR_TV; --i) { 4100f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang if (pickPredicate.apply(i)) { 4110f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang pollingCandidates.add(i); 4120f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang } 4130f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang } 4140f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang break; 4150f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang } 4160f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang return pollingCandidates; 4170f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang } 4180f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang 419a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang @ServiceThreadOnly 42002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang private boolean isAllocatedLocalDeviceAddress(int address) { 421a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang assertRunOnServiceThread(); 4227fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim for (int i = 0; i < mLocalDevices.size(); ++i) { 4237fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim if (mLocalDevices.valueAt(i).isAddressOf(address)) { 42402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang return true; 42502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang } 42602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang } 42702bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang return false; 42802bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang } 42902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang 430a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang @ServiceThreadOnly 4311de514256fd3015cf45256f3198ab5472024af9bJungshik Jang private void runDevicePolling(final int sourceAddress, 4321de514256fd3015cf45256f3198ab5472024af9bJungshik Jang final List<Integer> candidates, final int retryCount, 43302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang final DevicePollingCallback callback) { 43402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang assertRunOnServiceThread(); 43502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang runOnIoThread(new Runnable() { 43602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang @Override 43702bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang public void run() { 43802bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang final ArrayList<Integer> allocated = new ArrayList<>(); 43902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang for (Integer address : candidates) { 4401de514256fd3015cf45256f3198ab5472024af9bJungshik Jang if (sendPollMessage(sourceAddress, address, retryCount)) { 44102bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang allocated.add(address); 44202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang } 44302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang } 4442e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang HdmiLogger.debug("[P]:Allocated Address=" + allocated); 44502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang if (callback != null) { 44602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang runOnServiceThread(new Runnable() { 44702bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang @Override 44802bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang public void run() { 44902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang callback.onPollingFinished(allocated); 45002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang } 45102bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang }); 45202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang } 45302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang } 45402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang }); 45502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang } 45602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang 457a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang @IoThreadOnly 4581de514256fd3015cf45256f3198ab5472024af9bJungshik Jang private boolean sendPollMessage(int sourceAddress, int destinationAddress, int retryCount) { 45902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang assertRunOnIoThread(); 46002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang for (int i = 0; i < retryCount; ++i) { 4611de514256fd3015cf45256f3198ab5472024af9bJungshik Jang // <Polling Message> is a message which has empty body. 46202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang // If sending <Polling Message> failed (NAK), it becomes 46302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang // new logical address for the device because no device uses 46402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang // it as logical address of the device. 4651de514256fd3015cf45256f3198ab5472024af9bJungshik Jang if (nativeSendCecCommand(mNativePtr, sourceAddress, destinationAddress, EMPTY_BODY) 466c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim == Constants.SEND_RESULT_SUCCESS) { 46702bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang return true; 46802bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang } 46902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang } 47002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang return false; 47102bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang } 47202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang 47302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang private void assertRunOnIoThread() { 47402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang if (Looper.myLooper() != mIoHandler.getLooper()) { 47502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang throw new IllegalStateException("Should run on io thread."); 47602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang } 47702bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang } 47802bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang 47902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang private void assertRunOnServiceThread() { 48002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang if (Looper.myLooper() != mControlHandler.getLooper()) { 48102bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang throw new IllegalStateException("Should run on service thread."); 48202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang } 48302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang } 48402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang 48502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang // Run a Runnable on IO thread. 48602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang // It should be careful to access member variables on IO thread because 48702bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang // it can be accessed from system thread as well. 488d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang private void runOnIoThread(Runnable runnable) { 489d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang mIoHandler.post(runnable); 490d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang } 491d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang 492d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang private void runOnServiceThread(Runnable runnable) { 493d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang mControlHandler.post(runnable); 494d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang } 495d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang 496a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang private boolean isAcceptableAddress(int address) { 4972918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim // Can access command targeting devices available in local device or broadcast command. 498c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim if (address == Constants.ADDR_BROADCAST) { 4992918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim return true; 5002918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim } 50102bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang return isAllocatedLocalDeviceAddress(address); 502a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang } 503a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang 504a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang @ServiceThreadOnly 505e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang private void onReceiveCommand(HdmiCecMessage message) { 50602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang assertRunOnServiceThread(); 5076aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo if (isAcceptableAddress(message.getDestination()) && mService.handleCecCommand(message)) { 508a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang return; 509a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang } 5106aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo // Not handled message, so we will reply it with <Feature Abort>. 5116aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo maySendFeatureAbortCommand(message, Constants.ABORT_UNRECOGNIZED_OPCODE); 5126aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo } 5136aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo 5146aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo @ServiceThreadOnly 5156aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo void maySendFeatureAbortCommand(HdmiCecMessage message, int reason) { 5166aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo assertRunOnServiceThread(); 5176aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo // Swap the source and the destination. 5186aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo int src = message.getDestination(); 5196aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo int dest = message.getSource(); 5206aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo if (src == Constants.ADDR_BROADCAST || dest == Constants.ADDR_UNREGISTERED) { 5216aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo // Don't reply <Feature Abort> from the unregistered devices or for the broadcasted 5226aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo // messages. See CEC 12.2 Protocol General Rules for detail. 5231827fdcbf6a53378f05620790e4798201341097bJungshik Jang return; 5243a2f74373a6892ca5c11cd19b4b662c069634717Jinsuk Kim } 5256aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo int originalOpcode = message.getOpcode(); 5266aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo if (originalOpcode == Constants.MESSAGE_FEATURE_ABORT) { 5271827fdcbf6a53378f05620790e4798201341097bJungshik Jang return; 5281827fdcbf6a53378f05620790e4798201341097bJungshik Jang } 5296aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo sendCommand( 5306aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo HdmiCecMessageBuilder.buildFeatureAbortCommand(src, dest, originalOpcode, reason)); 531a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang } 532e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang 533a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang @ServiceThreadOnly 5342918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim void sendCommand(HdmiCecMessage cecMessage) { 535a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang assertRunOnServiceThread(); 5362918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim sendCommand(cecMessage, null); 5372918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim } 5382918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim 539a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang @ServiceThreadOnly 540d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang void sendCommand(final HdmiCecMessage cecMessage, 541d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang final HdmiControlService.SendMessageCallback callback) { 542a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang assertRunOnServiceThread(); 543d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang runOnIoThread(new Runnable() { 544d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang @Override 545d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang public void run() { 5462e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang HdmiLogger.debug("[S]:" + cecMessage); 547d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang byte[] body = buildBody(cecMessage.getOpcode(), cecMessage.getParams()); 5488ed86c467afa107f7aafacb85ca64c979cf56dc2Jungshik Jang int i = 0; 5498ed86c467afa107f7aafacb85ca64c979cf56dc2Jungshik Jang int errorCode = Constants.SEND_RESULT_SUCCESS; 5508ed86c467afa107f7aafacb85ca64c979cf56dc2Jungshik Jang do { 5518ed86c467afa107f7aafacb85ca64c979cf56dc2Jungshik Jang errorCode = nativeSendCecCommand(mNativePtr, cecMessage.getSource(), 5528ed86c467afa107f7aafacb85ca64c979cf56dc2Jungshik Jang cecMessage.getDestination(), body); 5538ed86c467afa107f7aafacb85ca64c979cf56dc2Jungshik Jang if (errorCode == Constants.SEND_RESULT_SUCCESS) { 5548ed86c467afa107f7aafacb85ca64c979cf56dc2Jungshik Jang break; 5558ed86c467afa107f7aafacb85ca64c979cf56dc2Jungshik Jang } 5568ed86c467afa107f7aafacb85ca64c979cf56dc2Jungshik Jang } while (i++ < HdmiConfig.RETRANSMISSION_COUNT); 5578ed86c467afa107f7aafacb85ca64c979cf56dc2Jungshik Jang 5588ed86c467afa107f7aafacb85ca64c979cf56dc2Jungshik Jang final int finalError = errorCode; 5598ed86c467afa107f7aafacb85ca64c979cf56dc2Jungshik Jang if (finalError != Constants.SEND_RESULT_SUCCESS) { 560ece603b7955938d6001c376f351ca0a2219330acYuncheol Heo Slog.w(TAG, "Failed to send " + cecMessage); 561ece603b7955938d6001c376f351ca0a2219330acYuncheol Heo } 562d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang if (callback != null) { 563d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang runOnServiceThread(new Runnable() { 564d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang @Override 565d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang public void run() { 5668ed86c467afa107f7aafacb85ca64c979cf56dc2Jungshik Jang callback.onSendCompleted(finalError); 567d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang } 568d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang }); 569d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang } 570d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang } 571d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang }); 572e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang } 573e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang 574e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang /** 575e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang * Called by native when incoming CEC message arrived. 576e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang */ 577a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang @ServiceThreadOnly 578e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang private void handleIncomingCecCommand(int srcAddress, int dstAddress, byte[] body) { 5794085d0ef62554c7e139b82bca0f97161bb159f8cJungshik Jang assertRunOnServiceThread(); 580c94ac5cffc982898bc4f7a5d97d8fad5520ff444Jungshik Jang HdmiCecMessage command = HdmiCecMessageBuilder.of(srcAddress, dstAddress, body); 5812e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang HdmiLogger.debug("[R]:" + command); 582c94ac5cffc982898bc4f7a5d97d8fad5520ff444Jungshik Jang onReceiveCommand(command); 583e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang } 584e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang 5850792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang /** 586e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang * Called by native when a hotplug event issues. 5870792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang */ 58842230728b8212738c2351939c5577730f05a58deJungshik Jang @ServiceThreadOnly 58942230728b8212738c2351939c5577730f05a58deJungshik Jang private void handleHotplug(int port, boolean connected) { 59042230728b8212738c2351939c5577730f05a58deJungshik Jang assertRunOnServiceThread(); 5912e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang HdmiLogger.debug("Hotplug event:[port:%d, connected:%b]", port, connected); 59242230728b8212738c2351939c5577730f05a58deJungshik Jang mService.onHotplug(port, connected); 5930792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang } 5940792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang 595959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo void dump(final IndentingPrintWriter pw) { 596959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo for (int i = 0; i < mLocalDevices.size(); ++i) { 597959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo pw.println("HdmiCecLocalDevice #" + i + ":"); 598959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo pw.increaseIndent(); 599959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo mLocalDevices.valueAt(i).dump(pw); 600959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo pw.decreaseIndent(); 601959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo } 602959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo } 603959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo 6044085d0ef62554c7e139b82bca0f97161bb159f8cJungshik Jang private static native long nativeInit(HdmiCecController handler, MessageQueue messageQueue); 605a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang private static native int nativeSendCecCommand(long controllerPtr, int srcAddress, 606e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang int dstAddress, byte[] body); 607a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang private static native int nativeAddLogicalAddress(long controllerPtr, int logicalAddress); 608a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang private static native void nativeClearLogicalAddress(long controllerPtr); 609a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang private static native int nativeGetPhysicalAddress(long controllerPtr); 610a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang private static native int nativeGetVersion(long controllerPtr); 611a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang private static native int nativeGetVendorId(long controllerPtr); 6120340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim private static native HdmiPortInfo[] nativeGetPortInfos(long controllerPtr); 613092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang private static native void nativeSetOption(long controllerPtr, int flag, int value); 614092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang private static native void nativeSetAudioReturnChannel(long controllerPtr, boolean flag); 615092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang private static native boolean nativeIsConnected(long controllerPtr, int port); 6160792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang} 617