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;
35b3ecb72af8cbbd4f6f8089d0dc22289f6e2588f6Jungshik Jangimport java.util.LinkedList;
367d9a843af83dbc75b1d170c743603198ede5a10fJungshik Jangimport java.util.List;
370792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
380792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang/**
390792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * Manages HDMI-CEC command and behaviors. It converts user's command into CEC command
400792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * and pass it to CEC HAL so that it sends message to other device. For incoming
410792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * message it translates the message and delegates it to proper module.
420792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang *
4302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang * <p>It should be careful to access member variables on IO thread because
4402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang * it can be accessed from system thread as well.
4502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang *
460792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * <p>It can be created only by {@link HdmiCecController#create}
470792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang *
480792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * <p>Declared as package-private, accessed by {@link HdmiControlService} only.
490792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang */
50a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jangfinal class HdmiCecController {
510792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private static final String TAG = "HdmiCecController";
520792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
533ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    /**
543ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     * Interface to report allocated logical address.
553ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     */
563ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    interface AllocateAddressCallback {
573ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        /**
583ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang         * Called when a new logical address is allocated.
593ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang         *
603ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang         * @param deviceType requested device type to allocate logical address
613ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang         * @param logicalAddress allocated logical address. If it is
6242230728b8212738c2351939c5577730f05a58deJungshik Jang         *                       {@link Constants#ADDR_UNREGISTERED}, it means that
633ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang         *                       it failed to allocate logical address for the given device type
643ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang         */
653ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        void onAllocated(int deviceType, int logicalAddress);
663ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
673ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
683f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang    private static final byte[] EMPTY_BODY = EmptyArray.BYTE;
693f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang
703f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang    private static final int NUM_LOGICAL_ADDRESS = 16;
713f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang
720f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    // Predicate for whether the given logical address is remote device's one or not.
730f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    private final Predicate<Integer> mRemoteDeviceAddressPredicate = new Predicate<Integer>() {
740f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        @Override
750f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        public boolean apply(Integer address) {
760f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            return !isAllocatedLocalDeviceAddress(address);
770f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        }
780f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    };
790f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
800f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    // Predicate whether the given logical address is system audio's one or not
810f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    private final Predicate<Integer> mSystemAudioAddressPredicate = new Predicate<Integer>() {
820f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        @Override
830f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        public boolean apply(Integer address) {
84c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            return HdmiUtils.getTypeFromAddress(address) == Constants.ADDR_AUDIO_SYSTEM;
850f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        }
860f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    };
870f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
880792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // Handler instance to process synchronous I/O (mainly send) message.
890792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private Handler mIoHandler;
900792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
910792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // Handler instance to process various messages coming from other CEC
920792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // device or issued by internal state change.
93e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    private Handler mControlHandler;
940792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
950792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // Stores the pointer to the native implementation of the service that
960792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // interacts with HAL.
9702bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    private volatile long mNativePtr;
980792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
997df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    private final HdmiControlService mService;
100a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang
1017fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim    // Stores the local CEC devices in the system. Device type is used for key.
1027fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim    private final SparseArray<HdmiCecLocalDevice> mLocalDevices = new SparseArray<>();
1037d9a843af83dbc75b1d170c743603198ede5a10fJungshik Jang
1040792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // Private constructor.  Use HdmiCecController.create().
1057df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    private HdmiCecController(HdmiControlService service) {
1067df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        mService = service;
1070792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    }
1080792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
1090792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    /**
1100792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang     * A factory method to get {@link HdmiCecController}. If it fails to initialize
1110792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang     * inner device or has no device it will return {@code null}.
1120792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang     *
1130792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang     * <p>Declared as package-private, accessed by {@link HdmiControlService} only.
114e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * @param service {@link HdmiControlService} instance used to create internal handler
115e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     *                and to pass callback for incoming message or event.
1160792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang     * @return {@link HdmiCecController} if device is initialized successfully. Otherwise,
1170792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang     *         returns {@code null}.
1180792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang     */
119e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    static HdmiCecController create(HdmiControlService service) {
1207df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        HdmiCecController controller = new HdmiCecController(service);
1214085d0ef62554c7e139b82bca0f97161bb159f8cJungshik Jang        long nativePtr = nativeInit(controller, service.getServiceLooper().getQueue());
1220792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        if (nativePtr == 0L) {
1232918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim            controller = null;
1240792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang            return null;
1250792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        }
1260792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
1277df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        controller.init(nativePtr);
1282918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim        return controller;
1290792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    }
1300792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
1317df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    private void init(long nativePtr) {
132f17024873b10dabed069e502030dd85d6257c0c4Yuncheol Heo        mIoHandler = new Handler(mService.getIoLooper());
1337df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        mControlHandler = new Handler(mService.getServiceLooper());
1342918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim        mNativePtr = nativePtr;
135a8a5e50c6f9ba3ae0ff59eda76354e93515d6f8fJinsuk Kim    }
136a8a5e50c6f9ba3ae0ff59eda76354e93515d6f8fJinsuk Kim
137a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
1383ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    void addLocalDevice(int deviceType, HdmiCecLocalDevice device) {
139a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
1403ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        mLocalDevices.put(deviceType, device);
1413f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang    }
1423f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang
1433f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang    /**
1443f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang     * Allocate a new logical address of the given device type. Allocated
1453ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     * address will be reported through {@link AllocateAddressCallback}.
1463f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang     *
1473f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang     * <p> Declared as package-private, accessed by {@link HdmiControlService} only.
1483f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang     *
1493f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang     * @param deviceType type of device to used to determine logical address
1503f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang     * @param preferredAddress a logical address preferred to be allocated.
15142230728b8212738c2351939c5577730f05a58deJungshik Jang     *                         If sets {@link Constants#ADDR_UNREGISTERED}, scans
1523f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang     *                         the smallest logical address matched with the given device type.
1533f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang     *                         Otherwise, scan address will start from {@code preferredAddress}
1543f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang     * @param callback callback interface to report allocated logical address to caller
1553f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang     */
156a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
157d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    void allocateLogicalAddress(final int deviceType, final int preferredAddress,
1583ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            final AllocateAddressCallback callback) {
15902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        assertRunOnServiceThread();
16002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang
161d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        runOnIoThread(new Runnable() {
162d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang            @Override
163d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang            public void run() {
164d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang                handleAllocateLogicalAddress(deviceType, preferredAddress, callback);
165d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang            }
166d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        });
167e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    }
168e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang
169a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @IoThreadOnly
170d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    private void handleAllocateLogicalAddress(final int deviceType, int preferredAddress,
1713ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            final AllocateAddressCallback callback) {
17202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        assertRunOnIoThread();
173d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        int startAddress = preferredAddress;
174d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        // If preferred address is "unregistered", start address will be the smallest
175d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        // address matched with the given device type.
176c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        if (preferredAddress == Constants.ADDR_UNREGISTERED) {
177d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang            for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) {
178c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                if (deviceType == HdmiUtils.getTypeFromAddress(i)) {
179d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang                    startAddress = i;
180e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang                    break;
181d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang                }
182e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang            }
183e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang        }
1843f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang
185c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        int logicalAddress = Constants.ADDR_UNREGISTERED;
186d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        // Iterates all possible addresses which has the same device type.
187d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) {
188d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang            int curAddress = (startAddress + i) % NUM_LOGICAL_ADDRESS;
189c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            if (curAddress != Constants.ADDR_UNREGISTERED
190c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                    && deviceType == HdmiUtils.getTypeFromAddress(curAddress)) {
1918e93c84739902f5adaa499b474f39e3c4807bc1cJungshik Jang                int failedPollingCount = 0;
1928e93c84739902f5adaa499b474f39e3c4807bc1cJungshik Jang                for (int j = 0; j < HdmiConfig.ADDRESS_ALLOCATION_RETRY; ++j) {
1938e93c84739902f5adaa499b474f39e3c4807bc1cJungshik Jang                    if (!sendPollMessage(curAddress, curAddress, 1)) {
1948e93c84739902f5adaa499b474f39e3c4807bc1cJungshik Jang                        failedPollingCount++;
1958e93c84739902f5adaa499b474f39e3c4807bc1cJungshik Jang                    }
1968e93c84739902f5adaa499b474f39e3c4807bc1cJungshik Jang                }
1978e93c84739902f5adaa499b474f39e3c4807bc1cJungshik Jang
1988e93c84739902f5adaa499b474f39e3c4807bc1cJungshik Jang                // Pick logical address if failed ratio is more than a half of all retries.
1998e93c84739902f5adaa499b474f39e3c4807bc1cJungshik Jang                if (failedPollingCount * 2 >  HdmiConfig.ADDRESS_ALLOCATION_RETRY) {
200d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang                    logicalAddress = curAddress;
201d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang                    break;
2023f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang                }
2033f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang            }
204d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        }
2053f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang
206d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        final int assignedAddress = logicalAddress;
2072e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang        HdmiLogger.debug("New logical address for device [%d]: [preferred:%d, assigned:%d]",
2082e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang                        deviceType, preferredAddress, assignedAddress);
209d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        if (callback != null) {
210d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang            runOnServiceThread(new Runnable() {
2118e93c84739902f5adaa499b474f39e3c4807bc1cJungshik Jang                @Override
212d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang                public void run() {
213d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang                    callback.onAllocated(deviceType, assignedAddress);
2143f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang                }
215d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang            });
2163f74ab0ee0ec27a6be31cdb5a4258f4f25909ba8Jungshik Jang        }
217e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    }
218e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang
219d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    private static byte[] buildBody(int opcode, byte[] params) {
220d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        byte[] body = new byte[params.length + 1];
221d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        body[0] = (byte) opcode;
222d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        System.arraycopy(params, 0, body, 1, params.length);
223d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        return body;
224e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    }
2250792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
2267d9a843af83dbc75b1d170c743603198ede5a10fJungshik Jang
2270340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    HdmiPortInfo[] getPortInfos() {
2280340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        return nativeGetPortInfos(mNativePtr);
2290340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    }
2300340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
231a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang    /**
2327fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim     * Return the locally hosted logical device of a given type.
2337fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim     *
2347fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim     * @param deviceType logical device type
2357fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim     * @return {@link HdmiCecLocalDevice} instance if the instance of the type is available;
2367fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim     *          otherwise null.
2377fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim     */
2387fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim    HdmiCecLocalDevice getLocalDevice(int deviceType) {
2397fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        return mLocalDevices.get(deviceType);
2407fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim    }
2417fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim
2427fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim    /**
243a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang     * Add a new logical address to the device. Device's HW should be notified
244a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang     * when a new logical address is assigned to a device, so that it can accept
245a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang     * a command having available destinations.
246a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang     *
247a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang     * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
248a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang     *
249a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang     * @param newLogicalAddress a logical address to be added
250a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang     * @return 0 on success. Otherwise, returns negative value
251a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang     */
252a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
253a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang    int addLogicalAddress(int newLogicalAddress) {
25402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        assertRunOnServiceThread();
255c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        if (HdmiUtils.isValidAddress(newLogicalAddress)) {
256a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang            return nativeAddLogicalAddress(mNativePtr, newLogicalAddress);
257a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang        } else {
258a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang            return -1;
259a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang        }
260a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang    }
261a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang
262a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang    /**
263a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang     * Clear all logical addresses registered in the device.
264a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang     *
265a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang     * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
266a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang     */
267a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
268a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang    void clearLogicalAddress() {
26902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        assertRunOnServiceThread();
2707fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        for (int i = 0; i < mLocalDevices.size(); ++i) {
2717fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            mLocalDevices.valueAt(i).clearAddress();
2722918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim        }
273a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang        nativeClearLogicalAddress(mNativePtr);
274a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang    }
275a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang
2764fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    @ServiceThreadOnly
2774fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    void clearLocalDevices() {
2784fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        assertRunOnServiceThread();
2794fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        mLocalDevices.clear();
2804fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    }
2814fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
282a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang    /**
283a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang     * Return the physical address of the device.
284a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang     *
285a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang     * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
286a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang     *
287a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang     * @return CEC physical address of the device. The range of success address
288a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang     *         is between 0x0000 and 0xFFFF. If failed it returns -1
289a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang     */
290a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
291a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang    int getPhysicalAddress() {
29202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        assertRunOnServiceThread();
293a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang        return nativeGetPhysicalAddress(mNativePtr);
294a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang    }
295a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang
296a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang    /**
297a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang     * Return CEC version of the device.
298a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang     *
299a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang     * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
300a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang     */
301a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
302a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang    int getVersion() {
30302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        assertRunOnServiceThread();
304a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang        return nativeGetVersion(mNativePtr);
305a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang    }
306a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang
307a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang    /**
308a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang     * Return vendor id of the device.
309a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang     *
310a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang     * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
311a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang     */
312a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
313a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang    int getVendorId() {
31402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        assertRunOnServiceThread();
315a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang        return nativeGetVendorId(mNativePtr);
316a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang    }
317a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang
31802bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    /**
319c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim     * Set an option to CEC HAL.
320092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     *
321c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim     * @param flag key of option
322c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim     * @param value value of option
323092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     */
324a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
325092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    void setOption(int flag, int value) {
326092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        assertRunOnServiceThread();
327f17024873b10dabed069e502030dd85d6257c0c4Yuncheol Heo        HdmiLogger.debug("setOption: [flag:%d, value:%d]", flag, value);
328092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        nativeSetOption(mNativePtr, flag, value);
329092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
330092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
331092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    /**
332092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     * Configure ARC circuit in the hardware logic to start or stop the feature.
333092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     *
3341481a4282818939436f590d8c88aea2d19166b8eJinsuk Kim     * @param port ID of HDMI port to which AVR is connected
335092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     * @param enabled whether to enable/disable ARC
336092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     */
337a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
3381481a4282818939436f590d8c88aea2d19166b8eJinsuk Kim    void setAudioReturnChannel(int port, boolean enabled) {
339092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        assertRunOnServiceThread();
3401481a4282818939436f590d8c88aea2d19166b8eJinsuk Kim        nativeSetAudioReturnChannel(mNativePtr, port, enabled);
341092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
342092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
343092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    /**
344092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     * Return the connection status of the specified port
345092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     *
346092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     * @param port port number to check connection status
347092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     * @return true if connected; otherwise, return false
348092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     */
349a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
350092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    boolean isConnected(int port) {
351092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        assertRunOnServiceThread();
352092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return nativeIsConnected(mNativePtr, port);
353092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
354092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
355092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    /**
35602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * Poll all remote devices. It sends &lt;Polling Message&gt; to all remote
35702bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * devices.
35802bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     *
35902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
36002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     *
36102bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * @param callback an interface used to get a list of all remote devices' address
3621de514256fd3015cf45256f3198ab5472024af9bJungshik Jang     * @param sourceAddress a logical address of source device where sends polling message
3630f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang     * @param pickStrategy strategy how to pick polling candidates
36402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * @param retryCount the number of retry used to send polling message to remote devices
36502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     */
366a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
3671de514256fd3015cf45256f3198ab5472024af9bJungshik Jang    void pollDevices(DevicePollingCallback callback, int sourceAddress, int pickStrategy,
3681de514256fd3015cf45256f3198ab5472024af9bJungshik Jang            int retryCount) {
36902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        assertRunOnServiceThread();
37002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang
3710f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        // Extract polling candidates. No need to poll against local devices.
3720f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        List<Integer> pollingCandidates = pickPollCandidates(pickStrategy);
373b3ecb72af8cbbd4f6f8089d0dc22289f6e2588f6Jungshik Jang        ArrayList<Integer> allocated = new ArrayList<>();
374b3ecb72af8cbbd4f6f8089d0dc22289f6e2588f6Jungshik Jang        runDevicePolling(sourceAddress, pollingCandidates, retryCount, callback, allocated);
37502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    }
37602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang
377cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    /**
378cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang     * Return a list of all {@link HdmiCecLocalDevice}s.
379cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang     *
380cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang     * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
381cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang     */
382a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
383cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    List<HdmiCecLocalDevice> getLocalDeviceList() {
384cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        assertRunOnServiceThread();
38579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return HdmiUtils.sparseArrayToList(mLocalDevices);
386cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    }
387cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
3880f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    private List<Integer> pickPollCandidates(int pickStrategy) {
389c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        int strategy = pickStrategy & Constants.POLL_STRATEGY_MASK;
3900f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        Predicate<Integer> pickPredicate = null;
3910f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        switch (strategy) {
392c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.POLL_STRATEGY_SYSTEM_AUDIO:
3930f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang                pickPredicate = mSystemAudioAddressPredicate;
3940f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang                break;
395c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.POLL_STRATEGY_REMOTES_DEVICES:
3960f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            default:  // The default is POLL_STRATEGY_REMOTES_DEVICES.
3970f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang                pickPredicate = mRemoteDeviceAddressPredicate;
3980f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang                break;
3990f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        }
4000f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
401c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        int iterationStrategy = pickStrategy & Constants.POLL_ITERATION_STRATEGY_MASK;
402b3ecb72af8cbbd4f6f8089d0dc22289f6e2588f6Jungshik Jang        LinkedList<Integer> pollingCandidates = new LinkedList<>();
4030f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        switch (iterationStrategy) {
404c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.POLL_ITERATION_IN_ORDER:
405c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                for (int i = Constants.ADDR_TV; i <= Constants.ADDR_SPECIFIC_USE; ++i) {
4060f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang                    if (pickPredicate.apply(i)) {
4070f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang                        pollingCandidates.add(i);
4080f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang                    }
4090f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang                }
4100f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang                break;
411c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.POLL_ITERATION_REVERSE_ORDER:
4120f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            default:  // The default is reverse order.
413c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                for (int i = Constants.ADDR_SPECIFIC_USE; i >= Constants.ADDR_TV; --i) {
4140f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang                    if (pickPredicate.apply(i)) {
4150f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang                        pollingCandidates.add(i);
4160f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang                    }
4170f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang                }
4180f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang                break;
4190f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        }
4200f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        return pollingCandidates;
4210f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    }
4220f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
423a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
42402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    private boolean isAllocatedLocalDeviceAddress(int address) {
425a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
4267fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        for (int i = 0; i < mLocalDevices.size(); ++i) {
4277fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            if (mLocalDevices.valueAt(i).isAddressOf(address)) {
42802bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang                return true;
42902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang            }
43002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        }
43102bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        return false;
43202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    }
43302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang
434a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
4351de514256fd3015cf45256f3198ab5472024af9bJungshik Jang    private void runDevicePolling(final int sourceAddress,
4361de514256fd3015cf45256f3198ab5472024af9bJungshik Jang            final List<Integer> candidates, final int retryCount,
437b3ecb72af8cbbd4f6f8089d0dc22289f6e2588f6Jungshik Jang            final DevicePollingCallback callback, final List<Integer> allocated) {
43802bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        assertRunOnServiceThread();
439b3ecb72af8cbbd4f6f8089d0dc22289f6e2588f6Jungshik Jang        if (candidates.isEmpty()) {
440b3ecb72af8cbbd4f6f8089d0dc22289f6e2588f6Jungshik Jang            if (callback != null) {
441b3ecb72af8cbbd4f6f8089d0dc22289f6e2588f6Jungshik Jang                HdmiLogger.debug("[P]:AllocatedAddress=%s", allocated.toString());
442b3ecb72af8cbbd4f6f8089d0dc22289f6e2588f6Jungshik Jang                callback.onPollingFinished(allocated);
443b3ecb72af8cbbd4f6f8089d0dc22289f6e2588f6Jungshik Jang            }
444b3ecb72af8cbbd4f6f8089d0dc22289f6e2588f6Jungshik Jang            return;
445b3ecb72af8cbbd4f6f8089d0dc22289f6e2588f6Jungshik Jang        }
446b3ecb72af8cbbd4f6f8089d0dc22289f6e2588f6Jungshik Jang
447b3ecb72af8cbbd4f6f8089d0dc22289f6e2588f6Jungshik Jang        final Integer candidate = candidates.remove(0);
448b3ecb72af8cbbd4f6f8089d0dc22289f6e2588f6Jungshik Jang        // Proceed polling action for the next address once polling action for the
449b3ecb72af8cbbd4f6f8089d0dc22289f6e2588f6Jungshik Jang        // previous address is done.
45002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        runOnIoThread(new Runnable() {
45102bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang            @Override
45202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang            public void run() {
453b3ecb72af8cbbd4f6f8089d0dc22289f6e2588f6Jungshik Jang                if (sendPollMessage(sourceAddress, candidate, retryCount)) {
454b3ecb72af8cbbd4f6f8089d0dc22289f6e2588f6Jungshik Jang                    allocated.add(candidate);
45502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang                }
456b3ecb72af8cbbd4f6f8089d0dc22289f6e2588f6Jungshik Jang                runOnServiceThread(new Runnable() {
457b3ecb72af8cbbd4f6f8089d0dc22289f6e2588f6Jungshik Jang                    @Override
458b3ecb72af8cbbd4f6f8089d0dc22289f6e2588f6Jungshik Jang                    public void run() {
459b3ecb72af8cbbd4f6f8089d0dc22289f6e2588f6Jungshik Jang                        runDevicePolling(sourceAddress, candidates, retryCount, callback,
460b3ecb72af8cbbd4f6f8089d0dc22289f6e2588f6Jungshik Jang                                allocated);
461b3ecb72af8cbbd4f6f8089d0dc22289f6e2588f6Jungshik Jang                    }
462b3ecb72af8cbbd4f6f8089d0dc22289f6e2588f6Jungshik Jang                });
46302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang            }
46402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        });
46502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    }
46602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang
467a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @IoThreadOnly
4681de514256fd3015cf45256f3198ab5472024af9bJungshik Jang    private boolean sendPollMessage(int sourceAddress, int destinationAddress, int retryCount) {
46902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        assertRunOnIoThread();
47002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        for (int i = 0; i < retryCount; ++i) {
4711de514256fd3015cf45256f3198ab5472024af9bJungshik Jang            // <Polling Message> is a message which has empty body.
47202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang            // If sending <Polling Message> failed (NAK), it becomes
47302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang            // new logical address for the device because no device uses
47402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang            // it as logical address of the device.
4751de514256fd3015cf45256f3198ab5472024af9bJungshik Jang            if (nativeSendCecCommand(mNativePtr, sourceAddress, destinationAddress, EMPTY_BODY)
476c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                    == Constants.SEND_RESULT_SUCCESS) {
47702bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang                return true;
47802bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang            }
47902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        }
48002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        return false;
48102bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    }
48202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang
48302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    private void assertRunOnIoThread() {
48402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        if (Looper.myLooper() != mIoHandler.getLooper()) {
48502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang            throw new IllegalStateException("Should run on io thread.");
48602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        }
48702bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    }
48802bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang
48902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    private void assertRunOnServiceThread() {
49002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        if (Looper.myLooper() != mControlHandler.getLooper()) {
49102bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang            throw new IllegalStateException("Should run on service thread.");
49202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        }
49302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    }
49402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang
49502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    // Run a Runnable on IO thread.
49602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    // It should be careful to access member variables on IO thread because
49702bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    // it can be accessed from system thread as well.
498d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    private void runOnIoThread(Runnable runnable) {
499d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        mIoHandler.post(runnable);
500d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    }
501d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang
502d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    private void runOnServiceThread(Runnable runnable) {
503d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        mControlHandler.post(runnable);
504d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    }
505d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang
506f17024873b10dabed069e502030dd85d6257c0c4Yuncheol Heo    @ServiceThreadOnly
507f17024873b10dabed069e502030dd85d6257c0c4Yuncheol Heo    void flush(final Runnable runnable) {
508f17024873b10dabed069e502030dd85d6257c0c4Yuncheol Heo        assertRunOnServiceThread();
509f17024873b10dabed069e502030dd85d6257c0c4Yuncheol Heo        runOnIoThread(new Runnable() {
510f17024873b10dabed069e502030dd85d6257c0c4Yuncheol Heo            @Override
511f17024873b10dabed069e502030dd85d6257c0c4Yuncheol Heo            public void run() {
512f17024873b10dabed069e502030dd85d6257c0c4Yuncheol Heo                // This ensures the runnable for cleanup is performed after all the pending
513f17024873b10dabed069e502030dd85d6257c0c4Yuncheol Heo                // commands are processed by IO thread.
514f17024873b10dabed069e502030dd85d6257c0c4Yuncheol Heo                runOnServiceThread(runnable);
515f17024873b10dabed069e502030dd85d6257c0c4Yuncheol Heo            }
516f17024873b10dabed069e502030dd85d6257c0c4Yuncheol Heo        });
517f17024873b10dabed069e502030dd85d6257c0c4Yuncheol Heo    }
518f17024873b10dabed069e502030dd85d6257c0c4Yuncheol Heo
519a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    private boolean isAcceptableAddress(int address) {
5202918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim        // Can access command targeting devices available in local device or broadcast command.
521c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        if (address == Constants.ADDR_BROADCAST) {
5222918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim            return true;
5232918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim        }
52402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        return isAllocatedLocalDeviceAddress(address);
525a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    }
526a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang
527a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
528e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    private void onReceiveCommand(HdmiCecMessage message) {
52902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        assertRunOnServiceThread();
5306aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo        if (isAcceptableAddress(message.getDestination()) && mService.handleCecCommand(message)) {
531a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang            return;
532a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        }
5336aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo        // Not handled message, so we will reply it with <Feature Abort>.
5346aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo        maySendFeatureAbortCommand(message, Constants.ABORT_UNRECOGNIZED_OPCODE);
5356aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo    }
5366aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo
5376aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo    @ServiceThreadOnly
5386aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo    void maySendFeatureAbortCommand(HdmiCecMessage message, int reason) {
5396aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo        assertRunOnServiceThread();
5406aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo        // Swap the source and the destination.
5416aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo        int src = message.getDestination();
5426aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo        int dest = message.getSource();
5436aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo        if (src == Constants.ADDR_BROADCAST || dest == Constants.ADDR_UNREGISTERED) {
5446aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo            // Don't reply <Feature Abort> from the unregistered devices or for the broadcasted
5456aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo            // messages. See CEC 12.2 Protocol General Rules for detail.
5461827fdcbf6a53378f05620790e4798201341097bJungshik Jang            return;
5473a2f74373a6892ca5c11cd19b4b662c069634717Jinsuk Kim        }
5486aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo        int originalOpcode = message.getOpcode();
5496aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo        if (originalOpcode == Constants.MESSAGE_FEATURE_ABORT) {
5501827fdcbf6a53378f05620790e4798201341097bJungshik Jang            return;
5511827fdcbf6a53378f05620790e4798201341097bJungshik Jang        }
5526aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo        sendCommand(
5536aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo                HdmiCecMessageBuilder.buildFeatureAbortCommand(src, dest, originalOpcode, reason));
554a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    }
555e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang
556a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
5572918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim    void sendCommand(HdmiCecMessage cecMessage) {
558a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
5592918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim        sendCommand(cecMessage, null);
5602918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim    }
5612918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim
562a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
563d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    void sendCommand(final HdmiCecMessage cecMessage,
564d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang            final HdmiControlService.SendMessageCallback callback) {
565a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
566d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        runOnIoThread(new Runnable() {
567d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang            @Override
568d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang            public void run() {
5692e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang                HdmiLogger.debug("[S]:" + cecMessage);
570d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang                byte[] body = buildBody(cecMessage.getOpcode(), cecMessage.getParams());
5718ed86c467afa107f7aafacb85ca64c979cf56dc2Jungshik Jang                int i = 0;
5728ed86c467afa107f7aafacb85ca64c979cf56dc2Jungshik Jang                int errorCode = Constants.SEND_RESULT_SUCCESS;
5738ed86c467afa107f7aafacb85ca64c979cf56dc2Jungshik Jang                do {
5748ed86c467afa107f7aafacb85ca64c979cf56dc2Jungshik Jang                    errorCode = nativeSendCecCommand(mNativePtr, cecMessage.getSource(),
5758ed86c467afa107f7aafacb85ca64c979cf56dc2Jungshik Jang                            cecMessage.getDestination(), body);
5768ed86c467afa107f7aafacb85ca64c979cf56dc2Jungshik Jang                    if (errorCode == Constants.SEND_RESULT_SUCCESS) {
5778ed86c467afa107f7aafacb85ca64c979cf56dc2Jungshik Jang                        break;
5788ed86c467afa107f7aafacb85ca64c979cf56dc2Jungshik Jang                    }
5798ed86c467afa107f7aafacb85ca64c979cf56dc2Jungshik Jang                } while (i++ < HdmiConfig.RETRANSMISSION_COUNT);
5808ed86c467afa107f7aafacb85ca64c979cf56dc2Jungshik Jang
5818ed86c467afa107f7aafacb85ca64c979cf56dc2Jungshik Jang                final int finalError = errorCode;
5828ed86c467afa107f7aafacb85ca64c979cf56dc2Jungshik Jang                if (finalError != Constants.SEND_RESULT_SUCCESS) {
583ece603b7955938d6001c376f351ca0a2219330acYuncheol Heo                    Slog.w(TAG, "Failed to send " + cecMessage);
584ece603b7955938d6001c376f351ca0a2219330acYuncheol Heo                }
585d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang                if (callback != null) {
586d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang                    runOnServiceThread(new Runnable() {
587d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang                        @Override
588d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang                        public void run() {
5898ed86c467afa107f7aafacb85ca64c979cf56dc2Jungshik Jang                            callback.onSendCompleted(finalError);
590d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang                        }
591d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang                    });
592d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang                }
593d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang            }
594d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        });
595e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    }
596e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang
597e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    /**
598e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * Called by native when incoming CEC message arrived.
599e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     */
600a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
601e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    private void handleIncomingCecCommand(int srcAddress, int dstAddress, byte[] body) {
6024085d0ef62554c7e139b82bca0f97161bb159f8cJungshik Jang        assertRunOnServiceThread();
603c94ac5cffc982898bc4f7a5d97d8fad5520ff444Jungshik Jang        HdmiCecMessage command = HdmiCecMessageBuilder.of(srcAddress, dstAddress, body);
6042e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang        HdmiLogger.debug("[R]:" + command);
605c94ac5cffc982898bc4f7a5d97d8fad5520ff444Jungshik Jang        onReceiveCommand(command);
606e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    }
607e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang
6080792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    /**
609e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * Called by native when a hotplug event issues.
6100792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang     */
61142230728b8212738c2351939c5577730f05a58deJungshik Jang    @ServiceThreadOnly
61242230728b8212738c2351939c5577730f05a58deJungshik Jang    private void handleHotplug(int port, boolean connected) {
61342230728b8212738c2351939c5577730f05a58deJungshik Jang        assertRunOnServiceThread();
6142e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang        HdmiLogger.debug("Hotplug event:[port:%d, connected:%b]", port, connected);
61542230728b8212738c2351939c5577730f05a58deJungshik Jang        mService.onHotplug(port, connected);
6160792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    }
6170792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
618959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo    void dump(final IndentingPrintWriter pw) {
619959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo        for (int i = 0; i < mLocalDevices.size(); ++i) {
620959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo            pw.println("HdmiCecLocalDevice #" + i + ":");
621959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo            pw.increaseIndent();
622959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo            mLocalDevices.valueAt(i).dump(pw);
623959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo            pw.decreaseIndent();
624959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo        }
625959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo    }
626959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo
6274085d0ef62554c7e139b82bca0f97161bb159f8cJungshik Jang    private static native long nativeInit(HdmiCecController handler, MessageQueue messageQueue);
628a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang    private static native int nativeSendCecCommand(long controllerPtr, int srcAddress,
629e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang            int dstAddress, byte[] body);
630a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang    private static native int nativeAddLogicalAddress(long controllerPtr, int logicalAddress);
631a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang    private static native void nativeClearLogicalAddress(long controllerPtr);
632a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang    private static native int nativeGetPhysicalAddress(long controllerPtr);
633a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang    private static native int nativeGetVersion(long controllerPtr);
634a9095ba488ea18aeafaf9c3a8258bf9f459bda71Jungshik Jang    private static native int nativeGetVendorId(long controllerPtr);
6350340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    private static native HdmiPortInfo[] nativeGetPortInfos(long controllerPtr);
636092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    private static native void nativeSetOption(long controllerPtr, int flag, int value);
6371481a4282818939436f590d8c88aea2d19166b8eJinsuk Kim    private static native void nativeSetAudioReturnChannel(long controllerPtr, int port, boolean flag);
638092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    private static native boolean nativeIsConnected(long controllerPtr, int port);
6390792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang}
640