HdmiControlService.java revision 79c58a4b97f27ede6a1b680d2fece9c2a0edf7b7
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
190792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport android.annotation.Nullable;
200792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport android.content.Context;
21a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jangimport android.hardware.hdmi.HdmiCec;
22c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kimimport android.hardware.hdmi.HdmiCecDeviceInfo;
23c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kimimport android.hardware.hdmi.HdmiCecMessage;
2460cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jangimport android.hardware.hdmi.HdmiHotplugEvent;
250340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kimimport android.hardware.hdmi.HdmiPortInfo;
26d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jangimport android.hardware.hdmi.IHdmiControlCallback;
27d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jangimport android.hardware.hdmi.IHdmiControlService;
28d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jangimport android.hardware.hdmi.IHdmiHotplugEventListener;
2942c9800f4f3acef10d19dca39e8b739546407c04Jungshik Jangimport android.os.Build;
3067ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jangimport android.os.Handler;
310792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport android.os.HandlerThread;
3278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kimimport android.os.IBinder;
33e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jangimport android.os.Looper;
3478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kimimport android.os.RemoteException;
350792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport android.util.Slog;
363ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jangimport android.util.SparseArray;
378b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jangimport android.util.SparseIntArray;
3878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
390792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport com.android.server.SystemService;
403ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jangimport com.android.server.hdmi.HdmiCecController.AllocateAddressCallback;
410792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
4278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kimimport java.util.ArrayList;
430340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kimimport java.util.Collections;
4402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jangimport java.util.List;
45a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang
460792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang/**
470792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * Provides a service for sending and processing HDMI control messages,
480792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * HDMI-CEC and MHL control command, and providing the information on both standard.
490792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang */
500792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangpublic final class HdmiControlService extends SystemService {
510792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private static final String TAG = "HdmiControlService";
520792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
5378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // TODO: Rename the permission to HDMI_CONTROL.
5478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private static final String PERMISSION = "android.permission.HDMI_CEC";
5578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
56ece603b7955938d6001c376f351ca0a2219330acYuncheol Heo    static final int SEND_RESULT_SUCCESS = 0;
57ece603b7955938d6001c376f351ca0a2219330acYuncheol Heo    static final int SEND_RESULT_NAK = -1;
58ece603b7955938d6001c376f351ca0a2219330acYuncheol Heo    static final int SEND_RESULT_FAILURE = -2;
59ece603b7955938d6001c376f351ca0a2219330acYuncheol Heo
600f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    static final int POLL_STRATEGY_MASK = 0x3;  // first and second bit.
610f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    static final int POLL_STRATEGY_REMOTES_DEVICES = 0x1;
620f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    static final int POLL_STRATEGY_SYSTEM_AUDIO = 0x2;
630f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
640f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    static final int POLL_ITERATION_STRATEGY_MASK = 0x30000;  // first and second bit.
650f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    static final int POLL_ITERATION_IN_ORDER = 0x10000;
660f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    static final int POLL_ITERATION_REVERSE_ORDER = 0x20000;
670f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
68d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    /**
69d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang     * Interface to report send result.
70d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang     */
71d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    interface SendMessageCallback {
72d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        /**
73d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang         * Called when {@link HdmiControlService#sendCecCommand} is completed.
74d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang         *
75ece603b7955938d6001c376f351ca0a2219330acYuncheol Heo         * @param error result of send request.
76ece603b7955938d6001c376f351ca0a2219330acYuncheol Heo         * @see {@link #SEND_RESULT_SUCCESS}
77ece603b7955938d6001c376f351ca0a2219330acYuncheol Heo         * @see {@link #SEND_RESULT_NAK}
78ece603b7955938d6001c376f351ca0a2219330acYuncheol Heo         * @see {@link #SEND_RESULT_FAILURE}
79d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang         */
80d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        void onSendCompleted(int error);
81d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    }
82d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang
8302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    /**
8402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * Interface to get a list of available logical devices.
8502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     */
8602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    interface DevicePollingCallback {
8702bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        /**
8802bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         * Called when device polling is finished.
8902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         *
9002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         * @param ackedAddress a list of logical addresses of available devices
9102bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         */
9202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        void onPollingFinished(List<Integer> ackedAddress);
9302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    }
9402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang
950792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // A thread to handle synchronous IO of CEC and MHL control service.
960792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // Since all of CEC and MHL HAL interfaces processed in short time (< 200ms)
970792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // and sparse call it shares a thread to handle IO operations.
980792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private final HandlerThread mIoThread = new HandlerThread("Hdmi Control Io Thread");
990792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
10078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // Used to synchronize the access to the service.
10178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final Object mLock = new Object();
10278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
1030340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // Type of logical devices hosted in the system. Stored in the unmodifiable list.
1040340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    private final List<Integer> mLocalDevices;
10578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
10678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // List of listeners registered by callers that want to get notified of
10778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // hotplug events.
10878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final ArrayList<IHdmiHotplugEventListener> mHotplugEventListeners = new ArrayList<>();
10978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
11078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // List of records for hotplug event listener to handle the the caller killed in action.
11178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final ArrayList<HotplugEventListenerRecord> mHotplugEventListenerRecords =
11278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            new ArrayList<>();
11378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
1140340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // Handler running on service thread. It's used to run a task in service thread.
1150340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    private final Handler mHandler = new Handler();
1160340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
1170792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    @Nullable
1180792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private HdmiCecController mCecController;
1190792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
1200792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    @Nullable
1210792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private HdmiMhlController mMhlController;
1220792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
1230340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // HDMI port information. Stored in the unmodifiable list to keep the static information
1240340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // from being modified.
1250340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    private List<HdmiPortInfo> mPortInfo;
1260340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
1270792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    public HdmiControlService(Context context) {
1280792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        super(context);
1290340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        mLocalDevices = HdmiUtils.asImmutableList(getContext().getResources().getIntArray(
1300340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                com.android.internal.R.array.config_hdmiCecLogicalDeviceType));
1310792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    }
1320792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
1330792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    @Override
1340792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    public void onStart() {
1352f51aec689226e259d08bf04d838251f249572e3Jungshik Jang        mIoThread.start();
136e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang        mCecController = HdmiCecController.create(this);
1378b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jang
1383ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        if (mCecController != null) {
1393ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            initializeLocalDevices(mLocalDevices);
140a8a5e50c6f9ba3ae0ff59eda76354e93515d6f8fJinsuk Kim        } else {
1410792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang            Slog.i(TAG, "Device does not support HDMI-CEC.");
1420792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        }
1430792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
144e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang        mMhlController = HdmiMhlController.create(this);
1450792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        if (mMhlController == null) {
1460792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang            Slog.i(TAG, "Device does not support MHL-control.");
1470792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        }
1480340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        mPortInfo = initPortInfo();
1498692fc68a413c7aca75f094bb02bcbabcc277c73Jinsuk Kim        publishBinderService(Context.HDMI_CONTROL_SERVICE, new BinderService());
15063a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
15163a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        // TODO: Read the preference for SystemAudioMode and initialize mSystemAudioMode and
15263a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        // start to monitor the preference value and invoke SystemAudioActionFromTv if needed.
1530792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    }
154e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang
1550340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    private void initializeLocalDevices(final List<Integer> deviceTypes) {
1563ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        // A container for [Logical Address, Local device info].
1573ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        final SparseArray<HdmiCecLocalDevice> devices = new SparseArray<>();
1583ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        final SparseIntArray finished = new SparseIntArray();
1593ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        for (int type : deviceTypes) {
1603ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            final HdmiCecLocalDevice localDevice = HdmiCecLocalDevice.create(this, type);
1613ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            localDevice.init();
1623ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            mCecController.allocateLogicalAddress(type,
1633ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    localDevice.getPreferredAddress(), new AllocateAddressCallback() {
1643ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                @Override
1653ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                public void onAllocated(int deviceType, int logicalAddress) {
1663ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    if (logicalAddress == HdmiCec.ADDR_UNREGISTERED) {
1673ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        Slog.e(TAG, "Failed to allocate address:[device_type:" + deviceType + "]");
1683ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    } else {
1693ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        HdmiCecDeviceInfo deviceInfo = createDeviceInfo(logicalAddress, deviceType);
1703ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        localDevice.setDeviceInfo(deviceInfo);
1713ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        mCecController.addLocalDevice(deviceType, localDevice);
1723ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        mCecController.addLogicalAddress(logicalAddress);
1733ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        devices.append(logicalAddress, localDevice);
1743ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    }
1753ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    finished.append(deviceType, logicalAddress);
1763ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
1773ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    // Once finish address allocation for all devices, notify
1783ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    // it to each device.
1790340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                    if (deviceTypes.size() == finished.size()) {
1803ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        notifyAddressAllocated(devices);
1813ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    }
1823ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                }
1833ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            });
1843ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        }
1853ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
1863ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
1873ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    private void notifyAddressAllocated(SparseArray<HdmiCecLocalDevice> devices) {
1883ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        for (int i = 0; i < devices.size(); ++i) {
1893ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            int address = devices.keyAt(i);
1903ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            HdmiCecLocalDevice device = devices.valueAt(i);
1913ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            device.onAddressAllocated(address);
1923ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        }
1933ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
1943ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
1950340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // Initialize HDMI port information. Combine the information from CEC and MHL HAL and
1960340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // keep them in one place.
1970340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    private List<HdmiPortInfo> initPortInfo() {
1980340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        HdmiPortInfo[] cecPortInfo = null;
1990340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
2000340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        // CEC HAL provides majority of the info while MHL does only MHL support flag for
2010340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        // each port. Return empty array if CEC HAL didn't provide the info.
2020340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        if (mCecController != null) {
2030340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            cecPortInfo = mCecController.getPortInfos();
2040340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        }
2050340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        if (cecPortInfo == null) {
2060340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            return Collections.emptyList();
2070340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        }
2080340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
2090340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        HdmiPortInfo[] mhlPortInfo = new HdmiPortInfo[0];
2100340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        if (mMhlController != null) {
2110340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            // TODO: Implement plumbing logic to get MHL port information.
2120340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            // mhlPortInfo = mMhlController.getPortInfos();
2130340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        }
2140340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
2150340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        // Use the id (port number) to find the matched info between CEC and MHL to combine them
2160340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        // into one. Leave the field `mhlSupported` to false if matched MHL entry is not found.
2170340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        ArrayList<HdmiPortInfo> result = new ArrayList<>(cecPortInfo.length);
2180340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        for (int i = 0; i < cecPortInfo.length; ++i) {
2190340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            HdmiPortInfo cec = cecPortInfo[i];
2200340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            int id = cec.getId();
2210340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            boolean mhlInfoFound = false;
2220340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            for (HdmiPortInfo mhl : mhlPortInfo) {
2230340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                if (id == mhl.getId()) {
2240340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                    result.add(new HdmiPortInfo(id, cec.getType(), cec.getAddress(),
2250340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                            cec.isCecSupported(), mhl.isMhlSupported(), cec.isArcSupported()));
2260340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                    mhlInfoFound = true;
2270340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                    break;
2280340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                }
2290340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            }
2300340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            if (!mhlInfoFound) {
2310340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                result.add(cec);
2320340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            }
2330340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        }
2340340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
2350340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        return Collections.unmodifiableList(result);
2360340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    }
2370340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
2380340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    /**
2390340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     * Returns HDMI port information for the given port id.
2400340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     *
2410340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     * @param portId HDMI port id
2420340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     * @return {@link HdmiPortInfo} for the given port
2430340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     */
2440340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    HdmiPortInfo getPortInfo(int portId) {
2450340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        // mPortInfo is an unmodifiable list and the only reference to its inner list.
2460340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        // No lock is necessary.
2470340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        for (HdmiPortInfo info : mPortInfo) {
2480340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            if (portId == info.getId()) {
2490340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                return info;
2500340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            }
2510340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        }
2520340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        return null;
2530340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    }
2540340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
255e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    /**
256401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * Returns the routing path (physical address) of the HDMI port for the given
257401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * port id.
258401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     */
259401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    int portIdToPath(int portId) {
260401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        HdmiPortInfo portInfo = getPortInfo(portId);
261401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        if (portInfo == null) {
262401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim            Slog.e(TAG, "Cannot find the port info: " + portId);
26360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            return HdmiConstants.INVALID_PHYSICAL_ADDRESS;
264401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        }
265401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        return portInfo.getAddress();
266401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    }
267401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim
268401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    /**
269401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * Returns the id of HDMI port located at the top of the hierarchy of
270401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * the specified routing path. For the routing path 0x1220 (1.2.2.0), for instance,
271401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * the port id to be returned is the ID associated with the port address
272401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * 0x1000 (1.0.0.0) which is the topmost path of the given routing path.
273401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     */
274401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    int pathToPortId(int path) {
275401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        int portAddress = path & HdmiConstants.ROUTING_PATH_TOP_MASK;
276401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        for (HdmiPortInfo info : mPortInfo) {
277401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim            if (portAddress == info.getAddress()) {
278401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim                return info.getId();
279401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim            }
280401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        }
28160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        return HdmiConstants.INVALID_PORT_ID;
282401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    }
283401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim
284401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    /**
285e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * Returns {@link Looper} for IO operation.
286e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     *
287e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * <p>Declared as package-private.
288e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     */
289e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    Looper getIoLooper() {
290e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang        return mIoThread.getLooper();
291e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    }
292e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang
293e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    /**
294e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * Returns {@link Looper} of main thread. Use this {@link Looper} instance
295e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * for tasks that are running on main service thread.
296e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     *
297e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * <p>Declared as package-private.
298e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     */
299e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    Looper getServiceLooper() {
30067ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        return mHandler.getLooper();
301e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    }
302c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim
303c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim    /**
3043ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     * Returns physical address of the device.
3053ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     */
3063ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    int getPhysicalAddress() {
3073ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        return mCecController.getPhysicalAddress();
3083ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
3093ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
3103ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    /**
3113ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     * Returns vendor id of CEC service.
3123ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     */
3133ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    int getVendorId() {
3143ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        return mCecController.getVendorId();
3153ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
3163ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
317a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim    HdmiCecDeviceInfo getDeviceInfo(int logicalAddress) {
3180340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        assertRunOnServiceThread();
31979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        HdmiCecLocalDeviceTv tv = tv();
32079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        if (tv == null) {
32179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            return null;
32279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        }
32379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return tv.getDeviceInfo(logicalAddress);
324a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim    }
325a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim
3263ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    /**
327092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     * Returns version of CEC.
328092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     */
329092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    int getCecVersion() {
330092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return mCecController.getVersion();
331092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
332092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
333092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    /**
33460cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang     * Whether a device of the specified physical address is connected to ARC enabled port.
33560cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang     */
33660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    boolean isConnectedToArcPort(int physicalAddress) {
33760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        for (HdmiPortInfo portInfo : mPortInfo) {
33860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            if (hasSameTopPort(portInfo.getAddress(), physicalAddress)
33960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang                    && portInfo.isArcSupported()) {
34060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang                return true;
34160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            }
34260cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
34360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        return false;
34460cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
34560cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
34679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    void runOnServiceThread(Runnable runnable) {
34767ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        mHandler.post(runnable);
34867ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    }
34967ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang
35063a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    void runOnServiceThreadAtFrontOfQueue(Runnable runnable) {
35163a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        mHandler.postAtFrontOfQueue(runnable);
35263a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    }
35363a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
35463a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    private void assertRunOnServiceThread() {
35563a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        if (Looper.myLooper() != mHandler.getLooper()) {
35663a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo            throw new IllegalStateException("Should run on service thread.");
35763a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        }
35863a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    }
35963a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
36067ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    /**
361c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     * Transmit a CEC command to CEC bus.
362c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     *
363c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     * @param command CEC command to send out
364d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang     * @param callback interface used to the result of send command
365c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     */
366d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    void sendCecCommand(HdmiCecMessage command, @Nullable SendMessageCallback callback) {
367d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        mCecController.sendCommand(command, callback);
368d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    }
369d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang
370d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    void sendCecCommand(HdmiCecMessage command) {
371d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        mCecController.sendCommand(command, null);
372c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim    }
373c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim
374a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    boolean handleCecCommand(HdmiCecMessage message) {
375092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return dispatchMessageToLocalDevice(message);
376092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
377092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
37879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    void setAudioReturnChannel(boolean enabled) {
37979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        mCecController.setAudioReturnChannel(enabled);
38060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
38160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
382092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    private boolean dispatchMessageToLocalDevice(HdmiCecMessage message) {
383092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
38479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            if (device.dispatchMessage(message)
38579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang                    && message.getDestination() != HdmiCec.ADDR_BROADCAST) {
386092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang                return true;
387092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang            }
388092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        }
38960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
39060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        Slog.w(TAG, "Unhandled cec command:" + message);
391092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return false;
392a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    }
393a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang
39467ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    /**
39567ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     * Called when a new hotplug event is issued.
39667ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     *
3978b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jang     * @param portNo hdmi port number where hot plug event issued.
39867ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     * @param connected whether to be plugged in or not
39967ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     */
40067ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    void onHotplug(int portNo, boolean connected) {
40160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        assertRunOnServiceThread();
40279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
40379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
40479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            device.onHotplug(portNo, connected);
40560cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
40660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
40760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        announceHotplugEvent(portNo, connected);
40867ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    }
40967ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang
41002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    /**
41102bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * Poll all remote devices. It sends &lt;Polling Message&gt; to all remote
41202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * devices.
41302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     *
41402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * @param callback an interface used to get a list of all remote devices' address
4150f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang     * @param pickStrategy strategy how to pick polling candidates
41602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * @param retryCount the number of retry used to send polling message to remote devices
4170f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang     * @throw IllegalArgumentException if {@code pickStrategy} is invalid value
41802bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     */
4190f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    void pollDevices(DevicePollingCallback callback, int pickStrategy, int retryCount) {
4200f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        mCecController.pollDevices(callback, checkPollStrategy(pickStrategy), retryCount);
4210f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    }
4220f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
4230f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    private int checkPollStrategy(int pickStrategy) {
4240f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        int strategy = pickStrategy & POLL_STRATEGY_MASK;
4250f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        if (strategy == 0) {
4260f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            throw new IllegalArgumentException("Invalid poll strategy:" + pickStrategy);
4270f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        }
4280f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        int iterationStrategy = pickStrategy & POLL_ITERATION_STRATEGY_MASK;
4290f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        if (iterationStrategy == 0) {
4300f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            throw new IllegalArgumentException("Invalid iteration strategy:" + pickStrategy);
4310f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        }
4320f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        return strategy | iterationStrategy;
43302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    }
43402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang
43560cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    List<HdmiCecLocalDevice> getAllLocalDevices() {
43660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        assertRunOnServiceThread();
43760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        return mCecController.getLocalDeviceList();
43860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
43960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
44079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    Object getServiceLock() {
44179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return mLock;
44279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
44379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
44479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    void setAudioStatus(boolean mute, int volume) {
44579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        // TODO: Hook up with AudioManager.
4463ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
4473ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
4483ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    private HdmiCecDeviceInfo createDeviceInfo(int logicalAddress, int deviceType) {
44942c9800f4f3acef10d19dca39e8b739546407c04Jungshik Jang        // TODO: find better name instead of model name.
45042c9800f4f3acef10d19dca39e8b739546407c04Jungshik Jang        String displayName = Build.MODEL;
4513ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        return new HdmiCecDeviceInfo(logicalAddress,
4523ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                getPhysicalAddress(), deviceType, getVendorId(), displayName);
4533ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
4543ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
45578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // Record class that monitors the event of the caller of being killed. Used to clean up
45678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // the listener list and record list accordingly.
45778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final class HotplugEventListenerRecord implements IBinder.DeathRecipient {
45878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        private final IHdmiHotplugEventListener mListener;
45978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
46078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        public HotplugEventListenerRecord(IHdmiHotplugEventListener listener) {
46178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mListener = listener;
46278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
46378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
46478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
46578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        public void binderDied() {
46678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            synchronized (mLock) {
46778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                mHotplugEventListenerRecords.remove(this);
46878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                mHotplugEventListeners.remove(mListener);
46978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            }
47078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
47178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
47278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
47378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private void enforceAccessPermission() {
47478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        getContext().enforceCallingOrSelfPermission(PERMISSION, TAG);
47578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
47678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
47778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final class BinderService extends IHdmiControlService.Stub {
47878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
47978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        public int[] getSupportedTypes() {
48078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
4810340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            // mLocalDevices is an unmodifiable list - no lock necesary.
4820340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            int[] localDevices = new int[mLocalDevices.size()];
4830340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            for (int i = 0; i < localDevices.length; ++i) {
4840340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                localDevices[i] = mLocalDevices.get(i);
48578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            }
4860340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            return localDevices;
48778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
48878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
48978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
490a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim        public void deviceSelect(final int logicalAddress, final IHdmiControlCallback callback) {
491a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim            enforceAccessPermission();
492a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim            runOnServiceThread(new Runnable() {
493a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                @Override
494a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                public void run() {
49579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
496a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                    if (tv == null) {
497a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                        Slog.w(TAG, "Local playback device not available");
498a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                        invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE);
499a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                        return;
500a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                    }
501a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                    tv.deviceSelect(logicalAddress, callback);
502a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                }
503a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim            });
504a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim        }
505a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim
506a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim        @Override
5077fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void oneTouchPlay(final IHdmiControlCallback callback) {
50878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
5097fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
5107fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
5117fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
5127fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.oneTouchPlay(callback);
5137fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
5147fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
51578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
51678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
51778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
5187fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void queryDisplayStatus(final IHdmiControlCallback callback) {
51978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
5207fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
5217fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
5227fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
5237fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.queryDisplayStatus(callback);
5247fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
5257fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
52678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
52778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
52878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
5297fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void addHotplugEventListener(final IHdmiHotplugEventListener listener) {
53078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
5317fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
5327fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
5337fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
5347fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.addHotplugEventListener(listener);
5357fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
5367fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
53778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
53878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
53978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
5407fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void removeHotplugEventListener(final IHdmiHotplugEventListener listener) {
54178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
5427fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
5437fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
5447fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
5457fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.removeHotplugEventListener(listener);
5467fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
5477fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
54878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
54978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
55078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
55179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private void oneTouchPlay(final IHdmiControlCallback callback) {
55279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
55379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        HdmiCecLocalDevicePlayback source = playback();
5547fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        if (source == null) {
5557fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.w(TAG, "Local playback device not available");
5567fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE);
5577fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            return;
5587fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
55979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        source.oneTouchPlay(callback);
56078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
56178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
56279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private void queryDisplayStatus(final IHdmiControlCallback callback) {
56379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
56479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        HdmiCecLocalDevicePlayback source = playback();
5657fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        if (source == null) {
5667fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.w(TAG, "Local playback device not available");
5677fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE);
5687fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            return;
5697fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
57079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        source.queryDisplayStatus(callback);
57178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
57278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
57378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private void addHotplugEventListener(IHdmiHotplugEventListener listener) {
57478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        HotplugEventListenerRecord record = new HotplugEventListenerRecord(listener);
57578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        try {
57678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            listener.asBinder().linkToDeath(record, 0);
57778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        } catch (RemoteException e) {
57878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            Slog.w(TAG, "Listener already died");
57978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            return;
58078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
58178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        synchronized (mLock) {
58278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mHotplugEventListenerRecords.add(record);
58378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mHotplugEventListeners.add(listener);
58478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
58578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
58678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
58778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private void removeHotplugEventListener(IHdmiHotplugEventListener listener) {
58878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        synchronized (mLock) {
58978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            for (HotplugEventListenerRecord record : mHotplugEventListenerRecords) {
59078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                if (record.mListener.asBinder() == listener.asBinder()) {
59178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                    listener.asBinder().unlinkToDeath(record, 0);
59278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                    mHotplugEventListenerRecords.remove(record);
59378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                    break;
59478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                }
59578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            }
59678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mHotplugEventListeners.remove(listener);
59778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
59878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
5997fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim
6007fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim    private void invokeCallback(IHdmiControlCallback callback, int result) {
6017fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        try {
6027fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            callback.onComplete(result);
6037fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        } catch (RemoteException e) {
6047fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.e(TAG, "Invoking callback failed:" + e);
6057fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
6067fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim    }
60763a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
60860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    private void announceHotplugEvent(int portNo, boolean connected) {
60960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        HdmiHotplugEvent event = new HdmiHotplugEvent(portNo, connected);
61060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        synchronized (mLock) {
61160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            for (IHdmiHotplugEventListener listener : mHotplugEventListeners) {
61260cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang                invokeHotplugEventListener(listener, event);
61360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            }
61460cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
61560cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
61660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
61760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    private void invokeHotplugEventListener(IHdmiHotplugEventListener listener,
61860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            HdmiHotplugEvent event) {
61960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        try {
62060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            listener.onReceived(event);
62160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        } catch (RemoteException e) {
62260cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            Slog.e(TAG, "Failed to report hotplug event:" + event.toString(), e);
62360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
624e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang    }
625e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang
62660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    private static boolean hasSameTopPort(int path1, int path2) {
62760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        return (path1 & HdmiConstants.ROUTING_PATH_TOP_MASK)
62860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang                == (path2 & HdmiConstants.ROUTING_PATH_TOP_MASK);
62960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
63060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
63179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private HdmiCecLocalDeviceTv tv() {
63279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return (HdmiCecLocalDeviceTv) mCecController.getLocalDevice(HdmiCec.DEVICE_TV);
63379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
63479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
63579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private HdmiCecLocalDevicePlayback playback() {
63679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return (HdmiCecLocalDevicePlayback) mCecController.getLocalDevice(HdmiCec.DEVICE_PLAYBACK);
63760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
6380792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang}
639