HdmiCecController.java revision 42230728b8212738c2351939c5577730f05a58de
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
260f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jangimport com.android.internal.util.Predicate;
27a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jangimport com.android.server.hdmi.HdmiAnnotations.IoThreadOnly;
28a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jangimport com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
2902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jangimport com.android.server.hdmi.HdmiControlService.DevicePollingCallback;
3002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang
313f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jangimport libcore.util.EmptyArray;
323f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang
337d9a843af83dbc75b1d170c743603198ede5a10fJungshik Jangimport java.util.ArrayList;
347d9a843af83dbc75b1d170c743603198ede5a10fJungshik Jangimport java.util.List;
350792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
360792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang/**
370792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * Manages HDMI-CEC command and behaviors. It converts user's command into CEC command
380792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * and pass it to CEC HAL so that it sends message to other device. For incoming
390792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * message it translates the message and delegates it to proper module.
400792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang *
4102bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang * <p>It should be careful to access member variables on IO thread because
4202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang * it can be accessed from system thread as well.
4302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang *
440792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * <p>It can be created only by {@link HdmiCecController#create}
450792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang *
460792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * <p>Declared as package-private, accessed by {@link HdmiControlService} only.
470792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang */
48a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jangfinal class HdmiCecController {
490792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private static final String TAG = "HdmiCecController";
500792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
513ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    /**
523ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     * Interface to report allocated logical address.
533ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     */
543ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    interface AllocateAddressCallback {
553ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        /**
563ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang         * Called when a new logical address is allocated.
573ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang         *
583ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang         * @param deviceType requested device type to allocate logical address
593ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang         * @param logicalAddress allocated logical address. If it is
6042230728b8212738c2351939c5577730f05a58deJungshik Jang         *                       {@link Constants#ADDR_UNREGISTERED}, it means that
613ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang         *                       it failed to allocate logical address for the given device type
623ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang         */
633ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        void onAllocated(int deviceType, int logicalAddress);
643ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
653ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
663f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang    private static final byte[] EMPTY_BODY = EmptyArray.BYTE;
673f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang
68e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    // A message to pass cec send command to IO looper.
69e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    private static final int MSG_SEND_CEC_COMMAND = 1;
703f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang    // A message to delegate logical allocation to IO looper.
713f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang    private static final int MSG_ALLOCATE_LOGICAL_ADDRESS = 2;
72e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang
73e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    // Message types to handle incoming message in main service looper.
74e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    private final static int MSG_RECEIVE_CEC_COMMAND = 1;
753f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang    // A message to report allocated logical address to main control looper.
763f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang    private final static int MSG_REPORT_LOGICAL_ADDRESS = 2;
77e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang
783f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang    private static final int NUM_LOGICAL_ADDRESS = 16;
793f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang
8002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    private static final int RETRY_COUNT_FOR_LOGICAL_ADDRESS_ALLOCATION = 3;
8102bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang
820f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    // Predicate for whether the given logical address is remote device's one or not.
830f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    private final Predicate<Integer> mRemoteDeviceAddressPredicate = new Predicate<Integer>() {
840f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        @Override
850f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        public boolean apply(Integer address) {
860f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            return !isAllocatedLocalDeviceAddress(address);
870f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        }
880f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    };
890f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
900f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    // Predicate whether the given logical address is system audio's one or not
910f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    private final Predicate<Integer> mSystemAudioAddressPredicate = new Predicate<Integer>() {
920f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        @Override
930f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        public boolean apply(Integer address) {
94c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            return HdmiUtils.getTypeFromAddress(address) == Constants.ADDR_AUDIO_SYSTEM;
950f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        }
960f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    };
970f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
980792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // Handler instance to process synchronous I/O (mainly send) message.
990792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private Handler mIoHandler;
1000792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
1010792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // Handler instance to process various messages coming from other CEC
1020792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // device or issued by internal state change.
103e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    private Handler mControlHandler;
1040792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
1050792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // Stores the pointer to the native implementation of the service that
1060792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // interacts with HAL.
10702bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    private volatile long mNativePtr;
1080792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
109a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    private HdmiControlService mService;
110a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang
1117fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim    // Stores the local CEC devices in the system. Device type is used for key.
1127fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim    private final SparseArray<HdmiCecLocalDevice> mLocalDevices = new SparseArray<>();
1137d9a843af83dbc75b1d170c743603198ede5a10fJungshik Jang
1140792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // Private constructor.  Use HdmiCecController.create().
1150792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private HdmiCecController() {
1160792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    }
1170792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
1180792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    /**
1190792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang     * A factory method to get {@link HdmiCecController}. If it fails to initialize
1200792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang     * inner device or has no device it will return {@code null}.
1210792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang     *
1220792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang     * <p>Declared as package-private, accessed by {@link HdmiControlService} only.
123e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * @param service {@link HdmiControlService} instance used to create internal handler
124e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     *                and to pass callback for incoming message or event.
1250792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang     * @return {@link HdmiCecController} if device is initialized successfully. Otherwise,
1260792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang     *         returns {@code null}.
1270792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang     */
128e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    static HdmiCecController create(HdmiControlService service) {
1292918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim        HdmiCecController controller = new HdmiCecController();
1304085d0ef62554c7e139b82bca0f97161bb159f8cJungshik Jang        long nativePtr = nativeInit(controller, service.getServiceLooper().getQueue());
1310792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        if (nativePtr == 0L) {
1322918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim            controller = null;
1330792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang            return null;
1340792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        }
1350792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
1362918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim        controller.init(service, nativePtr);
1372918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim        return controller;
1380792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    }
1390792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
1402918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim    private void init(HdmiControlService service, long nativePtr) {
1412918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim        mService = service;
1422918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim        mIoHandler = new Handler(service.getServiceLooper());
1432918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim        mControlHandler = new Handler(service.getServiceLooper());
1442918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim        mNativePtr = nativePtr;
145a8a5e50c6f9ba3ae0ff59eda76354e93515d6f8fJinsuk Kim    }
146a8a5e50c6f9ba3ae0ff59eda76354e93515d6f8fJinsuk Kim
147a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
1483ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    void addLocalDevice(int deviceType, HdmiCecLocalDevice device) {
149a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
1503ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        mLocalDevices.put(deviceType, device);
1513f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang    }
1523f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang
1533f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang    /**
1543f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang     * Allocate a new logical address of the given device type. Allocated
1553ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     * address will be reported through {@link AllocateAddressCallback}.
1563f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang     *
1573f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang     * <p> Declared as package-private, accessed by {@link HdmiControlService} only.
1583f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang     *
1593f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang     * @param deviceType type of device to used to determine logical address
1603f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang     * @param preferredAddress a logical address preferred to be allocated.
16142230728b8212738c2351939c5577730f05a58deJungshik Jang     *                         If sets {@link Constants#ADDR_UNREGISTERED}, scans
1623f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang     *                         the smallest logical address matched with the given device type.
1633f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang     *                         Otherwise, scan address will start from {@code preferredAddress}
1643f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang     * @param callback callback interface to report allocated logical address to caller
1653f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang     */
166a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
167d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    void allocateLogicalAddress(final int deviceType, final int preferredAddress,
1683ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            final AllocateAddressCallback callback) {
16902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        assertRunOnServiceThread();
17002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang
171d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        runOnIoThread(new Runnable() {
172d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang            @Override
173d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang            public void run() {
174d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang                handleAllocateLogicalAddress(deviceType, preferredAddress, callback);
175d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang            }
176d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        });
177e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    }
178e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang
179a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @IoThreadOnly
180d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    private void handleAllocateLogicalAddress(final int deviceType, int preferredAddress,
1813ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            final AllocateAddressCallback callback) {
18202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        assertRunOnIoThread();
183d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        int startAddress = preferredAddress;
184d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        // If preferred address is "unregistered", start address will be the smallest
185d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        // address matched with the given device type.
186c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        if (preferredAddress == Constants.ADDR_UNREGISTERED) {
187d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang            for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) {
188c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                if (deviceType == HdmiUtils.getTypeFromAddress(i)) {
189d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang                    startAddress = i;
190e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang                    break;
191d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang                }
192e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang            }
193e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang        }
1943f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang
195c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        int logicalAddress = Constants.ADDR_UNREGISTERED;
196d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        // Iterates all possible addresses which has the same device type.
197d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) {
198d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang            int curAddress = (startAddress + i) % NUM_LOGICAL_ADDRESS;
199c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            if (curAddress != Constants.ADDR_UNREGISTERED
200c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                    && deviceType == HdmiUtils.getTypeFromAddress(curAddress)) {
2011de514256fd3015cf45256f3198ab5472024af9bJungshik Jang                if (!sendPollMessage(curAddress, curAddress,
2021de514256fd3015cf45256f3198ab5472024af9bJungshik Jang                        RETRY_COUNT_FOR_LOGICAL_ADDRESS_ALLOCATION)) {
203d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang                    logicalAddress = curAddress;
204d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang                    break;
2053f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang                }
2063f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang            }
207d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        }
2083f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang
209d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        final int assignedAddress = logicalAddress;
210d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        if (callback != null) {
211d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang            runOnServiceThread(new Runnable() {
212d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang                    @Override
213d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang                public void run() {
214d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang                    callback.onAllocated(deviceType, assignedAddress);
2153f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang                }
216d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang            });
2173f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang        }
218e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    }
219e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang
220d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    private static byte[] buildBody(int opcode, byte[] params) {
221d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        byte[] body = new byte[params.length + 1];
222d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        body[0] = (byte) opcode;
223d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        System.arraycopy(params, 0, body, 1, params.length);
224d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        return body;
225e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    }
2260792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
2277d9a843af83dbc75b1d170c743603198ede5a10fJungshik Jang
2280340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    HdmiPortInfo[] getPortInfos() {
2290340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        return nativeGetPortInfos(mNativePtr);
2300340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    }
2310340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
232a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang    /**
2337fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim     * Return the locally hosted logical device of a given type.
2347fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim     *
2357fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim     * @param deviceType logical device type
2367fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim     * @return {@link HdmiCecLocalDevice} instance if the instance of the type is available;
2377fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim     *          otherwise null.
2387fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim     */
2397fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim    HdmiCecLocalDevice getLocalDevice(int deviceType) {
2407fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        return mLocalDevices.get(deviceType);
2417fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim    }
2427fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim
2437fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim    /**
244a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang     * Add a new logical address to the device. Device's HW should be notified
245a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang     * when a new logical address is assigned to a device, so that it can accept
246a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang     * a command having available destinations.
247a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang     *
248a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang     * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
249a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang     *
250a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang     * @param newLogicalAddress a logical address to be added
251a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang     * @return 0 on success. Otherwise, returns negative value
252a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang     */
253a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
254a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang    int addLogicalAddress(int newLogicalAddress) {
25502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        assertRunOnServiceThread();
256c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        if (HdmiUtils.isValidAddress(newLogicalAddress)) {
257a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang            return nativeAddLogicalAddress(mNativePtr, newLogicalAddress);
258a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang        } else {
259a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang            return -1;
260a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang        }
261a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang    }
262a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang
263a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang    /**
264a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang     * Clear all logical addresses registered in the device.
265a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang     *
266a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang     * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
267a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang     */
268a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
269a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang    void clearLogicalAddress() {
27002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        assertRunOnServiceThread();
271a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        // TODO: consider to backup logical address so that new logical address
272a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        // allocation can use it as preferred address.
2737fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        for (int i = 0; i < mLocalDevices.size(); ++i) {
2747fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            mLocalDevices.valueAt(i).clearAddress();
2752918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim        }
276a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang        nativeClearLogicalAddress(mNativePtr);
277a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang    }
278a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang
279a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang    /**
280a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang     * Return the physical address of the device.
281a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang     *
282a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang     * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
283a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang     *
284a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang     * @return CEC physical address of the device. The range of success address
285a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang     *         is between 0x0000 and 0xFFFF. If failed it returns -1
286a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang     */
287a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
288a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang    int getPhysicalAddress() {
28902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        assertRunOnServiceThread();
290a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang        return nativeGetPhysicalAddress(mNativePtr);
291a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang    }
292a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang
293a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang    /**
294a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang     * Return CEC version of the device.
295a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang     *
296a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang     * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
297a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang     */
298a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
299a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang    int getVersion() {
30002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        assertRunOnServiceThread();
301a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang        return nativeGetVersion(mNativePtr);
302a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang    }
303a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang
304a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang    /**
305a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang     * Return vendor id of the device.
306a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang     *
307a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang     * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
308a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang     */
309a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
310a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang    int getVendorId() {
31102bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        assertRunOnServiceThread();
312a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang        return nativeGetVendorId(mNativePtr);
313a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang    }
314a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang
31502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    /**
316c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim     * Set an option to CEC HAL.
317092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     *
318c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim     * @param flag key of option
319c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim     * @param value value of option
320092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     */
321a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
322092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    void setOption(int flag, int value) {
323092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        assertRunOnServiceThread();
324092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        nativeSetOption(mNativePtr, flag, value);
325092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
326092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
327092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    /**
328092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     * Configure ARC circuit in the hardware logic to start or stop the feature.
329092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     *
330092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     * @param enabled whether to enable/disable ARC
331092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     */
332a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
333092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    void setAudioReturnChannel(boolean enabled) {
334092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        assertRunOnServiceThread();
335092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        nativeSetAudioReturnChannel(mNativePtr, enabled);
336092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
337092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
338092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    /**
339092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     * Return the connection status of the specified port
340092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     *
341092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     * @param port port number to check connection status
342092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     * @return true if connected; otherwise, return false
343092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     */
344a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
345092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    boolean isConnected(int port) {
346092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        assertRunOnServiceThread();
347092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return nativeIsConnected(mNativePtr, port);
348092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
349092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
350092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    /**
35102bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * Poll all remote devices. It sends &lt;Polling Message&gt; to all remote
35202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * devices.
35302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     *
35402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
35502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     *
35602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * @param callback an interface used to get a list of all remote devices' address
3571de514256fd3015cf45256f3198ab5472024af9bJungshik Jang     * @param sourceAddress a logical address of source device where sends polling message
3580f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang     * @param pickStrategy strategy how to pick polling candidates
35902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * @param retryCount the number of retry used to send polling message to remote devices
36002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     */
361a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
3621de514256fd3015cf45256f3198ab5472024af9bJungshik Jang    void pollDevices(DevicePollingCallback callback, int sourceAddress, int pickStrategy,
3631de514256fd3015cf45256f3198ab5472024af9bJungshik Jang            int retryCount) {
36402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        assertRunOnServiceThread();
36502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang
3660f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        // Extract polling candidates. No need to poll against local devices.
3670f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        List<Integer> pollingCandidates = pickPollCandidates(pickStrategy);
3681de514256fd3015cf45256f3198ab5472024af9bJungshik Jang        runDevicePolling(sourceAddress, pollingCandidates, retryCount, callback);
36902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    }
37002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang
371cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    /**
372cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang     * Return a list of all {@link HdmiCecLocalDevice}s.
373cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang     *
374cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang     * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
375cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang     */
376a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
377cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    List<HdmiCecLocalDevice> getLocalDeviceList() {
378cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        assertRunOnServiceThread();
37979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return HdmiUtils.sparseArrayToList(mLocalDevices);
380cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    }
381cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
3820f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    private List<Integer> pickPollCandidates(int pickStrategy) {
383c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        int strategy = pickStrategy & Constants.POLL_STRATEGY_MASK;
3840f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        Predicate<Integer> pickPredicate = null;
3850f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        switch (strategy) {
386c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.POLL_STRATEGY_SYSTEM_AUDIO:
3870f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang                pickPredicate = mSystemAudioAddressPredicate;
3880f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang                break;
389c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.POLL_STRATEGY_REMOTES_DEVICES:
3900f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            default:  // The default is POLL_STRATEGY_REMOTES_DEVICES.
3910f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang                pickPredicate = mRemoteDeviceAddressPredicate;
3920f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang                break;
3930f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        }
3940f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
395c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        int iterationStrategy = pickStrategy & Constants.POLL_ITERATION_STRATEGY_MASK;
3960f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        ArrayList<Integer> pollingCandidates = new ArrayList<>();
3970f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        switch (iterationStrategy) {
398c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.POLL_ITERATION_IN_ORDER:
399c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                for (int i = Constants.ADDR_TV; i <= Constants.ADDR_SPECIFIC_USE; ++i) {
4000f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang                    if (pickPredicate.apply(i)) {
4010f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang                        pollingCandidates.add(i);
4020f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang                    }
4030f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang                }
4040f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang                break;
405c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.POLL_ITERATION_REVERSE_ORDER:
4060f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            default:  // The default is reverse order.
407c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                for (int i = Constants.ADDR_SPECIFIC_USE; i >= Constants.ADDR_TV; --i) {
4080f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang                    if (pickPredicate.apply(i)) {
4090f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang                        pollingCandidates.add(i);
4100f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang                    }
4110f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang                }
4120f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang                break;
4130f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        }
4140f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        return pollingCandidates;
4150f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    }
4160f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
417a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
41802bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    private boolean isAllocatedLocalDeviceAddress(int address) {
419a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
4207fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        for (int i = 0; i < mLocalDevices.size(); ++i) {
4217fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            if (mLocalDevices.valueAt(i).isAddressOf(address)) {
42202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang                return true;
42302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang            }
42402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        }
42502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        return false;
42602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    }
42702bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang
428a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
4291de514256fd3015cf45256f3198ab5472024af9bJungshik Jang    private void runDevicePolling(final int sourceAddress,
4301de514256fd3015cf45256f3198ab5472024af9bJungshik Jang            final List<Integer> candidates, final int retryCount,
43102bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang            final DevicePollingCallback callback) {
43202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        assertRunOnServiceThread();
43302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        runOnIoThread(new Runnable() {
43402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang            @Override
43502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang            public void run() {
43602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang                final ArrayList<Integer> allocated = new ArrayList<>();
43702bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang                for (Integer address : candidates) {
4381de514256fd3015cf45256f3198ab5472024af9bJungshik Jang                    if (sendPollMessage(sourceAddress, address, retryCount)) {
43902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang                        allocated.add(address);
44002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang                    }
44102bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang                }
44202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang                if (callback != null) {
44302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang                    runOnServiceThread(new Runnable() {
44402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang                        @Override
44502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang                        public void run() {
44602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang                            callback.onPollingFinished(allocated);
44702bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang                        }
44802bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang                    });
44902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang                }
45002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang            }
45102bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        });
45202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    }
45302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang
454a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @IoThreadOnly
4551de514256fd3015cf45256f3198ab5472024af9bJungshik Jang    private boolean sendPollMessage(int sourceAddress, int destinationAddress, int retryCount) {
45602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        assertRunOnIoThread();
45702bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        for (int i = 0; i < retryCount; ++i) {
4581de514256fd3015cf45256f3198ab5472024af9bJungshik Jang            // <Polling Message> is a message which has empty body.
45902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang            // If sending <Polling Message> failed (NAK), it becomes
46002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang            // new logical address for the device because no device uses
46102bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang            // it as logical address of the device.
4621de514256fd3015cf45256f3198ab5472024af9bJungshik Jang            if (nativeSendCecCommand(mNativePtr, sourceAddress, destinationAddress, EMPTY_BODY)
463c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                    == Constants.SEND_RESULT_SUCCESS) {
46402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang                return true;
46502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang            }
46602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        }
46702bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        return false;
46802bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    }
46902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang
47002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    private void assertRunOnIoThread() {
47102bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        if (Looper.myLooper() != mIoHandler.getLooper()) {
47202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang            throw new IllegalStateException("Should run on io thread.");
47302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        }
47402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    }
47502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang
47602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    private void assertRunOnServiceThread() {
47702bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        if (Looper.myLooper() != mControlHandler.getLooper()) {
47802bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang            throw new IllegalStateException("Should run on service thread.");
47902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        }
48002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    }
48102bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang
48202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    // Run a Runnable on IO thread.
48302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    // It should be careful to access member variables on IO thread because
48402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    // it can be accessed from system thread as well.
485d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    private void runOnIoThread(Runnable runnable) {
486d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        mIoHandler.post(runnable);
487d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    }
488d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang
489d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    private void runOnServiceThread(Runnable runnable) {
490d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        mControlHandler.post(runnable);
491d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    }
492d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang
493a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    private boolean isAcceptableAddress(int address) {
4942918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim        // Can access command targeting devices available in local device or broadcast command.
495c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        if (address == Constants.ADDR_BROADCAST) {
4962918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim            return true;
4972918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim        }
49802bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        return isAllocatedLocalDeviceAddress(address);
499a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    }
500a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang
501a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
502e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    private void onReceiveCommand(HdmiCecMessage message) {
50302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        assertRunOnServiceThread();
5044085d0ef62554c7e139b82bca0f97161bb159f8cJungshik Jang        if (isAcceptableAddress(message.getDestination())
5054085d0ef62554c7e139b82bca0f97161bb159f8cJungshik Jang                && mService.handleCecCommand(message)) {
506a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang            return;
507a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        }
508e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang
509c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        if (message.getDestination() != Constants.ADDR_BROADCAST) {
5103a2f74373a6892ca5c11cd19b4b662c069634717Jinsuk Kim            int sourceAddress = message.getDestination();
5113a2f74373a6892ca5c11cd19b4b662c069634717Jinsuk Kim            // Reply <Feature Abort> to initiator (source) for all requests.
5123a2f74373a6892ca5c11cd19b4b662c069634717Jinsuk Kim            HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildFeatureAbortCommand(
5133a2f74373a6892ca5c11cd19b4b662c069634717Jinsuk Kim                    sourceAddress, message.getSource(), message.getOpcode(),
514c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                    Constants.ABORT_REFUSED);
515a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang            sendCommand(cecMessage);
5163a2f74373a6892ca5c11cd19b4b662c069634717Jinsuk Kim        }
517a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    }
518e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang
519a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
5202918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim    void sendCommand(HdmiCecMessage cecMessage) {
521a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
5222918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim        sendCommand(cecMessage, null);
5232918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim    }
5242918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim
525a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
526d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    void sendCommand(final HdmiCecMessage cecMessage,
527d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang            final HdmiControlService.SendMessageCallback callback) {
528a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
529d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        runOnIoThread(new Runnable() {
530d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang            @Override
531d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang            public void run() {
532d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang                byte[] body = buildBody(cecMessage.getOpcode(), cecMessage.getParams());
533d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang                final int error = nativeSendCecCommand(mNativePtr, cecMessage.getSource(),
534d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang                        cecMessage.getDestination(), body);
535c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                if (error != Constants.SEND_RESULT_SUCCESS) {
536ece603b7955938d6001c376f351ca0a2219330acYuncheol Heo                    Slog.w(TAG, "Failed to send " + cecMessage);
537ece603b7955938d6001c376f351ca0a2219330acYuncheol Heo                }
538d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang                if (callback != null) {
539d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang                    runOnServiceThread(new Runnable() {
540d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang                        @Override
541d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang                        public void run() {
542d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang                            callback.onSendCompleted(error);
543d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang                        }
544d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang                    });
545d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang                }
546d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang            }
547d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        });
548e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    }
549e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang
550e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    /**
551e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * Called by native when incoming CEC message arrived.
552e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     */
553a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
554e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    private void handleIncomingCecCommand(int srcAddress, int dstAddress, byte[] body) {
5554085d0ef62554c7e139b82bca0f97161bb159f8cJungshik Jang        assertRunOnServiceThread();
5564085d0ef62554c7e139b82bca0f97161bb159f8cJungshik Jang        onReceiveCommand(HdmiCecMessageBuilder.of(srcAddress, dstAddress, body));
557e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    }
558e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang
5590792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    /**
560e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * Called by native when a hotplug event issues.
5610792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang     */
56242230728b8212738c2351939c5577730f05a58deJungshik Jang    @ServiceThreadOnly
56342230728b8212738c2351939c5577730f05a58deJungshik Jang    private void handleHotplug(int port, boolean connected) {
56442230728b8212738c2351939c5577730f05a58deJungshik Jang        assertRunOnServiceThread();
56542230728b8212738c2351939c5577730f05a58deJungshik Jang        mService.onHotplug(port, connected);
5660792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    }
5670792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
5684085d0ef62554c7e139b82bca0f97161bb159f8cJungshik Jang    private static native long nativeInit(HdmiCecController handler, MessageQueue messageQueue);
569a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang    private static native int nativeSendCecCommand(long controllerPtr, int srcAddress,
570e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang            int dstAddress, byte[] body);
571a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang    private static native int nativeAddLogicalAddress(long controllerPtr, int logicalAddress);
572a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang    private static native void nativeClearLogicalAddress(long controllerPtr);
573a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang    private static native int nativeGetPhysicalAddress(long controllerPtr);
574a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang    private static native int nativeGetVersion(long controllerPtr);
575a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang    private static native int nativeGetVendorId(long controllerPtr);
5760340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    private static native HdmiPortInfo[] nativeGetPortInfos(long controllerPtr);
577092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    private static native void nativeSetOption(long controllerPtr, int flag, int value);
578092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    private static native void nativeSetAudioReturnChannel(long controllerPtr, boolean flag);
579092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    private static native boolean nativeIsConnected(long controllerPtr, int port);
5800792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang}
581